1 /*$Id: ezmlm-return.c,v 1.26 1999/08/07 20:50:52 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
18 #include "readwrite.h"
22 #include "subscribe.h"
26 #define FATAL "ezmlm-return: fatal: "
27 #define INFO "ezmlm-return: info: "
29 { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return [-dD] dir"); }
30 void die_nomem() { strerr_die2x(111,FATAL
,ERR_NOMEM
); }
33 strerr_die2x(100,FATAL
,ERR_BAD_RETURN_ADDRESS
);
37 strerr_die2x(99,INFO
,"trash address");
45 char strnum
[FMT_ULONG
];
47 char hashcopy
[COOKIE
];
48 char *hashp
= (char *) 0;
49 unsigned long cookiedate
;
50 unsigned long addrno
= 0L;
51 unsigned long addrno1
= 0L;
53 stralloc fndate
= {0};
54 stralloc fndatenew
= {0};
55 stralloc fnhash
= {0};
56 stralloc fnhashnew
= {0};
57 void *psql
= (void *) 0;
59 stralloc quoted
= {0};
66 { strerr_die4sys(111,FATAL
,ERR_WRITE
,fnhashnew
.s
,": "); }
68 { strerr_die4sys(111,FATAL
,ERR_WRITE
,fndatenew
.s
,": "); }
70 { strerr_die2sys(111,FATAL
,ERR_READ_INPUT
); }
75 if (mkdir(s
,0755) == -1)
76 if (errno
!= error_exist
)
77 strerr_die4x(111,FATAL
,ERR_CREATE
,s
,": ");
80 void dowit(addr
,when
,bounce
)
87 unsigned long wdir
,wfile
;
89 if (!issub(workdir
,addr
,(char *) 0,FATAL
)) return;
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;
97 wfile
= when
- 10000 * wdir
;
98 if (!stralloc_catb(&fndate
,strnum
,fmt_ulong(strnum
,wdir
))) die_nomem();
99 if (!stralloc_0(&fndate
)) die_nomem();
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())))
108 if (!stralloc_0(&fndate
)) die_nomem();
109 if (!stralloc_copy(&fndatenew
,&fndate
)) die_nomem();
110 fndatenew
.s
[wpos
] = 'W';
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("ed
,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 */
126 if (rename(fndatenew
.s
,fndate
.s
) == -1)
127 strerr_die6sys(111,FATAL
,ERR_MOVE
,fndatenew
.s
," to ",fndate
.s
,": ");
130 void doit(addr
,msgnum
,when
,bounce
)
132 unsigned long msgnum
;
139 unsigned long ddir
,dfile
;
141 if (!issub(workdir
,addr
,(char *) 0,FATAL
)) return;
143 if (!stralloc_copys(&fndate
,workdir
)) die_nomem();
144 if (!stralloc_cats(&fndate
,"/bounce/d")) die_nomem();
145 if (!stralloc_0(&fndate
)) die_nomem();
147 fndate
.s
[fndate
.len
-1] = '/'; /* replace terminal '\0' */
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())))
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();
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 */
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("ed
,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 */
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();
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();
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';
201 fdnew
= open_trunc(fnhashnew
.s
);
202 if (fdnew
== -1) die_hashnew();
203 substdio_fdbuf(&ssout
,write
,fdnew
,outbuf
,sizeof(outbuf
));
205 fd
= open_read(fnhash
.s
);
207 if (errno
!= error_noent
)
208 strerr_die4sys(111,FATAL
,ERR_READ
,fnhash
.s
,": ");
210 if (rename(fndatenew
.s
,fndate
.s
) == -1)
211 strerr_die6sys(111,FATAL
,ERR_MOVE
,fndatenew
.s
," to ",fndate
.s
,": ");
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();
220 if (unlink(fndatenew
.s
) == -1)
221 strerr_die4sys(111,FATAL
,ERR_DELETE
,fndatenew
.s
,": ");
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 */
230 if (rename(fnhashnew
.s
,fnhash
.s
) == -1)
231 strerr_die6sys(111,FATAL
,ERR_MOVE
,fnhashnew
.s
," to ",fnhash
.s
,": ");
234 stralloc bounce
= {0};
236 stralloc header
= {0};
237 stralloc intro
= {0};
238 stralloc failure
= {0};
239 stralloc paragraph
= {0};
240 int flagmasterbounce
= 0;
258 unsigned long msgnum
;
259 unsigned long cookiedate
;
261 unsigned long listno
= 0L;
272 when
= (unsigned long) now();
275 if (!dir
) die_usage();
276 if (*dir
== '-') { /* for normal use */
279 } else if (dir
[1] == 'D') {
284 if (!dir
) die_usage();
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 */
293 if (chdir(dir
) == -1)
294 strerr_die4sys(111,FATAL
,ERR_SWITCH
,dir
,": ");
296 switch(slurp("key",&key
,32)) {
298 strerr_die4sys(111,FATAL
,ERR_READ
,dir
,"/key: ");
300 strerr_die4x(100,FATAL
,dir
,"/key",ERR_NOEXIST
);
305 if (str_start(action
,"receipt-")) {
309 ch
= *action
; /* -d -digest, -m -master, -g -getmaster */
310 if (ch
&& action
[1] == '-') {
312 case 'g': flagmaster
= 1; flagdig
= 1; action
+= 2; break;
313 case 'm': flagmaster
= 1; action
+= 2; break;
318 if (!stralloc_copys(&ddir
,dir
)) die_nomem();
319 if (!stralloc_cats(&ddir
,"/digest")) die_nomem();
320 if (!stralloc_0(&ddir
)) die_nomem();
324 if (!*action
) die_trash();
326 if (flagreceipt
|| flagmaster
) /* check cookie */
327 if (str_chr(action
,'-') == COOKIE
) {
328 action
[COOKIE
] = '\0';
330 action
+= COOKIE
+ 1;
334 if (!flagmaster
&& str_start(action
,"probe-")) {
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
);
343 if (*action
++ != '-') die_trash();
344 i
= str_rchr(action
,'=');
345 if (!stralloc_copyb(&line
,action
,i
)) die_nomem();
347 if (!stralloc_cats(&line
,"@")) die_nomem();
348 if (!stralloc_cats(&line
,action
+ i
+ 1)) die_nomem();
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();
355 (void) subscribe(workdir
,line
.s
,0,"","-probe",1,-1,(char *) 0,FATAL
);
359 if (!stralloc_copys(&line
,workdir
)) die_nomem();
360 if (!stralloc_cats(&line
,"/lockbounce")) die_nomem();
361 if (!stralloc_0(&line
)) die_nomem();
363 fdlock
= open_append(line
.s
);
365 strerr_die4sys(111,FATAL
,ERR_OPEN
,line
.s
,": ");
366 if (lock_ex(fdlock
) == -1)
367 strerr_die4sys(111,FATAL
,ERR_OBTAIN
,line
.s
,": ");
369 if (!flagmaster
&& str_start(action
,"warn-")) {
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
);
378 if (*action
++ != '-') die_trash();
379 i
= str_rchr(action
,'=');
380 if (!stralloc_copyb(&line
,action
,i
)) die_nomem();
382 if (!stralloc_cats(&line
,"@")) die_nomem();
383 if (!stralloc_cats(&line
,action
+ i
+ 1)) die_nomem();
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();
390 if (slurpclose(0,&bounce
,1024) == -1) die_msgin();
391 dowit(line
.s
,when
,&bounce
);
395 action
+= scan_ulong(action
,&msgnum
);
396 if (*action
++ != '-') die_badaddr();
398 if (*action
>= '0' && *action
<= '9') { /* listno */
399 action
+= scan_ulong(action
,&listno
);
400 listno
++; /* logging is 1-53, not 0-52 */
403 if (hashp
) { /* scrap bad cookies */
404 if ((ret
= checktag(workdir
,msgnum
,0L,"x",(char *) 0,hashp
))) {
406 strerr_die2x(111,FATAL
,*ret
);
409 } else if (flagreceipt
) {
410 if (!(ret
= logmsg(dir
,msgnum
,listno
,0L,5))) {
412 strerr_die6x(99,INFO
,"receipt:",cp
," [",hashp
,"]");
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))) {
419 strerr_die4x(0,INFO
,"bounce [",hashp
,"]");
421 if (*ret
) strerr_die2x(111,FATAL
,ret
);
422 else strerr_die2x(99,INFO
,ERR_DONE
);
424 } else if (flagreceipt
|| flagmaster
)
428 i
= str_rchr(action
,'=');
429 if (!stralloc_copyb(&line
,action
,i
)) die_nomem();
431 if (!stralloc_cats(&line
,"@")) die_nomem();
432 if (!stralloc_cats(&line
,action
+ i
+ 1)) die_nomem();
434 if (!stralloc_0(&line
)) die_nomem();
435 if (slurpclose(0,&bounce
,1024) == -1) die_msgin();
436 doit(line
.s
,msgnum
,when
,&bounce
);
440 /* pre-VERP bounce, in QSBMF format. Receipts are never pre-VERP */
442 substdio_fdbuf(&ssmsgin
,read
,0,msginbuf
,sizeof(msginbuf
));
448 if (!stralloc_copys(¶graph
,"")) die_nomem();
450 if (getln(&ssmsgin
,&line
,&match
,'\n') == -1) die_msgin();
451 if (!match
) die_trash();
452 if (!stralloc_cat(¶graph
,&line
)) die_nomem();
453 if (line
.len
<= 1) break;
456 if (!flaghaveheader
) {
457 if (!stralloc_copy(&header
,¶graph
)) die_nomem();
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
,¶graph
)) die_nomem();
472 if (paragraph
.s
[0] == '-')
475 if (paragraph
.s
[0] == '<') {
476 if (!stralloc_copy(&failure
,¶graph
)) die_nomem();
478 if (!stralloc_copy(&bounce
,&header
)) die_nomem();
479 if (!stralloc_cat(&bounce
,&intro
)) die_nomem();
480 if (!stralloc_cat(&bounce
,&failure
)) die_nomem();
482 i
= byte_chr(failure
.s
,failure
.len
,'\n');
483 if (i
< 3) die_trash();
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;
498 doit(line
.s
,msgnum
,when
,&bounce
);
503 if (flagmasterbounce
)
504 strerr_die3x(0,"[",hashp
,"]");