1 /*$Id: ezmlm-receipt.c,v 1.10 1999/02/05 04:57:44 lindberg Exp $*/
2 /*$Name: ezmlm-idx-0324 $*/
3 /* Handles receipts and bounces from sublists at the main list */
4 /* Set up instead of ezmlm-return in DIR/bouncer of main list */
20 #include "readwrite.h"
27 #define FATAL "ezmlm-receipt: fatal: "
28 #define INFO "ezmlm-receipt: info: "
32 strerr_die1x(100,"ezmlm-receipt: usage: ezmlm-receipt [-dD] dir");
35 void die_nomem() { strerr_die2x(111,FATAL
,ERR_NOMEM
); }
39 strerr_die2x(100,FATAL
,ERR_BAD_ADDRESS
);
43 strerr_die2x(0,INFO
,"trash address");
47 stralloc quoted
= {0};
49 stralloc bounce
= {0};
50 stralloc header
= {0};
51 stralloc failure
= {0};
52 stralloc paragraph
= {0};
54 stralloc outhost
= {0};
55 stralloc outlocal
= {0};
56 stralloc inlocal
= {0};
57 stralloc tagline
= {0};
58 stralloc listaddr
= {0};
59 stralloc fndate
= {0};
61 stralloc fndatenew
= {0};
64 { strerr_die4sys(111,FATAL
,ERR_WRITE
,fndatenew
.s
,": "); }
66 { strerr_die2sys(111,FATAL
,ERR_READ_INPUT
); }
68 char strnum
[FMT_ULONG
];
72 char outbuf
[256]; /* small - rarely used */
76 unsigned long addrno
= 0L;
81 void **psql
= (void **) 0;
82 stralloc listno
= {0};
85 void doit(addr
,msgnum
,when
,bounce
)
86 /* Just stores address\0nsgnum\0 followed by bounce. File name is */
87 /* dttt.ppp[.n], where 'ttt' is a time stamp, 'ppp' the pid, and 'n' the */
88 /* number when there are more than 1 addresses in a pre-VERP bounce. In */
89 /* this case, the first one is just dttt.ppp, the decond dttt.ppp.2, etc. */
90 /* For a main list, bounces come from sublists. They are rare and serious. */
102 if (!stralloc_copys(&fndir
,workdir
)) die_nomem();
103 if (!stralloc_cats(&fndir
,"/bounce")) die_nomem();
104 if (!stralloc_0(&fndir
)) die_nomem();
105 bouncedir
= opendir(fndir
.s
);
107 if (errno
!= error_noent
)
108 strerr_die4sys(111,FATAL
,ERR_OPEN
,line
.s
,": ");
110 strerr_die3x(111,FATAL
,fndir
.s
,ERR_NOEXIST
);
112 no
= MAX_MAIN_BOUNCES
; /* no more than this many allowed */
113 while (no
&& (d
= readdir(bouncedir
))) {
114 if (str_equal(d
->d_name
,".")) continue;
115 if (str_equal(d
->d_name
,"..")) continue;
119 if (!no
) /* max no of bounces exceeded */
120 strerr_die2x(0,INFO
,ERR_MAX_BOUNCE
);
122 if (!stralloc_copys(&fndate
,workdir
)) die_nomem();
123 if (!stralloc_cats(&fndate
,"/bounce/d")) die_nomem();
124 pos
= fndate
.len
- 1;
125 if (!stralloc_catb(&fndate
,strnum
,fmt_ulong(strnum
,when
))) die_nomem();
126 if (!stralloc_cats(&fndate
,".")) die_nomem();
127 if (!stralloc_catb(&fndate
,strnum
,fmt_ulong(strnum
,(unsigned long) getpid())))
129 if (addrno
) { /* so that pre-VERP bounces make a d... file per address */
130 /* for the first one we use the std-style fname */
131 if (!stralloc_cats(&fndate
,".")) die_nomem();
132 if (!stralloc_catb(&fndate
,strnum
,fmt_ulong(strnum
,addrno
))) die_nomem();
134 addrno
++; /* get ready for next */
135 if (!stralloc_0(&fndate
)) die_nomem();
136 if (!stralloc_copy(&fndatenew
,&fndate
)) die_nomem();
137 fndatenew
.s
[pos
] = 'D';
139 fd
= open_trunc(fndatenew
.s
);
140 if (fd
== -1) die_datenew();
141 substdio_fdbuf(&ssout
,write
,fd
,outbuf
,sizeof(outbuf
));
142 if (substdio_puts(&ssout
,addr
) == -1) die_datenew();
143 if (substdio_put(&ssout
,"",1) == -1) die_datenew();
144 if (substdio_put(&ssout
,strnum
,fmt_ulong(strnum
,msgnum
)) == -1)
146 if (substdio_put(&ssout
,"",1) == -1) die_datenew();
148 if (substdio_puts(&ssout
,"Return-Path: <") == -1) die_datenew();
149 if (!quote2("ed
,sender
)) die_nomem();
150 if (substdio_put(&ssout
,quoted
.s
,quoted
.len
) == -1) die_datenew();
151 if (substdio_puts(&ssout
,">\n") == -1) die_datenew();
152 if (substdio_put(&ssout
,bounce
->s
,bounce
->len
) == -1) die_datenew();
153 if (substdio_flush(&ssout
) == -1) die_datenew();
154 if (fsync(fd
) == -1) die_datenew();
155 if (close(fd
) == -1) die_datenew(); /* NFS stupidity */
156 if (rename(fndatenew
.s
,fndate
.s
) == -1)
157 strerr_die6sys(111,FATAL
,ERR_MOVE
,fndatenew
.s
," to ",fndate
.s
,": ");
172 unsigned long msgnum
;
180 when
= (unsigned long) now();
183 if (!dir
) die_usage();
187 } else if (dir
[1] == 'D') {
192 if (!dir
) die_usage();
194 if (chdir(dir
) == -1)
195 strerr_die4sys(111,FATAL
,ERR_SWITCH
,dir
,": ");
197 sender
= env_get("SENDER");
198 def
= env_get("DEFAULT");
199 local
= env_get("LOCAL");
201 getconf_line(&outhost
,"outhost",1,FATAL
,dir
);
202 getconf_line(&outlocal
,"outlocal",1,FATAL
,dir
);
204 if (def
) { /* qmail>=1.02 */
205 action
= def
; /* now see if -digest-return- */
208 if (str_len(local
) >= str_len(def
) + 14)
209 if (str_start(local
+ str_len(local
) - 14 - str_len(def
),"digest-"))
212 } else { /* older version of qmail */
213 getconf_line(&inlocal
,"inlocal",1,FATAL
,dir
);
214 if (inlocal
.len
> str_len(local
)) die_badaddr();
215 if (case_diffb(inlocal
.s
,inlocal
.len
,local
)) die_badaddr();
216 action
= local
+ inlocal
.len
;
219 if (str_start(action
,"-digest")) {
224 if (!str_start(action
,"-return-")) die_badaddr();
228 if (!stralloc_copys(&ddir
,dir
)) die_nomem();
229 if (!stralloc_cats(&ddir
,"/digest")) die_nomem();
230 if (!stralloc_0(&ddir
)) die_nomem();
232 if (!stralloc_cats(&outlocal
,"-digest")) die_nomem();
234 if (!*action
) die_trash();
236 substdio_fdbuf(&ssin
,read
,0,inbuf
,sizeof(inbuf
));
238 if (!case_diffs(action
,"receipt")) {
239 host
= sender
+ str_rchr(sender
,'@');
243 /* check recipient in case it's a bounce*/
244 while (*(cp
++)) { /* decode sender */
245 cp
+= str_chr(cp
,'-');
246 if (case_starts(cp
,"-return-")) {
247 if (!scan_ulong(cp
+ 8,&msgnum
))
248 strerr_die2x(100,FATAL
,"bad VERP format for receipt");
250 if (!stralloc_copys(&listaddr
,sender
)) die_nomem();
251 if (!stralloc_append(&listaddr
,"@")) die_nomem();
252 if (!stralloc_cats(&listaddr
,host
)) die_nomem();
253 if (!stralloc_0(&listaddr
)) die_nomem();
257 for(;;) { /* Get X-tag from hdr*/
258 if (getln(&ssin
,&line
,&match
,'\n') == -1) die_msgin();
262 if (line
.len
== 1) break;
263 if (case_startb(line
.s
,line
.len
,TXT_TAG
)) {
264 len
= str_len(TXT_TAG
);
265 if (!stralloc_catb(&tagline
,line
.s
+len
,line
.len
- len
-1)) die_nomem();
266 /* NOTE: tagline is dirty! We quote it for sql and rely on */
267 /* std log clean for maillog */
271 /* feedback ok even if not sub. Will be filtered by subreceipt*/
272 /* For instance, main list feedback is ok, but !issub. */
273 subreceipt(workdir
,msgnum
,&tagline
,listaddr
.s
,2,INFO
,FATAL
);
277 /* not receipt - maybe bounce */
278 /* no need to lock. dttt.pid can be assumed */
279 /* to be unique and if not would be over- */
280 /* written even with lock */
281 action
+= scan_ulong(action
,&msgnum
);
282 if (*action
!= '-') die_badaddr();
284 /* scan bounce for tag. It'll be in the BODY! */
286 if (getln(&ssin
,&line
,&match
,'\n') == -1)
287 strerr_die2sys(111,FATAL
,ERR_READ_INPUT
);
289 if (case_startb(line
.s
,line
.len
,TXT_TAG
)) {
290 len
= str_len(TXT_TAG
);
291 if (!stralloc_catb(&tagline
,line
.s
+len
,line
.len
- len
-1)) die_nomem();
292 /* NOTE: tagline is dirty! We quote it for sql and rely on */
293 /* std log clean for maillog */
297 if (seek_begin(0) == -1)
298 strerr_die2sys(111,FATAL
,ERR_SEEK_INPUT
);
299 substdio_fdbuf(&ssin
,read
,0,inbuf
,sizeof(inbuf
));
301 if (*action
) { /* normal bounce */
303 if (slurpclose(0,&bounce
,1024) == -1) die_msgin();
304 i
= str_rchr(action
,'=');
305 if (!stralloc_copyb(&listaddr
,action
,i
)) die_nomem();
307 if (!stralloc_cats(&listaddr
,"@")) die_nomem();
308 if (!stralloc_cats(&listaddr
,action
+ i
+ 1)) die_nomem();
310 if (!stralloc_0(&listaddr
)) die_nomem();
311 /* don't check for sub, since issub() doesn't see sublists */
312 switch (subreceipt(workdir
,msgnum
,&tagline
,listaddr
.s
,-1,INFO
,FATAL
)) {
313 case -1: strerr_die2x(0,INFO
,ERR_COOKIE
);
314 case -2: strerr_die2x(0,INFO
,ERR_NOT_ACTIVE
);
315 default: doit(listaddr
.s
,msgnum
,when
,&bounce
);
319 } /* pre-VERP bounce, in QSBMF format */
325 if (!stralloc_copys(¶graph
,"")) die_nomem();
327 if (getln(&ssin
,&line
,&match
,'\n') == -1)
328 strerr_die2sys(111,FATAL
,ERR_READ_INPUT
);
329 if (!match
) die_trash();
330 if (!stralloc_cat(¶graph
,&line
)) die_nomem();
331 if (line
.len
<= 1) break;
334 if (!flaghaveheader
) {
335 if (!stralloc_copy(&header
,¶graph
)) die_nomem();
340 if (!flaghaveintro
) {
341 if (paragraph
.s
[0] == '-' && paragraph
.s
[1] == '-')
342 continue; /* skip MIME boundary if it exists */
343 if (paragraph
.len
< 15) die_trash();
344 if (str_diffn(paragraph
.s
,"Hi. This is the",15)) die_trash();
345 if (!stralloc_copy(&intro
,¶graph
)) die_nomem();
350 if (paragraph
.s
[0] == '-')
353 if (paragraph
.s
[0] == '<') { /* find address */
354 if (!stralloc_copy(&failure
,¶graph
)) die_nomem();
356 if (!stralloc_copy(&bounce
,&header
)) die_nomem();
357 if (!stralloc_cat(&bounce
,&intro
)) die_nomem();
358 if (!stralloc_cat(&bounce
,&failure
)) die_nomem();
360 i
= byte_chr(failure
.s
,failure
.len
,'\n');
361 if (i
< 3) die_trash();
363 if (!stralloc_copyb(&listaddr
,failure
.s
+ 1,i
- 3)) die_nomem();
364 if (byte_chr(listaddr
.s
,listaddr
.len
,'\0') == listaddr
.len
) {
365 if (!stralloc_0(&listaddr
)) die_nomem();
366 if (subreceipt(workdir
,msgnum
,&tagline
,listaddr
.s
,-1,INFO
,FATAL
) == 0)
367 doit(listaddr
.s
,msgnum
,when
,&bounce
);