qmail-local.c
changeset 0 068428edee47
equal deleted inserted replaced
-1:000000000000 0:068428edee47
       
     1 #include <sys/types.h>
       
     2 #include <sys/stat.h>
       
     3 #include "readwrite.h"
       
     4 #include "sig.h"
       
     5 #include "env.h"
       
     6 #include "byte.h"
       
     7 #include "exit.h"
       
     8 #include "fork.h"
       
     9 #include "open.h"
       
    10 #include "wait.h"
       
    11 #include "lock.h"
       
    12 #include "seek.h"
       
    13 #include "substdio.h"
       
    14 #include "getln.h"
       
    15 #include "strerr.h"
       
    16 #include "subfd.h"
       
    17 #include "sgetopt.h"
       
    18 #include "alloc.h"
       
    19 #include "error.h"
       
    20 #include "stralloc.h"
       
    21 #include "fmt.h"
       
    22 #include "str.h"
       
    23 #include "now.h"
       
    24 #include "case.h"
       
    25 #include "quote.h"
       
    26 #include "qmail.h"
       
    27 #include "slurpclose.h"
       
    28 #include "myctime.h"
       
    29 #include "gfrom.h"
       
    30 #include "auto_patrn.h"
       
    31 
       
    32 void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
       
    33 
       
    34 void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); }
       
    35 void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
       
    36 void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
       
    37 void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); }
       
    38 void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno),". (#4.3.0)"); }
       
    39 void temp_slowlock()
       
    40 { strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
       
    41 void temp_qmail(fn) char *fn;
       
    42 { strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
       
    43 
       
    44 int flagdoit;
       
    45 int flag99;
       
    46 
       
    47 char *user;
       
    48 char *homedir;
       
    49 char *local;
       
    50 char *dash;
       
    51 char *ext;
       
    52 char *host;
       
    53 char *sender;
       
    54 char *aliasempty;
       
    55 
       
    56 stralloc safeext = {0};
       
    57 stralloc ufline = {0};
       
    58 stralloc rpline = {0};
       
    59 stralloc envrecip = {0};
       
    60 stralloc dtline = {0};
       
    61 stralloc qme = {0};
       
    62 stralloc ueo = {0};
       
    63 stralloc cmds = {0};
       
    64 stralloc messline = {0};
       
    65 stralloc foo = {0};
       
    66 
       
    67 char buf[1024];
       
    68 char outbuf[1024];
       
    69 
       
    70 /* child process */
       
    71 
       
    72 char fntmptph[80 + FMT_ULONG * 2];
       
    73 char fnnewtph[80 + FMT_ULONG * 2];
       
    74 void tryunlinktmp() { unlink(fntmptph); }
       
    75 void sigalrm() { tryunlinktmp(); _exit(3); }
       
    76 
       
    77 void maildir_child(dir)
       
    78 char *dir;
       
    79 {
       
    80  unsigned long pid;
       
    81  unsigned long time;
       
    82  char host[64];
       
    83  char *s;
       
    84  int loop;
       
    85  struct stat st;
       
    86  int fd;
       
    87  substdio ss;
       
    88  substdio ssout;
       
    89 
       
    90  sig_alarmcatch(sigalrm);
       
    91  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
       
    92  pid = getpid();
       
    93  host[0] = 0;
       
    94  gethostname(host,sizeof(host));
       
    95  for (loop = 0;;++loop)
       
    96   {
       
    97    time = now();
       
    98    s = fntmptph;
       
    99    s += fmt_str(s,"tmp/");
       
   100    s += fmt_ulong(s,time); *s++ = '.';
       
   101    s += fmt_ulong(s,pid); *s++ = '.';
       
   102    s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
       
   103    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
       
   104    /* really should never get to this point */
       
   105    if (loop == 2) _exit(1);
       
   106    sleep(2);
       
   107   }
       
   108  str_copy(fnnewtph,fntmptph);
       
   109  byte_copy(fnnewtph,3,"new");
       
   110 
       
   111  alarm(86400);
       
   112  fd = open_excl(fntmptph);
       
   113  if (fd == -1) _exit(1);
       
   114 
       
   115  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
       
   116  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
       
   117  if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail;
       
   118  if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail;
       
   119 
       
   120  switch(substdio_copy(&ssout,&ss))
       
   121   {
       
   122    case -2: tryunlinktmp(); _exit(4);
       
   123    case -3: goto fail;
       
   124   }
       
   125 
       
   126  if (substdio_flush(&ssout) == -1) goto fail;
       
   127  if (fsync(fd) == -1) goto fail;
       
   128  if (close(fd) == -1) goto fail; /* NFS dorks */
       
   129 
       
   130  if (link(fntmptph,fnnewtph) == -1) goto fail;
       
   131    /* if it was error_exist, almost certainly successful; i hate NFS */
       
   132  tryunlinktmp(); _exit(0);
       
   133 
       
   134  fail: tryunlinktmp(); _exit(1);
       
   135 }
       
   136 
       
   137 /* end child process */
       
   138 
       
   139 void maildir(fn)
       
   140 char *fn;
       
   141 {
       
   142  int child;
       
   143  int wstat;
       
   144 
       
   145  if (seek_begin(0) == -1) temp_rewind();
       
   146 
       
   147  switch(child = fork())
       
   148   {
       
   149    case -1:
       
   150      temp_fork();
       
   151    case 0:
       
   152      maildir_child(fn);
       
   153      _exit(111);
       
   154   }
       
   155 
       
   156  wait_pid(&wstat,child);
       
   157  if (wait_crashed(wstat))
       
   158    temp_childcrashed();
       
   159  switch(wait_exitcode(wstat))
       
   160   {
       
   161    case 0: break;
       
   162    case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
       
   163    case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
       
   164    case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
       
   165    default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
       
   166   }
       
   167 }
       
   168 
       
   169 void mailfile(fn)
       
   170 char *fn;
       
   171 {
       
   172  int fd;
       
   173  substdio ss;
       
   174  substdio ssout;
       
   175  int match;
       
   176  seek_pos pos;
       
   177  int flaglocked;
       
   178 
       
   179  if (seek_begin(0) == -1) temp_rewind();
       
   180 
       
   181  fd = open_append(fn);
       
   182  if (fd == -1)
       
   183    strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)");
       
   184 
       
   185  sig_alarmcatch(temp_slowlock);
       
   186  alarm(30);
       
   187  flaglocked = (lock_ex(fd) != -1);
       
   188  alarm(0);
       
   189  sig_alarmdefault();
       
   190 
       
   191  seek_end(fd);
       
   192  pos = seek_cur(fd);
       
   193 
       
   194  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
       
   195  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
       
   196  if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs;
       
   197  if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs;
       
   198  if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs;
       
   199  for (;;)
       
   200   {
       
   201    if (getln(&ss,&messline,&match,'\n') != 0) 
       
   202     {
       
   203      strerr_warn3("Unable to read message: ",error_str(errno),". (#4.3.0)",0);
       
   204      if (flaglocked) seek_trunc(fd,pos); close(fd);
       
   205      _exit(111);
       
   206     }
       
   207    if (!match && !messline.len) break;
       
   208    if (gfrom(messline.s,messline.len))
       
   209      if (substdio_bput(&ssout,">",1)) goto writeerrs;
       
   210    if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs;
       
   211    if (!match)
       
   212     {
       
   213      if (substdio_bputs(&ssout,"\n")) goto writeerrs;
       
   214      break;
       
   215     }
       
   216   }
       
   217  if (substdio_bputs(&ssout,"\n")) goto writeerrs;
       
   218  if (substdio_flush(&ssout)) goto writeerrs;
       
   219  if (fsync(fd) == -1) goto writeerrs;
       
   220  close(fd);
       
   221  return;
       
   222 
       
   223  writeerrs:
       
   224  strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
       
   225  if (flaglocked) seek_trunc(fd,pos);
       
   226  close(fd);
       
   227  _exit(111);
       
   228 }
       
   229 
       
   230 void mailprogram(prog)
       
   231 char *prog;
       
   232 {
       
   233  int child;
       
   234  char *(args[4]);
       
   235  int wstat;
       
   236 
       
   237  if (seek_begin(0) == -1) temp_rewind();
       
   238 
       
   239  switch(child = fork())
       
   240   {
       
   241    case -1:
       
   242      temp_fork();
       
   243    case 0:
       
   244      args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
       
   245      sig_pipedefault();
       
   246      execv(*args,args);
       
   247      strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)");
       
   248   }
       
   249 
       
   250  wait_pid(&wstat,child);
       
   251  if (wait_crashed(wstat))
       
   252    temp_childcrashed();
       
   253  switch(wait_exitcode(wstat))
       
   254   {
       
   255    case 100:
       
   256    case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
       
   257    case 0: break;
       
   258    case 99: flag99 = 1; break;
       
   259    default: _exit(111);
       
   260   }
       
   261 }
       
   262 
       
   263 unsigned long mailforward_qp = 0;
       
   264 
       
   265 void mailforward(recips)
       
   266 char **recips;
       
   267 {
       
   268  struct qmail qqt;
       
   269  char *qqx;
       
   270  substdio ss;
       
   271  int match;
       
   272 
       
   273  if (seek_begin(0) == -1) temp_rewind();
       
   274  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
       
   275 
       
   276  if (qmail_open(&qqt) == -1) temp_fork();
       
   277  mailforward_qp = qmail_qp(&qqt);
       
   278  qmail_put(&qqt,dtline.s,dtline.len);
       
   279  do
       
   280   {
       
   281    if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; }
       
   282    qmail_put(&qqt,messline.s,messline.len);
       
   283   }
       
   284  while (match);
       
   285  qmail_from(&qqt,ueo.s);
       
   286  while (*recips) qmail_to(&qqt,*recips++);
       
   287  qqx = qmail_close(&qqt);
       
   288  if (!*qqx) return;
       
   289  strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,".");
       
   290 }
       
   291 
       
   292 void bouncexf()
       
   293 {
       
   294  int match;
       
   295  substdio ss;
       
   296 
       
   297  if (seek_begin(0) == -1) temp_rewind();
       
   298  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
       
   299  for (;;)
       
   300   {
       
   301    if (getln(&ss,&messline,&match,'\n') != 0) temp_read();
       
   302    if (!match) break;
       
   303    if (messline.len <= 1)
       
   304      break;
       
   305    if (messline.len == dtline.len)
       
   306      if (!str_diffn(messline.s,dtline.s,dtline.len))
       
   307        strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)");
       
   308   }
       
   309 }
       
   310 
       
   311 void checkhome()
       
   312 {
       
   313  struct stat st;
       
   314 
       
   315  if (stat(".",&st) == -1)
       
   316    strerr_die3x(111,"Unable to stat home directory: ",error_str(errno),". (#4.3.0)");
       
   317  if (st.st_mode & auto_patrn)
       
   318    strerr_die1x(111,"Uh-oh: home directory is writable. (#4.7.0)");
       
   319  if (st.st_mode & 01000)
       
   320    if (flagdoit)
       
   321      strerr_die1x(111,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)");
       
   322    else
       
   323      strerr_warn1("Warning: home directory is sticky.",0);
       
   324 }
       
   325 
       
   326 int qmeox(dashowner)
       
   327 char *dashowner;
       
   328 {
       
   329  struct stat st;
       
   330 
       
   331  if (!stralloc_copys(&qme,".qmail")) temp_nomem();
       
   332  if (!stralloc_cats(&qme,dash)) temp_nomem();
       
   333  if (!stralloc_cat(&qme,&safeext)) temp_nomem();
       
   334  if (!stralloc_cats(&qme,dashowner)) temp_nomem();
       
   335  if (!stralloc_0(&qme)) temp_nomem();
       
   336  if (stat(qme.s,&st) == -1)
       
   337   {
       
   338    if (error_temp(errno)) temp_qmail(qme.s);
       
   339    return -1;
       
   340   }
       
   341  return 0;
       
   342 }
       
   343 
       
   344 int qmeexists(fd,cutable)
       
   345 int *fd;
       
   346 int *cutable;
       
   347 {
       
   348   struct stat st;
       
   349 
       
   350   if (!stralloc_0(&qme)) temp_nomem();
       
   351 
       
   352   *fd = open_read(qme.s);
       
   353   if (*fd == -1) {
       
   354     if (error_temp(errno)) temp_qmail(qme.s);
       
   355     if (errno == error_perm) temp_qmail(qme.s);
       
   356     if (errno == error_acces) temp_qmail(qme.s);
       
   357     return 0;
       
   358   }
       
   359 
       
   360   if (fstat(*fd,&st) == -1) temp_qmail(qme.s);
       
   361   if ((st.st_mode & S_IFMT) == S_IFREG) {
       
   362     if (st.st_mode & auto_patrn)
       
   363       strerr_die1x(111,"Uh-oh: .qmail file is writable. (#4.7.0)");
       
   364     *cutable = !!(st.st_mode & 0100);
       
   365     return 1;
       
   366   }
       
   367   close(*fd);
       
   368   return 0;
       
   369 }
       
   370 
       
   371 /* "" "": "" */
       
   372 /* "-/" "": "-/" "-/default" */
       
   373 /* "-/" "a": "-/a" "-/default" */
       
   374 /* "-/" "a-": "-/a-" "-/a-default" "-/default" */
       
   375 /* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */
       
   376 /* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */
       
   377 /* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */
       
   378 
       
   379 void qmesearch(fd,cutable)
       
   380 int *fd;
       
   381 int *cutable;
       
   382 {
       
   383   int i;
       
   384 
       
   385   if (!stralloc_copys(&qme,".qmail")) temp_nomem();
       
   386   if (!stralloc_cats(&qme,dash)) temp_nomem();
       
   387   if (!stralloc_cat(&qme,&safeext)) temp_nomem();
       
   388   if (qmeexists(fd,cutable)) {
       
   389     if (safeext.len >= 7) {
       
   390       i = safeext.len - 7;
       
   391       if (!byte_diff("default",7,safeext.s + i))
       
   392 	if (i <= str_len(ext)) /* paranoia */
       
   393 	  if (!env_put2("DEFAULT",ext + i)) temp_nomem();
       
   394     }
       
   395     return;
       
   396   }
       
   397 
       
   398   for (i = safeext.len;i >= 0;--i)
       
   399     if (!i || (safeext.s[i - 1] == '-')) {
       
   400       if (!stralloc_copys(&qme,".qmail")) temp_nomem();
       
   401       if (!stralloc_cats(&qme,dash)) temp_nomem();
       
   402       if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem();
       
   403       if (!stralloc_cats(&qme,"default")) temp_nomem();
       
   404       if (qmeexists(fd,cutable)) {
       
   405 	if (i <= str_len(ext)) /* paranoia */
       
   406 	  if (!env_put2("DEFAULT",ext + i)) temp_nomem();
       
   407         return;
       
   408       }
       
   409     }
       
   410 
       
   411   *fd = -1;
       
   412 }
       
   413 
       
   414 unsigned long count_file = 0;
       
   415 unsigned long count_forward = 0;
       
   416 unsigned long count_program = 0;
       
   417 char count_buf[FMT_ULONG];
       
   418 
       
   419 void count_print()
       
   420 {
       
   421  substdio_puts(subfdoutsmall,"did ");
       
   422  substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file));
       
   423  substdio_puts(subfdoutsmall,"+");
       
   424  substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward));
       
   425  substdio_puts(subfdoutsmall,"+");
       
   426  substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program));
       
   427  substdio_puts(subfdoutsmall,"\n");
       
   428  if (mailforward_qp)
       
   429   {
       
   430    substdio_puts(subfdoutsmall,"qp ");
       
   431    substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp));
       
   432    substdio_puts(subfdoutsmall,"\n");
       
   433   }
       
   434  substdio_flush(subfdoutsmall);
       
   435 }
       
   436 
       
   437 void sayit(type,cmd,len)
       
   438 char *type;
       
   439 char *cmd;
       
   440 int len;
       
   441 {
       
   442  substdio_puts(subfdoutsmall,type);
       
   443  substdio_put(subfdoutsmall,cmd,len);
       
   444  substdio_putsflush(subfdoutsmall,"\n");
       
   445 }
       
   446 
       
   447 void main(argc,argv)
       
   448 int argc;
       
   449 char **argv;
       
   450 {
       
   451  int opt;
       
   452  int i;
       
   453  int j;
       
   454  int k;
       
   455  int fd;
       
   456  int numforward;
       
   457  char **recips;
       
   458  datetime_sec starttime;
       
   459  int flagforwardonly;
       
   460  char *x;
       
   461 
       
   462  umask(077);
       
   463  sig_pipeignore();
       
   464 
       
   465  if (!env_init()) temp_nomem();
       
   466 
       
   467  flagdoit = 1;
       
   468  while ((opt = getopt(argc,argv,"nN")) != opteof)
       
   469    switch(opt)
       
   470     {
       
   471      case 'n': flagdoit = 0; break;
       
   472      case 'N': flagdoit = 1; break;
       
   473      default:
       
   474        usage();
       
   475     }
       
   476  argc -= optind;
       
   477  argv += optind;
       
   478 
       
   479  if (!(user = *argv++)) usage();
       
   480  if (!(homedir = *argv++)) usage();
       
   481  if (!(local = *argv++)) usage();
       
   482  if (!(dash = *argv++)) usage();
       
   483  if (!(ext = *argv++)) usage();
       
   484  if (!(host = *argv++)) usage();
       
   485  if (!(sender = *argv++)) usage();
       
   486  if (!(aliasempty = *argv++)) usage();
       
   487  if (*argv) usage();
       
   488 
       
   489  if (homedir[0] != '/') usage();
       
   490  if (chdir(homedir) == -1)
       
   491    strerr_die5x(111,"Unable to switch to ",homedir,": ",error_str(errno),". (#4.3.0)");
       
   492  checkhome();
       
   493 
       
   494  if (!env_put2("HOST",host)) temp_nomem();
       
   495  if (!env_put2("HOME",homedir)) temp_nomem();
       
   496  if (!env_put2("USER",user)) temp_nomem();
       
   497  if (!env_put2("LOCAL",local)) temp_nomem();
       
   498 
       
   499  if (!stralloc_copys(&envrecip,local)) temp_nomem();
       
   500  if (!stralloc_cats(&envrecip,"@")) temp_nomem();
       
   501  if (!stralloc_cats(&envrecip,host)) temp_nomem();
       
   502 
       
   503  if (!stralloc_copy(&foo,&envrecip)) temp_nomem();
       
   504  if (!stralloc_0(&foo)) temp_nomem();
       
   505  if (!env_put2("RECIPIENT",foo.s)) temp_nomem();
       
   506 
       
   507  if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem();
       
   508  if (!stralloc_cat(&dtline,&envrecip)) temp_nomem();
       
   509  for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_';
       
   510  if (!stralloc_cats(&dtline,"\n")) temp_nomem();
       
   511 
       
   512  if (!stralloc_copy(&foo,&dtline)) temp_nomem();
       
   513  if (!stralloc_0(&foo)) temp_nomem();
       
   514  if (!env_put2("DTLINE",foo.s)) temp_nomem();
       
   515 
       
   516  if (flagdoit)
       
   517    bouncexf();
       
   518 
       
   519  if (!env_put2("SENDER",sender)) temp_nomem();
       
   520 
       
   521  if (!quote2(&foo,sender)) temp_nomem();
       
   522  if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem();
       
   523  if (!stralloc_cat(&rpline,&foo)) temp_nomem();
       
   524  for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_';
       
   525  if (!stralloc_cats(&rpline,">\n")) temp_nomem();
       
   526 
       
   527  if (!stralloc_copy(&foo,&rpline)) temp_nomem();
       
   528  if (!stralloc_0(&foo)) temp_nomem();
       
   529  if (!env_put2("RPLINE",foo.s)) temp_nomem();
       
   530 
       
   531  if (!stralloc_copys(&ufline,"From ")) temp_nomem();
       
   532  if (*sender)
       
   533   {
       
   534    int len; int i; char ch;
       
   535 
       
   536    len = str_len(sender);
       
   537    if (!stralloc_readyplus(&ufline,len)) temp_nomem();
       
   538    for (i = 0;i < len;++i)
       
   539     {
       
   540      ch = sender[i];
       
   541      if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-';
       
   542      ufline.s[ufline.len + i] = ch;
       
   543     }
       
   544    ufline.len += len;
       
   545   }
       
   546  else
       
   547    if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem();
       
   548  if (!stralloc_cats(&ufline," ")) temp_nomem();
       
   549  starttime = now();
       
   550  if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem();
       
   551 
       
   552  if (!stralloc_copy(&foo,&ufline)) temp_nomem();
       
   553  if (!stralloc_0(&foo)) temp_nomem();
       
   554  if (!env_put2("UFLINE",foo.s)) temp_nomem();
       
   555 
       
   556  x = ext;
       
   557  if (!env_put2("EXT",x)) temp_nomem();
       
   558  x += str_chr(x,'-'); if (*x) ++x;
       
   559  if (!env_put2("EXT2",x)) temp_nomem();
       
   560  x += str_chr(x,'-'); if (*x) ++x;
       
   561  if (!env_put2("EXT3",x)) temp_nomem();
       
   562  x += str_chr(x,'-'); if (*x) ++x;
       
   563  if (!env_put2("EXT4",x)) temp_nomem();
       
   564 
       
   565  if (!stralloc_copys(&safeext,ext)) temp_nomem();
       
   566  case_lowerb(safeext.s,safeext.len);
       
   567  for (i = 0;i < safeext.len;++i)
       
   568    if (safeext.s[i] == '.')
       
   569      safeext.s[i] = ':';
       
   570 
       
   571  i = str_len(host);
       
   572  i = byte_rchr(host,i,'.');
       
   573  if (!stralloc_copyb(&foo,host,i)) temp_nomem();
       
   574  if (!stralloc_0(&foo)) temp_nomem();
       
   575  if (!env_put2("HOST2",foo.s)) temp_nomem();
       
   576  i = byte_rchr(host,i,'.');
       
   577  if (!stralloc_copyb(&foo,host,i)) temp_nomem();
       
   578  if (!stralloc_0(&foo)) temp_nomem();
       
   579  if (!env_put2("HOST3",foo.s)) temp_nomem();
       
   580  i = byte_rchr(host,i,'.');
       
   581  if (!stralloc_copyb(&foo,host,i)) temp_nomem();
       
   582  if (!stralloc_0(&foo)) temp_nomem();
       
   583  if (!env_put2("HOST4",foo.s)) temp_nomem();
       
   584 
       
   585  flagforwardonly = 0;
       
   586  qmesearch(&fd,&flagforwardonly);
       
   587  if (fd == -1)
       
   588    if (*dash)
       
   589      strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
       
   590 
       
   591  if (!stralloc_copys(&ueo,sender)) temp_nomem();
       
   592  if (str_diff(sender,""))
       
   593    if (str_diff(sender,"#@[]"))
       
   594      if (qmeox("-owner") == 0)
       
   595       {
       
   596        if (qmeox("-owner-default") == 0)
       
   597 	{
       
   598          if (!stralloc_copys(&ueo,local)) temp_nomem();
       
   599          if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem();
       
   600          if (!stralloc_cats(&ueo,host)) temp_nomem();
       
   601          if (!stralloc_cats(&ueo,"-@[]")) temp_nomem();
       
   602 	}
       
   603        else
       
   604 	{
       
   605          if (!stralloc_copys(&ueo,local)) temp_nomem();
       
   606          if (!stralloc_cats(&ueo,"-owner@")) temp_nomem();
       
   607          if (!stralloc_cats(&ueo,host)) temp_nomem();
       
   608 	}
       
   609       }
       
   610  if (!stralloc_0(&ueo)) temp_nomem();
       
   611  if (!env_put2("NEWSENDER",ueo.s)) temp_nomem();
       
   612 
       
   613  if (!stralloc_ready(&cmds,0)) temp_nomem();
       
   614  cmds.len = 0;
       
   615  if (fd != -1)
       
   616    if (slurpclose(fd,&cmds,256) == -1) temp_nomem();
       
   617 
       
   618  if (!cmds.len)
       
   619   {
       
   620    if (!stralloc_copys(&cmds,aliasempty)) temp_nomem();
       
   621    flagforwardonly = 0;
       
   622   }
       
   623  if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
       
   624    if (!stralloc_cats(&cmds,"\n")) temp_nomem();
       
   625 
       
   626  numforward = 0;
       
   627  i = 0;
       
   628  for (j = 0;j < cmds.len;++j)
       
   629    if (cmds.s[j] == '\n')
       
   630     {
       
   631      switch(cmds.s[i]) { case '#': case '.': case '/': case '|': break;
       
   632        default: ++numforward; }
       
   633      i = j + 1;
       
   634     }
       
   635 
       
   636  recips = (char **) alloc((numforward + 1) * sizeof(char *));
       
   637  if (!recips) temp_nomem();
       
   638  numforward = 0;
       
   639 
       
   640  flag99 = 0;
       
   641 
       
   642  i = 0;
       
   643  for (j = 0;j < cmds.len;++j)
       
   644    if (cmds.s[j] == '\n')
       
   645     {
       
   646      cmds.s[j] = 0;
       
   647      k = j;
       
   648      while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
       
   649        cmds.s[--k] = 0;
       
   650      switch(cmds.s[i])
       
   651       {
       
   652        case 0: /* k == i */
       
   653 	 if (i) break;
       
   654          strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
       
   655        case '#':
       
   656          break;
       
   657        case '.':
       
   658        case '/':
       
   659 	 ++count_file;
       
   660 	 if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)");
       
   661 	 if (cmds.s[k - 1] == '/')
       
   662            if (flagdoit) maildir(cmds.s + i);
       
   663            else sayit("maildir ",cmds.s + i,k - i);
       
   664 	 else
       
   665            if (flagdoit) mailfile(cmds.s + i);
       
   666            else sayit("mbox ",cmds.s + i,k - i);
       
   667          break;
       
   668        case '|':
       
   669 	 ++count_program;
       
   670 	 if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)");
       
   671          if (flagdoit) mailprogram(cmds.s + i + 1);
       
   672          else sayit("program ",cmds.s + i + 1,k - i - 1);
       
   673          break;
       
   674        case '+':
       
   675 	 if (str_equal(cmds.s + i + 1,"list"))
       
   676 	   flagforwardonly = 1;
       
   677 	 break;
       
   678        case '&':
       
   679          ++i;
       
   680        default:
       
   681 	 ++count_forward;
       
   682          if (flagdoit) recips[numforward++] = cmds.s + i;
       
   683          else sayit("forward ",cmds.s + i,k - i);
       
   684          break;
       
   685       }
       
   686      i = j + 1;
       
   687      if (flag99) break;
       
   688     }
       
   689 
       
   690  if (numforward) if (flagdoit)
       
   691   {
       
   692    recips[numforward] = 0;
       
   693    mailforward(recips);
       
   694   }
       
   695 
       
   696  count_print();
       
   697  _exit(0);
       
   698 }