Import ezmlm-idx 0.40
[ezmlm] / ezmlm-moderate.c
1 /*$Id: ezmlm-moderate.c,v 1.42 1999/10/09 16:49:56 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include "error.h"
7 #include "case.h"
8 #include "stralloc.h"
9 #include "str.h"
10 #include "env.h"
11 #include "error.h"
12 #include "sig.h"
13 #include "fork.h"
14 #include "wait.h"
15 #include "slurp.h"
16 #include "getconf.h"
17 #include "strerr.h"
18 #include "byte.h"
19 #include "getln.h"
20 #include "qmail.h"
21 #include "substdio.h"
22 #include "readwrite.h"
23 #include "seek.h"
24 #include "quote.h"
25 #include "datetime.h"
26 #include "now.h"
27 #include "date822fmt.h"
28 #include "fmt.h"
29 #include "sgetopt.h"
30 #include "auto_bin.h"
31 #include "cookie.h"
32 #include "errtxt.h"
33 #include "copy.h"
34 #include "idx.h"
35
36 int flagmime = MOD_MIME; /* default is message as attachment */
37 char flagcd = '\0'; /* default: do not use transfer encoding */
38
39 #define FATAL "ezmlm-moderate: fatal: "
40 #define INFO "ezmlm-moderate: info: "
41
42 void die_usage() { strerr_die1x(100,
43 "ezmlm-moderate: usage: ezmlm-moderate [-cCmMrRvV] [-t replyto] "
44 "dir [/path/ezmlm-send]"); }
45
46 void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
47
48 void die_badformat() { strerr_die2x(100,FATAL,ERR_BAD_REQUEST); }
49
50 void die_badaddr() {
51 strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
52 }
53
54 stralloc outhost = {0};
55 stralloc inlocal = {0};
56 stralloc outlocal = {0};
57 stralloc key = {0};
58 stralloc mydtline = {0};
59 stralloc mailinglist = {0};
60 stralloc accept = {0};
61 stralloc reject = {0};
62 stralloc to = {0};
63 stralloc send = {0};
64 stralloc sendopt = {0};
65 stralloc comment = {0};
66 stralloc charset = {0};
67 datetime_sec when;
68 struct datetime dt;
69
70 char strnum[FMT_ULONG];
71 char date[DATE822FMT];
72 char hash[COOKIE];
73 char boundary[COOKIE];
74 stralloc line = {0};
75 stralloc qline = {0};
76 stralloc text = {0};
77 stralloc quoted = {0};
78 stralloc fnbase = {0};
79 stralloc fnmsg = {0};
80 stralloc fnnew = {0};
81 stralloc fnsub = {0};
82 char subbuf[256];
83 substdio sssub;
84
85 char *dir;
86
87 struct stat st;
88
89 struct qmail qq;
90
91 void code_qput(s,n)
92 char *s;
93 unsigned int n;
94 {
95 if (!flagcd)
96 qmail_put(&qq,s,n);
97 else {
98 if (flagcd == 'B')
99 encodeB(s,n,&qline,0,FATAL);
100 else
101 encodeQ(s,n,&qline,FATAL);
102 qmail_put(&qq,qline.s,qline.len);
103 }
104 }
105
106 void transferenc()
107 {
108 if (flagcd) {
109 qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
110 if (flagcd == 'Q')
111 qmail_puts(&qq,"Quoted-printable\n\n");
112 else
113 qmail_puts(&qq,"base64\n\n");
114 } else
115 qmail_puts(&qq,"\n\n");
116 }
117
118 int checkfile(fn)
119 char *fn;
120 /* looks for DIR/mod/{pending|rejected|accept}/fn.*/
121 /* Returns: */
122 /* 1 found in pending */
123 /* 0 not found */
124 /* -1 found in accepted */
125 /* -2 found in rejected */
126 /* Handles errors. */
127 /* ALSO: if found, fnmsg contains the o-terminated*/
128 /* file name. */
129 {
130
131 if (!stralloc_copys(&fnmsg,"mod/pending/")) die_nomem();
132 if (!stralloc_cats(&fnmsg,fn)) die_nomem();
133 if (!stralloc_0(&fnmsg)) die_nomem();
134 if (stat(fnmsg.s,&st) == -1) {
135 if (errno != error_noent)
136 strerr_die6sys(111,FATAL,ERR_STAT,dir,"/",fnmsg.s,": ");
137 } else
138 return 1;
139
140 if (!stralloc_copys(&fnmsg,"mod/accepted/")) die_nomem();
141 if (!stralloc_cats(&fnmsg,fn)) die_nomem();
142 if (!stralloc_0(&fnmsg)) die_nomem();
143 if (stat(fnmsg.s,&st) == -1) {
144 if (errno != error_noent)
145 strerr_die6sys(111,FATAL,ERR_STAT,dir,"/",fnmsg.s,": ");
146 } else
147 return -1;
148
149 if (!stralloc_copys(&fnmsg,"mod/rejected/")) die_nomem();
150 if (!stralloc_cats(&fnmsg,fn)) die_nomem();
151 if (!stralloc_0(&fnmsg)) die_nomem();
152 if (stat(fnmsg.s,&st) == -1) {
153 if (errno != error_noent)
154 strerr_die6sys(111,FATAL,ERR_STAT,dir,"/",fnmsg.s,": ");
155 } else
156 return -2;
157 return 0;
158 }
159
160 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
161 {
162 qmail_put(&qq,buf,len);
163 return len;
164 }
165 char qqbuf[1];
166 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
167
168 char inbuf[512];
169 substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
170
171 substdio sstext;
172 char textbuf[1024];
173
174 void maketo()
175 /* expects line to be a return-path line. If it is and the format is valid */
176 /* to is set to to the sender. Otherwise, to is left untouched. Assuming */
177 /* to is empty to start with, it will remain empty if no sender is found. */
178 {
179 unsigned int x, y;
180
181 if (case_startb(line.s,line.len,"return-path:")) {
182 x = 12 + byte_chr(line.s + 12,line.len-12,'<');
183 if (x != line.len) {
184 y = byte_rchr(line.s + x,line.len-x,'>');
185 if (y + x != line.len) {
186 if (!stralloc_copyb(&to,line.s+x+1,y-1)) die_nomem();
187 if (!stralloc_0(&to)) die_nomem();
188 } /* no return path-> no addressee. A NUL in the sender */
189 } /* is no worse than a faked sender, so no problem */
190 }
191 }
192
193 void main(argc,argv)
194 int argc;
195 char **argv;
196 {
197 char *sender;
198 char *def;
199 char *local;
200 char *action;
201 int flaginheader;
202 int flagcomment;
203 int flaggoodfield;
204 int flagdone;
205 int fd, fdlock;
206 int match;
207 char *err;
208 char encin = '\0';
209 char szchar[2] = "-";
210 char *replyto = (char *) 0;
211 unsigned int start,confnum;
212 unsigned int pos,i;
213 int child;
214 int opt;
215 char *sendargs[4];
216 char *cp,*cpnext,*cpfirst,*cplast,*cpafter;
217 int wstat;
218
219 (void) umask(022);
220 sig_pipeignore();
221 when = now();
222
223 if (!stralloc_copys(&sendopt," -")) die_nomem();
224 while ((opt = getopt(argc,argv,"cCmMrRt:T:vV")) != opteof)
225 switch(opt) { /* pass on ezmlm-send options */
226 case 'c': /* ezmlm-send flags */
227 case 'C':
228 case 'r':
229 case 'R':
230 szchar[0] = (char) opt & 0xff;
231 if (!stralloc_append(&sendopt,szchar)) die_nomem();
232 break;
233 case 'm': flagmime = 1; break;
234 case 'M': flagmime = 0; break;
235 case 't':
236 case 'T': if (optarg) replyto = optarg; break;
237 case 'v':
238 case 'V': strerr_die2x(0,"ezmlm-moderate version: ",EZIDX_VERSION);
239 default:
240 die_usage();
241 }
242
243 dir = argv[optind++];
244 if (!dir) die_usage();
245
246 sender = env_get("SENDER");
247 if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
248 local = env_get("LOCAL");
249 if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
250 def = env_get("DEFAULT");
251
252 if (!*sender)
253 strerr_die2x(100,FATAL,ERR_BOUNCE);
254 if (!sender[str_chr(sender,'@')])
255 strerr_die2x(100,FATAL,ERR_ANONYMOUS);
256 if (str_equal(sender,"#@[]"))
257 strerr_die2x(100,FATAL,ERR_BOUNCE);
258
259 if (chdir(dir) == -1)
260 strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
261
262 switch(slurp("key",&key,32)) {
263 case -1:
264 strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
265 case 0:
266 strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
267 }
268 getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
269 getconf_line(&outhost,"outhost",1,FATAL,dir);
270 getconf_line(&outlocal,"outlocal",1,FATAL,dir);
271 set_cpoutlocal(&outlocal); /* for copy() */
272 set_cpouthost(&outhost); /* for copy() */
273
274 if (def) { /* qmail>=1.02 */
275 /* local should be >= def, but who knows ... */
276 cp = local + str_len(local) - str_len(def) - 2;
277 if (cp < local) die_badformat();
278 action = local + byte_rchr(local,cp - local,'-');
279 if (action == cp) die_badformat();
280 action++;
281 } else { /* older versions of qmail */
282 getconf_line(&inlocal,"inlocal",1,FATAL,dir);
283 if (inlocal.len > str_len(local)) die_badaddr();
284 if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
285 action = local + inlocal.len;
286 if (*(action++) != '-') die_badaddr();
287 }
288
289 if (!action[0]) die_badformat();
290 if (!str_start(action,ACTION_ACCEPT) && !str_start(action,ACTION_REJECT))
291 die_badformat();
292 start = str_chr(action,'-');
293 if (!action[start]) die_badformat();
294 confnum = 1 + start + str_chr(action + start + 1,'.');
295 if (!action[confnum]) die_badformat();
296 confnum += 1 + str_chr(action + confnum + 1,'.');
297 if (!action[confnum]) die_badformat();
298 if (!stralloc_copyb(&fnbase,action+start+1,confnum-start-1)) die_nomem();
299 if (!stralloc_0(&fnbase)) die_nomem();
300 cookie(hash,key.s,key.len,fnbase.s,"","a");
301 if (byte_diff(hash,COOKIE,action+confnum+1))
302 die_badformat();
303
304 fdlock = open_append("mod/lock");
305 if (fdlock == -1)
306 strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/mod/lock: ");
307 if (lock_ex(fdlock) == -1)
308 strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/mod/lock: ");
309
310 switch(checkfile(fnbase.s)) {
311 case 0:
312 strerr_die2x(100,FATAL,ERR_MOD_TIMEOUT);
313 case -1: /* only error if new request != action taken */
314 if (str_start(action,ACTION_ACCEPT))
315 strerr_die2x(0,INFO,ERR_MOD_ACCEPTED);
316 else
317 strerr_die2x(100,FATAL,ERR_MOD_ACCEPTED);
318 case -2:
319 if (str_start(action,ACTION_REJECT))
320 strerr_die2x(0,INFO,ERR_MOD_REJECTED);
321 else
322 strerr_die2x(100,FATAL,ERR_MOD_REJECTED);
323 default:
324 break;
325 }
326 /* Here, we have an existing filename in fnbase with the complete path */
327 /* from the current dir in fnmsg. */
328
329 if (str_start(action,ACTION_REJECT)) {
330
331 if (qmail_open(&qq, (stralloc *) 0) == -1)
332 strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
333
334
335 /* Build recipient from msg return-path */
336 fd = open_read(fnmsg.s);
337 if (fd == -1) {
338 if (errno != error_noent)
339 strerr_die4sys(111,FATAL,ERR_OPEN,fnmsg.s,": ");
340 else
341 strerr_die2x(100,FATAL,ERR_MOD_TIMEOUT);
342 }
343 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
344
345 if (getln(&sstext,&line,&match,'\n') == -1 || !match)
346 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
347 maketo(); /* extract SENDER from return-path */
348 /* Build message */
349 qmail_puts(&qq,"Mailing-List: ");
350 qmail_put(&qq,mailinglist.s,mailinglist.len);
351 if(getconf_line(&line,"listid",0,FATAL,dir)) {
352 qmail_puts(&qq,"\nList-ID: ");
353 qmail_put(&qq,line.s,line.len);
354 }
355 qmail_puts(&qq,"\nDate: ");
356 datetime_tai(&dt,when);
357 qmail_put(&qq,date,date822fmt(date,&dt));
358 qmail_puts(&qq,"Message-ID: <");
359 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
360 die_nomem();
361 if (!stralloc_append(&line,".")) die_nomem();
362 if (!stralloc_catb(&line,strnum,
363 fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
364 if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
365 if (!stralloc_cat(&line,&outhost)) die_nomem();
366 if (!stralloc_0(&line)) die_nomem();
367 qmail_puts(&qq,line.s);
368 /* "unique" MIME boundary as hash of messageid */
369 cookie(boundary,"",0,"",line.s,"");
370 qmail_puts(&qq,">\nFrom: ");
371 if (!quote(&quoted,&outlocal)) die_nomem();
372 qmail_put(&qq,quoted.s,quoted.len);
373 qmail_puts(&qq,"-owner@");
374 qmail_put(&qq,outhost.s,outhost.len);
375 if (replyto) {
376 qmail_puts(&qq,"\nReply-To: ");
377 qmail_puts(&qq,replyto);
378 }
379 qmail_puts(&qq, "\nTo: ");
380 qmail_puts(&qq,to.s);
381 qmail_puts(&qq,"\nSubject: ");
382 qmail_puts(&qq,TXT_RETURNED_POST);
383 qmail_put(&qq,quoted.s,quoted.len);
384 qmail_puts(&qq,"@");
385 qmail_put(&qq,outhost.s,outhost.len);
386
387 if (flagmime) {
388 if (getconf_line(&charset,"charset",0,FATAL,dir)) {
389 if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
390 if (charset.s[charset.len - 1] == 'B' ||
391 charset.s[charset.len - 1] == 'Q') {
392 flagcd = charset.s[charset.len - 1];
393 charset.s[charset.len - 2] = '\0';
394 }
395 }
396 } else
397 if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
398 if (!stralloc_0(&charset)) die_nomem();
399 qmail_puts(&qq,"\nMIME-Version: 1.0\n");
400 qmail_puts(&qq,"Content-Type: multipart/mixed;\n\tboundary=");
401 qmail_put(&qq,boundary,COOKIE);
402 qmail_puts(&qq,"\n\n--");
403 qmail_put(&qq,boundary,COOKIE);
404 qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
405 qmail_puts(&qq,charset.s);
406 transferenc();
407 }
408 copy(&qq,"text/top",flagcd,FATAL);
409 copy(&qq,"text/mod-reject",flagcd,FATAL);
410
411 flagcomment = 0;
412 flaginheader = 1;
413 if (!stralloc_copys(&text,"")) die_nomem();
414 if (!stralloc_ready(&text,1024)) die_nomem();
415 for (;;) { /* copy moderator's rejection comment */
416 if (getln(&ssin,&line,&match,'\n') == -1)
417 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
418 if (!match) break;
419 if (flaginheader) {
420 if (case_startb(line.s,line.len,"Content-Transfer-Encoding:")) {
421 pos = 26;
422 while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos;
423 if (case_startb(line.s+pos,line.len-pos,"base64"))
424 encin = 'B';
425 else if (case_startb(line.s+pos,line.len-pos,"quoted-printable"))
426 encin = 'Q';
427 }
428 if (line.len == 1)
429 flaginheader = 0;
430 } else
431 if (!stralloc_cat(&text,&line)) die_nomem();
432 } /* got body */
433 if (encin) {
434 if (encin == 'B')
435 decodeB(text.s,text.len,&line,FATAL);
436 else
437 decodeQ(text.s,text.len,&line,FATAL);
438 if (!stralloc_copy(&text,&line)) die_nomem();
439 }
440 cp = text.s;
441 cpafter = text.s + text.len;
442 if (!stralloc_copys(&line,"\n>>>>> -------------------- >>>>>\n"))
443 die_nomem();
444 flaggoodfield = 0;
445 flagdone = 0;
446 while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) {
447 i = byte_chr(cp,cpnext-cp,'%');
448 if (i <= 5 && cpnext-cp >= 8) {
449 /* max 5 "quote characters" and space for %%% */
450 if (cp[i+1] == '%' && cp[i+2] == '%') {
451 if (!flaggoodfield) { /* Start tag */
452 if (!stralloc_copyb(&quoted,cp,i)) die_nomem(); /* quote chars*/
453 flaggoodfield = 1;
454 cp = cpnext + 1;
455 cpfirst = cp;
456 continue;
457 } else { /* end tag */
458 if (flagdone) /* 0 no comment lines, 1 comment line */
459 flagdone = 2; /* 2 at least 1 comment line & end tag */
460 break;
461 }
462 }
463 }
464 if (flaggoodfield) {
465 cplast = cpnext - 1;
466 if (*cplast == '\r') /* CRLF -> '\n' for base64 encoding */
467 *cplast = '\n';
468 else
469 ++cplast;
470 /* NUL is now ok, so the test for it was removed */
471 flagdone = 1;
472 i = cplast - cp + 1;
473 if (quoted.len && quoted.len <= i &&
474 !str_diffn(cp,quoted.s,quoted.len)) { /* quote chars */
475 if (!stralloc_catb(&line,cp+quoted.len,i-quoted.len)) die_nomem();
476 } else
477 if (!stralloc_catb(&line,cp,i)) die_nomem(); /* no quote chars */
478 }
479 cp = cpnext + 1;
480 }
481 if (flagdone == 2) {
482 if (!stralloc_cats(&line,"<<<<< -------------------- <<<<<\n")) die_nomem();
483 code_qput(line.s,line.len);
484 }
485 if (flagcd == 'B') {
486 encodeB("",0,&line,2,FATAL);
487 qmail_put(&qq,line.s,line.len);
488 }
489 if (flagmime) {
490 qmail_puts(&qq,"\n--");
491 qmail_put(&qq,boundary,COOKIE);
492 qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n");
493 } else
494 qmail_puts(&qq,"\n");
495 if (seek_begin(fd) == -1)
496 strerr_die4sys(111,FATAL,ERR_SEEK,fnmsg.s,": ");
497
498 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
499 if (substdio_copy(&ssqq,&sstext) != 0)
500 strerr_die4sys(111,FATAL,ERR_READ,fnmsg.s,": ");
501 close(fd);
502
503 if (flagmime) {
504 qmail_puts(&qq,"\n--");
505 qmail_put(&qq,boundary,COOKIE);
506 qmail_puts(&qq,"--\n");
507 }
508
509 if (!stralloc_copy(&line,&outlocal)) die_nomem();
510 if (!stralloc_cats(&line,"-return-@")) die_nomem();
511 if (!stralloc_cat(&line,&outhost)) die_nomem();
512 if (!stralloc_0(&line)) die_nomem();
513 qmail_from(&qq,line.s);
514 if (to.len)
515 qmail_to(&qq,to.s);
516
517 if (!stralloc_copys(&fnnew,"mod/rejected/")) die_nomem();
518 if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem();
519 if (!stralloc_0(&fnnew)) die_nomem();
520
521 /* this is strictly to track what happended to a message to give informative */
522 /* messages to the 2nd-nth moderator that acts on the same message. Since */
523 /* this isn't vital we ignore errors. Also, it is no big ideal if unlinking */
524 /* the old file fails. In the worst case it gets acted on again. If we issue */
525 /* a temp error the reject will be redone, which is slightly worse. */
526
527 if (*(err = qmail_close(&qq)) == '\0') {
528 fd = open_trunc(fnnew.s);
529 if (fd != -1)
530 close(fd);
531 unlink(fnmsg.s);
532 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
533 strerr_die2x(0,"ezmlm-moderate: info: qp ",strnum);
534 } else
535 strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
536
537 } else if (str_start(action,ACTION_ACCEPT)) {
538 fd = open_read(fnmsg.s);
539 if (fd == -1)
540 if (errno !=error_noent)
541 strerr_die4sys(111,FATAL,ERR_OPEN,fnmsg.s,": ");
542 else /* shouldn't happen since we've got lock */
543 strerr_die3x(100,FATAL,fnmsg.s,ERR_MOD_TIMEOUT);
544
545 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
546 /* read "Return-Path:" line */
547 if (getln(&sstext,&line,&match,'\n') == -1 || !match)
548 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
549 maketo(); /* extract SENDER to "to" */
550 env_put2("SENDER",to.s); /* set SENDER */
551 if (seek_begin(fd) == -1) /* rewind, since we read an entire buffer */
552 strerr_die4sys(111,FATAL,ERR_SEEK,fnmsg.s,": ");
553
554 /* ##### NO REASON TO USE SH HERE ##### */
555 sendargs[0] = "/bin/sh";
556 sendargs[1] = "-c";
557 if (argc > optind) {
558 sendargs[2] = argv[optind];
559 } else {
560 if (!stralloc_copys(&send,auto_bin)) die_nomem();
561 if (!stralloc_cats(&send,"/ezmlm-send")) die_nomem();
562 if (sendopt.len > 2)
563 if (!stralloc_cat(&send,&sendopt)) die_nomem();
564 if (!stralloc_cats(&send," '")) die_nomem();
565 if (!stralloc_cats(&send,dir)) die_nomem();
566 if (!stralloc_cats(&send,"'")) die_nomem();
567 if (!stralloc_0(&send)) die_nomem();
568 sendargs[2] = send.s;
569 }
570 sendargs[3] = 0;
571
572 switch(child = fork()) {
573 case -1:
574 strerr_die2sys(111,FATAL,ERR_FORK);
575 case 0: /* child */
576 close(0);
577 dup(fd); /* make fnmsg.s stdin */
578 execv(*sendargs,sendargs);
579 if (errno == error_txtbsy || errno == error_nomem ||
580 errno == error_io)
581 strerr_die5sys(111,FATAL,ERR_EXECUTE,"/bin/sh -c ",sendargs[2],": ");
582 else
583 strerr_die5sys(100,FATAL,ERR_EXECUTE,"/bin/sh -c ",sendargs[2],": ");
584 }
585 /* parent */
586 wait_pid(&wstat,child);
587 close(fd);
588 if (wait_crashed(wstat))
589 strerr_die3x(111,FATAL,sendargs[2],ERR_CHILD_CRASHED);
590 switch(wait_exitcode(wstat)) {
591 case 100:
592 strerr_die2x(100,FATAL,"Fatal error from child");
593 case 111:
594 strerr_die2x(111,FATAL,"Temporary error from child");
595 case 0:
596 break;
597 default:
598 strerr_die2x(111,FATAL,"Unknown temporary error from child");
599 }
600 if (!stralloc_copys(&fnnew,"mod/accepted/")) die_nomem();
601
602 if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem();
603 if (!stralloc_0(&fnnew)) die_nomem();
604 /* ignore errors */
605 fd = open_trunc(fnnew.s);
606 if (fd != -1)
607 close(fd);
608 unlink(fnmsg.s);
609 _exit(0);
610 }
611 }