Commit | Line | Data |
---|---|---|
cbd69c7a MW |
1 | #include "sgetopt.h" |
2 | #include "substdio.h" | |
3 | #include "readwrite.h" | |
4 | #include "stralloc.h" | |
5 | #include "getln.h" | |
6 | #include "strerr.h" | |
7 | #include "error.h" | |
8 | #include "exit.h" | |
9 | #include "open.h" | |
10 | #include "wait.h" | |
11 | #include "seek.h" | |
12 | #include "env.h" | |
13 | #include "str.h" | |
14 | #include "fmt.h" | |
15 | #include "token822.h" | |
16 | #include "control.h" | |
17 | #include "qmail.h" | |
18 | #include "auto_qmail.h" | |
19 | ||
20 | #define FATAL "dot-forward: fatal: " | |
21 | #define INFO "dot-forward: info: " | |
22 | ||
23 | void die_nomem() | |
24 | { strerr_die2x(111,FATAL,"out of memory"); } | |
25 | void die_control() | |
26 | { strerr_die2sys(111,FATAL,"unable to read controls: "); } | |
27 | void die_qq() | |
28 | { strerr_die2sys(111,FATAL,"unable to run qq: "); } | |
29 | void die_readmess() | |
30 | { strerr_die2sys(111,FATAL,"unable to read message: "); } | |
31 | ||
32 | stralloc line = {0}; | |
33 | ||
34 | void die_parse() | |
35 | { | |
36 | if (!stralloc_0(&line)) die_nomem(); | |
37 | strerr_die3x(111,FATAL,"unable to parse this line: ",line.s); | |
38 | } | |
39 | ||
40 | int flagdoit = 1; | |
41 | int flagacted; | |
42 | int flagdirect; | |
43 | ||
44 | char *ufline; | |
45 | char *rpline; | |
46 | char *dtline; | |
47 | char *sender; | |
48 | char *user; | |
49 | int userlen; | |
50 | char *host; | |
51 | int hostlen; | |
52 | ||
53 | char messbuf[1024]; | |
54 | substdio ssmess; | |
55 | char childbuf[1024]; | |
56 | substdio sschild; | |
57 | ||
58 | int blindwrite(fd,buf,len) | |
59 | int fd; char *buf; int len; | |
60 | { | |
61 | write(fd,buf,len); | |
62 | return len; | |
63 | } | |
64 | ||
65 | void run(cmd) | |
66 | char *cmd; | |
67 | { | |
68 | int child; | |
69 | int pi[2]; | |
70 | char *args[4]; | |
71 | int wstat; | |
72 | ||
73 | if (!flagdoit) { | |
74 | strerr_warn2("pipe through ",cmd,0); | |
75 | return; | |
76 | } | |
77 | ||
78 | if (pipe(pi) == -1) | |
79 | strerr_die2sys(111,FATAL,"unable to create pipe: "); | |
80 | ||
81 | switch (child = fork()) { | |
82 | case -1: | |
83 | strerr_die2sys(111,FATAL,"unable to fork: "); | |
84 | case 0: | |
85 | close(pi[1]); | |
86 | if (fd_move(0,pi[0]) == -1) | |
87 | strerr_die2sys(111,FATAL,"unable to set fd: "); | |
88 | args[0] = "/bin/sh"; args[1] = "-c"; args[2] = cmd; args[3] = 0; | |
89 | sig_pipedefault(); | |
90 | execv(*args,args); | |
91 | strerr_die2sys(111,FATAL,"unable to run /bin/sh: "); | |
92 | } | |
93 | ||
94 | close(pi[0]); | |
95 | ||
96 | substdio_fdbuf(&ssmess,read,0,messbuf,sizeof messbuf); | |
97 | substdio_fdbuf(&sschild,blindwrite,pi[1],childbuf,sizeof childbuf); | |
98 | ||
99 | substdio_puts(&sschild,ufline); | |
100 | substdio_puts(&sschild,rpline); | |
101 | substdio_puts(&sschild,dtline); | |
102 | if (substdio_copy(&sschild,&ssmess) != 0) die_readmess(); | |
103 | substdio_flush(&sschild); | |
104 | ||
105 | close(pi[1]); | |
106 | ||
107 | wait_pid(&wstat,child); | |
108 | if (wait_crashed(wstat)) | |
109 | strerr_die2x(111,FATAL,"child crashed"); | |
110 | ||
111 | switch(wait_exitcode(wstat)) { | |
112 | case 100: | |
113 | case 64: case 65: case 70: case 76: case 77: case 78: case 112: | |
114 | _exit(100); | |
115 | case 0: | |
116 | break; | |
117 | default: | |
118 | _exit(111); | |
119 | } | |
120 | ||
121 | if (seek_begin(0) == -1) | |
122 | strerr_die2sys(111,FATAL,"unable to rewind input: "); | |
123 | } | |
124 | ||
125 | stralloc targets = {0}; | |
126 | ||
127 | stralloc me = {0}; | |
128 | stralloc defaulthost = {0}; | |
129 | stralloc defaultdomain = {0}; | |
130 | stralloc plusdomain = {0}; | |
131 | ||
132 | void readcontrols() | |
133 | { | |
134 | int r; | |
135 | int fddir; | |
136 | ||
137 | fddir = open_read("."); | |
138 | if (fddir == -1) | |
139 | strerr_die2sys(111,FATAL,"unable to open current directory: "); | |
140 | ||
141 | if (chdir(auto_qmail) == -1) | |
142 | strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": "); | |
143 | ||
144 | r = control_readline(&me,"control/me"); | |
145 | if (r == -1) die_control(); | |
146 | if (!r) if (!stralloc_copys(&me,"me")) die_nomem(); | |
147 | ||
148 | r = control_readline(&defaultdomain,"control/defaultdomain"); | |
149 | if (r == -1) die_control(); | |
150 | if (!r) if (!stralloc_copy(&defaultdomain,&me)) die_nomem(); | |
151 | ||
152 | r = control_readline(&defaulthost,"control/defaulthost"); | |
153 | if (r == -1) die_control(); | |
154 | if (!r) if (!stralloc_copy(&defaulthost,&me)) die_nomem(); | |
155 | ||
156 | r = control_readline(&plusdomain,"control/plusdomain"); | |
157 | if (r == -1) die_control(); | |
158 | if (!r) if (!stralloc_copy(&plusdomain,&me)) die_nomem(); | |
159 | ||
160 | if (fchdir(fddir) == -1) | |
161 | strerr_die2sys(111,FATAL,"unable to set current directory: "); | |
162 | } | |
163 | ||
164 | stralloc cbuf = {0}; | |
165 | token822_alloc toks = {0}; | |
166 | token822_alloc tokaddr = {0}; | |
167 | stralloc address = {0}; | |
168 | ||
169 | void gotaddr() | |
170 | { | |
171 | int i; | |
172 | int j; | |
173 | int flaghasat; | |
174 | ||
175 | token822_reverse(&tokaddr); | |
176 | if (token822_unquote(&address,&tokaddr) != 1) die_nomem(); | |
177 | ||
178 | flaghasat = 0; | |
179 | for (i = 0;i < tokaddr.len;++i) | |
180 | if (tokaddr.t[i].type == TOKEN822_AT) | |
181 | flaghasat = 1; | |
182 | ||
183 | tokaddr.len = 0; | |
184 | ||
185 | if (!address.len) return; | |
186 | ||
187 | if (!flaghasat) | |
188 | if (address.len == userlen) | |
189 | if (!case_diffb(address.s,address.len,user)) { | |
190 | flagacted = 1; | |
191 | flagdirect = 1; | |
192 | return; | |
193 | } | |
194 | ||
195 | if (flaghasat) | |
196 | if (address.len == userlen + 1 + hostlen) | |
197 | if (!case_diffb(address.s,userlen,user)) | |
198 | if (address.s[userlen] == '@') | |
199 | if (!case_diffb(address.s + userlen + 1,hostlen,host)) { | |
200 | flagacted = 1; | |
201 | flagdirect = 1; | |
202 | return; | |
203 | } | |
204 | ||
205 | if (!flaghasat) | |
206 | if (address.s[0] == '/') { | |
207 | if (!stralloc_0(&address)) die_nomem(); | |
208 | strerr_die4x(111,FATAL,"file delivery ",address.s," not supported"); | |
209 | } | |
210 | ||
211 | if (!flaghasat) | |
212 | if (address.s[0] == '|') { | |
213 | if (!stralloc_0(&address)) die_nomem(); | |
214 | flagacted = 1; | |
215 | run(address.s + 1); | |
216 | return; | |
217 | } | |
218 | ||
219 | if (!flaghasat) { | |
220 | if (!stralloc_cats(&address,"@")) die_nomem(); | |
221 | if (!stralloc_cat(&address,&defaulthost)) die_nomem(); | |
222 | } | |
223 | if (address.s[address.len - 1] == '+') { | |
224 | address.s[address.len - 1] = '.'; | |
225 | if (!stralloc_cat(&address,&plusdomain)) die_nomem(); | |
226 | } | |
227 | j = 0; | |
228 | for (i = 0;i < address.len;++i) if (address.s[i] == '@') j = i; | |
229 | for (i = j;i < address.len;++i) if (address.s[i] == '.') break; | |
230 | if (i == address.len) { | |
231 | if (!stralloc_cats(&address,".")) die_nomem(); | |
232 | if (!stralloc_cat(&address,&defaultdomain)) die_nomem(); | |
233 | } | |
234 | ||
235 | if (!stralloc_0(&address)) die_nomem(); | |
236 | ||
237 | if (!stralloc_cats(&targets,"T")) die_nomem(); | |
238 | if (!stralloc_cats(&targets,address.s)) die_nomem(); | |
239 | if (!stralloc_0(&targets)) die_nomem(); | |
240 | ||
241 | if (!flagdoit) | |
242 | strerr_warn2("forward ",address.s,0); | |
243 | } | |
244 | ||
245 | void parseline() | |
246 | { | |
247 | int wordok; | |
248 | struct token822 *t; | |
249 | struct token822 *beginning; | |
250 | int r; | |
251 | ||
252 | r = token822_parse(&toks,&line,&cbuf); | |
253 | if (r == -1) die_nomem(); | |
254 | if (r == 0) die_parse(); | |
255 | ||
256 | beginning = toks.t; | |
257 | t = toks.t + toks.len; | |
258 | wordok = 1; | |
259 | ||
260 | if (!token822_readyplus(&tokaddr,1)) die_nomem(); | |
261 | tokaddr.len = 0; | |
262 | ||
263 | while (t > beginning) | |
264 | switch((--t)->type) { | |
265 | case TOKEN822_SEMI: | |
266 | break; /*XXX*/ | |
267 | case TOKEN822_COLON: | |
268 | break; /*XXX*/ | |
269 | case TOKEN822_RIGHT: | |
270 | if (tokaddr.len) gotaddr(); | |
271 | while ((t > beginning) && (t[-1].type != TOKEN822_LEFT)) | |
272 | if (!token822_append(&tokaddr,--t)) die_nomem(); | |
273 | gotaddr(); | |
274 | if (t <= beginning) die_parse(); | |
275 | --t; | |
276 | while ((t > beginning) && ((t[-1].type == TOKEN822_COMMENT) || (t[-1].type == TOKEN822_ATOM) || (t[-1].type == TOKEN822_QUOTE) || (t[-1].type == TOKEN822_AT) || (t[-1].type == TOKEN822_DOT))) | |
277 | --t; | |
278 | wordok = 0; | |
279 | continue; | |
280 | case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: | |
281 | if (!wordok) if (tokaddr.len) gotaddr(); | |
282 | wordok = 0; | |
283 | if (!token822_append(&tokaddr,t)) die_nomem(); | |
284 | continue; | |
285 | case TOKEN822_COMMENT: | |
286 | /* comment is lexically a space; shouldn't affect wordok */ | |
287 | break; | |
288 | case TOKEN822_COMMA: | |
289 | if (tokaddr.len) gotaddr(); | |
290 | wordok = 1; | |
291 | break; | |
292 | default: | |
293 | wordok = 1; | |
294 | if (!token822_append(&tokaddr,t)) die_nomem(); | |
295 | continue; | |
296 | } | |
297 | if (tokaddr.len) gotaddr(); | |
298 | } | |
299 | ||
300 | struct qmail qq; | |
301 | unsigned long qp; | |
302 | char *qqx; | |
303 | char strnum[FMT_ULONG]; | |
304 | ||
305 | int mywrite(fd,buf,len) | |
306 | int fd; char *buf; int len; | |
307 | { | |
308 | qmail_put(&qq,buf,len); | |
309 | return len; | |
310 | } | |
311 | ||
312 | char qqbuf[256]; | |
313 | substdio ssqq = SUBSTDIO_FDBUF(mywrite,-1,qqbuf,sizeof qqbuf); | |
314 | ||
315 | char inbuf[256]; | |
316 | ||
317 | void try(fn) | |
318 | char *fn; | |
319 | { | |
320 | int fd; | |
321 | int match; | |
322 | substdio ss; | |
323 | ||
324 | fd = open_read(fn); | |
325 | if (fd == -1) { | |
326 | if (errno == error_noent) return; | |
327 | strerr_die4sys(111,FATAL,"unable to open ",fn,": "); | |
328 | } | |
329 | ||
330 | if (!stralloc_copys(&targets,"")) die_nomem(); | |
331 | flagacted = 0; | |
332 | flagdirect = 0; | |
333 | ||
334 | substdio_fdbuf(&ss,read,fd,inbuf,sizeof inbuf); | |
335 | ||
336 | for (;;) { | |
337 | if (getln(&ss,&line,&match,'\n') == -1) | |
338 | strerr_die4sys(111,FATAL,"unable to read ",fn,": "); | |
339 | if (!line.len) break; | |
340 | if (line.s[0] != '#') parseline(); | |
341 | if (!match) break; | |
342 | } | |
343 | ||
344 | close(fd); | |
345 | ||
346 | if (targets.len) { | |
347 | flagacted = 1; | |
348 | if (flagdoit) { | |
349 | if (qmail_open(&qq) == -1) | |
350 | strerr_die2sys(111,FATAL,"unable to run qmail-queue: "); | |
351 | qp = qmail_qp(&qq); | |
352 | qmail_puts(&qq,dtline); | |
353 | ||
354 | substdio_fdbuf(&ssmess,read,0,messbuf,sizeof messbuf); | |
355 | if (substdio_copy(&ssqq,&ssmess) != 0) die_readmess(); | |
356 | substdio_flush(&ssqq); | |
357 | ||
358 | qmail_from(&qq,sender); | |
359 | qmail_put(&qq,targets.s,targets.len); | |
360 | ||
361 | qqx = qmail_close(&qq); | |
362 | if (*qqx == 'D') | |
363 | strerr_die3x(100,FATAL,"unable to forward message: ",qqx + 1); | |
364 | if (*qqx) | |
365 | strerr_die3x(111,FATAL,"unable to forward message: ",qqx + 1); | |
366 | strnum[fmt_ulong(strnum,qp)] = 0; | |
367 | strerr_warn3(INFO,"qp ",strnum,0); | |
368 | } | |
369 | } | |
370 | ||
371 | if (flagdirect) { | |
372 | if (!flagdoit) strerr_warn1("direct delivery",0); | |
373 | _exit(0); | |
374 | } | |
375 | if (!flagacted) { | |
376 | if (!flagdoit) strerr_warn2("skipping empty file ",fn,0); | |
377 | return; | |
378 | } | |
379 | _exit(99); | |
380 | } | |
381 | ||
382 | void main(argc,argv) | |
383 | int argc; | |
384 | char **argv; | |
385 | { | |
386 | int opt; | |
387 | int fddir; | |
388 | ||
389 | sig_pipeignore(); | |
390 | ||
391 | while ((opt = getopt(argc,argv,"nN")) != opteof) | |
392 | switch(opt) { | |
393 | case 'n': | |
394 | flagdoit = 0; break; | |
395 | case 'N': | |
396 | flagdoit = 1; break; | |
397 | default: | |
398 | strerr_die1x(100,"dot-forward: usage: dot-forward [ -nN ] file ..."); | |
399 | } | |
400 | argv += optind; | |
401 | ||
402 | ufline = env_get("UFLINE"); if (!ufline) ufline = ""; | |
403 | rpline = env_get("RPLINE"); if (!rpline) rpline = ""; | |
404 | dtline = env_get("DTLINE"); if (!dtline) dtline = ""; | |
405 | sender = env_get("NEWSENDER"); if (!sender) sender = ""; | |
406 | ||
407 | user = env_get("USER"); if (!user) user = ""; | |
408 | userlen = str_len(user); | |
409 | host = env_get("HOST"); if (!host) host = ""; | |
410 | hostlen = str_len(host); | |
411 | ||
412 | readcontrols(); | |
413 | ||
414 | while (*argv) | |
415 | try(*argv++); | |
416 | ||
417 | if (!flagdoit) strerr_warn1("direct delivery",0); | |
418 | _exit(0); | |
419 | } |