Debianization, and minor fixes.
[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 }