chiark / gitweb /
Import fastforward 0.51
[fastforward] / fastforward.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include "stralloc.h"
4 #include "substdio.h"
5 #include "subfd.h"
6 #include "strset.h"
7 #include "sgetopt.h"
8 #include "readwrite.h"
9 #include "exit.h"
10 #include "strerr.h"
11 #include "env.h"
12 #include "sig.h"
13 #include "qmail.h"
14 #include "fmt.h"
15 #include "case.h"
16 #include "alloc.h"
17 #include "coe.h"
18 #include "seek.h"
19 #include "wait.h"
20 #include "fork.h"
21
22 #define FATAL "fastforward: fatal: "
23
24 void usage()
25 {
26   strerr_die1x(100,"fastforward: usage: fastforward [ -nNpP ] data.cdb");
27 }
28 void nomem()
29 {
30   strerr_die2x(111,FATAL,"out of memory");
31 }
32
33 void print(s)
34 char *s;
35 {
36   char ch;
37   while (ch = *s++) {
38     substdio_put(subfderr,&ch,1);
39   }
40 }
41
42 void printsafe(s)
43 char *s;
44 {
45   char ch;
46   while (ch = *s++) {
47     if (ch < 32) ch = '_';
48     substdio_put(subfderr,&ch,1);
49   }
50 }
51
52 struct qmail qq;
53 char qp[FMT_ULONG];
54 char qqbuf[1];
55
56 int qqwrite(fd,buf,len) int fd; char *buf; int len;
57 {
58   qmail_put(&qq,buf,len);
59   return len;
60 }
61
62 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof qqbuf);
63
64 char messbuf[4096];
65 substdio ssmess = SUBSTDIO_FDBUF(read,0,messbuf,sizeof messbuf);
66
67 int flagdeliver = 1;
68 int flagpassthrough = 0;
69
70 char *dtline;
71 stralloc sender = {0};
72 stralloc programs = {0};
73 stralloc forward = {0};
74
75 strset done;
76 stralloc todo = {0};
77
78 stralloc mailinglist = {0};
79
80 void dofile(fn)
81 char *fn;
82 {
83   int fd;
84   struct stat st;
85   int i;
86   int j;
87
88   if (!stralloc_copys(&mailinglist,"")) nomem();
89
90   fd = open_read(fn);
91   if (fd == -1)
92     strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
93   if (fstat(fd,&st) == -1)
94     strerr_die4sys(111,FATAL,"unable to stat ",fn,": ");
95   if ((st.st_mode & 0444) != 0444)
96     strerr_die3x(111,FATAL,fn," is not world-readable");
97   if (slurpclose(fd,&mailinglist,1024) == -1)
98     strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
99
100   i = 0;
101   for (j = 0;j < mailinglist.len;++j)
102     if (!mailinglist.s[j]) {
103       if ((mailinglist.s[i] == '.') || (mailinglist.s[i] == '/')) {
104         if (!stralloc_cats(&todo,mailinglist.s + i)) nomem();
105         if (!stralloc_0(&todo)) nomem();
106       }
107       else if ((mailinglist.s[i] == '&') && (j - i < 900)) {
108         if (!stralloc_cats(&todo,mailinglist.s + i)) nomem();
109         if (!stralloc_0(&todo)) nomem();
110       }
111       i = j + 1;
112     }
113 }
114
115 char *fncdb;
116 int fdcdb;
117 stralloc key = {0};
118 uint32 dlen;
119 stralloc data = {0};
120
121 void cdbreaderror()
122 {
123   strerr_die4sys(111,FATAL,"unable to read ",fncdb,": ");
124 }
125
126 int findtarget(flagwild,prepend,addr)
127 int flagwild;
128 char *prepend;
129 char *addr;
130 {
131   int r;
132   int at;
133
134   if (!stralloc_copys(&key,prepend)) nomem();
135   if (!stralloc_cats(&key,addr)) nomem();
136   case_lowerb(key.s,key.len);
137
138   r = cdb_seek(fdcdb,key.s,key.len,&dlen);
139   if (r == -1) cdbreaderror();
140   if (r) return 1;
141
142   if (!flagwild) return 0;
143   at = str_rchr(addr,'@');
144   if (!addr[at]) return 0;
145
146   if (!stralloc_copys(&key,prepend)) nomem();
147   if (!stralloc_cats(&key,addr + at)) nomem();
148   case_lowerb(key.s,key.len);
149
150   r = cdb_seek(fdcdb,key.s,key.len,&dlen);
151   if (r == -1) cdbreaderror();
152   if (r) return 1;
153
154   if (!stralloc_copys(&key,prepend)) nomem();
155   if (!stralloc_catb(&key,addr,at + 1)) nomem();
156   case_lowerb(key.s,key.len);
157
158   r = cdb_seek(fdcdb,key.s,key.len,&dlen);
159   if (r == -1) cdbreaderror();
160   if (r) return 1;
161
162   return 0;
163 }
164
165 int gettarget(flagwild,prepend,addr)
166 int flagwild;
167 char *prepend;
168 char *addr;
169 {
170   if (!findtarget(flagwild,prepend,addr)) return 0;
171
172   if (!stralloc_ready(&data,(unsigned int) dlen)) nomem();
173   data.len = dlen;
174   if (cdb_bread(fdcdb,data.s,data.len) == -1) cdbreaderror();
175
176   return 1;
177 }
178
179 void doprogram(arg)
180 char *arg;
181 {
182   char *args[5];
183   int child;
184   int wstat;
185
186   if (!flagdeliver) {
187     print("run ");
188     printsafe(arg);
189     print("\n");
190     substdio_flush(subfderr);
191     return;
192   }
193
194   if (*arg == '!') {
195     args[0] = "preline";
196     args[1] = "sh";
197     args[2] = "-c";
198     args[3] = arg + 1;
199     args[4] = 0;
200   }
201   else {
202     args[0] = "sh";
203     args[1] = "-c";
204     args[2] = arg + 1;
205     args[3] = 0;
206   }
207
208   switch(child = vfork()) {
209     case -1:
210       strerr_die2sys(111,FATAL,"unable to fork: ");
211     case 0:
212       sig_pipedefault();
213       execvp(*args,args);
214       strerr_die4sys(111,FATAL,"unable to run ",arg,": ");
215   }
216
217   wait_pid(&wstat,child);
218   if (wait_crashed(wstat))
219     strerr_die4sys(111,FATAL,"child crashed in ",arg,": ");
220
221   switch(wait_exitcode(wstat)) {
222     case 64: case 65: case 70: case 76: case 77: case 78: case 112:
223     case 100: _exit(100);
224     case 0: break;
225     default: _exit(111);
226   }
227
228   if (seek_begin(0) == -1)
229     strerr_die2sys(111,FATAL,"unable to rewind input: ");
230 }
231
232 void dodata()
233 {
234   int i;
235   int j;
236   i = 0;
237   for (j = 0;j < data.len;++j)
238     if (!data.s[j]) {
239       if ((data.s[i] == '|') || (data.s[i] == '!'))
240         doprogram(data.s + i);
241       else if ((data.s[i] == '.') || (data.s[i] == '/')) {
242         if (!stralloc_cats(&todo,data.s + i)) nomem();
243         if (!stralloc_0(&todo)) nomem();
244       }
245       else if ((data.s[i] == '&') && (j - i < 900)) {
246         if (!stralloc_cats(&todo,data.s + i)) nomem();
247         if (!stralloc_0(&todo)) nomem();
248       }
249       i = j + 1;
250     }
251 }
252
253 void dorecip(addr)
254 char *addr;
255 {
256
257   if (!findtarget(0,"?",addr))
258     if (gettarget(0,":",addr)) {
259       dodata();
260       return;
261     }
262   if (!stralloc_cats(&forward,addr)) nomem();
263   if (!stralloc_0(&forward)) nomem();
264 }
265
266 void doorigrecip(addr)
267 char *addr;
268 {
269   if (sender.len)
270     if ((sender.len != 4) || byte_diff(sender.s,4,"#@[]"))
271       if (gettarget(1,"?",addr))
272         if (!stralloc_copy(&sender,&data)) nomem();
273   if (!gettarget(1,":",addr))
274     if (flagpassthrough)
275       _exit(0);
276     else
277       strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
278   dodata();
279 }
280
281 stralloc recipient = {0};
282 int flagdefault = 0;
283
284 void main(argc,argv)
285 int argc;
286 char **argv;
287 {
288   int opt;
289   char *x;
290   int i;
291
292   sig_pipeignore();
293
294   dtline = env_get("DTLINE");
295   if (!dtline) dtline = "";
296
297   x = env_get("SENDER");
298   if (!x) x = "original envelope sender";
299   if (!stralloc_copys(&sender,x)) nomem();
300
301   if (!stralloc_copys(&forward,"")) nomem();
302   if (!strset_init(&done)) nomem();
303
304   while ((opt = getopt(argc,argv,"nNpPdD")) != opteof)
305     switch(opt) {
306       case 'n': flagdeliver = 0; break;
307       case 'N': flagdeliver = 1; break;
308       case 'p': flagpassthrough = 1; break;
309       case 'P': flagpassthrough = 0; break;
310       case 'd': flagdefault = 1; break;
311       case 'D': flagdefault = 0; break;
312       default: usage();
313     }
314   argv += optind;
315
316   fncdb = *argv;
317   if (!fncdb) usage();
318   fdcdb = open_read(fncdb);
319   if (fdcdb == -1) cdbreaderror();
320   coe(fdcdb);
321
322   if (flagdefault) {
323     x = env_get("DEFAULT");
324     if (!x) x = env_get("EXT");
325     if (!x) strerr_die2x(100,FATAL,"$DEFAULT or $EXT must be set");
326     if (!stralloc_copys(&recipient,x)) nomem();
327     if (!stralloc_cats(&recipient,"@")) nomem();
328     x = env_get("HOST");
329     if (!x) strerr_die2x(100,FATAL,"$HOST must be set");
330     if (!stralloc_cats(&recipient,x)) nomem();
331     if (!stralloc_0(&recipient)) nomem();
332     x = recipient.s;
333   }
334   else {
335     x = env_get("RECIPIENT");
336     if (!x) strerr_die2x(100,FATAL,"$RECIPIENT must be set");
337   }
338   if (!strset_add(&done,x)) nomem();
339   doorigrecip(x);
340
341   while (todo.len) {
342     i = todo.len - 1;
343     while ((i > 0) && todo.s[i - 1]) --i;
344     todo.len = i;
345
346     if (strset_in(&done,todo.s + i)) continue;
347
348     x = alloc(str_len(todo.s + i) + 1);
349     if (!x) nomem();
350     str_copy(x,todo.s + i);
351     if (!strset_add(&done,x)) nomem();
352
353     x = todo.s + i;
354     if (*x == 0)
355       continue;
356     else if ((*x == '.') || (*x == '/'))
357       dofile(x);
358     else
359       dorecip(x + 1);
360   }
361
362   if (!forward.len) {
363     if (!flagdeliver) {
364       print("no forwarding\n");
365       substdio_flush(subfderr);
366     }
367     _exit(flagpassthrough ? 99 : 0);
368   }
369
370   if (!stralloc_0(&sender)) nomem();
371
372   if (!flagdeliver) {
373     print("from <");
374     printsafe(sender.s);
375     print(">\n");
376     while (forward.len) {
377       i = forward.len - 1;
378       while ((i > 0) && forward.s[i - 1]) --i;
379       forward.len = i;
380       print("to <");
381       printsafe(forward.s + i);
382       print(">\n");
383     }
384     substdio_flush(subfderr);
385     _exit(flagpassthrough ? 99 : 0);
386   }
387
388   if (qmail_open(&qq) == -1)
389     strerr_die2sys(111,FATAL,"unable to fork: ");
390   qmail_puts(&qq,dtline);
391   if (substdio_copy(&ssqq,&ssmess) != 0)
392     strerr_die2sys(111,FATAL,"unable to read message: ");
393   substdio_flush(&ssqq);
394   qp[fmt_ulong(qp,qmail_qp(&qq))] = 0;
395
396   qmail_from(&qq,sender.s);
397
398   while (forward.len) {
399     i = forward.len - 1;
400     while ((i > 0) && forward.s[i - 1]) --i;
401     forward.len = i;
402     qmail_to(&qq,forward.s + i);
403   }
404
405   x = qmail_close(&qq);
406   if (*x) strerr_die2x(*x == 'D' ? 100 : 111,FATAL,x + 1);
407   strerr_die2x(flagpassthrough ? 99 : 0,"fastforward: qp ",qp);
408 }