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