Commit | Line | Data |
---|---|---|
f8beb284 MW |
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("ed,&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("ed,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 | } |