nightmaremail

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

qmail-inject.c (19165B)


      1 #include "sig.h"
      2 #include "substdio.h"
      3 #include "stralloc.h"
      4 #include "subfd.h"
      5 #include "sgetopt.h"
      6 #include "getln.h"
      7 #include "str.h"
      8 #include "fmt.h"
      9 #include "hfield.h"
     10 #include "token822.h"
     11 #include "control.h"
     12 #include "datetime.h"
     13 #include "env.h"
     14 #include "gen_alloc.h"
     15 #include "gen_allocdefs.h"
     16 #include "error.h"
     17 #include "qmail.h"
     18 #include "now.h"
     19 #include "exit.h"
     20 #include "noreturn.h"
     21 #include "quote.h"
     22 #include "headerbody.h"
     23 #include "auto_qmail.h"
     24 #include "newfield.h"
     25 #include "constmap.h"
     26 
     27 #define LINELEN 80
     28 
     29 datetime_sec starttime;
     30 
     31 char *qmopts;
     32 int flagdeletesender = 0;
     33 int flagdeletefrom = 0;
     34 int flagdeletemessid = 0;
     35 int flagnamecomment = 0;
     36 int flaghackmess = 0;
     37 int flaghackrecip = 0;
     38 char *mailhost;
     39 char *mailuser;
     40 int mailusertokentype;
     41 char *mailrhost;
     42 char *mailruser;
     43 
     44 stralloc control_idhost = {0};
     45 stralloc control_defaultdomain = {0};
     46 stralloc control_defaulthost = {0};
     47 stralloc control_plusdomain = {0};
     48 
     49 stralloc sender = {0};
     50 stralloc envsbuf = {0};
     51 token822_alloc envs = {0};
     52 int flagrh;
     53 
     54 int flagqueue;
     55 struct qmail qqt;
     56 
     57 void put(s,len) char *s; int len;
     58 { if (flagqueue) qmail_put(&qqt,s,len); else substdio_put(subfdout,s,len); }
     59 void puts(s) char *s; { put(s,str_len(s)); }
     60 
     61 void _noreturn_ perm() { _exit(100); }
     62 void _noreturn_ temp() { _exit(111); }
     63 void _noreturn_ die_nomem() {
     64  substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); }
     65 void _noreturn_ die_invalid(sa) stralloc *sa; {
     66  substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: ");
     67  substdio_putflush(subfderr,sa->s,sa->len); perm(); }
     68 void _noreturn_ die_qqt() {
     69  substdio_putsflush(subfderr,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); }
     70 void _noreturn_ die_chdir() {
     71  substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); }
     72 void _noreturn_ die_read() {
     73  if (errno == error_nomem) die_nomem();
     74  substdio_putsflush(subfderr,"qmail-inject: fatal: read error\n"); temp(); }
     75 void doordie(sa,r) stralloc *sa; int r; {
     76  if (r == 1) return; if (r == -1) die_nomem();
     77  substdio_putsflush(subfderr,"qmail-inject: fatal: unable to parse this line:\n");
     78  substdio_putflush(subfderr,sa->s,sa->len); perm(); }
     79 
     80 GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
     81 GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,10,saa_readyplus)
     82 
     83 static stralloc sauninit = {0};
     84 
     85 saa savedh = {0};
     86 saa hrlist = {0};
     87 saa tocclist = {0};
     88 saa hrrlist = {0};
     89 saa reciplist = {0};
     90 int flagresent;
     91 
     92 void _noreturn_ exitnicely()
     93 {
     94  char *qqx;
     95 
     96  if (!flagqueue) substdio_flush(subfdout);
     97 
     98  if (flagqueue)
     99   {
    100    int i;
    101 
    102    if (!stralloc_0(&sender)) die_nomem();
    103    qmail_from(&qqt,sender.s);
    104 
    105    for (i = 0;i < reciplist.len;++i)
    106     {
    107      if (!stralloc_0(&reciplist.sa[i])) die_nomem();
    108      qmail_to(&qqt,reciplist.sa[i].s);
    109     }
    110    if (flagrh) {
    111      if (flagresent)
    112        for (i = 0;i < hrrlist.len;++i)
    113 	{
    114          if (!stralloc_0(&hrrlist.sa[i])) die_nomem();
    115 	 qmail_to(&qqt,hrrlist.sa[i].s);
    116 	}
    117      else
    118        for (i = 0;i < hrlist.len;++i)
    119 	{
    120          if (!stralloc_0(&hrlist.sa[i])) die_nomem();
    121 	 qmail_to(&qqt,hrlist.sa[i].s);
    122 	}
    123    }
    124 
    125    qqx = qmail_close(&qqt);
    126    if (*qqx) {
    127      if (*qqx == 'D') {
    128        substdio_puts(subfderr,"qmail-inject: fatal: ");
    129        substdio_puts(subfderr,qqx + 1);
    130        substdio_puts(subfderr,"\n");
    131        substdio_flush(subfderr);
    132        perm();
    133      }
    134      else {
    135        substdio_puts(subfderr,"qmail-inject: fatal: ");
    136        substdio_puts(subfderr,qqx + 1);
    137        substdio_puts(subfderr,"\n");
    138        substdio_flush(subfderr);
    139        temp();
    140      }
    141    }
    142   }
    143 
    144  _exit(0);
    145 }
    146 
    147 void savedh_append(h)
    148 stralloc *h;
    149 {
    150  if (!saa_readyplus(&savedh,1)) die_nomem();
    151  savedh.sa[savedh.len] = sauninit;
    152  if (!stralloc_copy(savedh.sa + savedh.len,h)) die_nomem();
    153  ++savedh.len;
    154 }
    155 
    156 void savedh_print()
    157 {
    158  int i;
    159 
    160  for (i = 0;i < savedh.len;++i)
    161    put(savedh.sa[i].s,savedh.sa[i].len);
    162 }
    163 
    164 stralloc defaultdomainbuf = {0};
    165 token822_alloc defaultdomain = {0};
    166 stralloc defaulthostbuf = {0};
    167 token822_alloc defaulthost = {0};
    168 stralloc plusdomainbuf = {0};
    169 token822_alloc plusdomain = {0};
    170 
    171 void rwroute(addr)
    172 token822_alloc *addr;
    173 {
    174  if (addr->t[addr->len - 1].type == TOKEN822_AT)
    175    while (addr->len)
    176      if (addr->t[--addr->len].type == TOKEN822_COLON)
    177        return;
    178 }
    179 
    180 void rwextraat(addr)
    181 token822_alloc *addr;
    182 {
    183  int i;
    184  if (addr->t[0].type == TOKEN822_AT)
    185   {
    186    --addr->len;
    187    for (i = 0;i < addr->len;++i)
    188      addr->t[i] = addr->t[i + 1];
    189   }
    190 }
    191 
    192 void rwextradot(addr)
    193 token822_alloc *addr;
    194 {
    195  int i;
    196  if (addr->t[0].type == TOKEN822_DOT)
    197   {
    198    --addr->len;
    199    for (i = 0;i < addr->len;++i)
    200      addr->t[i] = addr->t[i + 1];
    201   }
    202 }
    203 
    204 void rwnoat(addr)
    205 token822_alloc *addr;
    206 {
    207  int i;
    208  int shift;
    209 
    210  for (i = 0;i < addr->len;++i)
    211    if (addr->t[i].type == TOKEN822_AT)
    212      return;
    213  shift = defaulthost.len;
    214  if (!token822_readyplus(addr,shift)) die_nomem();
    215  for (i = addr->len - 1;i >= 0;--i)
    216    addr->t[i + shift] = addr->t[i];
    217  addr->len += shift;
    218  for (i = 0;i < shift;++i)
    219    addr->t[i] = defaulthost.t[shift - 1 - i];
    220 }
    221 
    222 void rwnodot(addr)
    223 token822_alloc *addr;
    224 {
    225  int i;
    226  int shift;
    227  for (i = 0;i < addr->len;++i)
    228   {
    229    if (addr->t[i].type == TOKEN822_DOT)
    230      return;
    231    if (addr->t[i].type == TOKEN822_AT)
    232      break;
    233   }
    234  for (i = 0;i < addr->len;++i)
    235   {
    236    if (addr->t[i].type == TOKEN822_LITERAL)
    237      return;
    238    if (addr->t[i].type == TOKEN822_AT)
    239      break;
    240   }
    241  shift = defaultdomain.len;
    242  if (!token822_readyplus(addr,shift)) die_nomem();
    243  for (i = addr->len - 1;i >= 0;--i)
    244    addr->t[i + shift] = addr->t[i];
    245  addr->len += shift;
    246  for (i = 0;i < shift;++i)
    247    addr->t[i] = defaultdomain.t[shift - 1 - i];
    248 }
    249 
    250 void rwplus(addr)
    251 token822_alloc *addr;
    252 {
    253  int i;
    254  int shift;
    255 
    256  if (addr->t[0].type != TOKEN822_ATOM) return;
    257  if (!addr->t[0].slen) return;
    258  if (addr->t[0].s[addr->t[0].slen - 1] != '+') return;
    259 
    260  --addr->t[0].slen; /* remove + */
    261 
    262  shift = plusdomain.len;
    263  if (!token822_readyplus(addr,shift)) die_nomem();
    264  for (i = addr->len - 1;i >= 0;--i)
    265    addr->t[i + shift] = addr->t[i];
    266  addr->len += shift;
    267  for (i = 0;i < shift;++i)
    268    addr->t[i] = plusdomain.t[shift - 1 - i];
    269 }
    270 
    271 void rwgeneric(addr)
    272 token822_alloc *addr;
    273 {
    274  if (!addr->len) return; /* don't rewrite <> */
    275  if (addr->len >= 2)
    276    if (addr->t[1].type == TOKEN822_AT)
    277      if (addr->t[0].type == TOKEN822_LITERAL)
    278        if (!addr->t[0].slen) /* don't rewrite <foo@[]> */
    279 	 return;
    280  rwroute(addr);
    281  if (!addr->len) return; /* <@foo:> -> <> */
    282  rwextradot(addr);
    283  if (!addr->len) return; /* <.> -> <> */
    284  rwextraat(addr);
    285  if (!addr->len) return; /* <@> -> <> */
    286  rwnoat(addr);
    287  rwplus(addr);
    288  rwnodot(addr);
    289 }
    290 
    291 int setreturn(addr)
    292 token822_alloc *addr;
    293 {
    294  if (!sender.s)
    295   {
    296    token822_reverse(addr);
    297    if (token822_unquote(&sender,addr) != 1) die_nomem();
    298    if (flaghackrecip)
    299      if (!stralloc_cats(&sender,"-@[]")) die_nomem();
    300    token822_reverse(addr);
    301   }
    302  return 1;
    303 }
    304 
    305 int rwreturn(addr)
    306 token822_alloc *addr;
    307 {
    308  rwgeneric(addr);
    309  setreturn(addr);
    310  return 1;
    311 }
    312 
    313 int rwsender(addr)
    314 token822_alloc *addr;
    315 {
    316  rwgeneric(addr);
    317  return 1;
    318 }
    319 
    320 void rwappend(addr,xl)
    321 token822_alloc *addr;
    322 saa *xl;
    323 {
    324  token822_reverse(addr);
    325  if (!saa_readyplus(xl,1)) die_nomem();
    326  xl->sa[xl->len] = sauninit;
    327  if (token822_unquote(&xl->sa[xl->len],addr) != 1) die_nomem();
    328  ++xl->len;
    329  token822_reverse(addr);
    330 }
    331 
    332 int rwhrr(addr) token822_alloc *addr;
    333 { rwgeneric(addr); rwappend(addr,&hrrlist); return 1; }
    334 int rwhr(addr) token822_alloc *addr;
    335 { rwgeneric(addr); rwappend(addr,&hrlist); return 1; }
    336 int rwtocc(addr) token822_alloc *addr;
    337 { rwgeneric(addr); rwappend(addr,&hrlist); rwappend(addr,&tocclist); return 1; }
    338 
    339 int htypeseen[H_NUM];
    340 stralloc hfbuf = {0};
    341 token822_alloc hfin = {0};
    342 token822_alloc hfrewrite = {0};
    343 token822_alloc hfaddr = {0};
    344 
    345 void doheaderfield(h)
    346 stralloc *h;
    347 {
    348   int htype;
    349   int (*rw)() = 0;
    350  
    351   htype = hfield_known(h->s,h->len);
    352   if (flagdeletefrom) if (htype == H_FROM) return;
    353   if (flagdeletemessid) if (htype == H_MESSAGEID) return;
    354   if (flagdeletesender) if (htype == H_RETURNPATH) return;
    355  
    356   if (htype)
    357     htypeseen[htype] = 1;
    358   else
    359     if (!hfield_valid(h->s,h->len))
    360       die_invalid(h);
    361  
    362   switch(htype) {
    363     case H_TO: case H_CC:
    364       if (flagrh) rw = rwtocc;
    365       break;
    366     case H_BCC: case H_APPARENTLYTO:
    367       if (flagrh) rw = rwhr;
    368       break;
    369     case H_R_TO: case H_R_CC: case H_R_BCC:
    370       if (flagrh) rw = rwhrr;
    371       break;
    372     case H_RETURNPATH:
    373       rw = rwreturn; break;
    374     case H_SENDER: case H_FROM: case H_REPLYTO:
    375     case H_RETURNRECEIPTTO: case H_ERRORSTO:
    376     case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO:
    377       rw = rwsender; break;
    378   }
    379 
    380   if (rw) {
    381     doordie(h,token822_parse(&hfin,h,&hfbuf));
    382     doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rw));
    383     if (token822_unparse(h,&hfrewrite,LINELEN) != 1)
    384       die_nomem();
    385   }
    386  
    387   if (htype == H_BCC) return;
    388   if (htype == H_R_BCC) return;
    389   if (htype == H_RETURNPATH) return;
    390   if (htype == H_CONTENTLENGTH) return; /* some things are just too stupid */
    391   savedh_append(h);
    392 }
    393 
    394 void dobody(h)
    395 stralloc *h;
    396 {
    397  put(h->s,h->len);
    398 }
    399 
    400 stralloc torecip = {0};
    401 token822_alloc tr = {0};
    402 
    403 void dorecip(s)
    404 char *s;
    405 {
    406  if (!quote2(&torecip,s)) die_nomem();
    407  switch(token822_parse(&tr,&torecip,&hfbuf))
    408   {
    409    case -1: die_nomem();
    410    case 0:
    411      substdio_puts(subfderr,"qmail-inject: fatal: unable to parse address: ");
    412      substdio_puts(subfderr,s);
    413      substdio_putsflush(subfderr,"\n");
    414      perm();
    415   }
    416  token822_reverse(&tr);
    417  rwgeneric(&tr);
    418  rwappend(&tr,&reciplist);
    419 }
    420 
    421 stralloc defaultfrom = {0};
    422 token822_alloc df = {0};
    423 
    424 void defaultfrommake()
    425 {
    426  char *fullname;
    427  fullname = env_get("QMAILNAME");
    428  if (!fullname) fullname = env_get("MAILNAME");
    429  if (!fullname) fullname = env_get("NAME");
    430  if (!token822_ready(&df,20)) die_nomem();
    431  df.len = 0;
    432  df.t[df.len].type = TOKEN822_ATOM;
    433  df.t[df.len].s = "From";
    434  df.t[df.len].slen = 4;
    435  ++df.len;
    436  df.t[df.len].type = TOKEN822_COLON;
    437  ++df.len;
    438  if (fullname && !flagnamecomment)
    439   {
    440    df.t[df.len].type = TOKEN822_QUOTE;
    441    df.t[df.len].s = fullname;
    442    df.t[df.len].slen = str_len(fullname);
    443    ++df.len;
    444    df.t[df.len].type = TOKEN822_LEFT;
    445    ++df.len;
    446   }
    447  df.t[df.len].type = mailusertokentype;
    448  df.t[df.len].s = mailuser;
    449  df.t[df.len].slen = str_len(mailuser);
    450  ++df.len;
    451  if (mailhost)
    452   {
    453    df.t[df.len].type = TOKEN822_AT;
    454    ++df.len;
    455    df.t[df.len].type = TOKEN822_ATOM;
    456    df.t[df.len].s = mailhost;
    457    df.t[df.len].slen = str_len(mailhost);
    458    ++df.len;
    459   }
    460  if (fullname && !flagnamecomment)
    461   {
    462    df.t[df.len].type = TOKEN822_RIGHT;
    463    ++df.len;
    464   }
    465  if (fullname && flagnamecomment)
    466   {
    467    df.t[df.len].type = TOKEN822_COMMENT;
    468    df.t[df.len].s = fullname;
    469    df.t[df.len].slen = str_len(fullname);
    470    ++df.len;
    471   }
    472  if (token822_unparse(&defaultfrom,&df,LINELEN) != 1) die_nomem();
    473  doordie(&defaultfrom,token822_parse(&df,&defaultfrom,&hfbuf));
    474  doordie(&defaultfrom,token822_addrlist(&hfrewrite,&hfaddr,&df,rwsender));
    475  if (token822_unparse(&defaultfrom,&hfrewrite,LINELEN) != 1) die_nomem();
    476 }
    477 
    478 stralloc defaultreturnpath = {0};
    479 token822_alloc drp = {0};
    480 stralloc hackedruser = {0};
    481 char strnum[FMT_ULONG];
    482 
    483 void dodefaultreturnpath()
    484 {
    485  if (!stralloc_copys(&hackedruser,mailruser)) die_nomem();
    486  if (flaghackmess)
    487   {
    488    if (!stralloc_cats(&hackedruser,"-")) die_nomem();
    489    if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) starttime))) die_nomem();
    490    if (!stralloc_cats(&hackedruser,".")) die_nomem();
    491    if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
    492   }
    493  if (flaghackrecip)
    494    if (!stralloc_cats(&hackedruser,"-")) die_nomem();
    495  if (!token822_ready(&drp,10)) die_nomem();
    496  drp.len = 0;
    497  drp.t[drp.len].type = TOKEN822_ATOM;
    498  drp.t[drp.len].s = "Return-Path";
    499  drp.t[drp.len].slen = 11;
    500  ++drp.len;
    501  drp.t[drp.len].type = TOKEN822_COLON;
    502  ++drp.len;
    503  drp.t[drp.len].type = TOKEN822_QUOTE;
    504  drp.t[drp.len].s = hackedruser.s;
    505  drp.t[drp.len].slen = hackedruser.len;
    506  ++drp.len;
    507  if (mailrhost)
    508   {
    509    drp.t[drp.len].type = TOKEN822_AT;
    510    ++drp.len;
    511    drp.t[drp.len].type = TOKEN822_ATOM;
    512    drp.t[drp.len].s = mailrhost;
    513    drp.t[drp.len].slen = str_len(mailrhost);
    514    ++drp.len;
    515   }
    516  if (token822_unparse(&defaultreturnpath,&drp,LINELEN) != 1) die_nomem();
    517  doordie(&defaultreturnpath,token822_parse(&drp,&defaultreturnpath,&hfbuf));
    518  doordie(&defaultreturnpath
    519    ,token822_addrlist(&hfrewrite,&hfaddr,&drp,rwreturn));
    520  if (token822_unparse(&defaultreturnpath,&hfrewrite,LINELEN) != 1) die_nomem();
    521 }
    522 
    523 int flagmft = 0;
    524 stralloc mft = {0};
    525 struct constmap mapmft;
    526 
    527 void mft_init()
    528 {
    529   char *x;
    530   int r;
    531 
    532   x = env_get("QMAILMFTFILE");
    533   if (!x) return;
    534 
    535   r = control_readfile(&mft,x,0);
    536   if (r == -1) die_read(); /*XXX*/
    537   if (!r) return;
    538 
    539   if (!constmap_init(&mapmft,mft.s,mft.len,0)) die_nomem();
    540   flagmft = 1;
    541 }
    542 
    543 void finishmft()
    544 {
    545   int i;
    546   static stralloc sa = {0};
    547   static stralloc sa2 = {0};
    548 
    549   if (!flagmft) return;
    550   if (htypeseen[H_MAILFOLLOWUPTO]) return;
    551 
    552   for (i = 0;i < tocclist.len;++i)
    553     if (constmap(&mapmft,tocclist.sa[i].s,tocclist.sa[i].len))
    554       break;
    555 
    556   if (i == tocclist.len) return;
    557 
    558   puts("Mail-Followup-To: ");
    559   i = tocclist.len;
    560   while (i--) {
    561     if (!stralloc_copy(&sa,&tocclist.sa[i])) die_nomem();
    562     if (!stralloc_0(&sa)) die_nomem();
    563     if (!quote2(&sa2,sa.s)) die_nomem();
    564     put(sa2.s,sa2.len);
    565     if (i) puts(",\n  ");
    566   }
    567   puts("\n");
    568 }
    569 
    570 void finishheader()
    571 {
    572  flagresent =
    573    htypeseen[H_R_SENDER] || htypeseen[H_R_FROM] || htypeseen[H_R_REPLYTO]
    574    || htypeseen[H_R_TO] || htypeseen[H_R_CC] || htypeseen[H_R_BCC]
    575    || htypeseen[H_R_DATE] || htypeseen[H_R_MESSAGEID];
    576 
    577  if (!sender.s)
    578    dodefaultreturnpath();
    579 
    580  if (!flagqueue)
    581   {
    582    static stralloc sa = {0};
    583    static stralloc sa2 = {0};
    584 
    585    if (!stralloc_copy(&sa,&sender)) die_nomem();
    586    if (!stralloc_0(&sa)) die_nomem();
    587    if (!quote2(&sa2,sa.s)) die_nomem();
    588 
    589    puts("Return-Path: <");
    590    put(sa2.s,sa2.len);
    591    puts(">\n");
    592   }
    593 
    594  /* could check at this point whether there are any recipients */
    595  if (flagqueue)
    596    if (qmail_open(&qqt) == -1) die_qqt();
    597 
    598  if (flagresent)
    599   {
    600    if (!htypeseen[H_R_DATE])
    601     {
    602      if (!newfield_datemake(starttime)) die_nomem();
    603      puts("Resent-");
    604      put(newfield_date.s,newfield_date.len);
    605     }
    606    if (!htypeseen[H_R_MESSAGEID])
    607     {
    608      if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem();
    609      puts("Resent-");
    610      put(newfield_msgid.s,newfield_msgid.len);
    611     }
    612    if (!htypeseen[H_R_FROM])
    613     {
    614      defaultfrommake();
    615      puts("Resent-");
    616      put(defaultfrom.s,defaultfrom.len);
    617     }
    618    if (!htypeseen[H_R_TO] && !htypeseen[H_R_CC])
    619      puts("Resent-Cc: recipient list not shown: ;\n");
    620   }
    621  else
    622   {
    623    if (!htypeseen[H_DATE])
    624     {
    625      if (!newfield_datemake(starttime)) die_nomem();
    626      put(newfield_date.s,newfield_date.len);
    627     }
    628    if (!htypeseen[H_MESSAGEID])
    629     {
    630      if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem();
    631      put(newfield_msgid.s,newfield_msgid.len);
    632     }
    633    if (!htypeseen[H_FROM])
    634     {
    635      defaultfrommake();
    636      put(defaultfrom.s,defaultfrom.len);
    637     }
    638    if (!htypeseen[H_TO] && !htypeseen[H_CC])
    639      puts("Cc: recipient list not shown: ;\n");
    640    finishmft();
    641   }
    642 
    643  savedh_print();
    644 }
    645 
    646 void getcontrols()
    647 {
    648  static stralloc sa = {0};
    649  char *x;
    650 
    651  mft_init();
    652 
    653  if (chdir(auto_qmail) == -1) die_chdir();
    654  if (control_init() == -1) die_read();
    655 
    656  if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1)
    657    die_read();
    658  x = env_get("QMAILDEFAULTDOMAIN");
    659  if (x) if (!stralloc_copys(&control_defaultdomain,x)) die_nomem();
    660  if (!stralloc_copys(&sa,".")) die_nomem();
    661  if (!stralloc_cat(&sa,&control_defaultdomain)) die_nomem();
    662  doordie(&sa,token822_parse(&defaultdomain,&sa,&defaultdomainbuf));
    663 
    664  if (control_rldef(&control_defaulthost,"control/defaulthost",1,"defaulthost") != 1)
    665    die_read();
    666  x = env_get("QMAILDEFAULTHOST");
    667  if (x) if (!stralloc_copys(&control_defaulthost,x)) die_nomem();
    668  if (!stralloc_copys(&sa,"@")) die_nomem();
    669  if (!stralloc_cat(&sa,&control_defaulthost)) die_nomem();
    670  doordie(&sa,token822_parse(&defaulthost,&sa,&defaulthostbuf));
    671 
    672  if (control_rldef(&control_plusdomain,"control/plusdomain",1,"plusdomain") != 1)
    673    die_read();
    674  x = env_get("QMAILPLUSDOMAIN");
    675  if (x) if (!stralloc_copys(&control_plusdomain,x)) die_nomem();
    676  if (!stralloc_copys(&sa,".")) die_nomem();
    677  if (!stralloc_cat(&sa,&control_plusdomain)) die_nomem();
    678  doordie(&sa,token822_parse(&plusdomain,&sa,&plusdomainbuf));
    679 
    680  if (control_rldef(&control_idhost,"control/idhost",1,"idhost") != 1)
    681    die_read();
    682  x = env_get("QMAILIDHOST");
    683  if (x) if (!stralloc_copys(&control_idhost,x)) die_nomem();
    684 }
    685 
    686 #define RECIP_DEFAULT 1
    687 #define RECIP_ARGS 2
    688 #define RECIP_HEADER 3
    689 #define RECIP_AH 4
    690 
    691 int main(int argc, char **argv)
    692 {
    693  int i;
    694  int opt;
    695  int recipstrategy;
    696 
    697  sig_pipeignore();
    698 
    699  starttime = now();
    700 
    701  qmopts = env_get("QMAILINJECT");
    702  if (qmopts)
    703    while (*qmopts)
    704      switch(*qmopts++)
    705       {
    706        case 'c': flagnamecomment = 1; break;
    707        case 's': flagdeletesender = 1; break;
    708        case 'f': flagdeletefrom = 1; break;
    709        case 'i': flagdeletemessid = 1; break;
    710        case 'r': flaghackrecip = 1; break;
    711        case 'm': flaghackmess = 1; break;
    712       }
    713 
    714  mailhost = env_get("QMAILHOST");
    715  if (!mailhost) mailhost = env_get("MAILHOST");
    716  mailrhost = env_get("QMAILSHOST");
    717  if (!mailrhost) mailrhost = mailhost;
    718 
    719  mailuser = env_get("QMAILUSER");
    720  if (!mailuser) mailuser = env_get("MAILUSER");
    721  if (!mailuser) mailuser = env_get("USER");
    722  if (!mailuser) mailuser = env_get("LOGNAME");
    723  if (!mailuser) mailuser = "anonymous";
    724  mailusertokentype = TOKEN822_ATOM;
    725  if (quote_need(mailuser,str_len(mailuser))) mailusertokentype = TOKEN822_QUOTE;
    726  mailruser = env_get("QMAILSUSER");
    727  if (!mailruser) mailruser = mailuser;
    728 
    729  for (i = 0;i < H_NUM;++i) htypeseen[i] = 0;
    730 
    731  recipstrategy = RECIP_DEFAULT;
    732  flagqueue = 1;
    733 
    734  getcontrols();
    735 
    736  if (!saa_readyplus(&hrlist,1)) die_nomem();
    737  if (!saa_readyplus(&tocclist,1)) die_nomem();
    738  if (!saa_readyplus(&hrrlist,1)) die_nomem();
    739  if (!saa_readyplus(&reciplist,1)) die_nomem();
    740 
    741  while ((opt = getopt(argc,argv,"aAhHnNf:")) != opteof)
    742    switch(opt)
    743     {
    744      case 'a': recipstrategy = RECIP_ARGS; break;
    745      case 'A': recipstrategy = RECIP_DEFAULT; break;
    746      case 'h': recipstrategy = RECIP_HEADER; break;
    747      case 'H': recipstrategy = RECIP_AH; break;
    748      case 'n': flagqueue = 0; break;
    749      case 'N': flagqueue = 1; break;
    750      case 'f':
    751        if (!quote2(&sender,optarg)) die_nomem();
    752        doordie(&sender,token822_parse(&envs,&sender,&envsbuf));
    753        token822_reverse(&envs);
    754        rwgeneric(&envs);
    755        token822_reverse(&envs);
    756        if (token822_unquote(&sender,&envs) != 1) die_nomem();
    757        break;
    758      case '?':
    759      default:
    760        perm();
    761     }
    762  argc -= optind;
    763  argv += optind;
    764 
    765  if (recipstrategy == RECIP_DEFAULT)
    766    recipstrategy = (*argv ? RECIP_ARGS : RECIP_HEADER);
    767 
    768  if (recipstrategy != RECIP_HEADER)
    769    while (*argv)
    770      dorecip(*argv++);
    771 
    772  flagrh = (recipstrategy != RECIP_ARGS);
    773 
    774  if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1)
    775    die_read();
    776  exitnicely();
    777 }