chiark / gitweb /
Import ezmlm 0.53
[ezmlm] / ezmlm-warn.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include "direntry.h"
4 #include "readwrite.h"
5 #include "getln.h"
6 #include "substdio.h"
7 #include "stralloc.h"
8 #include "slurp.h"
9 #include "getconf.h"
10 #include "byte.h"
11 #include "error.h"
12 #include "str.h"
13 #include "strerr.h"
14 #include "sig.h"
15 #include "now.h"
16 #include "datetime.h"
17 #include "date822fmt.h"
18 #include "fmt.h"
19 #include "cookie.h"
20 #include "qmail.h"
21
22 #define FATAL "ezmlm-warn: fatal: "
23 void die_usage() { strerr_die1x(100,"ezmlm-warn: usage: ezmlm-warn dir"); }
24 void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
25
26 stralloc key = {0};
27 stralloc outhost = {0};
28 stralloc outlocal = {0};
29 stralloc mailinglist = {0};
30
31 unsigned long when;
32 char *dir;
33 stralloc fn = {0};
34 struct stat st;
35
36 void die_read() { strerr_die6sys(111,FATAL,"unable to read ",dir,"/",fn.s,": "); }
37
38 char inbuf[1024];
39 substdio ssin;
40 char textbuf[1024];
41 substdio sstext;
42
43 stralloc addr = {0};
44 char strnum[FMT_ULONG];
45 char hash[COOKIE];
46 stralloc fnhash = {0};
47 stralloc quoted = {0};
48 stralloc line = {0};
49
50 struct qmail qq;
51 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
52 {
53   qmail_put(&qq,buf,len);
54   return len;
55 }
56 char qqbuf[1];
57 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
58 struct datetime dt;
59 char date[DATE822FMT];
60
61 void copy(fn)
62 char *fn;
63 {
64   int fd;
65   int match;
66
67   fd = open_read(fn);
68   if (fd == -1)
69     strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
70
71   substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
72   for (;;) {
73     if (getln(&sstext,&line,&match,'\n') == -1)
74       strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
75     if (!match)
76       break;
77     qmail_put(&qq,line.s,line.len);
78   }
79
80   close(fd);
81 }
82
83 void doit(flagw)
84 int flagw;
85 {
86   int i;
87   int fd;
88   int match;
89   int fdhash;
90   datetime_sec msgwhen;
91
92   fd = open_read(fn.s);
93   if (fd == -1) die_read();
94   substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
95
96   if (getln(&ssin,&addr,&match,'\0') == -1) die_read();
97   if (!match) { close(fd); return; }
98
99   if (!issub(addr.s)) { close(fd); /*XXX*/unlink(fn.s); return; }
100
101   cookie(hash,"",0,"",addr.s,"");
102   if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
103   if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
104   if (!stralloc_0(&fnhash)) die_nomem();
105
106   if (qmail_open(&qq) == -1)
107     strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
108
109   msgwhen = now();
110   qmail_puts(&qq,"Mailing-List: ");
111   qmail_put(&qq,mailinglist.s,mailinglist.len);
112   qmail_puts(&qq,"\nDate: ");
113   datetime_tai(&dt,msgwhen);
114   qmail_put(&qq,date,date822fmt(date,&dt));
115   qmail_puts(&qq,"Message-ID: <");
116   qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) msgwhen));
117   qmail_puts(&qq,".");
118   qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) getpid()));
119   qmail_puts(&qq,".ezmlm-warn@");
120   qmail_put(&qq,outhost.s,outhost.len);
121   qmail_puts(&qq,">\nFrom: ");
122   if (!quote(&quoted,&outlocal)) die_nomem();
123   qmail_put(&qq,quoted.s,quoted.len);
124   qmail_puts(&qq,"-help@");
125   qmail_put(&qq,outhost.s,outhost.len);
126   qmail_puts(&qq,"\nTo: ");
127   if (!quote2(&quoted,addr.s)) die_nomem();
128   qmail_put(&qq,quoted.s,quoted.len);
129   qmail_puts(&qq,flagw ? "\nSubject: ezmlm probe\n\n" : "\nSubject: ezmlm warning\n\n");
130
131   copy("text/top");
132   copy(flagw ? "text/bounce-probe" : "text/bounce-warn");
133
134   if (!flagw) {
135     fdhash = open_read(fnhash.s);
136     if (fdhash == -1) {
137       if (errno != error_noent)
138         strerr_die6sys(111,FATAL,"unable to open ",dir,"/",fnhash.s,": ");
139     }
140     else {
141       copy("text/bounce-num");
142       substdio_fdbuf(&sstext,read,fdhash,textbuf,sizeof(textbuf));
143       if (substdio_copy(&ssqq,&sstext) < 0)
144         strerr_die6sys(111,FATAL,"unable to read ",dir,"/",fnhash.s,": ");
145       close(fdhash);
146     }
147   }
148
149   copy("text/bounce-bottom");
150   if (substdio_copy(&ssqq,&ssin) < 0) die_read();
151   close(fd);
152
153   strnum[fmt_ulong(strnum,when)] = 0;
154   cookie(hash,key.s,key.len,strnum,addr.s,flagw ? "P" : "W");
155   if (!stralloc_copy(&line,&outlocal)) die_nomem();
156   if (!stralloc_cats(&line,flagw ? "-return-probe-" : "-return-warn-")) die_nomem();
157   if (!stralloc_cats(&line,strnum)) die_nomem();
158   if (!stralloc_cats(&line,".")) die_nomem();
159   if (!stralloc_catb(&line,hash,COOKIE)) die_nomem();
160   if (!stralloc_cats(&line,"-")) die_nomem();
161   i = str_chr(addr.s,'@');
162   if (!stralloc_catb(&line,addr.s,i)) die_nomem();
163   if (addr.s[i]) {
164     if (!stralloc_cats(&line,"=")) die_nomem();
165     if (!stralloc_cats(&line,addr.s + i + 1)) die_nomem();
166   }
167   if (!stralloc_cats(&line,"@")) die_nomem();
168   if (!stralloc_cat(&line,&outhost)) die_nomem();
169   if (!stralloc_0(&line)) die_nomem();
170   qmail_from(&qq,line.s);
171
172   qmail_to(&qq,addr.s);
173   if (qmail_close(&qq) != 0)
174     strerr_die2x(111,FATAL,"temporary qmail-queue error");
175
176   strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
177   strerr_warn2("ezmlm-warn: info: qp ",strnum,0);
178
179   if (!flagw) {
180     if (unlink(fnhash.s) == -1)
181       if (errno != error_noent)
182         strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fnhash.s,": ");
183   }
184   if (unlink(fn.s) == -1)
185     strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fn.s,": ");
186 }
187
188 void main(argc,argv)
189 int argc;
190 char **argv;
191 {
192   DIR *bouncedir;
193   direntry *d;
194   unsigned long bouncedate;
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   if (chdir(dir) == -1)
205     strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
206
207   switch(slurp("key",&key,32)) {
208     case -1:
209       strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
210     case 0:
211       strerr_die3x(100,FATAL,dir,"/key does not exist");
212   }
213   getconf_line(&outhost,"outhost",1,FATAL,dir);
214   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
215   getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
216
217   fdlock = open_append("lockbounce");
218   if (fdlock == -1)
219     strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
220   if (lock_ex(fdlock) == -1)
221     strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
222
223   bouncedir = opendir("bounce");
224   if (!bouncedir)
225     strerr_die4sys(111,FATAL,"unable to open ",dir,"/bounce: ");
226
227   while (d = readdir(bouncedir)) {
228     if (str_equal(d->d_name,".")) continue;
229     if (str_equal(d->d_name,"..")) continue;
230
231     if (!stralloc_copys(&fn,"bounce/")) die_nomem();
232     if (!stralloc_cats(&fn,d->d_name)) die_nomem();
233     if (!stralloc_0(&fn)) die_nomem();
234
235     if (stat(fn.s,&st) == -1) {
236       if (errno == error_noent) continue;
237       strerr_die6sys(111,FATAL,"unable to stat ",dir,"/",fn.s,": ");
238     }
239
240     if (when > st.st_mtime + 3000000)
241       if (unlink(fn.s) == -1)
242         strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fn.s,": ");
243
244     if ((d->d_name[0] == 'd') || (d->d_name[0] == 'w')) {
245       scan_ulong(d->d_name + 1,&bouncedate);
246       if (when > bouncedate + 1000000)
247         doit(d->d_name[0] == 'w');
248     }
249   }
250
251   closedir(bouncedir);
252
253   _exit(0);
254 }