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