Import ezmlm-idx 0.40
[ezmlm] / ezmlm-return.c
1 /*$Id: ezmlm-return.c,v 1.26 1999/08/07 20:50:52 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
3 #include <sys/types.h>
4 #include "direntry.h"
5 #include "stralloc.h"
6 #include "str.h"
7 #include "env.h"
8 #include "sig.h"
9 #include "slurp.h"
10 #include "getconf.h"
11 #include "strerr.h"
12 #include "byte.h"
13 #include "case.h"
14 #include "getln.h"
15 #include "substdio.h"
16 #include "error.h"
17 #include "quote.h"
18 #include "readwrite.h"
19 #include "fmt.h"
20 #include "now.h"
21 #include "cookie.h"
22 #include "subscribe.h"
23 #include "errtxt.h"
24 #include "idx.h"
25
26 #define FATAL "ezmlm-return: fatal: "
27 #define INFO "ezmlm-return: info: "
28 void die_usage()
29 { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return [-dD] dir"); }
30 void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
31 void die_badaddr()
32 {
33 strerr_die2x(100,FATAL,ERR_BAD_RETURN_ADDRESS);
34 }
35 void die_trash()
36 {
37 strerr_die2x(99,INFO,"trash address");
38 }
39
40 char outbuf[1024];
41 substdio ssout;
42 char inbuf[1024];
43 substdio ssin;
44
45 char strnum[FMT_ULONG];
46 char hash[COOKIE];
47 char hashcopy[COOKIE];
48 char *hashp = (char *) 0;
49 unsigned long cookiedate;
50 unsigned long addrno = 0L;
51 unsigned long addrno1 = 0L;
52 stralloc fndir = {0};
53 stralloc fndate = {0};
54 stralloc fndatenew = {0};
55 stralloc fnhash = {0};
56 stralloc fnhashnew = {0};
57 void *psql = (void *) 0;
58
59 stralloc quoted = {0};
60 stralloc ddir = {0};
61 char *sender;
62 char *dir;
63 char *workdir;
64
65 void die_hashnew()
66 { strerr_die4sys(111,FATAL,ERR_WRITE,fnhashnew.s,": "); }
67 void die_datenew()
68 { strerr_die4sys(111,FATAL,ERR_WRITE,fndatenew.s,": "); }
69 void die_msgin()
70 { strerr_die2sys(111,FATAL,ERR_READ_INPUT); }
71
72 void makedir(s)
73 char *s;
74 {
75 if (mkdir(s,0755) == -1)
76 if (errno != error_exist)
77 strerr_die4x(111,FATAL,ERR_CREATE,s,": ");
78 }
79
80 void dowit(addr,when,bounce)
81 char *addr;
82 unsigned long when;
83 stralloc *bounce;
84 {
85 int fd;
86 unsigned int wpos;
87 unsigned long wdir,wfile;
88
89 if (!issub(workdir,addr,(char *) 0,FATAL)) return;
90
91 if (!stralloc_copys(&fndate,workdir)) die_nomem();
92 if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
93 if (!stralloc_0(&fndate)) die_nomem();
94 fndate.s[fndate.len - 1] = '/'; /* replace '\0' */
95 wpos = fndate.len - 1;
96 wdir = when / 10000;
97 wfile = when - 10000 * wdir;
98 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,wdir))) die_nomem();
99 if (!stralloc_0(&fndate)) die_nomem();
100 makedir(fndate.s);
101 --fndate.len; /* remove terminal '\0' */
102 if (!stralloc_cats(&fndate,"/w")) die_nomem();
103 wpos = fndate.len - 1;
104 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,wfile))) die_nomem();
105 if (!stralloc_cats(&fndate,".")) die_nomem();
106 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
107 die_nomem();
108 if (!stralloc_0(&fndate)) die_nomem();
109 if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
110 fndatenew.s[wpos] = 'W';
111
112 fd = open_trunc(fndatenew.s);
113 if (fd == -1) die_datenew();
114 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
115 if (substdio_puts(&ssout,addr) == -1) die_datenew();
116 if (substdio_put(&ssout,"",1) == -1) die_datenew();
117 if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
118 if (!quote2(&quoted,sender)) die_nomem();
119 if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
120 if (substdio_puts(&ssout,">\n") == -1) die_datenew();
121 if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
122 if (substdio_flush(&ssout) == -1) die_datenew();
123 if (fsync(fd) == -1) die_datenew();
124 if (close(fd) == -1) die_datenew(); /* NFS stupidity */
125
126 if (rename(fndatenew.s,fndate.s) == -1)
127 strerr_die6sys(111,FATAL,ERR_MOVE,fndatenew.s," to ",fndate.s,": ");
128 }
129
130 void doit(addr,msgnum,when,bounce)
131 char *addr;
132 unsigned long msgnum;
133 unsigned long when;
134 stralloc *bounce;
135 {
136 int fd;
137 int fdnew;
138 unsigned int pos;
139 unsigned long ddir,dfile;
140
141 if (!issub(workdir,addr,(char *) 0,FATAL)) return;
142
143 if (!stralloc_copys(&fndate,workdir)) die_nomem();
144 if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
145 if (!stralloc_0(&fndate)) die_nomem();
146 makedir(fndate.s);
147 fndate.s[fndate.len-1] = '/'; /* replace terminal '\0' */
148 ddir = when / 10000;
149 dfile = when - 10000 * ddir;
150 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,ddir))) die_nomem();
151 if (!stralloc_copy(&fndir,&fndate)) die_nomem();
152 if (!stralloc_0(&fndir)) die_nomem(); /* make later if necessary (new addr)*/
153 if (!stralloc_cats(&fndate,"/d")) die_nomem();
154 pos = fndate.len - 2;
155 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,dfile))) die_nomem();
156 if (!stralloc_cats(&fndate,".")) die_nomem();
157 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
158 die_nomem();
159 if (addrno) { /* so that pre-VERP bounces make a d... file per address */
160 /* for the first one we use the std-style fname */
161 if (!stralloc_cats(&fndate,".")) die_nomem();
162 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,addrno))) die_nomem();
163 }
164 addrno++; /* get ready for next */
165 if (!stralloc_0(&fndate)) die_nomem();
166 if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
167 fndatenew.s[pos] = '_'; /* fndate = bounce/d/nnnn/dmmmmmm */
168 /* fndatenew = bounce/d/nnnn_dmmmmmm */
169
170 fd = open_trunc(fndatenew.s);
171 if (fd == -1) die_datenew();
172 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
173 if (substdio_puts(&ssout,addr) == -1) die_datenew();
174 if (substdio_put(&ssout,"",1) == -1) die_datenew();
175 if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
176 if (!quote2(&quoted,sender)) die_nomem();
177 if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
178 if (substdio_puts(&ssout,">\n") == -1) die_datenew();
179 if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
180 if (substdio_flush(&ssout) == -1) die_datenew();
181 if (fsync(fd) == -1) die_datenew();
182 if (close(fd) == -1) die_datenew(); /* NFS stupidity */
183
184 cookie(hash,"",0,"",addr,"");
185 if (!stralloc_copys(&fnhash,workdir)) die_nomem();
186 if (!stralloc_cats(&fnhash,"/bounce/h")) die_nomem();
187 if (!stralloc_0(&fnhash)) die_nomem();
188 makedir(fnhash.s);
189 fnhash.s[fnhash.len - 1] = '/'; /* replace terminal '\0' */
190 if (!stralloc_catb(&fnhash,hash,1)) die_nomem();
191 if (!stralloc_0(&fnhash)) die_nomem();
192 makedir(fnhash.s);
193 --fnhash.len; /* remove terminal '\0' */
194 if (!stralloc_cats(&fnhash,"/h")) die_nomem();
195 pos = fnhash.len - 1;
196 if (!stralloc_catb(&fnhash,hash+1,COOKIE-1)) die_nomem();
197 if (!stralloc_0(&fnhash)) die_nomem();
198 if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
199 fnhashnew.s[pos] = 'H';
200
201 fdnew = open_trunc(fnhashnew.s);
202 if (fdnew == -1) die_hashnew();
203 substdio_fdbuf(&ssout,write,fdnew,outbuf,sizeof(outbuf));
204
205 fd = open_read(fnhash.s);
206 if (fd == -1) {
207 if (errno != error_noent)
208 strerr_die4sys(111,FATAL,ERR_READ,fnhash.s,": ");
209 makedir(fndir.s);
210 if (rename(fndatenew.s,fndate.s) == -1)
211 strerr_die6sys(111,FATAL,ERR_MOVE,fndatenew.s," to ",fndate.s,": ");
212 }
213 else {
214 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
215 switch(substdio_copy(&ssout,&ssin)) {
216 case -2: die_msgin();
217 case -3: die_hashnew();
218 }
219 close(fd);
220 if (unlink(fndatenew.s) == -1)
221 strerr_die4sys(111,FATAL,ERR_DELETE,fndatenew.s,": ");
222 }
223 if (substdio_puts(&ssout," ") == -1) die_hashnew();
224 if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
225 if (substdio_puts(&ssout,"\n") == -1) die_hashnew();
226 if (substdio_flush(&ssout) == -1) die_hashnew();
227 if (fsync(fdnew) == -1) die_hashnew();
228 if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
229
230 if (rename(fnhashnew.s,fnhash.s) == -1)
231 strerr_die6sys(111,FATAL,ERR_MOVE,fnhashnew.s," to ",fnhash.s,": ");
232 }
233
234 stralloc bounce = {0};
235 stralloc line = {0};
236 stralloc header = {0};
237 stralloc intro = {0};
238 stralloc failure = {0};
239 stralloc paragraph = {0};
240 int flagmasterbounce = 0;
241 int flaghaveheader;
242 int flaghaveintro;
243
244 stralloc key = {0};
245
246 char msginbuf[1024];
247 substdio ssmsgin;
248
249 void main(argc,argv)
250 int argc;
251 char **argv;
252 {
253 char *local;
254 char *action;
255 char *def;
256 char *ret;
257 char *cp;
258 unsigned long msgnum;
259 unsigned long cookiedate;
260 unsigned long when;
261 unsigned long listno = 0L;
262 int match;
263 unsigned int i;
264 int flagdig = 0;
265 int flagmaster = 0;
266 int flagreceipt = 0;
267 int fdlock;
268 register char ch;
269
270 umask(022);
271 sig_pipeignore();
272 when = (unsigned long) now();
273
274 dir = argv[1];
275 if (!dir) die_usage();
276 if (*dir == '-') { /* for normal use */
277 if (dir[1] == 'd') {
278 flagdig = 1;
279 } else if (dir[1] == 'D') {
280 flagdig = 0;
281 } else
282 die_usage();
283 dir = argv[2];
284 if (!dir) die_usage();
285 }
286
287 sender = env_get("SENDER");
288 if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
289 local = env_get("LOCAL");
290 if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
291 def = env_get("DEFAULT"); /* qmail-1.02 */
292
293 if (chdir(dir) == -1)
294 strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
295
296 switch(slurp("key",&key,32)) {
297 case -1:
298 strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
299 case 0:
300 strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
301 }
302 workdir = dir;
303 action = def;
304
305 if (str_start(action,"receipt-")) {
306 flagreceipt = 1;
307 action += 8;
308 }
309 ch = *action; /* -d -digest, -m -master, -g -getmaster */
310 if (ch && action[1] == '-') {
311 switch (ch) {
312 case 'g': flagmaster = 1; flagdig = 1; action += 2; break;
313 case 'm': flagmaster = 1; action += 2; break;
314 default: break;
315 }
316 }
317 if (flagdig) {
318 if (!stralloc_copys(&ddir,dir)) die_nomem();
319 if (!stralloc_cats(&ddir,"/digest")) die_nomem();
320 if (!stralloc_0(&ddir)) die_nomem();
321 workdir = ddir.s;
322 }
323
324 if (!*action) die_trash();
325
326 if (flagreceipt || flagmaster) /* check cookie */
327 if (str_chr(action,'-') == COOKIE) {
328 action[COOKIE] = '\0';
329 hashp = action;
330 action += COOKIE + 1;
331 }
332
333 if (!flagreceipt) {
334 if (!flagmaster && str_start(action,"probe-")) {
335 action += 6;
336 action += scan_ulong(action,&cookiedate);
337 if (now() - cookiedate > 3000000) die_trash();
338 if (*action++ != '.') die_trash();
339 i = str_chr(action,'-');
340 if (i != COOKIE) die_trash();
341 byte_copy(hashcopy,COOKIE,action);
342 action += COOKIE;
343 if (*action++ != '-') die_trash();
344 i = str_rchr(action,'=');
345 if (!stralloc_copyb(&line,action,i)) die_nomem();
346 if (action[i]) {
347 if (!stralloc_cats(&line,"@")) die_nomem();
348 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
349 }
350 if (!stralloc_0(&line)) die_nomem();
351 strnum[fmt_ulong(strnum,cookiedate)] = 0;
352 cookie(hash,key.s,key.len,strnum,line.s,"P");
353 if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
354
355 (void) subscribe(workdir,line.s,0,"","-probe",1,-1,(char *) 0,FATAL);
356 _exit(99);
357 }
358
359 if (!stralloc_copys(&line,workdir)) die_nomem();
360 if (!stralloc_cats(&line,"/lockbounce")) die_nomem();
361 if (!stralloc_0(&line)) die_nomem();
362
363 fdlock = open_append(line.s);
364 if (fdlock == -1)
365 strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
366 if (lock_ex(fdlock) == -1)
367 strerr_die4sys(111,FATAL,ERR_OBTAIN,line.s,": ");
368
369 if (!flagmaster && str_start(action,"warn-")) {
370 action += 5;
371 action += scan_ulong(action,&cookiedate);
372 if (now() - cookiedate > 3000000) die_trash();
373 if (*action++ != '.') die_trash();
374 i = str_chr(action,'-');
375 if (i != COOKIE) die_trash();
376 byte_copy(hashcopy,COOKIE,action);
377 action += COOKIE;
378 if (*action++ != '-') die_trash();
379 i = str_rchr(action,'=');
380 if (!stralloc_copyb(&line,action,i)) die_nomem();
381 if (action[i]) {
382 if (!stralloc_cats(&line,"@")) die_nomem();
383 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
384 }
385 if (!stralloc_0(&line)) die_nomem();
386 strnum[fmt_ulong(strnum,cookiedate)] = 0;
387 cookie(hash,key.s,key.len,strnum,line.s,"W");
388 if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
389
390 if (slurpclose(0,&bounce,1024) == -1) die_msgin();
391 dowit(line.s,when,&bounce);
392 _exit(99);
393 }
394 }
395 action += scan_ulong(action,&msgnum);
396 if (*action++ != '-') die_badaddr();
397 cp = action;
398 if (*action >= '0' && *action <= '9') { /* listno */
399 action += scan_ulong(action,&listno);
400 listno++; /* logging is 1-53, not 0-52 */
401 }
402
403 if (hashp) { /* scrap bad cookies */
404 if ((ret = checktag(workdir,msgnum,0L,"x",(char *) 0,hashp))) {
405 if (*ret)
406 strerr_die2x(111,FATAL,*ret);
407 else
408 die_trash();
409 } else if (flagreceipt) {
410 if (!(ret = logmsg(dir,msgnum,listno,0L,5))) {
411 closesql();
412 strerr_die6x(99,INFO,"receipt:",cp," [",hashp,"]");
413 }
414 if (*ret) strerr_die2x(111,FATAL,ret);
415 else strerr_die2x(0,INFO,ERR_DONE);
416 } else if (*action) { /* post VERP master bounce */
417 if ((ret = logmsg(dir,msgnum,listno,0L,-1))) {
418 closesql();
419 strerr_die4x(0,INFO,"bounce [",hashp,"]");
420 }
421 if (*ret) strerr_die2x(111,FATAL,ret);
422 else strerr_die2x(99,INFO,ERR_DONE);
423 }
424 } else if (flagreceipt || flagmaster)
425 die_badaddr();
426
427 if (*action) {
428 i = str_rchr(action,'=');
429 if (!stralloc_copyb(&line,action,i)) die_nomem();
430 if (action[i]) {
431 if (!stralloc_cats(&line,"@")) die_nomem();
432 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
433 }
434 if (!stralloc_0(&line)) die_nomem();
435 if (slurpclose(0,&bounce,1024) == -1) die_msgin();
436 doit(line.s,msgnum,when,&bounce);
437 _exit(99);
438 }
439
440 /* pre-VERP bounce, in QSBMF format. Receipts are never pre-VERP */
441
442 substdio_fdbuf(&ssmsgin,read,0,msginbuf,sizeof(msginbuf));
443
444 flaghaveheader = 0;
445 flaghaveintro = 0;
446
447 for (;;) {
448 if (!stralloc_copys(&paragraph,"")) die_nomem();
449 for (;;) {
450 if (getln(&ssmsgin,&line,&match,'\n') == -1) die_msgin();
451 if (!match) die_trash();
452 if (!stralloc_cat(&paragraph,&line)) die_nomem();
453 if (line.len <= 1) break;
454 }
455
456 if (!flaghaveheader) {
457 if (!stralloc_copy(&header,&paragraph)) die_nomem();
458 flaghaveheader = 1;
459 continue;
460 }
461
462 if (!flaghaveintro) {
463 if (paragraph.s[0] == '-' && paragraph.s[1] == '-')
464 continue; /* skip MIME boundary if it exists */
465 if (paragraph.len < 15) die_trash();
466 if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
467 if (!stralloc_copy(&intro,&paragraph)) die_nomem();
468 flaghaveintro = 1;
469 continue;
470 }
471
472 if (paragraph.s[0] == '-')
473 break;
474
475 if (paragraph.s[0] == '<') {
476 if (!stralloc_copy(&failure,&paragraph)) die_nomem();
477
478 if (!stralloc_copy(&bounce,&header)) die_nomem();
479 if (!stralloc_cat(&bounce,&intro)) die_nomem();
480 if (!stralloc_cat(&bounce,&failure)) die_nomem();
481
482 i = byte_chr(failure.s,failure.len,'\n');
483 if (i < 3) die_trash();
484
485 if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
486 if (byte_chr(line.s,line.len,'\0') == line.len) {
487 if (!stralloc_0(&line)) die_nomem();
488 if (flagmaster) { /* bounced msg slave! */
489 if ((i = str_rchr(line.s,'@')) >= 5) { /* 00_52@host */
490 line.s[i] = '\0'; /* 00_52 */
491 (void) scan_ulong(line.s + i - 5,&listno);
492 if ((ret = logmsg(dir,msgnum,listno + 1,0L,-1)) && *ret)
493 strerr_die2x(111,FATAL,ret);
494 strerr_warn3(INFO,"bounce ",line.s + i - 5,(struct strerr *) 0);
495 flagmasterbounce = 1;
496 }
497 } else
498 doit(line.s,msgnum,when,&bounce);
499 }
500 }
501 }
502 closesql();
503 if (flagmasterbounce)
504 strerr_die3x(0,"[",hashp,"]");
505 else
506 _exit(99);
507 }