Back to index

courier  0.68.2
deliver.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2009 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 #include      <stdio.h>
00010 #include      <stdlib.h>
00011 #include      <string.h>
00012 #include      <signal.h>
00013 #include      <ctype.h>
00014 #include      <sys/types.h>
00015 #include      <sys/stat.h>
00016 #if HAVE_SYS_FILE_H
00017 #include      <sys/file.h>
00018 #endif
00019 #if HAVE_FCNTL_H
00020 #include      <fcntl.h>
00021 #endif
00022 #include      <errno.h>
00023 #include      <pwd.h>
00024 #if TIME_WITH_SYS_TIME
00025 #include      <sys/time.h>
00026 #include      <time.h>
00027 #else
00028 #if HAVE_SYS_TIME_H
00029 #include      <sys/time.h>
00030 #else
00031 #include      <time.h>
00032 #endif
00033 #endif
00034 #if HAVE_UNISTD_H
00035 #include      <unistd.h>
00036 #endif
00037 #if HAVE_SYS_WAIT_H
00038 #include      <sys/wait.h>
00039 #endif
00040 #include      <courier.h>
00041 #if HAVE_SYSEXITS_H
00042 #include      <sysexits.h>
00043 #else
00044 #define       EX_SOFTWARE   70
00045 #define       EX_TEMPFAIL   75
00046 #define       EX_NOUSER     67
00047 #define       EX_NOPERM     77
00048 #endif
00049 
00050 #include      "rfc822.h"
00051 #include      "maildir/maildircreate.h"
00052 #include      "maildir/maildirquota.h"
00053 #include      "liblock/config.h"
00054 #include      "liblock/liblock.h"
00055 #include      "liblock/mail.h"
00056 
00057 
00058 extern char *local_dotcourier(const char *, const char *, const char **);
00059 extern char *local_extension();
00060 
00061 static int chkdelto(FILE *, const char *);
00062 static void dodel(const char *, const char *, FILE *, char *,
00063        const char *, const char *, const char *, const char *, const char *,
00064        const char *, int);
00065 
00066 int main(int argc, char **argv)
00067 {
00068 const char *extension;
00069 const char *receipient;
00070 const char *sender;
00071 const char *defaultext;
00072 const char *quota;
00073 const char *defaultmail;
00074 char *extname;
00075 char *ctlfile;
00076 struct stat   stat_buf;
00077 char          *username;
00078 char          *userhome;
00079 
00080        if (argc < 6) _exit(1);
00081        clog_open_syslog("deliver");
00082 
00083        username=argv[1];
00084        userhome=argv[2];
00085 
00086        extension=argv[3];
00087        receipient=argv[4];
00088        sender=argv[5];
00089        quota= argc > 6 ? argv[6]:"";
00090        defaultmail= argc > 7 ? argv[7]:"";
00091 
00092        if (argc > 8)
00093               set_courierdir(argv[8]);    /* Used by make check only! */
00094 
00095        extname=local_dotcourier(".", extension, &defaultext);
00096 
00097        if (extname == 0 && extension[0])
00098        {
00099               if (errno == ENOENT)
00100               {
00101                      fprintf(stderr, "Unknown user.\n");
00102                      exit(EX_NOUSER);
00103               }
00104 
00105               fprintf(stderr, "Unable to read .%s file.\n",
00106                                    local_extension());
00107               exit(EX_TEMPFAIL);
00108        }
00109 
00110        if (extname == 0)
00111        {
00112               ctlfile=0;
00113        }
00114        else
00115        {
00116               ctlfile=readfile(extname, &stat_buf);
00117               free(extname);
00118               extname=0;
00119        }
00120 
00121        if (ctlfile && (stat_buf.st_mode & S_IWOTH))
00122        {
00123               fprintf(stderr, ".%s file is globally writeable.\n",
00124                      local_extension());
00125               free(ctlfile);
00126               if (extname)  free(extname);
00127               exit(EX_TEMPFAIL);
00128        }
00129 
00130        if (chkdelto(stdin, receipient))
00131        {
00132               fprintf(stderr,
00133                      "Mail loop - already have my Delivered-To: header.");
00134               if (ctlfile)  free(ctlfile);
00135               if (extname)  free(extname);
00136               exit (EX_SOFTWARE);
00137        }
00138 
00139        dodel(username, userhome, stdin,
00140               ctlfile, extension, sender, receipient, defaultext, quota,
00141               defaultmail, 0);
00142        if (ctlfile)  free(ctlfile);
00143        if (extname)  free(extname);
00144        exit(0);
00145        return (0);
00146 }
00147 
00148 static int chkdelto(FILE *f, const char *delivto)
00149 {
00150 char   buf[BUFSIZ];
00151 char   *p, *q;
00152 
00153        /* Check for mail loops, by looking for my Delivered-To: line. */
00154 
00155        while ((p=fgets(buf, sizeof(buf), f)) != 0)
00156        {
00157               if ((p=strchr(buf, '\n')) != 0)
00158                      *p=0;
00159               else
00160               {
00161               int    c;
00162 
00163                      while ((c=getc(f)) >= 0 && c != '\n')
00164                             ;
00165               }
00166 
00167               if (buf[0] == '\0')  break;
00168               if ((p=strchr(buf, ':')) == 0)     continue;
00169               *p++=0;
00170               for (q=buf; *q; q++)
00171                      *q=tolower(*q);
00172               if (strcmp(buf, "delivered-to"))   continue;
00173               while (*p == ' ')
00174                      ++p;
00175               if (strcmp(p, delivto) == 0)
00176                      return (1);
00177        }
00178        return (0);
00179 }
00180 
00181 static int savemessage(const char *, const char *, const char *,
00182               FILE *, const char *,
00183               const char *, const char *, const char *, const char *);
00184 
00185 static int docommand(const char *, const char *, const char *, const char *,
00186               FILE *, const char *, const char *,
00187               const char *, const char *, const char *, const char *,
00188               const char *, const char *, int);
00189 
00190 /* Skip to end of line in command file.  Follow \ - terminated lines */
00191 
00192 static char *skip_eol(char *ctl, int zapbs)
00193 {
00194 char   *bs;
00195 
00196        while (*ctl)
00197        {
00198               if (*ctl == '\n')
00199               {
00200                      *ctl++=0;
00201                      break;
00202               }
00203 
00204               if (*ctl++ != '\\' || *ctl == '\0')
00205                      continue;
00206               bs=ctl-1;
00207               if (*ctl != ' ' && *ctl != '\t' && *ctl != '\n')
00208               {
00209                      if (*ctl)     ++ctl;
00210                      continue;
00211               }
00212               while (*ctl == ' ' || *ctl == '\t')
00213                      ++ctl;
00214               if (*ctl != '\n')
00215                      continue;
00216               if (zapbs)
00217               {
00218                      *bs=' ';
00219                      *ctl=' ';
00220               }
00221               ++ctl;
00222        }
00223        return (ctl);
00224 }
00225 
00226 /* Minor helper function */
00227 
00228 static void dodel(const char *username, const char *userhome,
00229        FILE *c, char *ctl,
00230        const char *extension, const char *sender, const char *receipient,
00231        const char *defaultext, const char *quota, const char *defaultmail,
00232        int recursion_level)
00233 {
00234 char   *ufromline;
00235 char   *dtline;
00236 char   *rpline;
00237 time_t t;
00238 const char *curtime;
00239 
00240        time(&t);
00241        curtime=ctime(&t);
00242        if ((ufromline=malloc(strlen(curtime)+strlen(sender)+30))==0||
00243               (dtline=malloc(strlen(receipient)+
00244                      sizeof("Delivered-To: "))) == 0 ||
00245               (rpline=malloc(strlen(sender) +
00246                      sizeof("Return-Path: <>"))) == 0)
00247        {
00248               perror("malloc");
00249               exit(EX_TEMPFAIL);
00250        }
00251 
00252        if ( (!ctl || !*ctl) && recursion_level == 0)
00253        {
00254        const char *p= *defaultmail ? defaultmail:config_defaultdelivery();
00255 
00256               if ((ctl=malloc(strlen(p)+1)) == 0)
00257               {
00258                      perror("malloc");
00259                      exit(EX_TEMPFAIL);
00260               }
00261               strcpy(ctl, p);
00262        }
00263 
00264        sprintf(ufromline, "From %s %s", sender, curtime);
00265 
00266        {
00267        char *p;
00268 
00269               if ((p=strchr(ufromline, '\n')) != 0)
00270                      *p=0;
00271        }
00272 
00273        strcat(strcpy(dtline, "Delivered-To: "), receipient);
00274        strcat(strcat(strcpy(rpline, "Return-Path: <"), sender), ">");
00275 
00276        while (*ctl)
00277        {
00278               if (*ctl == '#' || *ctl == '\n')
00279               {
00280                      while (*ctl)
00281                             if (*ctl++ == '\n')  break;
00282                      continue;
00283               }
00284 
00285               /*
00286               ** The fd hack is needed for BSD, whose C lib doesn't like
00287               ** mixing stdio and unistd seek calls.
00288               */
00289 
00290               if (*ctl == '.' || *ctl == '/')
00291               {
00292               const char *filename=ctl;
00293               int fd_hack=dup(fileno(stdin));
00294               FILE *fp_hack;
00295 
00296                      if (fd_hack < 0 || lseek(fd_hack, 0L, SEEK_SET) < 0 ||
00297                             (fp_hack=fdopen(fd_hack, "r")) == NULL)
00298                      {
00299                             perror("dup");
00300                             exit(EX_TEMPFAIL);
00301                      }
00302 
00303                      while (*ctl)
00304                      {
00305                             if (*ctl == '/' && (ctl[1] == '\n' ||
00306                                    ctl[1] == 0))
00307                                    *ctl=0; /* Strip trailing / */
00308                             if (*ctl == '\n')
00309                             {
00310                                    *ctl++='\0';
00311                                    break;
00312                             }
00313                             ++ctl;
00314                      }
00315 
00316                      if (savemessage(extension, sender, receipient,
00317                             fp_hack, filename,
00318                             ufromline,
00319                             dtline,
00320                             rpline, quota))
00321                             exit(EX_TEMPFAIL);
00322                      fclose(fp_hack);
00323                      close(fd_hack);
00324                      continue;
00325               }
00326               if (*ctl == '|')
00327               {
00328               const char *command=++ctl;
00329               int    rc;
00330               int fd_hack=dup(fileno(stdin));
00331               FILE *fp_hack;
00332 
00333                      if (fd_hack < 0 || lseek(fd_hack, 0L, SEEK_SET) < 0 ||
00334                             (fp_hack=fdopen(fd_hack, "r")) == NULL)
00335                      {
00336                             perror("dup");
00337                             exit(EX_TEMPFAIL);
00338                      }
00339 
00340                      ctl=skip_eol(ctl, 0);
00341                      while (*command == ' ' || *command == '\t')
00342                             ++command;
00343 
00344                      rc=docommand(extension, sender, receipient, defaultext,
00345                             fp_hack, username, userhome, command,
00346                             dtline,
00347                             rpline,
00348                             ufromline,
00349                             quota, defaultmail,
00350                             recursion_level);
00351                      if (rc)
00352                             exit(rc);
00353                      fclose(fp_hack);
00354                      close(fd_hack);
00355                      continue;
00356               }
00357 
00358               /* Forwarding instructions, parse RFC822 addresses */
00359 
00360               if (*ctl == '&' || *ctl == '!')    ++ctl; /* Legacy */
00361               {
00362                      const char *addresses=ctl;
00363                      struct rfc822t *tokens;
00364                      struct rfc822a *addrlist;
00365                      int n;
00366 
00367                      ctl=skip_eol(ctl, 1);
00368                      if ((tokens=rfc822t_alloc_new(addresses, NULL,
00369                                                 NULL)) == 0 ||
00370                             (addrlist=rfc822a_alloc(tokens)) == 0)
00371                      {
00372                             perror("malloc");
00373                             exit(EX_TEMPFAIL);
00374                      }
00375 
00376                      for (n=0; n<addrlist->naddrs; ++n)
00377                      {
00378                             char *p;
00379 
00380                             if (addrlist->addrs[n].tokens == NULL)
00381                                    continue;
00382 
00383                             p=rfc822_display_addr_tobuf(addrlist, n,
00384                                                      NULL);
00385 
00386                             if (!p)
00387                             {
00388                                    perror(addresses);
00389                                    exit(EX_TEMPFAIL);
00390                             }
00391 
00392                             printf("%s\n", p);
00393                             free(p);
00394                      }
00395                      rfc822a_free(addrlist);
00396                      rfc822t_free(tokens);
00397                      fflush(stdout);
00398               }
00399        }
00400                      
00401        free(rpline);
00402        free(dtline);
00403        free(ufromline);
00404 }
00405 
00406 static void delivery_error(const char *name)
00407 {
00408        if (errno == ENOSPC)
00409        {
00410               fprintf(stderr, "Maildir over quota.\n");
00411               exit(EX_TEMPFAIL);
00412        }
00413 
00414 #if    HAVE_STRERROR
00415 
00416        fprintf(stderr, "%s: %s\n", name, strerror(errno));
00417 #else
00418        fprintf(stderr, "%s: error %d\n", name, errno);
00419 #endif
00420        exit (EX_TEMPFAIL);
00421 }
00422 
00423 static off_t mbox_size;
00424 static int mbox_fd;
00425 
00426 static RETSIGTYPE truncmbox(int signum)
00427 {
00428 #if    HAVE_FTRUNCATE
00429        if (ftruncate(mbox_fd, mbox_size) < 0)
00430               ; /* ignore */
00431 #else
00432        /* Better than nothing */
00433        write(mbox_fd, "\n", 1);
00434 #endif
00435        _exit(0);
00436 #if    RETSIGTYPE != void
00437        return (0);
00438 #endif
00439 }
00440 
00441 static int savemessage(const char *extension, const char *sender,
00442               const char *receipient,
00443               FILE *f, const char *name,
00444               const char *ufromline,
00445               const char *dtline,
00446               const char *rpline,
00447               const char *quota)
00448 {
00449 FILE   *delivf;
00450 char   buf[BUFSIZ];
00451 int    c;
00452 static unsigned counter=0;
00453 struct stat stat_buf;
00454 struct maildirsize quotainfo;
00455 struct maildir_tmpcreate_info createInfo;
00456 
00457        umask(077);
00458 
00459        if ((delivf=fopen(name, "a")) != 0)
00460        {
00461               /* Ok, perhaps this is a mailbox */
00462 
00463               struct ll_mail *ll=ll_mail_alloc(name);
00464 
00465               fclose(delivf);
00466 
00467               if (!ll)
00468               {
00469                      delivery_error(name);       /* I Give up */
00470               }
00471 
00472               while ((mbox_fd=ll_mail_open(ll)) < 0)
00473               {
00474                      switch (errno) {
00475                      case EEXIST:
00476                      case EAGAIN:
00477                             sleep(5);
00478                             continue;
00479                             break;
00480                      }
00481 
00482                      delivery_error(name);
00483               }
00484 
00485               delivf=fdopen(mbox_fd, "w");
00486 
00487               if (delivf == NULL || fseek(delivf, 0L, SEEK_END) < 0
00488                   || (mbox_size=ftell(delivf)) == (off_t)-1)
00489               {
00490                      if (delivf)
00491                             fclose(delivf);
00492                      close(mbox_fd);
00493                      ll_mail_free(ll);
00494                      delivery_error(name);       /* I Give up */
00495               }
00496 
00497               signal(SIGHUP, truncmbox);
00498               signal(SIGINT, truncmbox);
00499               signal(SIGQUIT, truncmbox);
00500               signal(SIGTERM, truncmbox);
00501 
00502               fprintf(delivf, "%s\n%s\n%s\n",
00503                      ufromline,
00504                      dtline,
00505                      rpline);
00506 
00507               while (fgets(buf, sizeof(buf), f) != 0)
00508               {
00509               char   *q=buf;
00510 
00511                      while (*q == '>')    q++;
00512                      if (strncmp(q, "From ", 5) == 0)
00513                             putc('>', delivf);
00514                      fprintf(delivf, "%s", buf);
00515                      if (strchr(buf, '\n') == 0)
00516                      {
00517                             while ((c=getc(f)) >= 0)
00518                             {
00519                                    putc(c, delivf);
00520                                    if (c == '\n')       break;
00521                             }
00522                      }
00523               }
00524 
00525               if ( ferror(f) || fflush(delivf) || ferror(delivf)
00526 #if    EXPLICITSYNC
00527                      || fsync(fileno(delivf))
00528 #endif
00529                      ||
00530                             (signal(SIGHUP, SIG_DFL),
00531                             signal(SIGINT, SIG_DFL),
00532                             signal(SIGQUIT, SIG_DFL),
00533                             signal(SIGTERM, SIG_DFL),
00534                             fclose(delivf))
00535                      )
00536               {
00537 #if    HAVE_FTRUNCATE
00538                      if (ftruncate(mbox_fd, mbox_size) < 0)
00539                             ; /* ignore */
00540 #endif
00541                      close(mbox_fd);
00542                      ll_mail_free(ll);
00543                      delivery_error("mailbox.close");
00544               }
00545               close(mbox_fd);
00546               ll_mail_free(ll);
00547               return (0);
00548        }
00549 
00550        if (fstat(fileno(f), &stat_buf))
00551        {
00552               delivery_error("stat");
00553               return (-1);
00554        }
00555 
00556        stat_buf.st_size += strlen(dtline) + strlen(rpline) + 2;
00557 
00558        if (maildir_quota_add_start(name, &quotainfo, stat_buf.st_size, 1,
00559                                 quota && *quota != 0 ? quota:NULL))
00560        {
00561               errno=ENOSPC;
00562               delivery_error("out of memory.");
00563        }
00564 
00565        maildir_closequotafile(&quotainfo);       /* For now. */
00566 
00567        sprintf(buf, "%u", ++counter);
00568 
00569        maildir_tmpcreate_init(&createInfo);
00570        createInfo.maildir=name;
00571        createInfo.uniq=buf;
00572        createInfo.msgsize=stat_buf.st_size;
00573        createInfo.doordie=1;
00574 
00575        if ((delivf=maildir_tmpcreate_fp(&createInfo)) == NULL)
00576        {
00577               snprintf(buf, BUFSIZ-1,
00578                       "maildir.open: %s: %s", name,
00579                       strerror(errno));
00580               buf[BUFSIZ-1] = 0;
00581               delivery_error(buf);
00582               return (-1);
00583        }
00584 
00585        fprintf(delivf, "%s\n%s\n", dtline, rpline);
00586 
00587        {
00588               char buffer[BUFSIZ];
00589               int n;
00590 
00591               while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0)
00592                      if (fwrite(buffer, 1, n, delivf)  != n)
00593                             break;
00594        }
00595 
00596        if ( ferror(f) || fflush(delivf) || ferror(delivf)
00597 #if    EXPLICITSYNC
00598                      || fsync(fileno(delivf))
00599 #endif
00600                      || fclose(delivf)
00601                      || (delivf=0, maildir_movetmpnew(createInfo.tmpname,
00602                                                   createInfo.newname)))
00603        {
00604               snprintf(buf, BUFSIZ-1,
00605                       "maildir.close: %s: %s", name,
00606                       strerror(errno));
00607               buf[BUFSIZ-1] = 0;
00608 
00609               if (delivf)   fclose(delivf);
00610               unlink(createInfo.newname);
00611               delivery_error(buf);
00612               return (-1);
00613        }
00614 
00615 #if EXPLICITDIRSYNC
00616 
00617        {
00618               int fd;
00619 
00620               strcpy(strrchr(createInfo.newname, '/')+1, ".");
00621 
00622               fd=open(createInfo.newname, O_RDONLY);
00623 
00624               if (fd >= 0)
00625               {
00626                      fsync(fd);
00627                      close(fd);
00628               }
00629        }
00630 #endif
00631 
00632        maildir_tmpcreate_free(&createInfo);
00633 
00634        maildir_quota_deleted(name, stat_buf.st_size, 1);
00635        return (0);
00636 }
00637 
00638 static char *read_command(int);
00639 
00640 static int docommand(const char *extension, const char *sender,
00641               const char *receipient, const char *defaultext,
00642               FILE *f, const char *username, const char *userhome,
00643               const char *command,
00644               const char *dtline,
00645               const char *rpline,
00646               const char *ufromline,
00647               const char *quota,
00648               const char *defaultmail,
00649               int recursion_level)
00650 {
00651 char   *envs[19];
00652 const char *p;
00653 const char *hostp;
00654 pid_t  pid;
00655 int    i;
00656 int    wait_stat;
00657 int    pipefd[2];
00658 int    isrecursive=0;
00659 char   *newcommand=0;
00660 const char *maildropdefault=getenv("MAILDROPDEFAULT");
00661 const char *shell=getenv("SHELL");
00662 
00663        if (!maildropdefault)
00664               maildropdefault="./Maildir";
00665        if (!shell)
00666               shell="/bin/sh";
00667 
00668        envs[0]=courier_malloc(strlen(userhome)+sizeof("HOME="));
00669        strcat(strcpy(envs[0], "HOME="), userhome);
00670        envs[1]=courier_malloc(strlen(username)+sizeof("USER="));
00671        strcat(strcpy(envs[1], "USER="), username);
00672        envs[2]=courier_malloc(strlen(sender)+sizeof("SENDER="));
00673        strcat(strcpy(envs[2], "SENDER="), sender);
00674        envs[3]=courier_malloc(strlen(receipient)+sizeof("RECIPIENT="));
00675        strcat(strcpy(envs[3], "RECIPIENT="), receipient);
00676 
00677        p=strrchr(receipient, '@');
00678        if (p) hostp=p+1;
00679        else
00680        {
00681               hostp="localhost";
00682               p=receipient+strlen(receipient);
00683        }
00684 
00685        envs[4]=courier_malloc(strlen(hostp)+sizeof("HOST="));
00686        strcat(strcpy(envs[4], "HOST="), hostp);
00687        envs[5]=courier_malloc(p-receipient + sizeof("LOCAL="));
00688        strcpy(envs[5], "LOCAL=");
00689        memcpy(envs[5]+6, receipient,  p-receipient);
00690        envs[5][6+(p-receipient)]=0;
00691 
00692        envs[6]=courier_malloc(strlen(extension)+sizeof("EXT="));
00693        strcat(strcpy(envs[6], "EXT="), extension);
00694 
00695        p=strchr(extension, '-');
00696        if (p) p++;
00697 
00698        envs[7]=courier_malloc((p ? strlen(p):0)+sizeof("EXT2="));
00699        strcat(strcpy(envs[7], "EXT2="), p ? p:"");
00700 
00701        if (p)
00702               p=strchr(p, '-');
00703        if (p) p++;
00704 
00705        envs[8]=courier_malloc((p ? strlen(p):0)+sizeof("EXT3="));
00706        strcat(strcpy(envs[8], "EXT3="), p ? p:"");
00707 
00708        if (p)
00709               p=strchr(p, '-');
00710        if (p) p++;
00711 
00712        envs[9]=courier_malloc((p ? strlen(p):0)+sizeof("EXT4="));
00713        strcat(strcpy(envs[9], "EXT4="), p ? p:"");
00714 
00715        envs[10]=courier_malloc((defaultext ? strlen(defaultext):0)+sizeof("DEFAULT="));
00716        strcat(strcpy(envs[10], "DEFAULT="), defaultext ? defaultext:"");
00717 
00718        envs[11]=courier_malloc(strlen(dtline)+sizeof("DTLINE="));
00719        strcat(strcpy(envs[11], "DTLINE="), dtline);
00720 
00721        envs[12]=courier_malloc(strlen(rpline)+sizeof("RPLINE="));
00722        strcat(strcpy(envs[12], "RPLINE="), rpline);
00723 
00724        envs[13]=courier_malloc(strlen(ufromline)+sizeof("UFLINE="));
00725        strcat(strcpy(envs[13], "UFLINE="), ufromline);
00726 
00727        p=getenv("PATH");
00728        if (!p)       p="/bin:/usr/bin:/usr/local/bin";
00729 
00730        envs[14]=courier_malloc(strlen(p)+sizeof("PATH="));
00731        strcat(strcpy(envs[14], "PATH="), p);
00732 
00733        envs[15]=courier_malloc(strlen(quota)+sizeof("MAILDIRQUOTA="));
00734        strcat(strcpy(envs[15], "MAILDIRQUOTA="), quota);
00735 
00736        envs[16]=courier_malloc(strlen(maildropdefault)
00737                             +sizeof("MAILDROPDEFAULT="));
00738        strcat(strcpy(envs[16], "MAILDROPDEFAULT="), maildropdefault);
00739 
00740        envs[17]=courier_malloc(strlen(shell)
00741                             +sizeof("SHELL="));
00742        strcat(strcpy(envs[17], "SHELL="), shell);
00743        envs[18]=0;
00744 
00745        if (*command == '|')
00746        {
00747               isrecursive=1;
00748               ++command;
00749               while (*command == ' ' || *command == '\t')
00750                      ++command;
00751               if (pipe(pipefd) < 0)
00752               {
00753                      clog_msg_prerrno();
00754                      exit(EX_TEMPFAIL);
00755               }
00756        }
00757 
00758        if ((pid=fork()) < 0)
00759        {
00760               clog_msg_prerrno();
00761               exit(EX_TEMPFAIL);
00762        }
00763 
00764        if (pid == 0)
00765        {
00766        const char *args[8];
00767 
00768               /*
00769               ** External commands have stdout redirected to stderr,
00770               ** and stdin set to the message.
00771               */
00772               dup2(2, 1);
00773               if (fileno(f) != 0)
00774               {
00775                      dup2(fileno(f), 0);
00776                      fclose(f);
00777               }
00778 
00779               if (isrecursive)
00780               {
00781               const char *c;
00782 
00783                      dup2(pipefd[1], 1);
00784                      close(pipefd[0]);
00785                      close(pipefd[1]);
00786                      if (recursion_level >= 3)
00787                      {
00788                             fprintf(stderr, "Maximum recursion level for dynamic delivery instructions exceeded.\n");
00789                             fflush(stderr);
00790                             _exit(EX_NOUSER);
00791                      }
00792 
00793                      if ((c=getenv("DYNAMICDELIVERIES")) != 0 &&
00794                             atoi(c) == 0)
00795                      {
00796                             fprintf(stderr, "Dynamic delivery instructions disabled by administrator.\n");
00797                             fflush(stderr);
00798                             _exit(EX_NOUSER);
00799                      }
00800               }
00801 
00802               if ((p=config_maildropmda()) != 0 && *p &&
00803                      strcmp(p, command) == 0)
00804               {
00805                      /* Special magic for maildrop */
00806 
00807                      args[0]="maildrop";
00808                      args[1]="-A";
00809                      args[2]=dtline;
00810                      args[3]="-A";
00811                      args[4]=rpline;
00812                      args[5]="-f";
00813                      args[6]=sender;
00814                      args[7]=0;
00815               }
00816               else
00817               {
00818                      p=getenv("SHELL");
00819                      if (!p || !*p)       p="/bin/sh";
00820 
00821                      args[0]=p;
00822                      args[1]="-c";
00823                      args[2]=command;
00824                      args[3]=0;
00825               }
00826               lseek(0, 0L, SEEK_SET);
00827 
00828               for (i=0; envs[i]; ++i)
00829                      putenv(envs[i]);
00830 
00831               execv(p, (char **)args);
00832               fprintf(stderr, "Cannot run %s\n", p);
00833               _exit(EX_TEMPFAIL);
00834        }
00835        for (i=0; envs[i]; i++)
00836               free(envs[i]);
00837 
00838        if (isrecursive)
00839        {
00840               close(pipefd[1]);
00841               newcommand=read_command(pipefd[0]);
00842               close(pipefd[0]);
00843        }
00844 
00845        while (wait(&wait_stat) != pid)
00846               ;
00847 
00848        if (WIFEXITED(wait_stat))
00849        {
00850        int    rc=WEXITSTATUS(wait_stat);
00851 
00852               if (rc == 0 || rc == 99)
00853               {
00854                      if (isrecursive)
00855                             dodel(username, userhome, f, newcommand,
00856                                    extension, sender, receipient,
00857                                    defaultext, quota, defaultmail,
00858                                    recursion_level+1);
00859               }
00860               if (newcommand)      free(newcommand);
00861 
00862               return (rc);
00863        }
00864        if (newcommand)      free(newcommand);
00865 
00866        return (EX_NOPERM);
00867 }
00868 
00869 static char *read_command(int fd)
00870 {
00871 char   buf[BUFSIZ];
00872 int    n;
00873 char   *p;
00874 
00875        n=0;
00876        while (n<sizeof(buf)-1)
00877        {
00878        int    c=read(fd, buf+n, sizeof(buf)-n-1);
00879 
00880               if (c <= 0)   break;
00881               n += c;
00882        }
00883        close(fd);
00884        buf[n]=0;
00885        p=strdup(buf);
00886        if (!p)
00887        {
00888               clog_msg_prerrno();
00889               exit(EX_TEMPFAIL);
00890        }
00891        return (p);
00892 }