chiark / gitweb /
Import ezmlm 0.53
[ezmlm] / ezmlm-return.c
1 #include "stralloc.h"
2 #include "str.h"
3 #include "env.h"
4 #include "sig.h"
5 #include "slurp.h"
6 #include "getconf.h"
7 #include "strerr.h"
8 #include "byte.h"
9 #include "case.h"
10 #include "getln.h"
11 #include "substdio.h"
12 #include "error.h"
13 #include "quote.h"
14 #include "readwrite.h"
15 #include "fmt.h"
16 #include "now.h"
17 #include "cookie.h"
18 #include "subscribe.h"
19 #include "issub.h"
20
21 #define FATAL "ezmlm-return: fatal: "
22 void die_usage() { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return dir"); }
23 void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
24 void die_badaddr()
25 {
26   strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
27 }
28 void die_trash()
29 {
30   strerr_die1x(0,"ezmlm-return: info: trash address");
31 }
32
33 char outbuf[1024];
34 substdio ssout;
35 char inbuf[1024];
36 substdio ssin;
37
38 char strnum[FMT_ULONG];
39 char hash[COOKIE];
40 char hashcopy[COOKIE];
41 unsigned long cookiedate;
42 stralloc fndate = {0};
43 stralloc fndatenew = {0};
44 stralloc fnhash = {0};
45 stralloc fnhashnew = {0};
46
47 stralloc quoted = {0};
48 char *sender;
49
50 void die_hashnew()
51 { strerr_die4sys(111,FATAL,"unable to write ",fnhashnew.s,": "); }
52 void die_datenew()
53 { strerr_die4sys(111,FATAL,"unable to write ",fndatenew.s,": "); }
54 void die_msgin()
55 { strerr_die2sys(111,FATAL,"unable to read input: "); }
56
57 void dowit(addr,when,bounce)
58 char *addr;
59 unsigned long when;
60 stralloc *bounce;
61 {
62   int fd;
63
64   if (!issub(addr)) return;
65
66   if (!stralloc_copys(&fndate,"bounce/w")) die_nomem();
67   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
68   if (!stralloc_cats(&fndate,".")) die_nomem();
69   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
70   if (!stralloc_0(&fndate)) die_nomem();
71   if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
72   fndatenew.s[7] = 'W';
73
74   fd = open_trunc(fndatenew.s);
75   if (fd == -1) die_datenew();
76   substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
77   if (substdio_puts(&ssout,addr) == -1) die_datenew();
78   if (substdio_put(&ssout,"",1) == -1) die_datenew();
79   if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
80   if (!quote2(&quoted,sender)) die_nomem();
81   if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
82   if (substdio_puts(&ssout,">\n") == -1) die_datenew();
83   if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
84   if (substdio_flush(&ssout) == -1) die_datenew();
85   if (fsync(fd) == -1) die_datenew();
86   if (close(fd) == -1) die_datenew(); /* NFS stupidity */
87
88   if (rename(fndatenew.s,fndate.s) == -1)
89     strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
90 }
91
92 void doit(addr,msgnum,when,bounce)
93 char *addr;
94 unsigned long msgnum;
95 unsigned long when;
96 stralloc *bounce;
97 {
98   int fd;
99   int fdnew;
100
101   if (!issub(addr)) return;
102
103   if (!stralloc_copys(&fndate,"bounce/d")) die_nomem();
104   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
105   if (!stralloc_cats(&fndate,".")) die_nomem();
106   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
107   if (!stralloc_0(&fndate)) die_nomem();
108   if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
109   fndatenew.s[7] = 'D';
110
111   fd = open_trunc(fndatenew.s);
112   if (fd == -1) die_datenew();
113   substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
114   if (substdio_puts(&ssout,addr) == -1) die_datenew();
115   if (substdio_put(&ssout,"",1) == -1) die_datenew();
116   if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
117   if (!quote2(&quoted,sender)) die_nomem();
118   if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
119   if (substdio_puts(&ssout,">\n") == -1) die_datenew();
120   if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
121   if (substdio_flush(&ssout) == -1) die_datenew();
122   if (fsync(fd) == -1) die_datenew();
123   if (close(fd) == -1) die_datenew(); /* NFS stupidity */
124
125   cookie(hash,"",0,"",addr,"");
126   if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
127   if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
128   if (!stralloc_0(&fnhash)) die_nomem();
129   if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
130   fnhashnew.s[7] = 'H';
131
132   fdnew = open_trunc(fnhashnew.s);
133   if (fdnew == -1) die_hashnew();
134   substdio_fdbuf(&ssout,write,fdnew,outbuf,sizeof(outbuf));
135
136   fd = open_read(fnhash.s);
137   if (fd == -1) {
138     if (errno != error_noent)
139       strerr_die4sys(111,FATAL,"unable to read ",fnhash.s,": ");
140     if (rename(fndatenew.s,fndate.s) == -1)
141       strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
142   }
143   else {
144     substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
145     switch(substdio_copy(&ssout,&ssin)) {
146       case -2: die_msgin();
147       case -3: die_hashnew();
148     }
149     close(fd);
150     if (unlink(fndatenew.s) == -1)
151       strerr_die4sys(111,FATAL,"unable to unlink ",fndatenew.s,": ");
152   }
153   if (substdio_puts(&ssout,"   ") == -1) die_hashnew();
154   if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
155   if (substdio_puts(&ssout,"\n") == -1) die_hashnew();
156   if (substdio_flush(&ssout) == -1) die_hashnew();
157   if (fsync(fdnew) == -1) die_hashnew();
158   if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
159
160   if (rename(fnhashnew.s,fnhash.s) == -1)
161     strerr_die6sys(111,FATAL,"unable to rename ",fnhashnew.s," to ",fnhash.s,": ");
162 }
163
164 stralloc bounce = {0};
165 stralloc line = {0};
166 stralloc header = {0};
167 stralloc intro = {0};
168 stralloc failure = {0};
169 stralloc paragraph = {0};
170 int flaghaveheader;
171 int flaghaveintro;
172
173 stralloc key = {0};
174 stralloc inhost = {0};
175 stralloc outhost = {0};
176 stralloc inlocal = {0};
177 stralloc outlocal = {0};
178
179 char msginbuf[1024];
180 substdio ssmsgin;
181
182 void main(argc,argv)
183 int argc;
184 char **argv;
185 {
186   char *dir;
187   char *host;
188   char *local;
189   char *action;
190   unsigned long msgnum;
191   unsigned long cookiedate;
192   unsigned long when;
193   int match;
194   int i;
195   int fdlock;
196
197   umask(022);
198   sig_pipeignore();
199   when = (unsigned long) now();
200
201   dir = argv[1];
202   if (!dir) die_usage();
203
204   sender = env_get("SENDER");
205   if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
206   local = env_get("LOCAL");
207   if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
208   host = env_get("HOST");
209   if (!host) strerr_die2x(100,FATAL,"HOST not set");
210
211   if (chdir(dir) == -1)
212     strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
213
214   switch(slurp("key",&key,32)) {
215     case -1:
216       strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
217     case 0:
218       strerr_die3x(100,FATAL,dir,"/key does not exist");
219   }
220   getconf_line(&inhost,"inhost",1,FATAL,dir);
221   getconf_line(&inlocal,"inlocal",1,FATAL,dir);
222   getconf_line(&outhost,"outhost",1,FATAL,dir);
223   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
224
225   if (inhost.len != str_len(host)) die_badaddr();
226   if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
227   if (inlocal.len > str_len(local)) die_badaddr();
228   if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
229
230   action = local + inlocal.len;
231
232   if (!str_start(action,"-return-")) die_badaddr();
233   action += 8;
234
235   if (!*action) die_trash();
236
237   if (str_start(action,"probe-")) {
238     action += 6;
239     action += scan_ulong(action,&cookiedate);
240     if (now() - cookiedate > 3000000) die_trash();
241     if (*action++ != '.') die_trash();
242     i = str_chr(action,'-');
243     if (i != COOKIE) die_trash();
244     byte_copy(hashcopy,COOKIE,action);
245     action += COOKIE;
246     if (*action++ != '-') die_trash();
247     i = str_rchr(action,'=');
248     if (!stralloc_copyb(&line,action,i)) die_nomem();
249     if (action[i]) {
250       if (!stralloc_cats(&line,"@")) die_nomem();
251       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
252     }
253     if (!stralloc_0(&line)) die_nomem();
254     strnum[fmt_ulong(strnum,cookiedate)] = 0;
255     cookie(hash,key.s,key.len,strnum,line.s,"P");
256     if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
257
258     if (subscribe(line.s,0) == 1) log("-probe",line.s);
259     _exit(0);
260   }
261
262   fdlock = open_append("lockbounce");
263   if (fdlock == -1)
264     strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
265   if (lock_ex(fdlock) == -1)
266     strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
267
268   if (str_start(action,"warn-")) {
269     action += 5;
270     action += scan_ulong(action,&cookiedate);
271     if (now() - cookiedate > 3000000) die_trash();
272     if (*action++ != '.') die_trash();
273     i = str_chr(action,'-');
274     if (i != COOKIE) die_trash();
275     byte_copy(hashcopy,COOKIE,action);
276     action += COOKIE;
277     if (*action++ != '-') die_trash();
278     i = str_rchr(action,'=');
279     if (!stralloc_copyb(&line,action,i)) die_nomem();
280     if (action[i]) {
281       if (!stralloc_cats(&line,"@")) die_nomem();
282       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
283     }
284     if (!stralloc_0(&line)) die_nomem();
285     strnum[fmt_ulong(strnum,cookiedate)] = 0;
286     cookie(hash,key.s,key.len,strnum,line.s,"W");
287     if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
288
289     if (slurpclose(0,&bounce,1024) == -1) die_msgin();
290     dowit(line.s,when,&bounce);
291     _exit(0);
292   }
293
294   action += scan_ulong(action,&msgnum);
295   if (*action != '-') die_badaddr();
296   ++action;
297
298   if (*action) {
299     if (slurpclose(0,&bounce,1024) == -1) die_msgin();
300
301     i = str_rchr(action,'=');
302     if (!stralloc_copyb(&line,action,i)) die_nomem();
303     if (action[i]) {
304       if (!stralloc_cats(&line,"@")) die_nomem();
305       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
306     }
307     if (!stralloc_0(&line)) die_nomem();
308     doit(line.s,msgnum,when,&bounce);
309     _exit(0);
310   }
311
312   /* pre-VERP bounce, in QSBMF format */
313
314   substdio_fdbuf(&ssmsgin,read,0,msginbuf,sizeof(msginbuf));
315
316   flaghaveheader = 0;
317   flaghaveintro = 0;
318
319   for (;;) {
320     if (!stralloc_copys(&paragraph,"")) die_nomem();
321     for (;;) {
322       if (getln(&ssmsgin,&line,&match,'\n') == -1) die_msgin();
323       if (!match) die_trash();
324       if (!stralloc_cat(&paragraph,&line)) die_nomem();
325       if (line.len <= 1) break;
326     }
327
328     if (!flaghaveheader) {
329       if (!stralloc_copy(&header,&paragraph)) die_nomem();
330       flaghaveheader = 1;
331       continue;
332     }
333
334     if (!flaghaveintro) {
335       if (paragraph.len < 15) die_trash();
336       if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
337       if (!stralloc_copy(&intro,&paragraph)) die_nomem();
338       flaghaveintro = 1;
339       continue;
340     }
341
342     if (paragraph.s[0] == '-')
343       break;
344
345     if (paragraph.s[0] == '<') {
346       if (!stralloc_copy(&failure,&paragraph)) die_nomem();
347
348       if (!stralloc_copy(&bounce,&header)) die_nomem();
349       if (!stralloc_cat(&bounce,&intro)) die_nomem();
350       if (!stralloc_cat(&bounce,&failure)) die_nomem();
351
352       i = byte_chr(failure.s,failure.len,'\n');
353       if (i < 3) die_trash();
354
355       if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
356       if (byte_chr(line.s,line.len,'\0') == line.len) {
357         if (!stralloc_0(&line)) die_nomem();
358         doit(line.s,msgnum,when,&bounce);
359       }
360     }
361   }
362
363   _exit(0);
364 }