27 #include "slurpclose.h"
30 #include "auto_patrn.h"
32 void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
34 void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); }
35 void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
36 void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
37 void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno
),". (#4.3.0)"); }
38 void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno
),". (#4.3.0)"); }
40 { strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
41 void temp_qmail(fn
) char *fn
;
42 { strerr_die5x(111,"Unable to open ",fn
,": ",error_str(errno
),". (#4.3.0)"); }
56 stralloc safeext
= {0};
57 stralloc ufline
= {0};
58 stralloc rpline
= {0};
59 stralloc envrecip
= {0};
60 stralloc dtline
= {0};
64 stralloc messline
= {0};
66 stralloc qsender
= {0};
67 stralloc tmpline
= {0};
68 char *verhhost
= (char *)0;
69 char *verhlocal
= (char *)0;
70 int flagheader
,flagdobody
;
75 /* substitutes ##L => recipient local, ##H => recipient host if VERP sender */
76 /* returns 0 normally, -1 on out-of-memory */
79 char *cpnext
,*cpafter
;
81 if (!verhlocal
) return 0; /* no VERP SENDER */
84 cpafter
= cp
+ sa
->len
;
85 tmpline
.len
= 0; /* clear */
87 while (cp
< cpafter
&& *cp
++ != '#');
88 if (cp
+ 1 < cpafter
&& *cp
== '#') { /* found '##' */
90 if (*cp
== 'L') { /* ##L */
91 if (!stralloc_catb(&tmpline
,cpnext
,cp
- cpnext
- 2)) return -1;
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;
99 if (!stralloc_cats(&tmpline
,verhhost
)) return -1;
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;
117 char fntmptph
[80 + FMT_ULONG
* 2];
118 char fnnewtph
[80 + FMT_ULONG
* 2];
119 void tryunlinktmp() { unlink(fntmptph
); }
120 void sigalrm() { tryunlinktmp(); _exit(3); }
122 void maildir_child(dir
)
136 sig_alarmcatch(sigalrm
);
137 if (chdir(dir
) == -1) { if (error_temp(errno
)) _exit(1); _exit(2); }
140 gethostname(host
,sizeof(host
));
141 for (loop
= 0;;++loop
)
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);
154 str_copy(fnnewtph
,fntmptph
);
155 byte_copy(fnnewtph
,3,"new");
158 fd
= open_excl(fntmptph
);
159 if (fd
== -1) _exit(1);
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
;
169 if (getln(&ss
,&messline
,&match
,'\n') != 0)
170 { tryunlinktmp(); _exit(4); }
172 if (match
&& messline
.len
== 1) {
174 if (!flagdobody
) verhlocal
= (char *)0;
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 */
184 if (verhline(&messline
) == -1) goto fail
;
185 if (substdio_put(&ssout
,messline
.s
,messline
.len
) == -1) goto fail
;
188 if (substdio_flush(&ssout
) == -1) goto fail
;
189 if (fsync(fd
) == -1) goto fail
;
190 if (close(fd
) == -1) goto fail
; /* NFS dorks */
192 if (link(fntmptph
,fnnewtph
) == -1) goto fail
;
193 /* if it was error_exist, almost certainly successful; i hate NFS */
194 tryunlinktmp(); _exit(0);
196 fail
: tryunlinktmp(); _exit(1);
199 /* end child process */
207 if (seek_begin(0) == -1) temp_rewind();
209 switch(child
= fork())
218 wait_pid(&wstat
,child
);
219 if (wait_crashed(wstat
))
221 switch(wait_exitcode(wstat
))
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)");
241 if (seek_begin(0) == -1) temp_rewind();
243 fd
= open_append(fn
);
245 strerr_die5x(111,"Unable to open ",fn
,": ",error_str(errno
),". (#4.2.1)");
247 sig_alarmcatch(temp_slowlock
);
249 flaglocked
= (lock_ex(fd
) != -1);
256 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
257 substdio_fdbuf(&ssout
,write
,fd
,outbuf
,sizeof(outbuf
));
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
;
265 if (getln(&ss
,&messline
,&match
,'\n') != 0)
267 strerr_warn3("Unable to read message: ",error_str(errno
),". (#4.3.0)",0);
268 if (flaglocked
) seek_trunc(fd
,pos
); close(fd
);
271 if (!match
&& !messline
.len
) break;
272 if (gfrom(messline
.s
,messline
.len
))
273 if (substdio_bput(&ssout
,">",1)) goto writeerrs
;
275 if (match
&& messline
.len
== 1) {
276 if (!flagdobody
) verhlocal
= (char *)0;
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
];
287 if (verhline(&messline
) == -1) goto writeerrs
;
288 if (substdio_bput(&ssout
,messline
.s
,messline
.len
)) goto writeerrs
;
291 if (substdio_bputs(&ssout
,"\n")) goto writeerrs
;
295 if (substdio_bputs(&ssout
,"\n")) goto writeerrs
;
296 if (substdio_flush(&ssout
)) goto writeerrs
;
297 if (fsync(fd
) == -1) goto writeerrs
;
302 strerr_warn5("Unable to write ",fn
,": ",error_str(errno
),". (#4.3.0)",0);
303 if (flaglocked
) seek_trunc(fd
,pos
);
308 void mailprogram(prog
)
315 if (seek_begin(0) == -1) temp_rewind();
317 switch(child
= fork())
322 args
[0] = "/bin/sh"; args
[1] = "-c"; args
[2] = prog
; args
[3] = 0;
325 strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno
),". (#4.3.0)");
328 wait_pid(&wstat
,child
);
329 if (wait_crashed(wstat
))
331 switch(wait_exitcode(wstat
))
334 case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
336 case 99: flag99
= 1; break;
341 unsigned long mailforward_qp
= 0;
343 void mailforward(recips
)
351 if (seek_begin(0) == -1) temp_rewind();
352 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
354 if (qmail_open(&qqt
) == -1) temp_fork();
355 mailforward_qp
= qmail_qp(&qqt
);
356 qmail_put(&qqt
,dtline
.s
,dtline
.len
);
360 if (getln(&ss
,&messline
,&match
,'\n') != 0) { qmail_fail(&qqt
); break; }
362 if (match
&& messline
.len
== 1) {
364 if (!flagdobody
) verhlocal
= (char *)0;
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
];
374 if (verhline(&messline
) == -1) { qmail_fail(&qqt
); break; }
375 qmail_put(&qqt
,messline
.s
,messline
.len
);
378 qmail_from(&qqt
,ueo
.s
);
379 while (*recips
) qmail_to(&qqt
,*recips
++);
380 qqx
= qmail_close(&qqt
);
382 strerr_die3x(*qqx
== 'D' ?
100 : 111,"Unable to forward message: ",qqx
+ 1,".");
390 if (seek_begin(0) == -1) temp_rewind();
391 substdio_fdbuf(&ss
,read
,0,buf
,sizeof(buf
));
394 if (getln(&ss
,&messline
,&match
,'\n') != 0) temp_read();
396 if (messline
.len
<= 1)
398 if (messline
.len
== dtline
.len
)
399 if (!str_diffn(messline
.s
,dtline
.s
,dtline
.len
))
400 strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)");
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)");
412 if (st
.st_mode
& 01000)
414 strerr_die1x(111,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)");
416 strerr_warn1("Warning: home directory is sticky.",0);
424 if (!stralloc_copys(&qme
,".qmail")) temp_nomem();
425 if (!stralloc_cats(&qme
,dash
)) temp_nomem();
426 if (!stralloc_cat(&qme
,&safeext
)) temp_nomem();
427 if (!stralloc_cats(&qme
,dashowner
)) temp_nomem();
428 if (!stralloc_0(&qme
)) temp_nomem();
429 if (stat(qme
.s
,&st
) == -1)
431 if (error_temp(errno
)) temp_qmail(qme
.s
);
437 int qmeexists(fd
,cutable
)
443 if (!stralloc_0(&qme
)) temp_nomem();
445 *fd
= open_read(qme
.s
);
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
);
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);
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" */
472 void qmesearch(fd
,cutable
)
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) {
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();
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();
507 unsigned long count_file
= 0;
508 unsigned long count_forward
= 0;
509 unsigned long count_program
= 0;
510 char count_buf
[FMT_ULONG
];
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");
523 substdio_puts(subfdoutsmall
,"qp ");
524 substdio_put(subfdoutsmall
,count_buf
,fmt_ulong(count_buf
,mailforward_qp
));
525 substdio_puts(subfdoutsmall
,"\n");
527 substdio_flush(subfdoutsmall
);
530 void sayit(type
,cmd
,len
)
535 substdio_puts(subfdoutsmall
,type
);
536 substdio_put(subfdoutsmall
,cmd
,len
);
537 substdio_putsflush(subfdoutsmall
,"\n");
551 datetime_sec starttime
;
559 if (!env_init()) temp_nomem();
562 while ((opt
= getopt(argc
,argv
,"nN")) != opteof
)
565 case 'n': flagdoit
= 0; break;
566 case 'N': flagdoit
= 1; break;
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();
583 if (homedir
[0] != '/') usage();
584 if (chdir(homedir
) == -1)
585 strerr_die5x(111,"Unable to switch to ",homedir
,": ",error_str(errno
),". (#4.3.0)");
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();
593 if (!stralloc_copys(&envrecip
,local
)) temp_nomem();
594 if (!stralloc_cats(&envrecip
,"@")) temp_nomem();
595 if (!stralloc_cats(&envrecip
,host
)) temp_nomem();
597 if (!stralloc_copy(&foo
,&envrecip
)) temp_nomem();
598 if (!stralloc_0(&foo
)) temp_nomem();
599 if (!env_put2("RECIPIENT",foo
.s
)) temp_nomem();
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();
606 if (!stralloc_copy(&foo
,&dtline
)) temp_nomem();
607 if (!stralloc_0(&foo
)) temp_nomem();
608 if (!env_put2("DTLINE",foo
.s
)) temp_nomem();
613 if (!env_put2("SENDER",sender
)) temp_nomem();
615 if (!quote2(&qsender
,sender
)) temp_nomem();
616 if (!stralloc_copys(&rpline
,"Return-Path: <")) temp_nomem();
617 if (!stralloc_cat(&rpline
,&qsender
)) temp_nomem();
618 for (i
= 0;i
< rpline
.len
;++i
) if (rpline
.s
[i
] == '\n') rpline
.s
[i
] = '_';
619 if (!stralloc_cats(&rpline
,">\n")) temp_nomem();
621 if (!stralloc_copy(&foo
,&rpline
)) temp_nomem();
622 if (!stralloc_0(&foo
)) temp_nomem();
623 if (!env_put2("RPLINE",foo
.s
)) temp_nomem();
625 i
= byte_rchr(qsender
.s
,qsender
.len
,'@'); /* for VERH */
626 if (i
!= qsender
.len
) { /* got @ */
627 cplast
= qsender
.s
+ i
;
629 if (qsender
.s
[i
= str_rchr(qsender
.s
,'=')]) { /* got = */
631 cplast
= qsender
.s
+ i
;
632 verhhost
= qsender
.s
+ i
+ 1;
633 i
= str_rchr(qsender
.s
,'-');
634 if (qsender
.s
[i
] == '-') {
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 */
644 j
= byte_rchr(qsender
.s
,i
,'-');
652 if (!stralloc_copys(&ufline
,"From ")) temp_nomem();
655 int len
; int i
; char ch
;
657 len
= str_len(sender
);
658 if (!stralloc_readyplus(&ufline
,len
)) temp_nomem();
659 for (i
= 0;i
< len
;++i
)
662 if ((ch
== ' ') || (ch
== '\t') || (ch
== '\n')) ch
= '-';
663 ufline
.s
[ufline
.len
+ i
] = ch
;
668 if (!stralloc_cats(&ufline
,"MAILER-DAEMON")) temp_nomem();
669 if (!stralloc_cats(&ufline
," ")) temp_nomem();
671 if (!stralloc_cats(&ufline
,myctime(starttime
))) temp_nomem();
673 if (!stralloc_copy(&foo
,&ufline
)) temp_nomem();
674 if (!stralloc_0(&foo
)) temp_nomem();
675 if (!env_put2("UFLINE",foo
.s
)) temp_nomem();
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();
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
] == '.')
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();
707 qmesearch(&fd
,&flagforwardonly
);
710 strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
712 if (!stralloc_copys(&ueo
,sender
)) temp_nomem();
713 if (str_diff(sender
,""))
714 if (str_diff(sender
,"#@[]"))
715 if (qmeox("-owner") == 0)
717 if (qmeox("-owner-default") == 0)
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();
726 if (!stralloc_copys(&ueo
,local
)) temp_nomem();
727 if (!stralloc_cats(&ueo
,"-owner@")) temp_nomem();
728 if (!stralloc_cats(&ueo
,host
)) temp_nomem();
731 if (!stralloc_0(&ueo
)) temp_nomem();
732 if (!env_put2("NEWSENDER",ueo
.s
)) temp_nomem();
734 if (!stralloc_ready(&cmds
,0)) temp_nomem();
737 if (slurpclose(fd
,&cmds
,256) == -1) temp_nomem();
741 if (!stralloc_copys(&cmds
,aliasempty
)) temp_nomem();
744 if (!cmds
.len
|| (cmds
.s
[cmds
.len
- 1] != '\n'))
745 if (!stralloc_cats(&cmds
,"\n")) temp_nomem();
749 for (j
= 0;j
< cmds
.len
;++j
)
750 if (cmds
.s
[j
] == '\n')
752 switch(cmds
.s
[i
]) { case '#': case '.': case '/': case '|': break;
753 default: ++numforward
; }
757 recips
= (char **) alloc((numforward
+ 1) * sizeof(char *));
758 if (!recips
) temp_nomem();
764 for (j
= 0;j
< cmds
.len
;++j
)
765 if (cmds
.s
[j
] == '\n')
769 while ((k
> i
) && (cmds
.s
[k
- 1] == ' ') || (cmds
.s
[k
- 1] == '\t'))
775 strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
779 if (!stralloc_copys(&ueo
, cmds
.s
+ i
+ 1) ||
781 !env_put2("NEWSENDER", ueo
.s
))
787 if (flagforwardonly
) strerr_die1x(111,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)");
788 if (cmds
.s
[k
- 1] == '/')
789 if (flagdoit
) maildir(cmds
.s
+ i
);
790 else sayit("maildir ",cmds
.s
+ i
,k
- i
);
792 if (flagdoit
) mailfile(cmds
.s
+ i
);
793 else sayit("mbox ",cmds
.s
+ i
,k
- i
);
797 if (flagforwardonly
) strerr_die1x(111,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)");
798 if (flagdoit
) mailprogram(cmds
.s
+ i
+ 1);
799 else sayit("program ",cmds
.s
+ i
+ 1,k
- i
- 1);
802 if (str_equal(cmds
.s
+ i
+ 1,"list"))
810 if (!cmds
.s
[i
+ str_chr(cmds
.s
+ i
, '=')]) {
811 if (!env_unset(cmds
.s
+ i
)) temp_nomem();
814 if (!env_put(cmds
.s
+ i
)) temp_nomem();
818 if (cmds
.s
[i
+ str_chr(cmds
.s
+ i
, '=')])
822 if (flagdoit
) recips
[numforward
++] = cmds
.s
+ i
;
823 else sayit("forward ",cmds
.s
+ i
,k
- i
);
830 if (numforward
) if (flagdoit
)
832 recips
[numforward
] = 0;