nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

qmail-send.c (39577B)


      1 #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <sys/time.h>
      4 #include "readwrite.h"
      5 #include "sig.h"
      6 #include "direntry.h"
      7 #include "control.h"
      8 #include "select.h"
      9 #include "open.h"
     10 #include "seek.h"
     11 #include "exit.h"
     12 #include "lock.h"
     13 #include "ndelay.h"
     14 #include "now.h"
     15 #include "getln.h"
     16 #include "substdio.h"
     17 #include "alloc.h"
     18 #include "error.h"
     19 #include "stralloc.h"
     20 #include "str.h"
     21 #include "byte.h"
     22 #include "fmt.h"
     23 #include "scan.h"
     24 #include "case.h"
     25 #include "auto_qmail.h"
     26 #include "trigger.h"
     27 #include "newfield.h"
     28 #include "quote.h"
     29 #include "qmail.h"
     30 #include "qsutil.h"
     31 #include "prioq.h"
     32 #include "constmap.h"
     33 #include "fmtqfn.h"
     34 #include "readsubdir.h"
     35 
     36 /* critical timing feature #1: if not triggered, do not busy-loop */
     37 /* critical timing feature #2: if triggered, respond within fixed time */
     38 /* important timing feature: when triggered, respond instantly */
     39 #define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
     40 #define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
     41 #define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
     42 #define SLEEP_CLEANUP 76431 /* time between cleanups */
     43 #define SLEEP_SYSFAIL 123
     44 #define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */
     45 
     46 int lifetime = 604800;
     47 
     48 stralloc percenthack = {0};
     49 struct constmap mappercenthack;
     50 stralloc locals = {0};
     51 struct constmap maplocals;
     52 stralloc vdoms = {0};
     53 struct constmap mapvdoms;
     54 stralloc envnoathost = {0};
     55 stralloc bouncefrom = {0};
     56 stralloc bouncehost = {0};
     57 stralloc doublebounceto = {0};
     58 stralloc doublebouncehost = {0};
     59 
     60 char strnum2[FMT_ULONG];
     61 char strnum3[FMT_ULONG];
     62 
     63 #define CHANNELS 2
     64 char *chanaddr[CHANNELS] = { "local/", "remote/" };
     65 char *chanstatusmsg[CHANNELS] = { " local ", " remote " };
     66 char *tochan[CHANNELS] = { " to local ", " to remote " };
     67 int chanfdout[CHANNELS] = { 1, 3 };
     68 int chanfdin[CHANNELS] = { 2, 4 };
     69 int chanskip[CHANNELS] = { 10, 20 };
     70 
     71 int flagexitasap = 0; void sigterm() { flagexitasap = 1; }
     72 int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }
     73 int flagreadasap = 0; void sighup() { flagreadasap = 1; }
     74 
     75 void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n");
     76  flagexitasap = 1; }
     77 
     78 int flagspawnalive[CHANNELS];
     79 void spawndied(c) int c; { log1("alert: oh no! lost spawn connection! dying...\n");
     80  flagspawnalive[c] = 0; flagexitasap = 1; }
     81 
     82 #define REPORTMAX 10000
     83 
     84 datetime_sec recent;
     85 
     86 
     87 /* this file is too long ----------------------------------------- FILENAMES */
     88 
     89 stralloc fn = {0};
     90 stralloc fn2 = {0};
     91 char fnmake_strnum[FMT_ULONG];
     92 
     93 void fnmake_init()
     94 {
     95  while (!stralloc_ready(&fn,FMTQFN)) nomem();
     96  while (!stralloc_ready(&fn2,FMTQFN)) nomem();
     97 }
     98 
     99 void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }
    100 void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }
    101 void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }
    102 void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }
    103 void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }
    104 void fnmake2_bounce(id) unsigned long id;
    105 { fn2.len = fmtqfn(fn2.s,"bounce/",id,0); }
    106 void fnmake_chanaddr(id,c) unsigned long id; int c;
    107 { fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
    108 
    109 
    110 /* this file is too long ----------------------------------------- REWRITING */
    111 
    112 stralloc rwline = {0};
    113 
    114 /* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
    115 /* Daniel.. by sea? Don't you mean by air? */
    116 /* may trash recip. must set up rwline, between a T and a \0. */
    117 int rewrite(recip)
    118 char *recip;
    119 {
    120   unsigned int i;
    121   char *x;
    122   static stralloc addr = {0};
    123   unsigned int at;
    124 
    125   if (!stralloc_copys(&rwline,"T")) return 0;
    126   if (!stralloc_copys(&addr,recip)) return 0;
    127 
    128   i = byte_rchr(addr.s,addr.len,'@');
    129   if (i == addr.len) {
    130     if (!stralloc_cats(&addr,"@")) return 0;
    131     if (!stralloc_cat(&addr,&envnoathost)) return 0;
    132   }
    133 
    134   while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
    135     unsigned int j = byte_rchr(addr.s,i,'%');
    136     if (j == i) break;
    137     addr.len = i;
    138     i = j;
    139     addr.s[i] = '@';
    140   }
    141 
    142   at = byte_rchr(addr.s,addr.len,'@');
    143 
    144   if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
    145     if (!stralloc_cat(&rwline,&addr)) return 0;
    146     if (!stralloc_0(&rwline)) return 0;
    147     return 1;
    148   }
    149 
    150   for (i = 0;i <= addr.len;++i)
    151     if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
    152       if ((x = constmap(&mapvdoms,addr.s + i,addr.len - i))) {
    153         if (!*x) break;
    154         if (!stralloc_cats(&rwline,x)) return 0;
    155         if (!stralloc_cats(&rwline,"-")) return 0;
    156         if (!stralloc_cat(&rwline,&addr)) return 0;
    157         if (!stralloc_0(&rwline)) return 0;
    158         return 1;
    159       }
    160  
    161   if (!stralloc_cat(&rwline,&addr)) return 0;
    162   if (!stralloc_0(&rwline)) return 0;
    163   return 2;
    164 }
    165 
    166 void senderadd(sa,sender,recip)
    167 stralloc *sa;
    168 char *sender;
    169 char *recip;
    170 {
    171  unsigned int i;
    172 
    173  i = str_len(sender);
    174  if (i >= 4)
    175    if (str_equal(sender + i - 4,"-@[]"))
    176     {
    177      unsigned int j = byte_rchr(sender,i - 4,'@');
    178      unsigned int k = str_rchr(recip,'@');
    179      if (recip[k] && (j + 5 <= i))
    180       {
    181        /* owner-@host-@[] -> owner-recipbox=reciphost@host */
    182        while (!stralloc_catb(sa,sender,j)) nomem();
    183        while (!stralloc_catb(sa,recip,k)) nomem();
    184        while (!stralloc_cats(sa,"=")) nomem();
    185        while (!stralloc_cats(sa,recip + k + 1)) nomem();
    186        while (!stralloc_cats(sa,"@")) nomem();
    187        while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem();
    188        return;
    189       }
    190     }
    191  while (!stralloc_cats(sa,sender)) nomem();
    192 }
    193 
    194 
    195 /* this file is too long ---------------------------------------------- INFO */
    196 
    197 int getinfo(sa,dt,id)
    198 stralloc *sa;
    199 datetime_sec *dt;
    200 unsigned long id;
    201 {
    202  int fdinfo;
    203  struct stat st;
    204  static stralloc line = {0};
    205  int match;
    206  substdio ss;
    207  char buf[128];
    208 
    209  fnmake_info(id);
    210  fdinfo = open_read(fn.s);
    211  if (fdinfo == -1) return 0;
    212  if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; }
    213  substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf));
    214  if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; }
    215  close(fdinfo);
    216  if (!match) return 0;
    217  if (line.s[0] != 'F') return 0;
    218 
    219  *dt = st.st_mtime;
    220  while (!stralloc_copys(sa,line.s + 1)) nomem();
    221  while (!stralloc_0(sa)) nomem();
    222  return 1;
    223 }
    224 
    225 
    226 /* this file is too long ------------------------------------- COMMUNICATION */
    227 
    228 substdio sstoqc; char sstoqcbuf[1024];
    229 substdio ssfromqc; char ssfromqcbuf[1024];
    230 stralloc comm_buf[CHANNELS] = { {0}, {0} };
    231 int comm_pos[CHANNELS];
    232 
    233 void comm_init()
    234 {
    235  int c;
    236  substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf));
    237  substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf));
    238  for (c = 0;c < CHANNELS;++c)
    239    if (ndelay_on(chanfdout[c]) == -1)
    240    /* this is so stupid: NDELAY semantics should be default on write */
    241      spawndied(c); /* drastic, but better than risking deadlock */
    242 }
    243 
    244 int comm_canwrite(c)
    245 int c;
    246 {
    247  /* XXX: could allow a bigger buffer; say 10 recipients */
    248  if (comm_buf[c].s && comm_buf[c].len) return 0;
    249  return 1;
    250 }
    251 
    252 void comm_write(c,delnum,id,sender,recip)
    253 int c;
    254 int delnum;
    255 unsigned long id;
    256 char *sender;
    257 char *recip;
    258 {
    259  char ch;
    260  if (comm_buf[c].s && comm_buf[c].len) return;
    261  while (!stralloc_copys(&comm_buf[c],"")) nomem();
    262  ch = delnum;
    263  while (!stralloc_append(&comm_buf[c],&ch)) nomem();
    264  fnmake_split(id);
    265  while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
    266  while (!stralloc_0(&comm_buf[c])) nomem();
    267  senderadd(&comm_buf[c],sender,recip);
    268  while (!stralloc_0(&comm_buf[c])) nomem();
    269  while (!stralloc_cats(&comm_buf[c],recip)) nomem();
    270  while (!stralloc_0(&comm_buf[c])) nomem();
    271  comm_pos[c] = 0;
    272 }
    273 
    274 void comm_selprep(nfds,wfds)
    275 int *nfds;
    276 fd_set *wfds;
    277 {
    278  int c;
    279  for (c = 0;c < CHANNELS;++c)
    280    if (flagspawnalive[c])
    281      if (comm_buf[c].s && comm_buf[c].len)
    282       {
    283        FD_SET(chanfdout[c],wfds);
    284        if (*nfds <= chanfdout[c])
    285          *nfds = chanfdout[c] + 1;
    286       }
    287 }
    288 
    289 void comm_do(wfds)
    290 fd_set *wfds;
    291 {
    292  int c;
    293  for (c = 0;c < CHANNELS;++c)
    294    if (flagspawnalive[c])
    295      if (comm_buf[c].s && comm_buf[c].len)
    296        if (FD_ISSET(chanfdout[c],wfds))
    297         {
    298          int w;
    299          int len;
    300          len = comm_buf[c].len;
    301          w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]);
    302          if (w == 0 || w == -1)
    303 	  {
    304 	   if ((w == -1) && (errno == error_pipe))
    305 	     spawndied(c);
    306 	   else
    307 	     continue; /* kernel select() bug; can't avoid busy-looping */
    308 	  }
    309 	 else
    310 	  {
    311 	   comm_pos[c] += w;
    312 	   if (comm_pos[c] == len)
    313 	     comm_buf[c].len = 0;
    314 	  }
    315         }
    316 }
    317 
    318 
    319 /* this file is too long ------------------------------------------ CLEANUPS */
    320 
    321 int flagcleanup; /* if 1, cleanupdir is initialized and ready */
    322 readsubdir cleanupdir;
    323 datetime_sec cleanuptime;
    324 
    325 void cleanup_init()
    326 {
    327  flagcleanup = 0;
    328  cleanuptime = now();
    329 }
    330 
    331 void cleanup_selprep(wakeup)
    332 datetime_sec *wakeup;
    333 {
    334  if (flagcleanup) *wakeup = 0;
    335  if (*wakeup > cleanuptime) *wakeup = cleanuptime;
    336 }
    337 
    338 void cleanup_do()
    339 {
    340  char ch;
    341  struct stat st;
    342  unsigned long id;
    343 
    344  if (!flagcleanup)
    345   {
    346    if (recent < cleanuptime) return;
    347    readsubdir_init(&cleanupdir,"mess",pausedir);
    348    flagcleanup = 1;
    349   }
    350 
    351  switch(readsubdir_next(&cleanupdir,&id))
    352   {
    353    case 1:
    354      break;
    355    case 0:
    356      flagcleanup = 0;
    357      cleanuptime = recent + SLEEP_CLEANUP;
    358    default:
    359      return;
    360   }
    361 
    362  fnmake_mess(id);
    363  if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */
    364  if (recent <= st.st_atime + OSSIFIED) return;
    365 
    366  fnmake_info(id);
    367  if (stat(fn.s,&st) == 0) return;
    368  if (errno != error_noent) return;
    369  fnmake_todo(id);
    370  if (stat(fn.s,&st) == 0) return;
    371  if (errno != error_noent) return;
    372 
    373  fnmake_foop(id);
    374  if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
    375  if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
    376  if (ch != '+')
    377    log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
    378 }
    379 
    380 
    381 /* this file is too long ----------------------------------- PRIORITY QUEUES */
    382 
    383 prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */
    384 prioq pqchan[CHANNELS] = { {0}, {0} };
    385 /* pqchan 0: -todo +info +local ?remote */
    386 /* pqchan 1: -todo +info ?local +remote */
    387 prioq pqfail = {0}; /* stat() failure; has to be pqadded again */
    388 
    389 void pqadd(id)
    390 unsigned long id;
    391 {
    392  struct prioq_elt pe;
    393  struct prioq_elt pechan[CHANNELS];
    394  int flagchan[CHANNELS];
    395  struct stat st;
    396  int c;
    397 
    398 #define CHECKSTAT if (errno != error_noent) goto fail;
    399 
    400  fnmake_info(id);
    401  if (stat(fn.s,&st) == -1)
    402   {
    403    CHECKSTAT
    404    return; /* someone yanking our chain */
    405   }
    406 
    407  fnmake_todo(id);
    408  if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */
    409  CHECKSTAT
    410 
    411  for (c = 0;c < CHANNELS;++c)
    412   {
    413    fnmake_chanaddr(id,c);
    414    if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT }
    415    else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; }
    416   }
    417 
    418  for (c = 0;c < CHANNELS;++c)
    419    if (flagchan[c])
    420      while (!prioq_insert(&pqchan[c],&pechan[c])) nomem();
    421 
    422  for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
    423  if (c == CHANNELS)
    424   {
    425    pe.id = id; pe.dt = now();
    426    while (!prioq_insert(&pqdone,&pe)) nomem();
    427   }
    428 
    429  return;
    430 
    431  fail:
    432  log3("warning: unable to stat ",fn.s,"; will try again later\n");
    433  pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
    434  while (!prioq_insert(&pqfail,&pe)) nomem();
    435 }
    436 
    437 void pqstart()
    438 {
    439  readsubdir rs;
    440  int x;
    441  unsigned long id;
    442 
    443  readsubdir_init(&rs,"info",pausedir);
    444 
    445  while ((x = readsubdir_next(&rs,&id)))
    446    if (x > 0)
    447      pqadd(id);
    448 }
    449 
    450 void pqfinish()
    451 {
    452  int c;
    453  struct prioq_elt pe;
    454  struct timeval ut[2] = { 0 };
    455 
    456  for (c = 0;c < CHANNELS;++c)
    457    while (prioq_min(&pqchan[c],&pe))
    458     {
    459      prioq_delmin(&pqchan[c]);
    460      fnmake_chanaddr(pe.id,c);
    461      ut[0].tv_sec = ut[1].tv_sec = pe.dt;
    462      if (utimes(fn.s,ut) == -1)
    463        log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n");
    464     }
    465 }
    466 
    467 void pqrun()
    468 {
    469  int c;
    470  unsigned int i;
    471  for (c = 0;c < CHANNELS;++c)
    472    if (pqchan[c].p)
    473      if (pqchan[c].len)
    474        for (i = 0;i < pqchan[c].len;++i)
    475 	 pqchan[c].p[i].dt = recent;
    476 }
    477 
    478 
    479 /* this file is too long ---------------------------------------------- JOBS */
    480 
    481 struct job
    482  {
    483   int refs; /* if 0, this struct is unused */
    484   unsigned long id;
    485   int channel;
    486   datetime_sec retry;
    487   stralloc sender;
    488   int numtodo;
    489   int flaghiteof;
    490   int flagdying;
    491  }
    492 ;
    493 
    494 int numjobs;
    495 struct job *jo;
    496 
    497 void job_init()
    498 {
    499  int j;
    500  while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem();
    501  for (j = 0;j < numjobs;++j)
    502   {
    503    jo[j].refs = 0;
    504    jo[j].sender.s = 0;
    505   }
    506 }
    507 
    508 int job_avail()
    509 {
    510  int j;
    511  for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1;
    512  return 0;
    513 }
    514 
    515 int job_open(id,channel)
    516 unsigned long id;
    517 int channel;
    518 {
    519  int j;
    520  for (j = 0;j < numjobs;++j) if (!jo[j].refs) break;
    521  if (j == numjobs) return -1;
    522  jo[j].refs = 1;
    523  jo[j].id = id;
    524  jo[j].channel = channel;
    525  jo[j].numtodo = 0;
    526  jo[j].flaghiteof = 0;
    527  return j;
    528 }
    529 
    530 void job_close(j)
    531 int j;
    532 {
    533  struct prioq_elt pe;
    534  struct stat st;
    535 
    536  if (0 < --jo[j].refs) return;
    537 
    538  pe.id = jo[j].id;
    539  pe.dt = jo[j].retry;
    540  if (jo[j].flaghiteof && !jo[j].numtodo)
    541   {
    542    fnmake_chanaddr(jo[j].id,jo[j].channel);
    543    if (unlink(fn.s) == -1)
    544     {
    545      log3("warning: unable to unlink ",fn.s,"; will try again later\n");
    546      pe.dt = now() + SLEEP_SYSFAIL;
    547     }
    548    else
    549     {
    550      int c;
    551      for (c = 0;c < CHANNELS;++c) if (c != jo[j].channel)
    552       {
    553        fnmake_chanaddr(jo[j].id,c);
    554        if (stat(fn.s,&st) == 0) return; /* more channels going */
    555        if (errno != error_noent)
    556 	{
    557          log3("warning: unable to stat ",fn.s,"\n");
    558 	 break; /* this is the only reason for HOPEFULLY */
    559 	}
    560       }
    561      pe.dt = now();
    562      while (!prioq_insert(&pqdone,&pe)) nomem();
    563      return;
    564     }
    565   }
    566 
    567  while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem();
    568 }
    569 
    570 
    571 /* this file is too long ------------------------------------------- BOUNCES */
    572 
    573 char *stripvdomprepend(recip)
    574 char *recip;
    575 {
    576  unsigned int i;
    577  char *domain;
    578  unsigned int domainlen;
    579  char *prepend;
    580 
    581  i = str_rchr(recip,'@');
    582  if (!recip[i]) return recip;
    583  domain = recip + i + 1;
    584  domainlen = str_len(domain);
    585 
    586  for (i = 0;i <= domainlen;++i)
    587    if ((i == 0) || (i == domainlen) || (domain[i] == '.'))
    588      if ((prepend = constmap(&mapvdoms,domain + i,domainlen - i)))
    589       {
    590        if (!*prepend) break;
    591        i = str_len(prepend);
    592        if (str_diffn(recip,prepend,i)) break;
    593        if (recip[i] != '-') break;
    594        return recip + i + 1;
    595       }
    596  return recip;
    597 }
    598 
    599 stralloc bouncetext = {0};
    600 
    601 void addbounce(id,recip,report)
    602 unsigned long id;
    603 char *recip;
    604 char *report;
    605 {
    606  int fd;
    607  unsigned int pos;
    608  int w;
    609  while (!stralloc_copys(&bouncetext,"<")) nomem();
    610  while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem();
    611  for (pos = 0;pos < bouncetext.len;++pos)
    612    if (bouncetext.s[pos] == '\n')
    613      bouncetext.s[pos] = '_';
    614  while (!stralloc_cats(&bouncetext,">:\n")) nomem();
    615  while (!stralloc_cats(&bouncetext,report)) nomem();
    616  if (report[0])
    617    if (report[str_len(report) - 1] != '\n')
    618      while (!stralloc_cats(&bouncetext,"\n")) nomem();
    619  for (pos = bouncetext.len - 2;pos > 0;--pos)
    620    if (bouncetext.s[pos] == '\n')
    621      if (bouncetext.s[pos - 1] == '\n')
    622        bouncetext.s[pos] = '/';
    623  while (!stralloc_cats(&bouncetext,"\n")) nomem();
    624  fnmake2_bounce(id);
    625  for (;;)
    626   {
    627    fd = open_append(fn2.s);
    628    if (fd != -1) break;
    629    log1("alert: unable to append to bounce message; HELP! sleeping...\n");
    630    sleep(10);
    631   }
    632  pos = 0;
    633  while (pos < bouncetext.len)
    634   {
    635    w = write(fd,bouncetext.s + pos,bouncetext.len - pos);
    636    if (w == 0 || w == -1)
    637     {
    638      log1("alert: unable to append to bounce message; HELP! sleeping...\n");
    639      sleep(10);
    640     }
    641    else
    642      pos += w;
    643   }
    644  close(fd);
    645 }
    646 
    647 int injectbounce(id)
    648 unsigned long id;
    649 {
    650  struct qmail qqt;
    651  struct stat st;
    652  char *bouncesender;
    653  char *bouncerecip;
    654  int r;
    655  int fd;
    656  substdio ssread;
    657  char buf[128];
    658  char inbuf[128];
    659  static stralloc sender = {0};
    660  static stralloc quoted = {0};
    661  datetime_sec birth;
    662  unsigned long qp;
    663  char *qclose;
    664 
    665  if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */
    666 
    667  /* owner-@host-@[] -> owner-@host */
    668  if (sender.len >= 5)
    669    if (str_equal(sender.s + sender.len - 5,"-@[]"))
    670     {
    671      sender.len -= 4;
    672      sender.s[sender.len - 1] = 0;
    673     }
    674 
    675  fnmake2_bounce(id);
    676  fnmake_mess(id);
    677  if (stat(fn2.s,&st) == -1)
    678   {
    679    if (errno == error_noent)
    680      return 1;
    681    log3("warning: unable to stat ",fn2.s,"\n");
    682    return 0;
    683   }
    684  if (str_equal(sender.s,"#@[]"))
    685    log3("triple bounce: discarding ",fn2.s,"\n");
    686  else
    687   {
    688    if (qmail_open(&qqt) == -1)
    689     { log1("warning: unable to start qmail-queue, will try later\n"); return 0; }
    690    qp = qmail_qp(&qqt);
    691 
    692    if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
    693    else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
    694 
    695    while (!newfield_datemake(now())) nomem();
    696    qmail_put(&qqt,newfield_date.s,newfield_date.len);
    697    qmail_puts(&qqt,"From: ");
    698    while (!quote(&quoted,&bouncefrom)) nomem();
    699    qmail_put(&qqt,quoted.s,quoted.len);
    700    qmail_puts(&qqt,"@");
    701    qmail_put(&qqt,bouncehost.s,bouncehost.len);
    702    qmail_puts(&qqt,"\nTo: ");
    703    while (!quote2(&quoted,bouncerecip)) nomem();
    704    qmail_put(&qqt,quoted.s,quoted.len);
    705    qmail_puts(&qqt,"\n\
    706 Subject: Failure Notice\n\
    707 \n\
    708 Hi. This is the qmail-send (NightmareMail queue manager) program at ");
    709    qmail_put(&qqt,bouncehost.s,bouncehost.len);
    710    qmail_puts(&qqt,*sender.s ? ".\n\
    711 I'm afraid I wasn't able to deliver your message to the following addresses.\n\
    712 This is a permanent error. I've given up. Sorry it didn't work out.\n\
    713 For further assistance, mail the postmaster.\n\
    714 NB: If you're the postmaster, these errors should mean something to you.\n\
    715 \n\
    716 " : ".\n\
    717 I tried to deliver a bounce message to this address, but the bounce bounced!\n\
    718 \n\
    719 ");
    720 
    721    fd = open_read(fn2.s);
    722    if (fd == -1)
    723      qmail_fail(&qqt);
    724    else
    725     {
    726      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
    727      while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
    728        qmail_put(&qqt,buf,r);
    729      close(fd);
    730      if (r == -1)
    731        qmail_fail(&qqt);
    732     }
    733 
    734    qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n");
    735    qmail_puts(&qqt,"Return-Path: <");
    736    while (!quote2(&quoted,sender.s)) nomem();
    737    qmail_put(&qqt,quoted.s,quoted.len);
    738    qmail_puts(&qqt,">\n");
    739 
    740    fd = open_read(fn.s);
    741    if (fd == -1)
    742      qmail_fail(&qqt);
    743    else
    744     {
    745      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
    746      while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
    747        qmail_put(&qqt,buf,r);
    748      close(fd);
    749      if (r == -1)
    750        qmail_fail(&qqt);
    751     }
    752 
    753    qmail_from(&qqt,bouncesender);
    754    qmail_to(&qqt,bouncerecip);
    755    if (*(qclose = qmail_close(&qqt)))
    756     { log1("warning: trouble injecting bounce message, will try later: ");
    757       log1(qclose);
    758       log1("\n");
    759       return 0; }
    760 
    761    strnum2[fmt_ulong(strnum2,id)] = 0;
    762    qslog2("bounce msg ",strnum2);
    763    strnum2[fmt_ulong(strnum2,qp)] = 0;
    764    log3(" qp ",strnum2,"\n");
    765   }
    766  if (unlink(fn2.s) == -1)
    767   {
    768    log3("warning: unable to unlink ",fn2.s,"\n");
    769    return 0;
    770   }
    771  return 1;
    772 }
    773 
    774 
    775 /* this file is too long ---------------------------------------- DELIVERIES */
    776 
    777 struct del
    778  {
    779   int used;
    780   int j;
    781   unsigned long delid;
    782   seek_pos mpos;
    783   stralloc recip;
    784  }
    785 ;
    786 
    787 unsigned long masterdelid = 1;
    788 unsigned int concurrency[CHANNELS] = { 10, 20 };
    789 unsigned int concurrencyused[CHANNELS] = { 0, 0 };
    790 struct del *d[CHANNELS];
    791 stralloc dline[CHANNELS];
    792 char delbuf[2048];
    793 
    794 void del_status()
    795 {
    796   int c;
    797 
    798   log1("status:");
    799   for (c = 0;c < CHANNELS;++c) {
    800     strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0;
    801     strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0;
    802     qslog2(chanstatusmsg[c],strnum2);
    803     qslog2("/",strnum3);
    804   }
    805   if (flagexitasap) log1(" exitasap");
    806   log1("\n");
    807 }
    808 
    809 void del_init()
    810 {
    811  int c;
    812  unsigned int i;
    813  for (c = 0;c < CHANNELS;++c)
    814   {
    815    flagspawnalive[c] = 1;
    816    while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del))))
    817      nomem();
    818    for (i = 0;i < concurrency[c];++i)
    819     { d[c][i].used = 0; d[c][i].recip.s = 0; }
    820    dline[c].s = 0;
    821    while (!stralloc_copys(&dline[c],"")) nomem();
    822   }
    823  del_status();
    824 }
    825 
    826 int del_canexit()
    827 {
    828  int c;
    829  for (c = 0;c < CHANNELS;++c)
    830    if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */
    831      if (concurrencyused[c]) return 0;
    832  return 1;
    833 }
    834 
    835 int del_avail(c)
    836 int c;
    837 {
    838   return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]);
    839 }
    840 
    841 void del_start(j,mpos,recip)
    842 int j;
    843 seek_pos mpos;
    844 char *recip;
    845 {
    846  unsigned int i;
    847  int c;
    848 
    849  c = jo[j].channel;
    850  if (!flagspawnalive[c]) return;
    851  if (!comm_canwrite(c)) return;
    852 
    853  for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) break;
    854  if (i == concurrency[c]) return;
    855 
    856  if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; }
    857  if (!stralloc_0(&d[c][i].recip)) { nomem(); return; }
    858  d[c][i].j = j; ++jo[j].refs;
    859  d[c][i].delid = masterdelid++;
    860  d[c][i].mpos = mpos;
    861  d[c][i].used = 1; ++concurrencyused[c];
    862 
    863  comm_write(c,i,jo[j].id,jo[j].sender.s,recip);
    864 
    865  strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0;
    866  strnum3[fmt_ulong(strnum3,jo[j].id)] = 0;
    867  qslog2("starting delivery ",strnum2);
    868  log3(": msg ",strnum3,tochan[c]);
    869  logsafe(recip);
    870  log1("\n");
    871  del_status();
    872 }
    873 
    874 void markdone(c,id,pos)
    875 int c;
    876 unsigned long id;
    877 seek_pos pos;
    878 {
    879  struct stat st;
    880  int fd;
    881  fnmake_chanaddr(id,c);
    882  for (;;)
    883   {
    884    fd = open_write(fn.s);
    885    if (fd == -1) break;
    886    if (fstat(fd,&st) == -1) { close(fd); break; }
    887    if (seek_set(fd,pos) == -1) { close(fd); break; }
    888    if (write(fd,"D",1) != 1) { close(fd); break; }
    889    /* further errors -> double delivery without us knowing about it, oh well */
    890    close(fd);
    891    return;
    892   }
    893  log3("warning: trouble marking ",fn.s,"; message will be delivered twice!\n");
    894 }
    895 
    896 void del_dochan(c)
    897 int c;
    898 {
    899  int r;
    900  char ch;
    901  int i;
    902  int delnum;
    903  r = read(chanfdin[c],delbuf,sizeof(delbuf));
    904  if (r == -1) return;
    905  if (r == 0) { spawndied(c); return; }
    906  for (i = 0;i < r;++i)
    907   {
    908    ch = delbuf[i];
    909    while (!stralloc_append(&dline[c],&ch)) nomem();
    910    if (dline[c].len > REPORTMAX)
    911      dline[c].len = REPORTMAX;
    912      /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
    913      /* but from a security point of view, we don't trust rspawn */
    914    if (!ch && (dline[c].len > 1))
    915     {
    916      delnum = (unsigned int) (unsigned char) dline[c].s[0];
    917      if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
    918        log1("warning: internal error: delivery report out of range\n");
    919      else
    920       {
    921        strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
    922        if (dline[c].s[1] == 'Z')
    923 	 if (jo[d[c][delnum].j].flagdying)
    924 	  {
    925 	   dline[c].s[1] = 'D';
    926 	   --dline[c].len;
    927 	   while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
    928 	   while (!stralloc_0(&dline[c])) nomem();
    929 	  }
    930        switch(dline[c].s[1])
    931 	{
    932 	 case 'K':
    933 	   log3("delivery ",strnum3,": success: ");
    934 	   logsafe(dline[c].s + 2);
    935 	   log1("\n");
    936 	   markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
    937 	   --jo[d[c][delnum].j].numtodo;
    938 	   break;
    939 	 case 'Z':
    940 	   log3("delivery ",strnum3,": deferral: ");
    941 	   logsafe(dline[c].s + 2);
    942 	   log1("\n");
    943 	   break;
    944 	 case 'D':
    945 	   log3("delivery ",strnum3,": failure: ");
    946 	   logsafe(dline[c].s + 2);
    947 	   log1("\n");
    948 	   addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
    949 	   markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
    950 	   --jo[d[c][delnum].j].numtodo;
    951 	   break;
    952 	 default:
    953 	   log3("delivery ",strnum3,": report mangled, will defer\n");
    954 	}
    955        job_close(d[c][delnum].j);
    956        d[c][delnum].used = 0; --concurrencyused[c];
    957        del_status();
    958       }
    959      dline[c].len = 0;
    960     }
    961   }
    962 }
    963 
    964 void del_selprep(nfds,rfds)
    965 int *nfds;
    966 fd_set *rfds;
    967 {
    968  int c;
    969  for (c = 0;c < CHANNELS;++c)
    970    if (flagspawnalive[c])
    971     {
    972      FD_SET(chanfdin[c],rfds);
    973      if (*nfds <= chanfdin[c])
    974        *nfds = chanfdin[c] + 1;
    975     }
    976 }
    977 
    978 void del_do(rfds)
    979 fd_set *rfds;
    980 {
    981  int c;
    982  for (c = 0;c < CHANNELS;++c)
    983    if (flagspawnalive[c])
    984      if (FD_ISSET(chanfdin[c],rfds))
    985        del_dochan(c);
    986 }
    987 
    988 
    989 /* this file is too long -------------------------------------------- PASSES */
    990 
    991 struct
    992  {
    993   unsigned long id; /* if 0, need a new pass */
    994   int j; /* defined if id; job number */
    995   int fd; /* defined if id; reading from {local,remote} */
    996   seek_pos mpos; /* defined if id; mark position */
    997   substdio ss;
    998   char buf[128];
    999  }
   1000 pass[CHANNELS];
   1001 
   1002 void pass_init()
   1003 {
   1004  int c;
   1005  for (c = 0;c < CHANNELS;++c) pass[c].id = 0;
   1006 }
   1007 
   1008 void pass_selprep(wakeup)
   1009 datetime_sec *wakeup;
   1010 {
   1011  int c;
   1012  struct prioq_elt pe;
   1013  if (flagexitasap) return;
   1014  for (c = 0;c < CHANNELS;++c)
   1015    if (pass[c].id)
   1016      if (del_avail(c))
   1017       { *wakeup = 0; return; }
   1018  if (job_avail())
   1019    for (c = 0;c < CHANNELS;++c)
   1020      if (!pass[c].id)
   1021        if (prioq_min(&pqchan[c],&pe))
   1022          if (*wakeup > pe.dt)
   1023            *wakeup = pe.dt;
   1024  if (prioq_min(&pqfail,&pe))
   1025    if (*wakeup > pe.dt)
   1026      *wakeup = pe.dt;
   1027  if (prioq_min(&pqdone,&pe))
   1028    if (*wakeup > pe.dt)
   1029      *wakeup = pe.dt;
   1030 }
   1031 
   1032 static datetime_sec squareroot(x) /* result^2 <= x < (result + 1)^2 */
   1033 datetime_sec x; /* assuming: >= 0 */
   1034 {
   1035  datetime_sec y;
   1036  datetime_sec yy;
   1037  datetime_sec y21;
   1038  int j;
   1039 
   1040  y = 0; yy = 0;
   1041  for (j = 15;j >= 0;--j)
   1042   {
   1043    y21 = (y << (j + 1)) + (1 << (j + j));
   1044    if (y21 <= x - yy) { y += (1 << j); yy += y21; }
   1045   }
   1046  return y;
   1047 }
   1048 
   1049 datetime_sec nextretry(birth,c)
   1050 datetime_sec birth;
   1051 int c;
   1052 {
   1053  datetime_sec n;
   1054 
   1055  if (birth > recent) n = 0;
   1056  else n = squareroot(recent - birth); /* no need to add fuzz to recent */
   1057  n += chanskip[c];
   1058  return birth + n * n;
   1059 }
   1060 
   1061 void pass_dochan(c)
   1062 int c;
   1063 {
   1064  datetime_sec birth;
   1065  struct prioq_elt pe;
   1066  static stralloc line = {0};
   1067  int match;
   1068 
   1069  if (flagexitasap) return;
   1070 
   1071  if (!pass[c].id)
   1072   {
   1073    if (!job_avail()) return;
   1074    if (!prioq_min(&pqchan[c],&pe)) return;
   1075    if (pe.dt > recent) return;
   1076    fnmake_chanaddr(pe.id,c);
   1077 
   1078    prioq_delmin(&pqchan[c]);
   1079    pass[c].mpos = 0;
   1080    pass[c].fd = open_read(fn.s);
   1081    if (pass[c].fd == -1) goto trouble;
   1082    if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; }
   1083    pass[c].id = pe.id;
   1084    substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf));
   1085    pass[c].j = job_open(pe.id,c);
   1086    jo[pass[c].j].retry = nextretry(birth,c);
   1087    jo[pass[c].j].flagdying = (recent > birth + lifetime);
   1088    while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem();
   1089   }
   1090 
   1091  if (!del_avail(c)) return;
   1092 
   1093  if (getln(&pass[c].ss,&line,&match,'\0') == -1)
   1094   {
   1095    fnmake_chanaddr(pass[c].id,c);
   1096    log3("warning: trouble reading ",fn.s,"; will try again later\n");
   1097    close(pass[c].fd);
   1098    job_close(pass[c].j);
   1099    pass[c].id = 0;
   1100    return;
   1101   }
   1102  if (!match)
   1103   {
   1104    close(pass[c].fd);
   1105    jo[pass[c].j].flaghiteof = 1;
   1106    job_close(pass[c].j);
   1107    pass[c].id = 0;
   1108    return;
   1109   }
   1110  switch(line.s[0])
   1111   {
   1112    case 'T':
   1113      ++jo[pass[c].j].numtodo;
   1114      del_start(pass[c].j,pass[c].mpos,line.s + 1);
   1115      break;
   1116    case 'D':
   1117      break;
   1118    default:
   1119      fnmake_chanaddr(pass[c].id,c);
   1120      log3("warning: unknown record type in ",fn.s,"!\n");
   1121      close(pass[c].fd);
   1122      job_close(pass[c].j);
   1123      pass[c].id = 0;
   1124      return;
   1125   }
   1126 
   1127  pass[c].mpos += line.len;
   1128  return;
   1129 
   1130  trouble:
   1131  log3("warning: trouble opening ",fn.s,"; will try again later\n");
   1132  pe.dt = recent + SLEEP_SYSFAIL;
   1133  while (!prioq_insert(&pqchan[c],&pe)) nomem();
   1134 }
   1135 
   1136 void messdone(id)
   1137 unsigned long id;
   1138 {
   1139  char ch;
   1140  int c;
   1141  struct prioq_elt pe;
   1142  struct stat st;
   1143 
   1144  for (c = 0;c < CHANNELS;++c)
   1145   {
   1146    fnmake_chanaddr(id,c);
   1147    if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */
   1148    if (errno != error_noent)
   1149     {
   1150      log3("warning: unable to stat ",fn.s,"; will try again later\n");
   1151      goto fail;
   1152     }
   1153   }
   1154 
   1155  fnmake_todo(id);
   1156  if (stat(fn.s,&st) == 0) return;
   1157  if (errno != error_noent)
   1158   {
   1159    log3("warning: unable to stat ",fn.s,"; will try again later\n");
   1160    goto fail;
   1161   }
   1162  
   1163  fnmake_info(id);
   1164  if (stat(fn.s,&st) == -1)
   1165   {
   1166    if (errno == error_noent) return;
   1167    log3("warning: unable to stat ",fn.s,"; will try again later\n");
   1168    goto fail;
   1169   }
   1170  
   1171  /* -todo +info -local -remote ?bounce */
   1172  if (!injectbounce(id))
   1173    goto fail; /* injectbounce() produced error message */
   1174 
   1175  strnum3[fmt_ulong(strnum3,id)] = 0;
   1176  log3("end msg ",strnum3,"\n");
   1177 
   1178  /* -todo +info -local -remote -bounce */
   1179  fnmake_info(id);
   1180  if (unlink(fn.s) == -1)
   1181   {
   1182    log3("warning: unable to unlink ",fn.s,"; will try again later\n");
   1183    goto fail;
   1184   }
   1185 
   1186  /* -todo -info -local -remote -bounce; we can relax */
   1187  fnmake_foop(id);
   1188  if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
   1189  if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
   1190  if (ch != '+')
   1191    log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
   1192 
   1193  return;
   1194 
   1195  fail:
   1196  pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
   1197  while (!prioq_insert(&pqdone,&pe)) nomem();
   1198 }
   1199 
   1200 void pass_do()
   1201 {
   1202  int c;
   1203  struct prioq_elt pe;
   1204 
   1205  for (c = 0;c < CHANNELS;++c) pass_dochan(c);
   1206  if (prioq_min(&pqfail,&pe))
   1207    if (pe.dt <= recent)
   1208     {
   1209      prioq_delmin(&pqfail);
   1210      pqadd(pe.id);
   1211     }
   1212  if (prioq_min(&pqdone,&pe))
   1213    if (pe.dt <= recent)
   1214     {
   1215      prioq_delmin(&pqdone);
   1216      messdone(pe.id);
   1217     }
   1218 }
   1219 
   1220 
   1221 /* this file is too long ---------------------------------------------- TODO */
   1222 
   1223 datetime_sec nexttodorun;
   1224 DIR *tododir; /* if 0, have to opendir again */
   1225 stralloc todoline = {0};
   1226 char todobuf[SUBSTDIO_INSIZE];
   1227 char todobufinfo[512];
   1228 char todobufchan[CHANNELS][1024];
   1229 
   1230 void todo_init()
   1231 {
   1232  tododir = 0;
   1233  nexttodorun = now();
   1234  trigger_set();
   1235 }
   1236 
   1237 void todo_selprep(nfds,rfds,wakeup)
   1238 int *nfds;
   1239 fd_set *rfds;
   1240 datetime_sec *wakeup;
   1241 {
   1242  if (flagexitasap) return;
   1243  trigger_selprep(nfds,rfds);
   1244  if (tododir) *wakeup = 0;
   1245  if (*wakeup > nexttodorun) *wakeup = nexttodorun;
   1246 }
   1247 
   1248 void todo_do(rfds)
   1249 fd_set *rfds;
   1250 {
   1251  struct stat st;
   1252  substdio ss; int fd;
   1253  substdio ssinfo; int fdinfo;
   1254  substdio sschan[CHANNELS];
   1255  int fdchan[CHANNELS];
   1256  int flagchan[CHANNELS];
   1257  struct prioq_elt pe;
   1258  char ch;
   1259  int match;
   1260  unsigned long id;
   1261  unsigned int len;
   1262  direntry *dent;
   1263  int c;
   1264  unsigned long uid;
   1265  unsigned long pid;
   1266 
   1267  fd = -1;
   1268  fdinfo = -1;
   1269  for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
   1270 
   1271  if (flagexitasap) return;
   1272 
   1273  if (!tododir)
   1274   {
   1275    if (!trigger_pulled(rfds))
   1276      if (recent < nexttodorun)
   1277        return;
   1278    trigger_set();
   1279    tododir = opendir("todo");
   1280    if (!tododir)
   1281     {
   1282      pausedir("todo");
   1283      return;
   1284     }
   1285    nexttodorun = recent + SLEEP_TODO;
   1286   }
   1287 
   1288  dent = readdir(tododir);
   1289  if (!dent)
   1290   {
   1291    closedir(tododir);
   1292    tododir = 0;
   1293    return;
   1294   }
   1295  if (str_equal(dent->d_name,".")) return;
   1296  if (str_equal(dent->d_name,"..")) return;
   1297  len = scan_ulong(dent->d_name,&id);
   1298  if (!len || dent->d_name[len]) return;
   1299 
   1300  fnmake_todo(id);
   1301 
   1302  fd = open_read(fn.s);
   1303  if (fd == -1) { log3("warning: unable to open ",fn.s,"\n"); return; }
   1304 
   1305  fnmake_mess(id);
   1306  /* just for the statistics */
   1307  if (stat(fn.s,&st) == -1)
   1308   { log3("warning: unable to stat ",fn.s,"\n"); goto fail; }
   1309 
   1310  for (c = 0;c < CHANNELS;++c)
   1311   {
   1312    fnmake_chanaddr(id,c);
   1313    if (unlink(fn.s) == -1) if (errno != error_noent)
   1314     { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
   1315   }
   1316 
   1317  fnmake_info(id);
   1318  if (unlink(fn.s) == -1) if (errno != error_noent)
   1319   { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
   1320 
   1321  fdinfo = open_excl(fn.s);
   1322  if (fdinfo == -1)
   1323   { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
   1324 
   1325  strnum3[fmt_ulong(strnum3,id)] = 0;
   1326  log3("new msg ",strnum3,"\n");
   1327 
   1328  for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
   1329 
   1330  substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
   1331  substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
   1332 
   1333  uid = 0;
   1334  pid = 0;
   1335 
   1336  for (;;)
   1337   {
   1338    if (getln(&ss,&todoline,&match,'\0') == -1)
   1339     {
   1340      /* perhaps we're out of memory, perhaps an I/O error */
   1341      fnmake_todo(id);
   1342      log3("warning: trouble reading ",fn.s,"\n"); goto fail;
   1343     }
   1344    if (!match) break;
   1345 
   1346    switch(todoline.s[0])
   1347     {
   1348      case 'u':
   1349        scan_ulong(todoline.s + 1,&uid);
   1350        break;
   1351      case 'p':
   1352        scan_ulong(todoline.s + 1,&pid);
   1353        break;
   1354      case 'F':
   1355        if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
   1356 	{
   1357 	 fnmake_info(id);
   1358          log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
   1359 	}
   1360        qslog2("info msg ",strnum3);
   1361        strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0;
   1362        qslog2(": bytes ",strnum2);
   1363        log1(" from <"); logsafe(todoline.s + 1);
   1364        strnum2[fmt_ulong(strnum2,pid)] = 0;
   1365        qslog2("> qp ",strnum2);
   1366        strnum2[fmt_ulong(strnum2,uid)] = 0;
   1367        qslog2(" uid ",strnum2);
   1368        log1("\n");
   1369        break;
   1370      case 'T':
   1371        switch(rewrite(todoline.s + 1))
   1372 	{
   1373 	 case 0: nomem(); goto fail;
   1374 	 case 2: c = 1; break;
   1375 	 default: c = 0; break;
   1376         }
   1377        if (fdchan[c] == -1)
   1378 	{
   1379 	 fnmake_chanaddr(id,c);
   1380 	 fdchan[c] = open_excl(fn.s);
   1381 	 if (fdchan[c] == -1)
   1382           { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
   1383 	 substdio_fdbuf(&sschan[c]
   1384 	   ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
   1385 	 flagchan[c] = 1;
   1386 	}
   1387        if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
   1388         {
   1389 	 fnmake_chanaddr(id,c);
   1390          log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
   1391         }
   1392        break;
   1393      default:
   1394        fnmake_todo(id);
   1395        log3("warning: unknown record type in ",fn.s,"\n"); goto fail;
   1396     }
   1397   }
   1398 
   1399  close(fd); fd = -1;
   1400 
   1401  fnmake_info(id);
   1402  if (substdio_flush(&ssinfo) == -1)
   1403   { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
   1404  if (fsync(fdinfo) == -1)
   1405   { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
   1406  close(fdinfo); fdinfo = -1;
   1407 
   1408  for (c = 0;c < CHANNELS;++c)
   1409    if (fdchan[c] != -1)
   1410     {
   1411      fnmake_chanaddr(id,c);
   1412      if (substdio_flush(&sschan[c]) == -1)
   1413       { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
   1414      if (fsync(fdchan[c]) == -1)
   1415       { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
   1416      close(fdchan[c]); fdchan[c] = -1;
   1417     }
   1418 
   1419  fnmake_todo(id);
   1420  if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
   1421  if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
   1422  if (ch != '+')
   1423   {
   1424    log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
   1425    return;
   1426   }
   1427 
   1428  pe.id = id; pe.dt = now();
   1429  for (c = 0;c < CHANNELS;++c)
   1430    if (flagchan[c])
   1431      while (!prioq_insert(&pqchan[c],&pe)) nomem();
   1432 
   1433  for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
   1434  if (c == CHANNELS)
   1435    while (!prioq_insert(&pqdone,&pe)) nomem();
   1436 
   1437  return;
   1438 
   1439  fail:
   1440  if (fd != -1) close(fd);
   1441  if (fdinfo != -1) close(fdinfo);
   1442  for (c = 0;c < CHANNELS;++c)
   1443    if (fdchan[c] != -1) close(fdchan[c]);
   1444 }
   1445 
   1446 
   1447 /* this file is too long ---------------------------------------------- MAIN */
   1448 
   1449 int getcontrols() {
   1450  if (control_init() == -1) return 0;
   1451  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
   1452  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
   1453  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
   1454  if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
   1455  if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
   1456  if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
   1457  if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
   1458  if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
   1459  if (!stralloc_cats(&doublebounceto,"@")) return 0;
   1460  if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
   1461  if (!stralloc_0(&doublebounceto)) return 0;
   1462  if (control_readfile(&locals,"control/locals",1) != 1) return 0;
   1463  if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
   1464  switch(control_readfile(&percenthack,"control/percenthack",0))
   1465   {
   1466    case -1: return 0;
   1467    case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
   1468    case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
   1469   }
   1470  switch(control_readfile(&vdoms,"control/virtualdomains",0))
   1471   {
   1472    case -1: return 0;
   1473    case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
   1474    case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
   1475   }
   1476  return 1;
   1477 }
   1478 
   1479 stralloc newlocals = {0};
   1480 stralloc newvdoms = {0};
   1481 
   1482 void regetcontrols()
   1483 {
   1484  int r;
   1485 
   1486  if (control_readfile(&newlocals,"control/locals",1) != 1)
   1487   { log1("alert: unable to reread control/locals\n"); return; }
   1488  r = control_readfile(&newvdoms,"control/virtualdomains",0);
   1489  if (r == -1)
   1490   { log1("alert: unable to reread control/virtualdomains\n"); return; }
   1491 
   1492  constmap_free(&maplocals);
   1493  constmap_free(&mapvdoms);
   1494 
   1495  while (!stralloc_copy(&locals,&newlocals)) nomem();
   1496  while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
   1497 
   1498  if (r)
   1499   {
   1500    while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
   1501    while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
   1502   }
   1503  else
   1504    while (!constmap_init(&mapvdoms,"",0,1)) nomem();
   1505 }
   1506 
   1507 void reread()
   1508 {
   1509  if (chdir(auto_qmail) == -1)
   1510   {
   1511    log1("alert: unable to reread controls: unable to switch to home directory\n");
   1512    return;
   1513   }
   1514  regetcontrols();
   1515  while (chdir("queue") == -1)
   1516   {
   1517    log1("alert: unable to switch back to queue directory; HELP! sleeping...\n");
   1518    sleep(10);
   1519   }
   1520 }
   1521 
   1522 int main(void)
   1523 {
   1524  int fd;
   1525  datetime_sec wakeup;
   1526  fd_set rfds;
   1527  fd_set wfds;
   1528  int nfds;
   1529  struct timeval tv;
   1530  int c;
   1531 
   1532  if (chdir(auto_qmail) == -1)
   1533   { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); }
   1534  if (!getcontrols())
   1535   { log1("alert: cannot start: unable to read controls\n"); _exit(111); }
   1536  if (chdir("queue") == -1)
   1537   { log1("alert: cannot start: unable to switch to queue directory\n"); _exit(111); }
   1538  sig_pipeignore();
   1539  sig_termcatch(sigterm);
   1540  sig_alarmcatch(sigalrm);
   1541  sig_hangupcatch(sighup);
   1542  sig_childdefault();
   1543  umask(077);
   1544 
   1545  fd = open_write("lock/sendmutex");
   1546  if (fd == -1)
   1547   { log1("alert: cannot start: unable to open mutex\n"); _exit(111); }
   1548  if (lock_exnb(fd) == -1)
   1549   { log1("alert: cannot start: qmail-send is already running\n"); _exit(111); }
   1550 
   1551  numjobs = 0;
   1552  for (c = 0;c < CHANNELS;++c)
   1553   {
   1554    char ch;
   1555    int u;
   1556    int r;
   1557    do
   1558      r = read(chanfdin[c],&ch,1);
   1559    while ((r == -1) && (errno == error_intr));
   1560    if (r < 1)
   1561     { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
   1562    u = (unsigned int) (unsigned char) ch;
   1563    if (concurrency[c] > u) concurrency[c] = u;
   1564    numjobs += concurrency[c];
   1565   }
   1566 
   1567  fnmake_init();
   1568 
   1569  comm_init();
   1570 
   1571  pqstart();
   1572  job_init();
   1573  del_init();
   1574  pass_init();
   1575  todo_init();
   1576  cleanup_init();
   1577 
   1578  while (!flagexitasap || !del_canexit())
   1579   {
   1580    recent = now();
   1581 
   1582    if (flagrunasap) { flagrunasap = 0; pqrun(); }
   1583    if (flagreadasap) { flagreadasap = 0; reread(); }
   1584 
   1585    wakeup = recent + SLEEP_FOREVER;
   1586    FD_ZERO(&rfds);
   1587    FD_ZERO(&wfds);
   1588    nfds = 1;
   1589 
   1590    comm_selprep(&nfds,&wfds);
   1591    del_selprep(&nfds,&rfds);
   1592    pass_selprep(&wakeup);
   1593    todo_selprep(&nfds,&rfds,&wakeup);
   1594    cleanup_selprep(&wakeup);
   1595 
   1596    if (wakeup <= recent) tv.tv_sec = 0;
   1597    else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
   1598    tv.tv_usec = 0;
   1599 
   1600    if (select(nfds,&rfds,&wfds,NULL,&tv) == -1)
   1601      if (errno == error_intr)
   1602        ;
   1603      else
   1604        log1("warning: trouble in select\n");
   1605    else
   1606     {
   1607      recent = now();
   1608 
   1609      comm_do(&wfds);
   1610      del_do(&rfds);
   1611      todo_do(&rfds);
   1612      pass_do();
   1613      cleanup_do();
   1614     }
   1615   }
   1616  pqfinish();
   1617  log1("status: exiting\n");
   1618  _exit(0);
   1619 }