39#define WHO "qmail-local"
41static void usage() { logmsg(
WHO,100,USAGE,
"qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
43static void temp_nomem() { logmsg(
WHO,111,FATAL,
"Out of memory. (#4.3.0)"); }
44static void temp_rewind() { logmsg(
WHO,111,FATAL,
"Unable to rewind message. (#4.3.0)"); }
45static void temp_childcrashed() { logmsg(
WHO,111,FATAL,
"Aack, child crashed. (#4.3.0)"); }
46static void temp_fork() { logmsg(
WHO,111,FATAL,B(
"Unable to fork: ",error_str(errno),
". (#4.3.0)")); }
47static void temp_read() { logmsg(
WHO,111,ERROR,B(
"Unable to read message: ",error_str(errno),
". (#4.3.0)")); }
48static void temp_slowlock(
int d) { logmsg(
WHO,111,ERROR,
"File has been locked for 30 seconds straight. (#4.3.0)"); }
49static void temp_qmail(
char *
fn) { logmsg(
WHO,111,FATAL,B(
"Unable to open: ",
fn,
" ",error_str(errno),
". (#4.3.0)")); }
85static void maildir_child(
char *dir)
98 if (chdir(dir) == -1) {
if (errno != ENOENT)
_exit(1);
_exit(2); }
104 for (loop = 0; loop < str_len(
host); ++loop) {
105 if (
host[loop] ==
'/') {
109 if (
host[loop] ==
':') {
116 for (loop = 0 ;; ++loop) {
117 gettimeofday(&time,0);
119 s += fmt_str(s,
"tmp/");
120 s += fmt_ulong(s,time.tv_sec); *s++ =
'.';
121 *s++ =
'M'; s += fmt_ulong(s,time.tv_usec);
122 *s++ =
'P'; s += fmt_ulong(s,pid); *s++ =
'.';
125 if (stat(
fntmptph,&st) == -1)
if (errno == ENOENT)
break;
127 if (loop == 2)
_exit(1);
140 switch (buffer_copy(&
bo,&
bi)) {
145 if (buffer_flush(&
bo) == -1)
goto FAIL;
146 if (fstat(
fd,&st) == -1)
goto FAIL;
147 if (fsync(
fd) == -1)
goto FAIL;
148 if (close(
fd) == -1)
goto FAIL;
151 s += fmt_str(s,
"new/");
152 s += fmt_ulong(s,time.tv_sec); *s++ =
'.';
155 *s++ =
'I'; s += fmt_xlong(s,st.st_ino);
156 *s++ =
'V'; s += fmt_xlong(s,st.st_dev);
159 *s++ =
'M'; s += fmt_ulong(s,time.tv_usec);
160 *s++ =
'P'; s += fmt_ulong(s,pid); *s++ =
'.';
165 if ((
fd = open(
fnnewtph,O_RDONLY)) < 0 || fsync(
fd) < 0 || close(
fd))
goto FAIL;
174static void maildir(
char *
fn)
179 if (seek_begin(0) == -1) temp_rewind();
181 switch (child = fork()) {
189 wait_pid(&wstat,child);
190 if (wait_crashed(wstat))
193 switch (wait_exitcode(wstat)) {
195 case 2: logmsg(
WHO,111,ERROR,
"Unable to chdir to maildir. (#4.2.1)");
196 case 3: logmsg(
WHO,111,ERROR,
"Timeout on maildir delivery. (#4.3.0)");
197 case 4: logmsg(
WHO,111,ERROR,
"Unable to read message. (#4.3.0)");
198 default: logmsg(
WHO,111,ERROR,
"Temporary error on maildir delivery. (#4.3.0)");
202static void mailfile(
char *
fn)
211 if (seek_begin(0) == -1) temp_rewind();
213 fd = open_append(
fn);
215 logmsg(
WHO,111,ERROR,B(
"Unable to open:",
fn,
" ",error_str(errno),
". (#4.2.1)"));
217 sig_alarmcatch(temp_slowlock);
219 flaglocked = (lock_ex(
fd) != -1);
234 logmsg(
WHO,0,WARN,B(
"Unable to read message: ",error_str(errno),
". (#4.3.0)"));
235 if (flaglocked) seek_trunc(
fd,pos);
241 if (buffer_puts(&
bo,
">"))
goto WRITERRS;
244 if (buffer_puts(&
bo,
"\n"))
goto WRITERRS;
249 if (buffer_puts(&
bo,
"\n"))
goto WRITERRS;
250 if (buffer_flush(&
bo))
goto WRITERRS;
251 if (fsync(
fd) == -1)
goto WRITERRS;
256 logmsg(
WHO,0,WARN,B(
"Unable to write ",
fn,
": ",error_str(errno),
". (#4.3.0)"));
257 if (flaglocked) seek_trunc(
fd,pos);
262static void mailprogram(
char *prog)
268 if (seek_begin(0) == -1) temp_rewind();
270 switch (child = fork()) {
280 logmsg(
WHO,0,ERROR,B(
"Unable to run /bin/sh: ",error_str(errno),
". (#4.3.0)"));
283 wait_pid(&wstat,child);
284 if (wait_crashed(wstat))
287 switch (wait_exitcode(wstat)) {
289 case 64:
case 65:
case 70:
case 76:
case 77:
case 78:
case 112:
_exit(100);
291 case 99:
flag99 = 1;
break;
298static void mailforward(
char **recips)
305 if (seek_begin(0) == -1) temp_rewind();
321 logmsg(
WHO,*qqx ==
'D' ? 100 : 111,ERROR,B(
"Unable to forward message: ",qqx + 1,
"."));
324static void bouncexf()
329 if (seek_begin(0) == -1) temp_rewind();
339 logmsg(
WHO,100,ERROR,
"This message is looping: it already has my Delivered-To line. (#5.4.6)");
343static void checkhome()
347 if (stat(
".",&st) == -1)
348 logmsg(
WHO,111,ERROR,B(
"Unable to stat home directory: ",error_str(errno),
". (#4.3.0)"));
350 logmsg(
WHO,111,ERROR,
"Uh-oh: home directory is writable. (#4.7.0)");
351 if (st.st_mode & 01000)
353 logmsg(
WHO,111,ERROR,
"Home directory is sticky: user is editing his .qmail file. (#4.2.1)");
355 logmsg(
WHO,0,WARN,
"Warning: home directory is sticky.");
358static int qmeox(
char *dashowner)
368 if (stat(
qme.s,&st) == -1) {
369 if (errno != ENOENT) temp_qmail(
qme.s);
375static int qmeexists(
int *
fd,
int *cutable)
381 *
fd = open_read(
qme.s);
383 if (errno != ENOENT) temp_qmail(
qme.s);
384 if (errno == EPERM) temp_qmail(
qme.s);
385 if (errno == EACCES) temp_qmail(
qme.s);
389 if (fstat(*
fd,&st) == -1) temp_qmail(
qme.s);
390 if ((st.st_mode & S_IFMT) == S_IFREG) {
392 logmsg(
WHO,111,ERROR,
"Uh-oh: .qmail file is writable. (#4.7.0)");
393 *cutable = !!(st.st_mode & 0100);
408static void qmesearch(
int *
fd,
int *cutable)
415 if (qmeexists(
fd,cutable)) {
418 if (!byte_diff(
"default",7,
safeext.s + i))
419 if (i <= str_len(
ext))
425 for (i =
safeext.len; i >= 0 ;--i)
426 if (!i || (
safeext.s[i - 1] ==
'-')) {
431 if (qmeexists(
fd,cutable)) {
432 if (i <= str_len(
ext))
448static void count_print()
450 buffer_puts(&
bl,
"did ");
452 buffer_puts(&
bl,
"+");
454 buffer_puts(&
bl,
"+");
456 buffer_puts(&
bl,
"\n");
459 buffer_puts(&
bl,
"qp ");
461 buffer_puts(&
bl,
"\n");
466static void sayit(
char *type,
char *cmd,
int len)
468 buffer_puts(&
bl,type);
469 buffer_put(&
bl,cmd,len);
470 buffer_putsflush(&
bl,
"\n");
473int main(
int argc,
char *
const *argv)
490 while ((opt = getoptb(argc,(
char **)argv,
"nN")) != opteof)
511 logmsg(
WHO,111,ERROR,B(
"Unable to switch to: ",
homedir,
" ",error_str(errno),
". (#4.3.0)"));
519#ifdef HIDEVIRTUALUSER
520 if (str_len(
ext) > 1) {
536 for (i = 0; i <
dtline.len; ++i)
561 int len;
int i;
char ch;
566 for (i = 0;i < len;++i) {
568 if ((ch ==
' ') || (ch ==
'\t') || (ch ==
'\n')) ch =
'-';
585 x += str_chr(x,
'-');
if (*x) ++x;
587 x += str_chr(x,
'-');
if (*x) ++x;
589 x += str_chr(x,
'-');
if (*x) ++x;
595 for (i = 0; i <
safeext.len; ++i)
600 i = byte_rchr(
host,i,
'.');
604 i = byte_rchr(
host,i,
'.');
608 i = byte_rchr(
host,i,
'.');
614 qmesearch(&
fd,&flagforwardonly);
618 logmsg(
WHO,100,ERROR,
"Sorry, no mailbox here by that name. (#5.1.1)");
622 if (str_diff(
sender,
"#@[]"))
623 if (qmeox(
"-owner") == 0) {
624 if (qmeox(
"-owner-default") == 0) {
655 if (
cmds.s[
j] ==
'\n') {
657 case '#':
case '.':
case '/':
case '|':
break;
658 default: ++numforward;
663 recips = (
char **) alloc((numforward + 1) *
sizeof(
char *));
671 if (
cmds.s[
j] ==
'\n') {
675 while ((
k > i) && ((
cmds.s[
k - 1] ==
' ') || (
cmds.s[
k - 1] ==
'\t')))
680 logmsg(
WHO,111,ERROR,
"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
686 if (flagforwardonly) logmsg(
WHO,111,ERROR,
"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)");
687 if (
cmds.s[
k - 1] ==
'/')
689 else sayit(
"maildir ",
cmds.s + i,
k - i);
692 else sayit(
"mbox ",
cmds.s + i,
k - i);
696 if (flagforwardonly) logmsg(
WHO,111,ERROR,
"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)");
698 else sayit(
"program ",
cmds.s + i + 1,
k - i - 1);
701 if (str_equal(
cmds.s + i + 1,
"list"))
709 else sayit(
"forward ",
cmds.s + i,
k - i);
717 recips[numforward] = 0;
char outbuf[BUFSIZE_MESS]
int stralloc_copys(stralloc *, char const *)
int gfrom(char *s, int len)
char * myctime(datetime_sec)
unsigned long count_forward
unsigned long mailforward_qp
char fntmptph[80+FMT_ULONG *2]
char fnnewtph[80+FMT_ULONG *2]
unsigned long count_program
char count_buf[FMT_ULONG]
void qmail_to(struct qmail *, char *)
void qmail_from(struct qmail *, char *)
void qmail_put(struct qmail *, char *, int)
char * qmail_close(struct qmail *)
unsigned long qmail_qp(struct qmail *)
int qmail_open(struct qmail *)
void qmail_fail(struct qmail *)
int quote2(stralloc *, char *)