14 #include "gen_alloc.h"
15 #include "gen_allocdefs.h"
21 #include "headerbody.h"
22 #include "auto_qmail.h"
28 datetime_sec starttime
;
31 int flagdeletesender
= 0;
32 int flagdeletefrom
= 0;
33 int flagdeletemessid
= 0;
34 int flagnamecomment
= 0;
36 int flaghackrecip
= 0;
39 int mailusertokentype
;
43 stralloc control_idhost
= {0};
44 stralloc control_defaultdomain
= {0};
45 stralloc control_defaulthost
= {0};
46 stralloc control_plusdomain
= {0};
48 stralloc sender
= {0};
49 stralloc envsbuf
= {0};
50 token822_alloc envs
= {0};
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
)); }
60 void perm() { _exit(100); }
61 void temp() { _exit(111); }
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(); }
68 substdio_putsflush(subfderr
,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); }
70 substdio_putsflush(subfderr
,"qmail-inject: fatal: internal bug\n"); temp(); }
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(); }
79 GEN_ALLOC_typedef(saa
,stralloc
,sa
,len
,a
)
80 GEN_ALLOC_readyplus(saa
,stralloc
,sa
,len
,a
,i
,n
,x
,10,saa_readyplus
)
82 static stralloc sauninit
= {0};
95 if (!flagqueue
) substdio_flush(subfdout
);
101 if (!stralloc_0(&sender
)) die_nomem();
102 qmail_from(&qqt
,sender
.s
);
104 for (i
= 0;i
< reciplist
.len
;++i
)
106 if (!stralloc_0(&reciplist
.sa
[i
])) die_nomem();
107 qmail_to(&qqt
,reciplist
.sa
[i
].s
);
111 for (i
= 0;i
< hrrlist
.len
;++i
)
113 if (!stralloc_0(&hrrlist
.sa
[i
])) die_nomem();
114 qmail_to(&qqt
,hrrlist
.sa
[i
].s
);
117 for (i
= 0;i
< hrlist
.len
;++i
)
119 if (!stralloc_0(&hrlist
.sa
[i
])) die_nomem();
120 qmail_to(&qqt
,hrlist
.sa
[i
].s
);
123 qqx
= qmail_close(&qqt
);
126 substdio_puts(subfderr
,"qmail-inject: fatal: ");
127 substdio_puts(subfderr
,qqx
+ 1);
128 substdio_puts(subfderr
,"\n");
129 substdio_flush(subfderr
);
133 substdio_puts(subfderr
,"qmail-inject: fatal: ");
134 substdio_puts(subfderr
,qqx
+ 1);
135 substdio_puts(subfderr
,"\n");
136 substdio_flush(subfderr
);
144 void savedh_append(h
)
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();
157 for (i
= 0;i
< savedh
.len
;++i
)
158 put(savedh
.sa
[i
].s
,savedh
.sa
[i
].len
);
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};
169 token822_alloc
*addr
;
171 if (addr
->t
[addr
->len
- 1].type
== TOKEN822_AT
)
173 if (addr
->t
[--addr
->len
].type
== TOKEN822_COLON
)
178 token822_alloc
*addr
;
181 if (addr
->t
[0].type
== TOKEN822_AT
)
184 for (i
= 0;i
< addr
->len
;++i
)
185 addr
->t
[i
] = addr
->t
[i
+ 1];
189 void rwextradot(addr
)
190 token822_alloc
*addr
;
193 if (addr
->t
[0].type
== TOKEN822_DOT
)
196 for (i
= 0;i
< addr
->len
;++i
)
197 addr
->t
[i
] = addr
->t
[i
+ 1];
202 token822_alloc
*addr
;
207 for (i
= 0;i
< addr
->len
;++i
)
208 if (addr
->t
[i
].type
== TOKEN822_AT
)
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
];
215 for (i
= 0;i
< shift
;++i
)
216 addr
->t
[i
] = defaulthost
.t
[shift
- 1 - i
];
220 token822_alloc
*addr
;
224 for (i
= 0;i
< addr
->len
;++i
)
226 if (addr
->t
[i
].type
== TOKEN822_DOT
)
228 if (addr
->t
[i
].type
== TOKEN822_AT
)
231 for (i
= 0;i
< addr
->len
;++i
)
233 if (addr
->t
[i
].type
== TOKEN822_LITERAL
)
235 if (addr
->t
[i
].type
== TOKEN822_AT
)
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
];
243 for (i
= 0;i
< shift
;++i
)
244 addr
->t
[i
] = defaultdomain
.t
[shift
- 1 - i
];
248 token822_alloc
*addr
;
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;
257 --addr
->t
[0].slen
; /* remove + */
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
];
264 for (i
= 0;i
< shift
;++i
)
265 addr
->t
[i
] = plusdomain
.t
[shift
- 1 - i
];
269 token822_alloc
*addr
;
271 if (!addr
->len
) return; /* don't rewrite <> */
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@[]> */
278 if (!addr
->len
) return; /* <@foo:> -> <> */
280 if (!addr
->len
) return; /* <.> -> <> */
282 if (!addr
->len
) return; /* <@> -> <> */
289 token822_alloc
*addr
;
293 token822_reverse(addr
);
294 if (token822_unquote(&sender
,addr
) != 1) die_nomem();
296 if (!stralloc_cats(&sender
,"-@[]")) die_nomem();
297 token822_reverse(addr
);
303 token822_alloc
*addr
;
311 token822_alloc
*addr
;
317 void rwappend(addr
,xl
)
318 token822_alloc
*addr
;
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();
326 token822_reverse(addr
);
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; }
336 int htypeseen
[H_NUM
];
337 stralloc hfbuf
= {0};
338 token822_alloc hfin
= {0};
339 token822_alloc hfrewrite
= {0};
340 token822_alloc hfaddr
= {0};
342 void doheaderfield(h
)
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;
354 htypeseen
[htype
] = 1;
356 if (!hfield_valid(h
->s
,h
->len
))
360 case H_TO
: case H_CC
:
362 case H_BCC
: case H_APPARENTLYTO
:
364 case H_R_TO
: case H_R_CC
: case H_R_BCC
:
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;
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)
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 */
394 stralloc torecip
= {0};
395 token822_alloc tr
= {0};
400 if (!quote2(&torecip
,s
)) die_nomem();
401 switch(token822_parse(&tr
,&torecip
,&hfbuf
))
403 case -1: die_nomem();
405 substdio_puts(subfderr
,"qmail-inject: fatal: unable to parse address: ");
406 substdio_puts(subfderr
,s
);
407 substdio_putsflush(subfderr
,"\n");
410 token822_reverse(&tr
);
412 rwappend(&tr
,&reciplist
);
415 stralloc defaultfrom
= {0};
416 token822_alloc df
= {0};
418 void defaultfrommake()
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();
426 df
.t
[df
.len
].type
= TOKEN822_ATOM
;
427 df
.t
[df
.len
].s
= "From";
428 df
.t
[df
.len
].slen
= 4;
430 df
.t
[df
.len
].type
= TOKEN822_COLON
;
432 if (fullname
&& !flagnamecomment
)
434 df
.t
[df
.len
].type
= TOKEN822_QUOTE
;
435 df
.t
[df
.len
].s
= fullname
;
436 df
.t
[df
.len
].slen
= str_len(fullname
);
438 df
.t
[df
.len
].type
= TOKEN822_LEFT
;
441 df
.t
[df
.len
].type
= mailusertokentype
;
442 df
.t
[df
.len
].s
= mailuser
;
443 df
.t
[df
.len
].slen
= str_len(mailuser
);
447 df
.t
[df
.len
].type
= TOKEN822_AT
;
449 df
.t
[df
.len
].type
= TOKEN822_ATOM
;
450 df
.t
[df
.len
].s
= mailhost
;
451 df
.t
[df
.len
].slen
= str_len(mailhost
);
454 if (fullname
&& !flagnamecomment
)
456 df
.t
[df
.len
].type
= TOKEN822_RIGHT
;
459 if (fullname
&& flagnamecomment
)
461 df
.t
[df
.len
].type
= TOKEN822_COMMENT
;
462 df
.t
[df
.len
].s
= fullname
;
463 df
.t
[df
.len
].slen
= str_len(fullname
);
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();
472 stralloc defaultreturnpath
= {0};
473 token822_alloc drp
= {0};
474 stralloc hackedruser
= {0};
475 char strnum
[FMT_ULONG
];
477 void dodefaultreturnpath()
479 if (!stralloc_copys(&hackedruser
,mailruser
)) die_nomem();
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();
488 if (!stralloc_cats(&hackedruser
,"-")) die_nomem();
489 if (!token822_ready(&drp
,10)) die_nomem();
491 drp
.t
[drp
.len
].type
= TOKEN822_ATOM
;
492 drp
.t
[drp
.len
].s
= "Return-Path";
493 drp
.t
[drp
.len
].slen
= 11;
495 drp
.t
[drp
.len
].type
= TOKEN822_COLON
;
497 drp
.t
[drp
.len
].type
= TOKEN822_QUOTE
;
498 drp
.t
[drp
.len
].s
= hackedruser
.s
;
499 drp
.t
[drp
.len
].slen
= hackedruser
.len
;
503 drp
.t
[drp
.len
].type
= TOKEN822_AT
;
505 drp
.t
[drp
.len
].type
= TOKEN822_ATOM
;
506 drp
.t
[drp
.len
].s
= mailrhost
;
507 drp
.t
[drp
.len
].slen
= str_len(mailrhost
);
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();
519 struct constmap mapmft
;
526 x
= env_get("QMAILMFTFILE");
529 r
= control_readfile(&mft
,x
,0);
530 if (r
== -1) die_read(); /*XXX*/
533 if (!constmap_init(&mapmft
,mft
.s
,mft
.len
,0)) die_nomem();
540 static stralloc sa
= {0};
541 static stralloc sa2
= {0};
543 if (!flagmft
) return;
544 if (htypeseen
[H_MAILFOLLOWUPTO
]) return;
546 for (i
= 0;i
< tocclist
.len
;++i
)
547 if (constmap(&mapmft
,tocclist
.sa
[i
].s
,tocclist
.sa
[i
].len
))
550 if (i
== tocclist
.len
) return;
552 puts("Mail-Followup-To: ");
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();
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
];
572 dodefaultreturnpath();
576 static stralloc sa
= {0};
577 static stralloc sa2
= {0};
579 if (!stralloc_copy(&sa
,&sender
)) die_nomem();
580 if (!stralloc_0(&sa
)) die_nomem();
581 if (!quote2(&sa2
,sa
.s
)) die_nomem();
583 puts("Return-Path: <");
588 /* could check at this point whether there are any recipients */
590 if (qmail_open(&qqt
) == -1) die_qqt();
594 if (!htypeseen
[H_R_DATE
])
596 if (!newfield_datemake(starttime
)) die_nomem();
598 put(newfield_date
.s
,newfield_date
.len
);
600 if (!htypeseen
[H_R_MESSAGEID
])
602 if (!newfield_msgidmake(control_idhost
.s
,control_idhost
.len
,starttime
)) die_nomem();
604 put(newfield_msgid
.s
,newfield_msgid
.len
);
606 if (!htypeseen
[H_R_FROM
])
610 put(defaultfrom
.s
,defaultfrom
.len
);
612 if (!htypeseen
[H_R_TO
] && !htypeseen
[H_R_CC
])
613 puts("Resent-Cc: recipient list not shown: ;\n");
617 if (!htypeseen
[H_DATE
])
619 if (!newfield_datemake(starttime
)) die_nomem();
620 put(newfield_date
.s
,newfield_date
.len
);
622 if (!htypeseen
[H_MESSAGEID
])
624 if (!newfield_msgidmake(control_idhost
.s
,control_idhost
.len
,starttime
)) die_nomem();
625 put(newfield_msgid
.s
,newfield_msgid
.len
);
627 if (!htypeseen
[H_FROM
])
630 put(defaultfrom
.s
,defaultfrom
.len
);
632 if (!htypeseen
[H_TO
] && !htypeseen
[H_CC
])
633 puts("Cc: recipient list not shown: ;\n");
642 static stralloc sa
= {0};
647 if (chdir(auto_qmail
) == -1) die_chdir();
648 if (control_init() == -1) die_read();
650 if (control_rldef(&control_defaultdomain
,"control/defaultdomain",1,"defaultdomain") != 1)
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
));
658 if (control_rldef(&control_defaulthost
,"control/defaulthost",1,"defaulthost") != 1)
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
));
666 if (control_rldef(&control_plusdomain
,"control/plusdomain",1,"plusdomain") != 1)
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
));
674 if (control_rldef(&control_idhost
,"control/idhost",1,"idhost") != 1)
676 x
= env_get("QMAILIDHOST");
677 if (x
) if (!stralloc_copys(&control_idhost
,x
)) die_nomem();
680 #define RECIP_DEFAULT 1
682 #define RECIP_HEADER 3
697 qmopts
= env_get("QMAILINJECT");
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;
710 mailhost
= env_get("QMAILHOST");
711 if (!mailhost
) mailhost
= env_get("MAILHOST");
712 mailrhost
= env_get("QMAILSHOST");
713 if (!mailrhost
) mailrhost
= mailhost
;
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
;
725 for (i
= 0;i
< H_NUM
;++i
) htypeseen
[i
] = 0;
727 recipstrategy
= RECIP_DEFAULT
;
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();
737 while ((opt
= getopt(argc
,argv
,"aAhHnNf:")) != opteof
)
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;
747 if (!quote2(&sender
,optarg
)) die_nomem();
748 doordie(&sender
,token822_parse(&envs
,&sender
,&envsbuf
));
749 token822_reverse(&envs
);
751 token822_reverse(&envs
);
752 if (token822_unquote(&sender
,&envs
) != 1) die_nomem();
761 if (recipstrategy
== RECIP_DEFAULT
)
762 recipstrategy
= (*argv ? RECIP_ARGS
: RECIP_HEADER
);
764 if (recipstrategy
!= RECIP_HEADER
)
768 flagrh
= (recipstrategy
!= RECIP_ARGS
);
770 if (headerbody(subfdin
,doheaderfield
,finishheader
,dobody
) == -1)