5c441cf6ae7af414dfb78e95e7de1e73e7045cea
26 #include "slurpclose.h"
29 #include "auto_patrn.h"
31 void err(s
) char *s
; { substdio_putsflush(subfderr
,s
); }
32 void soft() { _exit(111); }
33 void hard() { _exit(100); }
35 void temp_childcrashed() { err("Aack, child crashed. (#4.3.0)\n"); soft(); }
36 void temp_rewind() { err("Unable to rewind message. (#4.3.0)\n"); soft(); }
37 void temp_fork() { err("Unable to fork. (#4.3.0)\n"); soft(); }
38 void temp_read() { err("Error while reading message. (#4.3.0)\n"); soft(); }
39 void temp_write() { err("Error while writing message. (#4.3.0)\n"); soft(); }
40 void temp_child() { err("Temporary error in forwarding message. (#4.3.0)\n"); soft(); }
41 void temp_maildirtimeout() { err("Timeout on maildir delivery. (#4.3.0)\n"); soft(); }
42 void temp_maildir() { err("Temporary error on maildir delivery. (#4.3.0)\n"); soft(); }
43 void temp_nomaildir() { err("Unable to chdir to maildir. (#4.2.1)\n"); soft(); }
44 void temp_open(fn
) char *fn
; { err("Unable to open "); err(fn
); err(". (#4.2.1)\n"); soft(); }
46 void temp_blankline() { err("Uh-oh: first line of .qmail file is blank. (#4.2.1)\n"); soft(); }
47 void temp_fofile() { err("Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)\n"); soft(); }
48 void temp_foprog() { err("Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)\n"); soft(); }
49 void temp_nomem() { err("Out of memory. (#4.3.0)\n"); soft(); }
50 void temp_chdir() { err("Unable to switch to home directory. (#4.3.0)\n"); soft(); }
51 void temp_homestat() { err("Unable to stat home directory. (#4.3.0)\n"); soft(); }
52 void temp_homesticky() { err("Home directory is sticky: user is editing his .qmail file. (#4.2.1)\n"); soft(); }
53 void temp_homewritable() { err("Uh-oh: home directory is writable. (#4.7.0)\n"); soft(); }
54 void temp_qmwritable() { err("Uh-oh: .qmail file is writable. (#4.7.0)\n"); soft(); }
55 void temp_nfsqmail() { err("Temporary error trying to open .qmail file. (#4.3.0)\n"); soft(); }
56 void temp_denyqmail() { err("Permission error trying to open .qmail file. (#4.3.0)\n"); soft(); }
57 void temp_slowlock() { err("File has been locked for 30 seconds straight. (#4.3.0)\n"); soft(); }
59 void bounce_childperm() { err("Permanent error in forwarding message. (#5.2.4)\n"); hard(); }
60 void bounce_loop() { err("This message is looping: it already has my Delivered-To line. (#5.4.6)\n"); hard(); }
61 void bounce_ext() { err("Sorry, no mailbox here by that name. (#5.1.1)\n"); hard(); }
62 void usage() { err("qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty\n"); hard(); }
64 void warn_homesticky() { err("Warning: home directory is sticky.\n"); }
78 stralloc dashext
= {0};
79 stralloc ufline
= {0};
80 stralloc rpline
= {0};
81 stralloc envrecip
= {0};
82 stralloc dtline
= {0};
86 stralloc messline
= {0};
94 char fntmptph
[80 + FMT_ULONG
* 2];
95 char fnnewtph
[80 + FMT_ULONG
* 2];
96 void tryunlinktmp() { unlink(fntmptph
); }
97 void sigalrm() { tryunlinktmp(); _exit(3); }
99 void maildir_child(dir
)
112 sig_alarmcatch(sigalrm
);
113 if (chdir(dir
) == -1) { if (error_temp(errno
)) _exit(1); _exit(2); }
116 gethostname(host
,sizeof(host
));
117 for (loop
= 0;;++loop
)
121 s
+= fmt_str(s
,"tmp/");
122 s
+= fmt_ulong(s
,time
); *s
++ = '.';
123 s
+= fmt_ulong(s
,pid
); *s
++ = '.';
124 s
+= fmt_strn(s
,host
,sizeof(host
)); *s
++ = 0;
125 if (stat(fntmptph
,&st
) == -1) if (errno
== error_noent
) break;
126 /* really should never get to this point */
127 if (loop
== 2) _exit(1);
130 str_copy(fnnewtph
,fntmptph
);
131 byte_copy(fnnewtph
,3,"new");
134 fd
= open_excl(fntmptph
);
135 if (fd
== -1) _exit(1);
137 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
138 substdio_fdbuf(&ssout
,write
,fd
,outbuf
,sizeof(outbuf
));
139 if (substdio_put(&ssout
,rpline
.s
,rpline
.len
) == -1) goto fail
;
140 if (substdio_put(&ssout
,dtline
.s
,dtline
.len
) == -1) goto fail
;
142 switch(substdio_copy(&ssout
,&ss
))
144 case -2: tryunlinktmp(); _exit(4);
148 if (substdio_flush(&ssout
) == -1) goto fail
;
149 if (fsync(fd
) == -1) goto fail
;
150 if (close(fd
) == -1) goto fail
; /* NFS dorks */
152 if (link(fntmptph
,fnnewtph
) == -1) goto fail
;
153 /* if it was error_exist, almost certainly successful; i hate NFS */
154 tryunlinktmp(); _exit(0);
156 fail
: tryunlinktmp(); _exit(1);
159 /* end child process */
167 if (seek_begin(0) == -1) temp_rewind();
169 switch(child
= fork())
178 wait_pid(&wstat
,child
);
179 if (wait_crashed(wstat
))
181 switch(wait_exitcode(wstat
))
184 case 2: temp_nomaildir();
185 case 3: temp_maildirtimeout();
187 default: temp_maildir();
191 void slowlock() { temp_slowlock(); }
203 if (seek_begin(0) == -1) temp_rewind();
205 fd
= open_append(fn
);
206 if (fd
== -1) temp_open(fn
);
208 sig_alarmcatch(slowlock
);
210 flaglocked
= (lock_ex(fd
) != -1);
217 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
218 substdio_fdbuf(&ssout
,write
,fd
,outbuf
,sizeof(outbuf
));
219 if (substdio_put(&ssout
,ufline
.s
,ufline
.len
)) goto writeerrs
;
220 if (substdio_put(&ssout
,rpline
.s
,rpline
.len
)) goto writeerrs
;
221 if (substdio_put(&ssout
,dtline
.s
,dtline
.len
)) goto writeerrs
;
224 if (getln(&ss
,&messline
,&match
,'\n') != 0)
225 { if (flaglocked
) seek_trunc(fd
,pos
); close(fd
); temp_read(); }
226 if (!match
&& !messline
.len
) break;
227 if (gfrom(messline
.s
,messline
.len
))
228 if (substdio_bput(&ssout
,">",1)) goto writeerrs
;
229 if (substdio_bput(&ssout
,messline
.s
,messline
.len
)) goto writeerrs
;
232 if (substdio_bputs(&ssout
,"\n")) goto writeerrs
;
236 if (substdio_bputs(&ssout
,"\n")) goto writeerrs
;
237 if (substdio_flush(&ssout
)) goto writeerrs
;
238 if (fsync(fd
) == -1) goto writeerrs
;
243 if (flaglocked
) seek_trunc(fd
,pos
);
248 void mailprogram(prog
)
255 if (seek_begin(0) == -1) temp_rewind();
257 switch(child
= fork())
262 args
[0] = "sh"; args
[1] = "-c"; args
[2] = prog
; args
[3] = 0;
265 if (errno
== error_txtbsy
) { err("Text busy. (#4.3.0)\n"); soft(); }
266 if (errno
== error_nomem
) { err("Out of memory. (#4.3.0)\n"); soft(); }
267 if (errno
== error_io
) { err("I/O error. (#4.3.0)\n"); soft(); }
268 if (error_temp(errno
)) { err("Temporary error. (#4.3.0)\n"); soft(); }
269 err("Unable to execute "); err(*args
); err(" (#5.2.4)\n");
273 wait_pid(&wstat
,child
);
274 if (wait_crashed(wstat
))
276 switch(wait_exitcode(wstat
))
279 case 64: case 65: case 70: case 76: case 77: case 78: case 112: hard();
281 case 99: flag99
= 1; break;
286 unsigned long mailforward_qp
= 0;
288 void mailforward(recips
)
295 if (seek_begin(0) == -1) temp_rewind();
296 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
298 if (qmail_open(&qqt
) == -1) temp_fork();
299 mailforward_qp
= qmail_qp(&qqt
);
300 qmail_put(&qqt
,dtline
.s
,dtline
.len
);
303 if (getln(&ss
,&messline
,&match
,'\n') != 0) { qmail_fail(&qqt
); break; }
304 qmail_put(&qqt
,messline
.s
,messline
.len
);
307 qmail_from(&qqt
,ueo
.s
);
308 while (*recips
) qmail_to(&qqt
,*recips
++);
309 switch(qmail_close(&qqt
))
311 case QMAIL_TOOLONG
: bounce_childperm();
312 case QMAIL_READ
: temp_read();
314 default: temp_child();
323 if (seek_begin(0) == -1) temp_rewind();
324 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
327 if (getln(&ss
,&messline
,&match
,'\n') != 0) temp_read();
329 if (messline
.len
<= 1)
331 if (messline
.len
== dtline
.len
)
332 if (!str_diffn(messline
.s
,dtline
.s
,dtline
.len
))
341 if (stat(".",&st
) == -1) temp_homestat();
342 if (st
.st_mode
& auto_patrn
) temp_homewritable();
343 if (st
.st_mode
& 01000)
344 if (flagdoit
) temp_homesticky(); else warn_homesticky();
352 if (!stralloc_copys(&qme
,".qmail")) temp_nomem();
353 if (!stralloc_cat(&qme
,&dashext
)) temp_nomem();
354 if (!stralloc_cats(&qme
,dashowner
)) temp_nomem();
355 if (!stralloc_0(&qme
)) temp_nomem();
356 if (stat(qme
.s
,&st
) == -1)
358 if (error_temp(errno
)) temp_nfsqmail();
374 if (!stralloc_copys(&qme
,".qmail")) temp_nomem();
375 if (!stralloc_catb(&qme
,dashext
.s
,i
)) temp_nomem();
376 if (i
< dashext
.len
) if (!stralloc_cats(&qme
,"-default")) temp_nomem();
377 if (!stralloc_0(&qme
)) temp_nomem();
378 fd
= open_read(qme
.s
);
381 if (error_temp(errno
)) temp_nfsqmail();
382 if (errno
== error_perm
) temp_denyqmail();
383 if (errno
== error_acces
) temp_denyqmail();
387 if (fstat(fd
,&st
) == -1) temp_nfsqmail();
388 if ((st
.st_mode
& S_IFMT
) == S_IFREG
)
390 if (st
.st_mode
& auto_patrn
) temp_qmwritable();
391 *cutable
= !!(st
.st_mode
& 0100);
398 if (dashext
.s
[--i
] == '-') break;
403 unsigned long count_file
= 0;
404 unsigned long count_forward
= 0;
405 unsigned long count_program
= 0;
406 char count_buf
[FMT_ULONG
];
410 substdio_puts(subfdoutsmall
,"did ");
411 substdio_put(subfdoutsmall
,count_buf
,fmt_ulong(count_buf
,count_file
));
412 substdio_puts(subfdoutsmall
,"+");
413 substdio_put(subfdoutsmall
,count_buf
,fmt_ulong(count_buf
,count_forward
));
414 substdio_puts(subfdoutsmall
,"+");
415 substdio_put(subfdoutsmall
,count_buf
,fmt_ulong(count_buf
,count_program
));
416 substdio_puts(subfdoutsmall
,"\n");
419 substdio_puts(subfdoutsmall
,"qp ");
420 substdio_put(subfdoutsmall
,count_buf
,fmt_ulong(count_buf
,mailforward_qp
));
421 substdio_puts(subfdoutsmall
,"\n");
423 substdio_flush(subfdoutsmall
);
426 void sayit(type
,cmd
,len
)
431 substdio_puts(subfdoutsmall
,type
);
432 substdio_put(subfdoutsmall
,cmd
,len
);
433 substdio_putsflush(subfdoutsmall
,"\n");
447 datetime_sec starttime
;
454 if (!env_init()) temp_nomem();
457 while ((opt
= getopt(argc
,argv
,"nN")) != opteof
)
460 case 'n': flagdoit
= 0; break;
461 case 'N': flagdoit
= 1; break;
469 if (!(user
= *argv
++)) usage();
470 if (!(homedir
= *argv
++)) usage();
471 if (!(local
= *argv
++)) usage();
472 if (!(dash
= *argv
++)) usage();
473 if (!(ext
= *argv
++)) usage();
474 if (!(host
= *argv
++)) usage();
475 if (!(sender
= *argv
++)) usage();
476 if (!(aliasempty
= *argv
++)) usage();
479 if (homedir
[0] != '/') usage();
480 if (chdir(homedir
) == -1) temp_chdir();
483 if (!env_put2("HOST",host
)) temp_nomem();
484 if (!env_put2("HOME",homedir
)) temp_nomem();
485 if (!env_put2("USER",user
)) temp_nomem();
486 if (!env_put2("LOCAL",local
)) temp_nomem();
488 if (!stralloc_copys(&envrecip
,local
)) temp_nomem();
489 if (!stralloc_cats(&envrecip
,"@")) temp_nomem();
490 if (!stralloc_cats(&envrecip
,host
)) temp_nomem();
492 if (!stralloc_copy(&foo
,&envrecip
)) temp_nomem();
493 if (!stralloc_0(&foo
)) temp_nomem();
494 if (!env_put2("RECIPIENT",foo
.s
)) temp_nomem();
496 if (!stralloc_copys(&dtline
,"Delivered-To: ")) temp_nomem();
497 if (!stralloc_cat(&dtline
,&envrecip
)) temp_nomem();
498 for (i
= 0;i
< dtline
.len
;++i
) if (dtline
.s
[i
] == '\n') dtline
.s
[i
] = '_';
499 if (!stralloc_cats(&dtline
,"\n")) temp_nomem();
501 if (!stralloc_copy(&foo
,&dtline
)) temp_nomem();
502 if (!stralloc_0(&foo
)) temp_nomem();
503 if (!env_put2("DTLINE",foo
.s
)) temp_nomem();
508 if (!env_put2("SENDER",sender
)) temp_nomem();
510 if (!quote2(&foo
,sender
)) temp_nomem();
511 if (!stralloc_copys(&rpline
,"Return-Path: <")) temp_nomem();
512 if (!stralloc_cat(&rpline
,&foo
)) temp_nomem();
513 for (i
= 0;i
< rpline
.len
;++i
) if (rpline
.s
[i
] == '\n') rpline
.s
[i
] = '_';
514 if (!stralloc_cats(&rpline
,">\n")) temp_nomem();
516 if (!stralloc_copy(&foo
,&rpline
)) temp_nomem();
517 if (!stralloc_0(&foo
)) temp_nomem();
518 if (!env_put2("RPLINE",foo
.s
)) temp_nomem();
520 if (!stralloc_copys(&ufline
,"From ")) temp_nomem();
523 int len
; int i
; char ch
;
525 len
= str_len(sender
);
526 if (!stralloc_readyplus(&ufline
,len
)) temp_nomem();
527 for (i
= 0;i
< len
;++i
)
530 if ((ch
== ' ') || (ch
== '\t') || (ch
== '\n')) ch
= '-';
531 ufline
.s
[ufline
.len
+ i
] = ch
;
536 if (!stralloc_cats(&ufline
,"MAILER-DAEMON")) temp_nomem();
537 if (!stralloc_cats(&ufline
," ")) temp_nomem();
539 if (!stralloc_cats(&ufline
,myctime(starttime
))) temp_nomem();
541 if (!stralloc_copy(&foo
,&ufline
)) temp_nomem();
542 if (!stralloc_0(&foo
)) temp_nomem();
543 if (!env_put2("UFLINE",foo
.s
)) temp_nomem();
545 if (!stralloc_copys(&dashext
,dash
)) temp_nomem();
546 if (!stralloc_cats(&dashext
,ext
)) temp_nomem();
547 for (i
= 0;i
< dashext
.len
;++i
)
548 if (dashext
.s
[i
] == '.')
550 case_lowerb(dashext
.s
,dashext
.len
);
553 if (!env_put2("EXT",extx
)) temp_nomem();
554 extx
+= str_chr(extx
,'-'); if (*extx
) ++extx
;
555 if (!env_put2("EXT2",extx
)) temp_nomem();
556 extx
+= str_chr(extx
,'-'); if (*extx
) ++extx
;
557 if (!env_put2("EXT3",extx
)) temp_nomem();
558 extx
+= str_chr(extx
,'-'); if (*extx
) ++extx
;
559 if (!env_put2("EXT4",extx
)) temp_nomem();
562 fd
= qmeopen(&flagforwardonly
);
563 if (fd
== -1) if (*dash
) bounce_ext();
565 if (!stralloc_copys(&ueo
,sender
)) temp_nomem();
566 if (str_diff(sender
,""))
567 if (str_diff(sender
,"#@[]"))
568 if (qmeox("-owner") == 0)
570 if (qmeox("-owner-default") == 0)
572 if (!stralloc_copys(&ueo
,local
)) temp_nomem();
573 if (!stralloc_cats(&ueo
,"-owner-@")) temp_nomem();
574 if (!stralloc_cats(&ueo
,host
)) temp_nomem();
575 if (!stralloc_cats(&ueo
,"-@[]")) temp_nomem();
579 if (!stralloc_copys(&ueo
,local
)) temp_nomem();
580 if (!stralloc_cats(&ueo
,"-owner@")) temp_nomem();
581 if (!stralloc_cats(&ueo
,host
)) temp_nomem();
584 if (!stralloc_0(&ueo
)) temp_nomem();
585 if (!env_put2("NEWSENDER",ueo
.s
)) temp_nomem();
587 if (!stralloc_ready(&cmds
,0)) temp_nomem();
590 if (slurpclose(fd
,&cmds
,256) == -1) temp_nomem();
594 if (!stralloc_copys(&cmds
,aliasempty
)) temp_nomem();
597 if (!cmds
.len
|| (cmds
.s
[cmds
.len
- 1] != '\n'))
598 if (!stralloc_cats(&cmds
,"\n")) temp_nomem();
602 for (j
= 0;j
< cmds
.len
;++j
)
603 if (cmds
.s
[j
] == '\n')
605 switch(cmds
.s
[i
]) { case '#': case '.': case '/': case '|': break;
606 default: ++numforward
; }
610 recips
= (char **) alloc((numforward
+ 1) * sizeof(char *));
611 if (!recips
) temp_nomem();
617 for (j
= 0;j
< cmds
.len
;++j
)
618 if (cmds
.s
[j
] == '\n')
622 while ((k
> i
) && (cmds
.s
[k
- 1] == ' ') || (cmds
.s
[k
- 1] == '\t'))
634 if (flagforwardonly
) temp_fofile();
635 if (cmds
.s
[k
- 1] == '/')
636 if (flagdoit
) maildir(cmds
.s
+ i
);
637 else sayit("maildir ",cmds
.s
+ i
,k
- i
);
639 if (flagdoit
) mailfile(cmds
.s
+ i
);
640 else sayit("mbox ",cmds
.s
+ i
,k
- i
);
644 if (flagforwardonly
) temp_foprog();
645 if (flagdoit
) mailprogram(cmds
.s
+ i
+ 1);
646 else sayit("program ",cmds
.s
+ i
+ 1,k
- i
- 1);
649 if (str_equal(cmds
.s
+ i
+ 1,"list"))
656 if (flagdoit
) recips
[numforward
++] = cmds
.s
+ i
;
657 else sayit("forward ",cmds
.s
+ i
,k
- i
);
664 if (numforward
) if (flagdoit
)
666 recips
[numforward
] = 0;