debian/rules: Use `git' potty wrapper.
[qmail] / qmail-local.c
CommitLineData
2117e02e
MW
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "readwrite.h"
4#include "sig.h"
5#include "env.h"
6#include "byte.h"
7#include "exit.h"
8#include "fork.h"
9#include "open.h"
10#include "wait.h"
11#include "lock.h"
12#include "seek.h"
13#include "substdio.h"
14#include "getln.h"
212b6f5d 15#include "strerr.h"
2117e02e
MW
16#include "subfd.h"
17#include "sgetopt.h"
18#include "alloc.h"
19#include "error.h"
20#include "stralloc.h"
21#include "fmt.h"
22#include "str.h"
23#include "now.h"
24#include "case.h"
25#include "quote.h"
26#include "qmail.h"
27#include "slurpclose.h"
28#include "myctime.h"
29#include "gfrom.h"
30#include "auto_patrn.h"
31
212b6f5d
MW
32void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
33
34void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); }
35void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
36void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
37void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); }
38void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno),". (#4.3.0)"); }
39void temp_slowlock()
40{ strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
41void temp_qmail(fn) char *fn;
42{ strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
2117e02e
MW
43
44int flagdoit;
45int flag99;
46
47char *user;
48char *homedir;
49char *local;
50char *dash;
51char *ext;
52char *host;
53char *sender;
54char *aliasempty;
55
212b6f5d 56stralloc safeext = {0};
2117e02e
MW
57stralloc ufline = {0};
58stralloc rpline = {0};
59stralloc envrecip = {0};
60stralloc dtline = {0};
61stralloc qme = {0};
62stralloc ueo = {0};
63stralloc cmds = {0};
64stralloc messline = {0};
65stralloc foo = {0};
34c57ee0
MW
66stralloc qsender = {0};
67stralloc tmpline = {0};
68char *verhhost = (char *)0;
69char *verhlocal = (char *)0;
70int flagheader,flagdobody;
71unsigned int i;
72
73int verhline(sa)
74stralloc *sa;
75/* substitutes ##L => recipient local, ##H => recipient host if VERP sender */
76/* returns 0 normally, -1 on out-of-memory */
77{
78 register char *cp;
79 char *cpnext,*cpafter;
80
81 if (!verhlocal) return 0; /* no VERP SENDER */
82 cp = sa->s;
83 cpnext = sa->s;
84 cpafter = cp + sa->len;
85 tmpline.len = 0; /* clear */
86 for (;;) {
87 while (cp < cpafter && *cp++ != '#');
88 if (cp + 1 < cpafter && *cp == '#') { /* found '##' */
89 cp++;
90 if (*cp == 'L') { /* ##L */
91 if (!stralloc_catb(&tmpline,cpnext,cp - cpnext - 2)) return -1;
92 cp++;
93 cpnext = cp;
94 if (!stralloc_cats(&tmpline,verhlocal)) return -1;
95 } else if (*cp == 'H') { /* ##H */
96 if (!stralloc_catb(&tmpline,cpnext,cp - cpnext - 2)) return -1;
97 cp++;
98 cpnext = cp;
99 if (!stralloc_cats(&tmpline,verhhost)) return -1;
100 }
101 }
102 if (cp >= cpafter) {
103 if (tmpline.len) { /* true if we've done any substitutions */
104 if (!stralloc_catb(&tmpline,cpnext,cpafter - cpnext)) return -1;
105 if (!stralloc_copy(sa,&tmpline)) return -1;
106 }
107 return 0;
108 }
109 }
110}
2117e02e
MW
111
112char buf[1024];
113char outbuf[1024];
114
115/* child process */
116
117char fntmptph[80 + FMT_ULONG * 2];
118char fnnewtph[80 + FMT_ULONG * 2];
119void tryunlinktmp() { unlink(fntmptph); }
120void sigalrm() { tryunlinktmp(); _exit(3); }
121
122void maildir_child(dir)
123char *dir;
124{
125 unsigned long pid;
126 unsigned long time;
127 char host[64];
128 char *s;
129 int loop;
34c57ee0 130 int match;
2117e02e
MW
131 struct stat st;
132 int fd;
133 substdio ss;
134 substdio ssout;
135
136 sig_alarmcatch(sigalrm);
137 if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
138 pid = getpid();
139 host[0] = 0;
140 gethostname(host,sizeof(host));
141 for (loop = 0;;++loop)
142 {
143 time = now();
144 s = fntmptph;
145 s += fmt_str(s,"tmp/");
146 s += fmt_ulong(s,time); *s++ = '.';
147 s += fmt_ulong(s,pid); *s++ = '.';
148 s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
149 if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
150 /* really should never get to this point */
151 if (loop == 2) _exit(1);
152 sleep(2);
153 }
154 str_copy(fnnewtph,fntmptph);
155 byte_copy(fnnewtph,3,"new");
156
157 alarm(86400);
158 fd = open_excl(fntmptph);
159 if (fd == -1) _exit(1);
160
161 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
162 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
163 if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail;
164 if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail;
165
34c57ee0
MW
166 flagheader = 1;
167 flagdobody = 0;
168 do { /* for VERH */
169 if (getln(&ss,&messline,&match,'\n') != 0)
170 { tryunlinktmp(); _exit(4); }
171 if (flagheader) {
172 if (match && messline.len == 1) {
173 flagheader = 0;
174 if (!flagdobody) verhlocal = (char *)0;
175 }
176 if (messline.s[0] == '#') { /* continue in body */
177 flagdobody = 1; /* remove leading '#' */
178 for (i = 1; i < messline.len; i++)
179 messline.s[i - 1] = messline.s[i];
180 messline.len--; /* always >= 1 from \n */
181 }
182 }
183 if (verhlocal)
184 if (verhline(&messline) == -1) goto fail;
185 if (substdio_put(&ssout,messline.s,messline.len) == -1) goto fail;
186 } while (match);
2117e02e
MW
187
188 if (substdio_flush(&ssout) == -1) goto fail;
189 if (fsync(fd) == -1) goto fail;
190 if (close(fd) == -1) goto fail; /* NFS dorks */
191
192 if (link(fntmptph,fnnewtph) == -1) goto fail;
193 /* if it was error_exist, almost certainly successful; i hate NFS */
194 tryunlinktmp(); _exit(0);
195
196 fail: tryunlinktmp(); _exit(1);
197}
198
199/* end child process */
200
201void maildir(fn)
202char *fn;
203{
204 int child;
205 int wstat;
206
207 if (seek_begin(0) == -1) temp_rewind();
208
209 switch(child = fork())
210 {
211 case -1:
212 temp_fork();
213 case 0:
214 maildir_child(fn);
212b6f5d 215 _exit(111);
2117e02e
MW
216 }
217
218 wait_pid(&wstat,child);
219 if (wait_crashed(wstat))
220 temp_childcrashed();
221 switch(wait_exitcode(wstat))
222 {
223 case 0: break;
212b6f5d
MW
224 case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
225 case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
226 case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
227 default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
2117e02e
MW
228 }
229}
230
2117e02e
MW
231void mailfile(fn)
232char *fn;
233{
234 int fd;
235 substdio ss;
236 substdio ssout;
237 int match;
238 seek_pos pos;
239 int flaglocked;
240
241 if (seek_begin(0) == -1) temp_rewind();
242
243 fd = open_append(fn);
212b6f5d
MW
244 if (fd == -1)
245 strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)");
2117e02e 246
212b6f5d 247 sig_alarmcatch(temp_slowlock);
2117e02e
MW
248 alarm(30);
249 flaglocked = (lock_ex(fd) != -1);
250 alarm(0);
251 sig_alarmdefault();
252
253 seek_end(fd);
254 pos = seek_cur(fd);
255
256 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
257 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
34c57ee0
MW
258 flagheader = 1;
259 flagdobody = 0;
2117e02e
MW
260 if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs;
261 if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs;
262 if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs;
263 for (;;)
264 {
265 if (getln(&ss,&messline,&match,'\n') != 0)
212b6f5d
MW
266 {
267 strerr_warn3("Unable to read message: ",error_str(errno),". (#4.3.0)",0);
268 if (flaglocked) seek_trunc(fd,pos); close(fd);
269 _exit(111);
270 }
2117e02e
MW
271 if (!match && !messline.len) break;
272 if (gfrom(messline.s,messline.len))
273 if (substdio_bput(&ssout,">",1)) goto writeerrs;
34c57ee0
MW
274 if (flagheader) {
275 if (match && messline.len == 1) {
276 if (!flagdobody) verhlocal = (char *)0;
277 flagheader = 0;
278 }
279 if (messline.s[0] == '#') { /* continue in body */
280 flagdobody = 1; /* remove leading '#' */
281 for (i = 1; i < messline.len; i++)
282 messline.s[i - 1] = messline.s[i];
283 messline.len--;
284 }
285 }
286 if (verhlocal)
287 if (verhline(&messline) == -1) goto writeerrs;
2117e02e
MW
288 if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs;
289 if (!match)
290 {
291 if (substdio_bputs(&ssout,"\n")) goto writeerrs;
292 break;
293 }
294 }
295 if (substdio_bputs(&ssout,"\n")) goto writeerrs;
296 if (substdio_flush(&ssout)) goto writeerrs;
297 if (fsync(fd) == -1) goto writeerrs;
298 close(fd);
299 return;
300
301 writeerrs:
212b6f5d 302 strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
2117e02e
MW
303 if (flaglocked) seek_trunc(fd,pos);
304 close(fd);
212b6f5d 305 _exit(111);
2117e02e
MW
306}
307
308void mailprogram(prog)
309char *prog;
310{
311 int child;
312 char *(args[4]);
313 int wstat;
314
315 if (seek_begin(0) == -1) temp_rewind();
316
317 switch(child = fork())
318 {
319 case -1:
320 temp_fork();
321 case 0:
212b6f5d 322 args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
2117e02e 323 sig_pipedefault();
212b6f5d
MW
324 execv(*args,args);
325 strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)");
2117e02e
MW
326 }
327
328 wait_pid(&wstat,child);
329 if (wait_crashed(wstat))
330 temp_childcrashed();
331 switch(wait_exitcode(wstat))
332 {
333 case 100:
212b6f5d 334 case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
2117e02e
MW
335 case 0: break;
336 case 99: flag99 = 1; break;
212b6f5d 337 default: _exit(111);
2117e02e
MW
338 }
339}
340
341unsigned long mailforward_qp = 0;
342
343void mailforward(recips)
344char **recips;
345{
346 struct qmail qqt;
212b6f5d 347 char *qqx;
2117e02e
MW
348 substdio ss;
349 int match;
350
351 if (seek_begin(0) == -1) temp_rewind();
352 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
353
354 if (qmail_open(&qqt) == -1) temp_fork();
355 mailforward_qp = qmail_qp(&qqt);
356 qmail_put(&qqt,dtline.s,dtline.len);
34c57ee0 357 flagheader = 1;
2117e02e
MW
358 do
359 {
360 if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; }
34c57ee0
MW
361 if (flagheader) {
362 if (match && messline.len == 1) {
363 flagheader = 0;
364 if (!flagdobody) verhlocal = (char *)0;
365 }
366 if (messline.s[0] == '#') { /* continue in body */
367 flagdobody = 1; /* remove leading '#' */
368 for (i = 1; i < messline.len; i++)
369 messline.s[i - 1] = messline.s[i];
370 messline.len--;
371 }
372 }
373 if (verhlocal)
374 if (verhline(&messline) == -1) { qmail_fail(&qqt); break; }
2117e02e
MW
375 qmail_put(&qqt,messline.s,messline.len);
376 }
377 while (match);
378 qmail_from(&qqt,ueo.s);
379 while (*recips) qmail_to(&qqt,*recips++);
212b6f5d
MW
380 qqx = qmail_close(&qqt);
381 if (!*qqx) return;
382 strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,".");
2117e02e
MW
383}
384
385void bouncexf()
386{
387 int match;
388 substdio ss;
389
390 if (seek_begin(0) == -1) temp_rewind();
391 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
392 for (;;)
393 {
394 if (getln(&ss,&messline,&match,'\n') != 0) temp_read();
395 if (!match) break;
396 if (messline.len <= 1)
397 break;
398 if (messline.len == dtline.len)
399 if (!str_diffn(messline.s,dtline.s,dtline.len))
212b6f5d 400 strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)");
2117e02e
MW
401 }
402}
403
404void checkhome()
405{
406 struct stat st;
407
212b6f5d
MW
408 if (stat(".",&st) == -1)
409 strerr_die3x(111,"Unable to stat home directory: ",error_str(errno),". (#4.3.0)");
410 if (st.st_mode & auto_patrn)
411 strerr_die1x(111,"Uh-oh: home directory is writable. (#4.7.0)");
2117e02e 412 if (st.st_mode & 01000)
212b6f5d
MW
413 if (flagdoit)
414 strerr_die1x(111,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)");
415 else
416 strerr_warn1("Warning: home directory is sticky.",0);
2117e02e
MW
417}
418
419int qmeox(dashowner)
420char *dashowner;
421{
422 struct stat st;
423
424 if (!stralloc_copys(&qme,".qmail")) temp_nomem();
212b6f5d
MW
425 if (!stralloc_cats(&qme,dash)) temp_nomem();
426 if (!stralloc_cat(&qme,&safeext)) temp_nomem();
2117e02e
MW
427 if (!stralloc_cats(&qme,dashowner)) temp_nomem();
428 if (!stralloc_0(&qme)) temp_nomem();
429 if (stat(qme.s,&st) == -1)
430 {
212b6f5d 431 if (error_temp(errno)) temp_qmail(qme.s);
2117e02e
MW
432 return -1;
433 }
434 return 0;
435}
436
212b6f5d
MW
437int qmeexists(fd,cutable)
438int *fd;
2117e02e
MW
439int *cutable;
440{
212b6f5d 441 struct stat st;
2117e02e 442
212b6f5d
MW
443 if (!stralloc_0(&qme)) temp_nomem();
444
445 *fd = open_read(qme.s);
446 if (*fd == -1) {
447 if (error_temp(errno)) temp_qmail(qme.s);
448 if (errno == error_perm) temp_qmail(qme.s);
449 if (errno == error_acces) temp_qmail(qme.s);
450 return 0;
451 }
452
453 if (fstat(*fd,&st) == -1) temp_qmail(qme.s);
454 if ((st.st_mode & S_IFMT) == S_IFREG) {
455 if (st.st_mode & auto_patrn)
456 strerr_die1x(111,"Uh-oh: .qmail file is writable. (#4.7.0)");
457 *cutable = !!(st.st_mode & 0100);
458 return 1;
459 }
460 close(*fd);
461 return 0;
462}
463
464/* "" "": "" */
465/* "-/" "": "-/" "-/default" */
466/* "-/" "a": "-/a" "-/default" */
467/* "-/" "a-": "-/a-" "-/a-default" "-/default" */
468/* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */
469/* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */
470/* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */
471
472void qmesearch(fd,cutable)
473int *fd;
474int *cutable;
475{
476 int i;
477
478 if (!stralloc_copys(&qme,".qmail")) temp_nomem();
479 if (!stralloc_cats(&qme,dash)) temp_nomem();
480 if (!stralloc_cat(&qme,&safeext)) temp_nomem();
481 if (qmeexists(fd,cutable)) {
482 if (safeext.len >= 7) {
483 i = safeext.len - 7;
484 if (!byte_diff("default",7,safeext.s + i))
485 if (i <= str_len(ext)) /* paranoia */
486 if (!env_put2("DEFAULT",ext + i)) temp_nomem();
2117e02e 487 }
212b6f5d
MW
488 return;
489 }
490
491 for (i = safeext.len;i >= 0;--i)
492 if (!i || (safeext.s[i - 1] == '-')) {
493 if (!stralloc_copys(&qme,".qmail")) temp_nomem();
494 if (!stralloc_cats(&qme,dash)) temp_nomem();
495 if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem();
496 if (!stralloc_cats(&qme,"default")) temp_nomem();
497 if (qmeexists(fd,cutable)) {
498 if (i <= str_len(ext)) /* paranoia */
499 if (!env_put2("DEFAULT",ext + i)) temp_nomem();
500 return;
2117e02e 501 }
2117e02e 502 }
212b6f5d
MW
503
504 *fd = -1;
2117e02e
MW
505}
506
507unsigned long count_file = 0;
508unsigned long count_forward = 0;
509unsigned long count_program = 0;
510char count_buf[FMT_ULONG];
511
512void count_print()
513{
514 substdio_puts(subfdoutsmall,"did ");
515 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file));
516 substdio_puts(subfdoutsmall,"+");
517 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward));
518 substdio_puts(subfdoutsmall,"+");
519 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program));
520 substdio_puts(subfdoutsmall,"\n");
521 if (mailforward_qp)
522 {
523 substdio_puts(subfdoutsmall,"qp ");
524 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp));
525 substdio_puts(subfdoutsmall,"\n");
526 }
527 substdio_flush(subfdoutsmall);
528}
529
530void sayit(type,cmd,len)
531char *type;
532char *cmd;
533int len;
534{
535 substdio_puts(subfdoutsmall,type);
536 substdio_put(subfdoutsmall,cmd,len);
537 substdio_putsflush(subfdoutsmall,"\n");
538}
539
540void main(argc,argv)
541int argc;
542char **argv;
543{
544 int opt;
545 int i;
546 int j;
547 int k;
548 int fd;
549 int numforward;
550 char **recips;
551 datetime_sec starttime;
552 int flagforwardonly;
212b6f5d 553 char *x;
34c57ee0 554 char *cplast;
2117e02e
MW
555
556 umask(077);
557 sig_pipeignore();
558
559 if (!env_init()) temp_nomem();
560
561 flagdoit = 1;
562 while ((opt = getopt(argc,argv,"nN")) != opteof)
563 switch(opt)
564 {
565 case 'n': flagdoit = 0; break;
566 case 'N': flagdoit = 1; break;
2117e02e 567 default:
212b6f5d 568 usage();
2117e02e
MW
569 }
570 argc -= optind;
571 argv += optind;
572
573 if (!(user = *argv++)) usage();
574 if (!(homedir = *argv++)) usage();
575 if (!(local = *argv++)) usage();
576 if (!(dash = *argv++)) usage();
577 if (!(ext = *argv++)) usage();
578 if (!(host = *argv++)) usage();
579 if (!(sender = *argv++)) usage();
580 if (!(aliasempty = *argv++)) usage();
581 if (*argv) usage();
582
583 if (homedir[0] != '/') usage();
212b6f5d
MW
584 if (chdir(homedir) == -1)
585 strerr_die5x(111,"Unable to switch to ",homedir,": ",error_str(errno),". (#4.3.0)");
2117e02e
MW
586 checkhome();
587
588 if (!env_put2("HOST",host)) temp_nomem();
589 if (!env_put2("HOME",homedir)) temp_nomem();
590 if (!env_put2("USER",user)) temp_nomem();
591 if (!env_put2("LOCAL",local)) temp_nomem();
592
593 if (!stralloc_copys(&envrecip,local)) temp_nomem();
594 if (!stralloc_cats(&envrecip,"@")) temp_nomem();
595 if (!stralloc_cats(&envrecip,host)) temp_nomem();
596
597 if (!stralloc_copy(&foo,&envrecip)) temp_nomem();
598 if (!stralloc_0(&foo)) temp_nomem();
599 if (!env_put2("RECIPIENT",foo.s)) temp_nomem();
600
601 if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem();
602 if (!stralloc_cat(&dtline,&envrecip)) temp_nomem();
603 for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_';
604 if (!stralloc_cats(&dtline,"\n")) temp_nomem();
605
606 if (!stralloc_copy(&foo,&dtline)) temp_nomem();
607 if (!stralloc_0(&foo)) temp_nomem();
608 if (!env_put2("DTLINE",foo.s)) temp_nomem();
609
610 if (flagdoit)
611 bouncexf();
612
613 if (!env_put2("SENDER",sender)) temp_nomem();
614
34c57ee0 615 if (!quote2(&qsender,sender)) temp_nomem();
2117e02e 616 if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem();
34c57ee0 617 if (!stralloc_cat(&rpline,&qsender)) temp_nomem();
2117e02e
MW
618 for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_';
619 if (!stralloc_cats(&rpline,">\n")) temp_nomem();
620
621 if (!stralloc_copy(&foo,&rpline)) temp_nomem();
622 if (!stralloc_0(&foo)) temp_nomem();
623 if (!env_put2("RPLINE",foo.s)) temp_nomem();
624
34c57ee0
MW
625 i = byte_rchr(qsender.s,qsender.len,'@'); /* for VERH */
626 if (i != qsender.len) { /* got @ */
627 cplast = qsender.s + i;
628 *cplast = '\0';
629 if (qsender.s[i = str_rchr(qsender.s,'=')]) { /* got = */
630 qsender.s[i] = '\0';
631 cplast = qsender.s + i;
632 verhhost = qsender.s + i + 1;
633 i = str_rchr(qsender.s,'-');
634 if (qsender.s[i] == '-') {
635 for (;;) {
636 if (case_starts(qsender.s + i + 1,"return-")) {
637 verhlocal = qsender.s + i + 9 + str_chr(qsender.s + i + 8,'-');
638 /* here to avoid work if not VERP */
639 /* verhhost not used if verhlocal=0 */
640 for (x = verhlocal; x < cplast; x++)
641 if (*x == '\n') *x = '_'; /* \n would ruin */
642 break;
643 }
644 j = byte_rchr(qsender.s,i,'-');
645 if (j == i) break;
646 i = j;
647 }
648 }
649 }
650 }
651
2117e02e
MW
652 if (!stralloc_copys(&ufline,"From ")) temp_nomem();
653 if (*sender)
654 {
655 int len; int i; char ch;
656
657 len = str_len(sender);
658 if (!stralloc_readyplus(&ufline,len)) temp_nomem();
659 for (i = 0;i < len;++i)
660 {
661 ch = sender[i];
662 if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-';
663 ufline.s[ufline.len + i] = ch;
664 }
665 ufline.len += len;
666 }
667 else
668 if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem();
669 if (!stralloc_cats(&ufline," ")) temp_nomem();
670 starttime = now();
671 if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem();
672
673 if (!stralloc_copy(&foo,&ufline)) temp_nomem();
674 if (!stralloc_0(&foo)) temp_nomem();
675 if (!env_put2("UFLINE",foo.s)) temp_nomem();
676
212b6f5d
MW
677 x = ext;
678 if (!env_put2("EXT",x)) temp_nomem();
679 x += str_chr(x,'-'); if (*x) ++x;
680 if (!env_put2("EXT2",x)) temp_nomem();
681 x += str_chr(x,'-'); if (*x) ++x;
682 if (!env_put2("EXT3",x)) temp_nomem();
683 x += str_chr(x,'-'); if (*x) ++x;
684 if (!env_put2("EXT4",x)) temp_nomem();
685
686 if (!stralloc_copys(&safeext,ext)) temp_nomem();
687 case_lowerb(safeext.s,safeext.len);
688 for (i = 0;i < safeext.len;++i)
689 if (safeext.s[i] == '.')
690 safeext.s[i] = ':';
691
692 i = str_len(host);
693 i = byte_rchr(host,i,'.');
694 if (!stralloc_copyb(&foo,host,i)) temp_nomem();
695 if (!stralloc_0(&foo)) temp_nomem();
696 if (!env_put2("HOST2",foo.s)) temp_nomem();
697 i = byte_rchr(host,i,'.');
698 if (!stralloc_copyb(&foo,host,i)) temp_nomem();
699 if (!stralloc_0(&foo)) temp_nomem();
700 if (!env_put2("HOST3",foo.s)) temp_nomem();
701 i = byte_rchr(host,i,'.');
702 if (!stralloc_copyb(&foo,host,i)) temp_nomem();
703 if (!stralloc_0(&foo)) temp_nomem();
704 if (!env_put2("HOST4",foo.s)) temp_nomem();
2117e02e
MW
705
706 flagforwardonly = 0;
212b6f5d
MW
707 qmesearch(&fd,&flagforwardonly);
708 if (fd == -1)
709 if (*dash)
710 strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
2117e02e
MW
711
712 if (!stralloc_copys(&ueo,sender)) temp_nomem();
713 if (str_diff(sender,""))
714 if (str_diff(sender,"#@[]"))
715 if (qmeox("-owner") == 0)
716 {
717 if (qmeox("-owner-default") == 0)
718 {
719 if (!stralloc_copys(&ueo,local)) temp_nomem();
720 if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem();
721 if (!stralloc_cats(&ueo,host)) temp_nomem();
722 if (!stralloc_cats(&ueo,"-@[]")) temp_nomem();
723 }
724 else
725 {
726 if (!stralloc_copys(&ueo,local)) temp_nomem();
727 if (!stralloc_cats(&ueo,"-owner@")) temp_nomem();
728 if (!stralloc_cats(&ueo,host)) temp_nomem();
729 }
730 }
731 if (!stralloc_0(&ueo)) temp_nomem();
732 if (!env_put2("NEWSENDER",ueo.s)) temp_nomem();
733
734 if (!stralloc_ready(&cmds,0)) temp_nomem();
735 cmds.len = 0;
736 if (fd != -1)
737 if (slurpclose(fd,&cmds,256) == -1) temp_nomem();
738
739 if (!cmds.len)
740 {
741 if (!stralloc_copys(&cmds,aliasempty)) temp_nomem();
742 flagforwardonly = 0;
743 }
744 if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
745 if (!stralloc_cats(&cmds,"\n")) temp_nomem();
746
747 numforward = 0;
748 i = 0;
749 for (j = 0;j < cmds.len;++j)
750 if (cmds.s[j] == '\n')
751 {
752 switch(cmds.s[i]) { case '#': case '.': case '/': case '|': break;
753 default: ++numforward; }
754 i = j + 1;
755 }
756
757 recips = (char **) alloc((numforward + 1) * sizeof(char *));
758 if (!recips) temp_nomem();
759 numforward = 0;
760
761 flag99 = 0;
762
763 i = 0;
764 for (j = 0;j < cmds.len;++j)
765 if (cmds.s[j] == '\n')
766 {
767 cmds.s[j] = 0;
768 k = j;
769 while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
770 cmds.s[--k] = 0;
771 switch(cmds.s[i])
772 {
773 case 0: /* k == i */
774 if (i) break;
212b6f5d 775 strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
2117e02e
MW
776 case '#':
777 break;
d708ae5a
MW
778 case '<':
779 if (!stralloc_copys(&ueo, cmds.s + i + 1) ||
780 !stralloc_0(&ueo) ||
781 !env_put2("NEWSENDER", ueo.s))
782 temp_nomem();
783 break;
2117e02e
MW
784 case '.':
785 case '/':
786 ++count_file;
212b6f5d 787 if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)");
2117e02e
MW
788 if (cmds.s[k - 1] == '/')
789 if (flagdoit) maildir(cmds.s + i);
790 else sayit("maildir ",cmds.s + i,k - i);
791 else
792 if (flagdoit) mailfile(cmds.s + i);
793 else sayit("mbox ",cmds.s + i,k - i);
794 break;
795 case '|':
796 ++count_program;
212b6f5d 797 if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)");
2117e02e
MW
798 if (flagdoit) mailprogram(cmds.s + i + 1);
799 else sayit("program ",cmds.s + i + 1,k - i - 1);
800 break;
801 case '+':
802 if (str_equal(cmds.s + i + 1,"list"))
803 flagforwardonly = 1;
804 break;
805 case '&':
806 ++i;
d708ae5a
MW
807 goto fwd;
808 case '!':
809 i++;
810 if (!cmds.s[i + str_chr(cmds.s + i, '=')]) {
811 if (!env_unset(cmds.s + i)) temp_nomem();
812 } else {
813 env:
814 if (!env_put(cmds.s + i)) temp_nomem();
815 }
816 break;
2117e02e 817 default:
d708ae5a
MW
818 if (cmds.s[i + str_chr(cmds.s + i, '=')])
819 goto env;
820 fwd:
2117e02e
MW
821 ++count_forward;
822 if (flagdoit) recips[numforward++] = cmds.s + i;
823 else sayit("forward ",cmds.s + i,k - i);
824 break;
825 }
826 i = j + 1;
827 if (flag99) break;
828 }
829
830 if (numforward) if (flagdoit)
831 {
832 recips[numforward] = 0;
833 mailforward(recips);
834 }
835
836 count_print();
837 _exit(0);
838}