Codebase list fdm / 288b30e
- Change so that the child uses poll(2) on all the fds rather than letting the fetch-* stuff poll and periodically checking the parent fd. - Make delivery children be handled in the main loop (fdm.c) along with the fetch children. This means that different accounts will not block each other when delivering. I tried allowing multiple simultaneous deliveries for each account but it doesn't work very well, particularly regarding errors. Nicholas Marriott 17 years ago
24 changed file(s) with 1712 addition(s) and 1445 deletion(s). Raw diff Collapse all Expand all
0 16 March 2007
1
2 * Change so that the child uses poll(2) on all the fds rather than letting
3 the fetch-* stuff poll and periodically checking the parent fd.
4 * Make delivery children be handled in the main loop (fdm.c) along with
5 the fetch children. This means that different accounts will not block each
6 other when delivering. I tried allowing multiple simultaneous deliveries
7 for each account but it doesn't work very well, particularly regarding
8 errors.
9
010 15 March 2007
111
212 * Seriously reduce -v output in favour of -vv and -vvv.
1616 deliver-keep.c deliver-maildir.c deliver-mbox.c deliver-write.c \
1717 deliver-append.c deliver-rewrite.c match-regexp.c match-command.c \
1818 match-tagged.c match-size.c match-string.c match-matched.c match-age.c \
19 match-unmatched.c match-attachment.c child.c parent.c privsep.c attach.c \
19 match-unmatched.c match-attachment.c child.c privsep.c attach.c \
2020 cleanup.c imap-common.c fetch-imappipe.c deliver-remove-header.c \
2121 deliver-stdout.c deliver-append-string.c strb.c deliver-add-header.c \
22 deliver-exec.c \
22 deliver-exec.c child-fetch.c parent-fetch.c child-deliver.c \
23 parent-deliver.c \
2324 parse.y lex.l
2425
2526 LEX= lex
2929 - look at .netrc
3030 -- move lbuf stuff into fetch_*_data
3131 -- split fetch into next and get(line) and alloc mail outside
32 -- introduce eg fetch_pop3_state structs and make fetch reentrant
33 -- rework IO_NOWAIT and FETCH_NOWAIT
32 -- rework IO_NOWAIT
3433 -- tidy child.c
3534 -- audit for uses of special "off" variables where m->size would do
3635 - is there any reason struct mail shouldn't point to a struct account?
36 - coalesce multiple IMAP deletions into a single call
0 /* $Id$ */
1
2 /*
3 * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
14 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
15 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19
20 #include <string.h>
21 #include <unistd.h>
22
23 #include "fdm.h"
24 #include "match.h"
25
26 int child_deliver(struct child *child, struct io *io);
27
28 int
29 child_deliver(struct child *child, struct io *io)
30 {
31 struct child_deliver_data *data = child->data;
32 struct account *a = data->account;
33 struct mail *m = data->mail;
34 struct msg msg;
35 int error = 0;
36
37 #ifndef NO_SETPROCTITLE
38 setproctitle("%s[%lu]", data->name, (u_long) geteuid());
39 #endif
40
41 /* refresh user and home and fix tags */
42 fill_info(NULL);
43 update_tags(&m->tags);
44 log_debug2("%s: user is: %s, home is: %s", a->name, conf.info.user,
45 conf.info.home);
46
47 /* call the hook */
48 data->hook(0, a, &msg, data, &msg.data.error);
49
50 /* inform parent we're done */
51 msg.type = MSG_DONE;
52 if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0)
53 fatalx("deliver: privsep_send error");
54 if (privsep_recv(io, &msg, NULL, 0) != 0)
55 fatalx("deliver: privsep_recv error");
56 if (msg.type != MSG_EXIT)
57 fatalx("deliver: unexpected message");
58
59 return (error);
60 }
61
62 void
63 child_deliver_action_hook(pid_t pid, struct account *a, struct msg *msg,
64 struct child_deliver_data *data, int *result)
65 {
66 struct action *t = data->action;
67 struct deliver_ctx *dctx = data->dctx;
68 struct mail *m = data->mail;
69 struct mail *md = &dctx->wr_mail;
70
71 /* check if this is the parent */
72 if (pid != 0) {
73 /* use new mail if necessary */
74 if (t->deliver->type != DELIVER_WRBACK) {
75 xfree(dctx);
76 return;
77 }
78
79 if (*result != DELIVER_SUCCESS) {
80 mail_destroy(md);
81
82 xfree(dctx);
83 return;
84 }
85
86 mail_close(md);
87 mail_receive(m, msg);
88
89 xfree(dctx);
90 return;
91 }
92
93 /* this is the child. do the delivery */
94 *result = t->deliver->deliver(dctx, t);
95 if (t->deliver->type != DELIVER_WRBACK || *result != DELIVER_SUCCESS)
96 return;
97
98 mail_send(md, msg);
99 log_debug2("%s: using new mail, size %zu", a->name, md->size);
100 }
101
102 void
103 child_deliver_cmd_hook(pid_t pid, struct account *a, unused struct msg *msg,
104 struct child_deliver_data *data, int *result)
105 {
106 struct match_ctx *mctx = data->mctx;
107 struct mail *m = mctx->mail;
108 struct match_command_data *cmddata = data->cmddata;
109 int flags, status, found = 0;
110 char *s, *cause, *lbuf, *out, *err, tag[24];
111 size_t llen;
112 struct cmd *cmd = NULL;
113 struct rmlist rml;
114 u_int i;
115
116 /* if this is the parent, do nothing */
117 if (pid != 0) {
118 xfree(mctx);
119 return;
120 }
121
122 /* sort out the command */
123 s = replacepath(&cmddata->cmd, m->tags, m, &m->rml);
124 if (s == NULL || *s == '\0') {
125 log_warnx("%s: empty command", a->name);
126 goto error;
127 }
128
129 log_debug2("%s: %s: started (ret=%d re=%s)", a->name, s, cmddata->ret,
130 cmddata->re.str == NULL ? "none" : cmddata->re.str);
131 flags = CMD_ONCE;
132 if (cmddata->pipe)
133 flags |= CMD_IN;
134 if (cmddata->re.str != NULL)
135 flags |= CMD_OUT;
136 cmd = cmd_start(s, flags, conf.timeout, m->data, m->size, &cause);
137 if (cmd == NULL) {
138 log_warnx("%s: %s: %s", a->name, s, cause);
139 goto error;
140 }
141
142 llen = IO_LINESIZE;
143 lbuf = xmalloc(llen);
144
145 do {
146 status = cmd_poll(cmd, &out, &err, &lbuf, &llen, &cause);
147 if (status > 0) {
148 log_warnx("%s: %s: %s", a->name, s, cause);
149 goto error;
150 }
151 if (status < 0)
152 break;
153 if (err != NULL)
154 log_warnx("%s: %s: %s", a->name, s, err);
155 if (out == NULL)
156 continue;
157 log_debug3("%s: %s: out: %s", a->name, s, out);
158 if (found) /* XXX stop early? */
159 continue;
160
161 found = re_string(&cmddata->re, out, &rml, &cause);
162 if (found == -1) {
163 log_warnx("%s: %s", a->name, cause);
164 goto error;
165 }
166 if (found != 1)
167 continue;
168 /* save the matches */
169 if (!rml.valid)
170 continue;
171 for (i = 0; i < NPMATCH; i++) {
172 if (!rml.list[i].valid)
173 break;
174 xsnprintf(tag, sizeof tag, "command%u", i);
175 add_tag(&m->tags, tag, "%.*s", (int) (rml.list[i].eo -
176 rml.list[i].so), out + rml.list[i].so);
177 }
178 } while (status >= 0);
179 status = -1 - status;
180
181 log_debug2("%s: %s: returned %d, found %d", a->name, s, status, found);
182
183 cmd_free(cmd);
184 xfree(s);
185 xfree(lbuf);
186
187 status = cmddata->ret == status;
188 if (cmddata->ret != -1 && cmddata->re.str != NULL)
189 *result = (found && status) ? MATCH_TRUE : MATCH_FALSE;
190 else if (cmddata->ret != -1 && cmddata->re.str == NULL)
191 *result = status ? MATCH_TRUE : MATCH_FALSE;
192 else if (cmddata->ret == -1 && cmddata->re.str != NULL)
193 *result = found ? MATCH_TRUE : MATCH_FALSE;
194 else
195 *result = MATCH_ERROR;
196 return;
197
198 error:
199 if (cause != NULL)
200 xfree(cause);
201 if (cmd != NULL)
202 cmd_free(cmd);
203 if (s != NULL)
204 xfree(s);
205 if (lbuf != NULL)
206 xfree(lbuf);
207 *result = MATCH_ERROR;
208 }
0 /* $Id$ */
1
2 /*
3 * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
14 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
15 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/wait.h>
22
23 #include <fcntl.h>
24 #include <fnmatch.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include "fdm.h"
31 #include "deliver.h"
32 #include "fetch.h"
33 #include "match.h"
34
35 int poll_account(struct io *, struct account *);
36 int fetch_account(struct io *, struct account *, struct ios *, double);
37
38 int fetch_flush(struct account *, struct io *, int *, int *, int *,
39 const char **);
40 int fetch_poll(struct account *a, int, struct ios *, struct io **);
41 int fetch_transform(struct account *, struct mail *);
42 int fetch_rule(struct match_ctx *, const char **);
43
44 int run_match(struct account *, const char **);
45 int run_deliver(struct account *, struct io *, int *, const char **);
46 int run_done(struct account *, int *, int *, const char **);
47
48 void flush_queue(struct match_queue *);
49 u_int queue_length(struct match_queue *);
50
51 struct strings *get_users(struct match_ctx *, struct rule *, struct action *,
52 int *);
53
54 int do_expr(struct rule *, struct match_ctx *);
55 int do_deliver(struct rule *, struct match_ctx *);
56 int do_rules(struct match_ctx *, struct rules *, const char **);
57
58 int start_action(struct io *, struct deliver_ctx *);
59 int finish_action(struct deliver_ctx *, struct msg *, void *, size_t);
60
61 /* XXX wrap in struct (match_state?) and pass, with kept/dropped etc */
62 struct match_queue matchq;
63 struct match_queue deliverq;
64 struct match_queue doneq;
65
66 int
67 child_fetch(struct child *child, struct io *io)
68 {
69 struct child_fetch_data *data = child->data;
70 enum fdmop op = data->op;
71 struct account *a = data->account;
72 struct msg msg;
73 int error = 1;
74 double tim;
75 struct ios ios;
76
77 #ifdef DEBUG
78 xmalloc_clear();
79 COUNTFDS(a->name);
80 #endif
81
82 io->flags |= IO_NOWAIT;
83 log_debug2("%s: started, pid %ld", a->name, (long) getpid());
84
85 #ifndef NO_SETPROCTITLE
86 setproctitle("child: %s", a->name);
87 #endif
88
89 if (op == FDMOP_POLL && a->fetch->poll == NULL) {
90 log_info("%s: polling not supported", a->name);
91 goto out;
92 } else if (op == FDMOP_FETCH && a->fetch->fetch == NULL) {
93 log_info("%s: fetching not supported", a->name);
94 goto out;
95 }
96 tim = get_time();
97
98 /* start fetch */
99 ARRAY_INIT(&ios);
100 ARRAY_ADD(&ios, io, struct io *);
101 if (a->fetch->start != NULL &&
102 a->fetch->start(a, &ios) != FETCH_SUCCESS) {
103 log_warnx("%s: start error. aborting", a->name);
104 goto out;
105 }
106
107 /* process fetch */
108 log_debug2("%s: started processing", a->name);
109 switch (op) {
110 case FDMOP_POLL:
111 error = poll_account(io, a);
112 break;
113 case FDMOP_FETCH:
114 error = fetch_account(io, a, &ios, tim);
115 break;
116 default:
117 fatalx("child: unexpected command");
118 }
119 log_debug2("%s: finished processing. exiting", a->name);
120
121 out:
122 /* finish fetch */
123 if (a->fetch->finish != NULL && a->fetch->finish(a) != FETCH_SUCCESS)
124 error = 1;
125
126 io->flags &= ~IO_NOWAIT;
127 memset(&msg, 0, sizeof msg);
128
129 msg.type = MSG_EXIT;
130 log_debug3("%s: sending exit message to parent", a->name);
131 if (privsep_send(io, &msg, NULL, 0) != 0)
132 fatalx("child: privsep_send error");
133 log_debug3("%s: waiting for exit message from parent", a->name);
134 if (privsep_recv(io, &msg, NULL, 0) != 0)
135 fatalx("child: privsep_recv error");
136 if (msg.type != MSG_EXIT)
137 fatalx("child: unexpected message");
138
139 io_close(io);
140 io_free(io);
141
142 #ifdef DEBUG
143 COUNTFDS(a->name);
144 xmalloc_report(a->name);
145 #endif
146
147 return (error);
148 }
149
150 int
151 poll_account(unused struct io *io, struct account *a)
152 {
153 u_int n;
154
155 log_debug2("%s: polling", a->name);
156
157 if (a->fetch->poll(a, &n) == FETCH_ERROR) {
158 log_warnx("%s: polling error. aborted", a->name);
159 return (1);
160 }
161
162 log_info("%s: %u messages found", a->name, n);
163
164 return (0);
165 }
166
167 int
168 run_match(struct account *a, const char **cause)
169 {
170 struct match_ctx *mctx;
171
172 if (TAILQ_EMPTY(&matchq))
173 return (0);
174
175 mctx = TAILQ_FIRST(&matchq);
176 log_debug3("%s: running match queue", a->name);
177
178 switch (fetch_rule(mctx, cause)) {
179 case FETCH_ERROR:
180 return (1);
181 case FETCH_AGAIN:
182 /* delivering mail, queue for delivery */
183 log_debug3("%s: adding to deliver queue", a->name);
184 TAILQ_REMOVE(&matchq, mctx, match_entry);
185 TAILQ_INSERT_TAIL(&deliverq, mctx, deliver_entry);
186 break;
187 case FETCH_COMPLETE:
188 /* finished with mail, queue on done queue */
189 log_debug3("%s: adding to done queue", a->name);
190 TAILQ_REMOVE(&matchq, mctx, match_entry);
191 TAILQ_INSERT_TAIL(&doneq, mctx, done_entry);
192 break;
193 }
194
195 return (0);
196 }
197
198 int
199 run_deliver(struct account *a, struct io *io, int *blocked, const char **cause)
200 {
201 struct match_ctx *mctx;
202 struct deliver_ctx *dctx;
203 struct msg msg;
204 void *buf;
205 size_t len;
206
207 *blocked = 0;
208 if (TAILQ_EMPTY(&deliverq))
209 return (0);
210
211 mctx = TAILQ_FIRST(&deliverq);
212 if (TAILQ_EMPTY(&mctx->dqueue)) {
213 /* delivery done. return to match queue */
214 log_debug3("%s: returning to match queue", a->name);
215 TAILQ_REMOVE(&deliverq, mctx, deliver_entry);
216 TAILQ_INSERT_TAIL(&matchq, mctx, match_entry);
217 return (0);
218 }
219
220 /* start the first action */
221 log_debug3("%s: running deliver queue", a->name);
222 dctx = TAILQ_FIRST(&mctx->dqueue);
223
224 if (dctx->blocked) {
225 /* check for reply from parent and finish */
226 if (!privsep_check(io)) {
227 *blocked = 1;
228 return (0);
229 }
230
231 if (privsep_recv(io, &msg, &buf, &len) != 0)
232 fatalx("child: privsep_recv error");
233 if (msg.type != MSG_DONE)
234 fatalx("child: unexpected message");
235
236 if (finish_action(dctx, &msg, buf, len) != 0) {
237 *cause = "delivery";
238 return (1);
239 }
240
241 goto remove;
242 }
243
244 if (start_action(mctx->io, dctx) != 0) {
245 *cause = "delivery";
246 return (1);
247 }
248 if (dctx->blocked) {
249 *blocked = 1;
250 return (0);
251 }
252
253 remove:
254 TAILQ_REMOVE(&mctx->dqueue, dctx, entry);
255 xfree(dctx);
256 return (0);
257 }
258
259 int
260 run_done(struct account *a, int *dropped, int *kept, const char **cause)
261 {
262 struct match_ctx *mctx;
263 struct mail *m;
264 int error = 0;
265 const char *type;
266
267 if (TAILQ_EMPTY(&doneq))
268 return (0);
269
270 mctx = TAILQ_FIRST(&doneq);
271 m = mctx->mail;
272 log_debug3("%s: running done queue", a->name);
273
274 TAILQ_REMOVE(&doneq, mctx, done_entry);
275 ARRAY_FREE(&mctx->stack);
276 xfree(mctx);
277
278 if (mctx->account->fetch->done != NULL) {
279 switch (mctx->mail->decision) {
280 case DECISION_DROP:
281 type = "deleting";
282 (*dropped)++;
283 break;
284 case DECISION_KEEP:
285 type = "keeping";
286 (*kept)++;
287 break;
288 default:
289 fatalx("invalid decision");
290 }
291 log_debug("%s: %s message %u", a->name, type, m->idx);
292
293 if (a->fetch->done(a, m) != FETCH_SUCCESS) {
294 *cause = type;
295 error = 1;
296 }
297 }
298
299 mail_destroy(m);
300 xfree(m);
301
302 return (error);
303 }
304
305 void
306 flush_queue(struct match_queue *mq)
307 {
308 struct match_ctx *mctx;
309 struct mail *m;
310
311 while (!TAILQ_EMPTY(mq)) {
312 mctx = TAILQ_FIRST(mq);
313 m = mctx->mail;
314
315 TAILQ_REMOVE(mq, mctx, done_entry);
316 ARRAY_FREE(&mctx->stack);
317 xfree(mctx);
318
319 mail_destroy(m);
320 xfree(m);
321 }
322 }
323
324 u_int
325 queue_length(struct match_queue *mq)
326 {
327 struct match_ctx *mctx;
328 u_int n;
329
330 n = 0;
331 TAILQ_FOREACH(mctx, mq, entry)
332 n++;
333
334 return (n);
335 }
336
337 int
338 fetch_poll(struct account *a, int blocked, struct ios *ios, struct io **rio)
339 {
340 int timeout;
341 char *cause;
342
343 if (TAILQ_EMPTY(&matchq) && (blocked || TAILQ_EMPTY(&deliverq)))
344 timeout = conf.timeout;
345 else
346 timeout = 0;
347
348 log_debug3("%s: polling %u fds, timeout=%d", a->name, ARRAY_LENGTH(ios),
349 timeout);
350 switch (io_polln(ios->list, ARRAY_LENGTH(ios), rio, timeout, &cause)) {
351 case 0:
352 log_warnx("%s: connection unexpectedly closed", a->name);
353 return (1);
354 case -1:
355 if (errno == EAGAIN)
356 return (0);
357 log_warnx("%s: %s", a->name, cause);
358 xfree(cause);
359 return (1);
360 }
361
362 return (0);
363 }
364
365 int
366 fetch_flush(struct account *a, struct io *pio, int *blocked, int *dropped,
367 int *kept, const char **cause)
368 {
369 while (!TAILQ_EMPTY(&matchq) || !TAILQ_EMPTY(&deliverq)) {
370 if (run_match(a, cause) != 0)
371 return (1);
372 if (run_deliver(a, pio, blocked, cause) != 0)
373 return (1);
374
375 log_debug3("%s: queue lengths: match %u, deliver %u, done %u; "
376 "blocked=%d", a->name, queue_length(&matchq),
377 queue_length(&deliverq), queue_length(&doneq), *blocked);
378
379 if (!TAILQ_EMPTY(&deliverq) && *blocked) {
380 pio->flags &= ~IO_NOWAIT;
381 if (!TAILQ_EMPTY(&matchq))
382 pio->flags |= IO_NOWAIT;
383 switch (io_poll(pio, NULL)) {
384 case 0:
385 fatalx("child: parent socket closed");
386 case -1:
387 if (errno == EAGAIN)
388 break;
389 fatalx("child: parent socket error");
390 }
391 }
392
393 if (run_done(a, dropped, kept, cause) != 0)
394 return (1);
395 }
396
397 while (!TAILQ_EMPTY(&doneq)) {
398 if (run_done(a, dropped, kept, cause) != 0)
399 return (1);
400 }
401
402 return (0);
403 }
404
405 int
406 fetch_account(struct io *pio, struct account *a, struct ios *ios, double tim)
407 {
408 struct mail *m;
409 u_int n, dropped, kept;
410 int error, blocked;
411 const char *cause = NULL;
412 struct match_ctx *mctx;
413 struct io *rio;
414
415 log_debug2("%s: fetching", a->name);
416
417 TAILQ_INIT(&matchq);
418 TAILQ_INIT(&deliverq);
419 TAILQ_INIT(&doneq);
420
421 n = dropped = kept = 0;
422 m = NULL;
423 for (;;) {
424 m = xcalloc(1, sizeof *m);
425 m->body = -1;
426 m->decision = DECISION_DROP;
427 m->done = 0;
428 m->idx = ++a->idx;
429
430 /* fetch a message */
431 error = FETCH_AGAIN;
432 rio = NULL;
433 while (error == FETCH_AGAIN) {
434 log_debug3("%s: queue lengths: match %u, deliver %u, "
435 "done %u; blocked=%d", a->name,
436 queue_length(&matchq), queue_length(&deliverq),
437 queue_length(&doneq), blocked);
438
439 if (rio != pio) {
440 error = a->fetch->fetch(a, m);
441 switch (error) {
442 case FETCH_ERROR:
443 if (rio == pio)
444 fatalx("child: lost parent");
445 cause = "fetching";
446 goto out;
447 case FETCH_COMPLETE:
448 goto out;
449 }
450 }
451 if (error == FETCH_AGAIN) {
452 if (fetch_poll(a, blocked, ios, &rio) != 0)
453 goto out;
454 }
455
456 if (run_match(a, &cause) != 0)
457 goto out;
458 if (run_deliver(a, pio, &blocked, &cause) != 0)
459 goto out;
460 }
461
462 if (error != FETCH_OVERSIZE && error != FETCH_EMPTY) {
463 trim_from(m);
464 if (m->size == 0)
465 error = FETCH_EMPTY;
466 }
467
468 switch (error) {
469 case FETCH_EMPTY:
470 log_warnx("%s: empty message", a->name);
471 cause = "fetching";
472 goto out;
473 case FETCH_OVERSIZE:
474 log_warnx("%s: message too big: %zu bytes (limit %zu)",
475 a->name, m->size, conf.max_size);
476 if (conf.del_big)
477 break;
478 cause = "fetching";
479 goto out;
480 }
481
482 log_debug("%s: got message %u: size %zu, body %zd", a->name,
483 m->idx, m->size, m->body);
484 fetch_transform(a, m);
485
486 /* construct mctx */
487 mctx = xcalloc(1, sizeof *mctx);
488 mctx->io = pio;
489 mctx->account = a;
490 mctx->mail = m;
491 ARRAY_INIT(&mctx->stack);
492 mctx->rule = TAILQ_FIRST(&conf.rules);
493 mctx->matched = mctx->stopped = 0;
494 TAILQ_INIT(&mctx->dqueue);
495 m = NULL; /* clear m to avoid double-free if out later */
496
497 /* and queue it */
498 log_debug3("%s: adding to match queue", a->name);
499 TAILQ_INSERT_TAIL(&matchq, mctx, match_entry);
500
501 /* finish up a done mail */
502 if (run_done(a, &dropped, &kept, &cause) != 0)
503 goto out;
504
505 if (conf.purge_after == 0 || a->fetch->purge == NULL)
506 continue;
507
508 n++;
509 if (n >= conf.purge_after) {
510 log_debug("%s: got %u mails, purging", a->name, n);
511
512 /*
513 * Must empty queues before purge to make sure things
514 * like POP3 indexing don't get ballsed up.
515 */
516 if (fetch_flush(a, pio, &blocked, &dropped, &kept,
517 &cause) != 0)
518 goto out;
519
520 ARRAY_FREE(ios);
521 ARRAY_ADD(ios, pio, struct io *);
522 if (a->fetch->purge(a, ios) != FETCH_SUCCESS) {
523 cause = "purging";
524 goto out;
525 }
526
527 n = 0;
528 }
529 }
530
531 out:
532 if (m != NULL) {
533 mail_destroy(m);
534 xfree(m);
535 }
536
537 if (cause == NULL)
538 fetch_flush(a, pio, &blocked, &dropped, &kept, &cause);
539 if (cause != NULL) {
540 flush_queue(&matchq);
541 flush_queue(&deliverq);
542 flush_queue(&doneq);
543
544 log_warnx("%s: %s error. aborted", a->name, cause);
545 }
546
547 tim = get_time() - tim;
548 n = dropped + kept;
549 if (n > 0) {
550 log_info("%s: %u messages processed (%u kept) in %.3f seconds "
551 "(average %.3f)", a->name, n, kept, tim, tim / n);
552 } else {
553 log_info("%s: %u messages processed in %.3f seconds",
554 a->name, n, tim);
555 }
556
557 return (cause != NULL);
558 }
559
560 int
561 fetch_transform(struct account *a, struct mail *m)
562 {
563 char *hdr, rtm[64], *rnm;
564 u_int lines;
565 size_t len;
566 int error;
567
568 hdr = find_header(m, "message-id", &len, 1);
569 if (hdr == NULL || len == 0 || len > INT_MAX)
570 log_debug2("%s: message-id not found", a->name);
571 else {
572 log_debug2("%s: message-id is: %.*s", a->name, (int) len, hdr);
573 add_tag(&m->tags, "message_id", "%.*s", (int) len, hdr);
574 }
575
576 /*
577 * Insert received header.
578 *
579 * No header line must exceed 998 bytes. Limiting the user-supplied
580 * stuff to 900 bytes gives plenty of space for the other stuff, and if
581 * it gets truncated, who cares?
582 */
583 if (!conf.no_received) {
584 error = 1;
585 if (rfc822_time(time(NULL), rtm, sizeof rtm) != NULL) {
586 rnm = conf.info.fqdn;
587 if (rnm == NULL)
588 rnm = conf.info.host;
589
590 error = insert_header(m, "received",
591 "Received: by %.450s (%s " BUILD ", "
592 "account \"%.450s\");\n\t%s",
593 rnm, __progname, a->name, rtm);
594 }
595 if (error != 0)
596 log_debug3("%s: couldn't add received header", a->name);
597 }
598
599 /* fill wrapped line list */
600 lines = fill_wrapped(m);
601 log_debug2("%s: found %u wrapped lines", a->name, lines);
602
603 return (FETCH_SUCCESS);
604 }
605
606 int
607 fetch_rule(struct match_ctx *mctx, const char **cause)
608 {
609 struct account *a = mctx->account;
610 struct strings *aa;
611 struct mail *m = mctx->mail;
612 struct rule *r = mctx->rule;
613 u_int i;
614 int error;
615 char *tkey, *tvalue;
616
617 /* matching finished */
618 if (m->done) {
619 if (conf.keep_all || a->keep)
620 m->decision = DECISION_KEEP;
621 return (FETCH_COMPLETE);
622 }
623
624 /* end of ruleset reached */
625 if (r == NULL) {
626 switch (conf.impl_act) {
627 case DECISION_NONE:
628 log_warnx("%s: reached end of ruleset. no "
629 "unmatched-mail option; keeping mail", a->name);
630 m->decision = DECISION_KEEP;
631 break;
632 case DECISION_KEEP:
633 log_debug2("%s: reached end of ruleset. keeping mail",
634 a->name);
635 m->decision = DECISION_KEEP;
636 break;
637 case DECISION_DROP:
638 log_debug2("%s: reached end of ruleset. dropping mail",
639 a->name);
640 m->decision = DECISION_DROP;
641 break;
642 }
643 m->done = 1;
644 return (FETCH_SUCCESS);
645 }
646
647 mctx->rule = TAILQ_NEXT(mctx->rule, entry);
648 while (mctx->rule == NULL) {
649 if (ARRAY_EMPTY(&mctx->stack))
650 break;
651 mctx->rule = ARRAY_LAST(&mctx->stack, struct rule *);
652 mctx->rule = TAILQ_NEXT(mctx->rule, entry);
653 ARRAY_TRUNC(&mctx->stack, 1, struct rule *);
654 }
655
656 aa = r->accounts;
657 if (!ARRAY_EMPTY(aa)) {
658 for (i = 0; i < ARRAY_LENGTH(aa); i++) {
659 if (name_match(ARRAY_ITEM(aa, i, char *), a->name))
660 break;
661 }
662 if (i == ARRAY_LENGTH(aa))
663 return (FETCH_SUCCESS);
664 }
665
666 /* match all the regexps */
667 switch (r->type) {
668 case RULE_EXPRESSION:
669 /* combine wrapped lines */
670 set_wrapped(m, ' ');
671
672 /* perform the expression */
673 if ((error = do_expr(r, mctx)) == -1) {
674 *cause = "matching";
675 return (FETCH_ERROR);
676 }
677
678 /* continue if no match */
679 if (!error)
680 return (FETCH_SUCCESS);
681 break;
682 case RULE_ALL:
683 break;
684 }
685
686 /* reset wrapped lines */
687 set_wrapped(m, '\n');
688
689 /* report rule number */
690 if (TAILQ_EMPTY(&r->rules))
691 log_debug2("%s: matched to rule %u", a->name, r->idx);
692 else
693 log_debug2("%s: matched to rule %u (nested)", a->name, r->idx);
694
695 /* deal with nested rules */
696 if (!TAILQ_EMPTY(&r->rules)) {
697 log_debug2("%s: entering nested rules", a->name);
698 ARRAY_ADD(&mctx->stack, r, struct rule *);
699 mctx->rule = TAILQ_FIRST(&r->rules);
700 return (FETCH_SUCCESS);
701 }
702
703 /* tag mail if needed */
704 if (r->key.str != NULL) {
705 tkey = replacestr(&r->key, m->tags, m, &m->rml);
706 tvalue = replacestr(&r->value, m->tags, m, &m->rml);
707
708 if (tkey != NULL && *tkey != '\0' && tvalue != NULL) {
709 log_debug2("%s: tagging message: %s (%s)",
710 a->name, tkey, tvalue);
711 add_tag(&m->tags, tkey, "%s", tvalue);
712 }
713
714 if (tkey != NULL)
715 xfree(tkey);
716 if (tvalue != NULL)
717 xfree(tvalue);
718 }
719
720 /* if this rule is marked as stop, mark the mail as done */
721 if (r->stop)
722 m->done = 1;
723
724 /* handle delivery */
725 if (r->actions != NULL) {
726 log_debug2("%s: delivering message", a->name);
727 mctx->matched = 1;
728 if (do_deliver(r, mctx) != 0) {
729 *cause = "delivery";
730 return (FETCH_ERROR);
731 }
732 return (FETCH_AGAIN);
733 }
734
735 return (FETCH_SUCCESS);
736 }
737
738 int
739 do_expr(struct rule *r, struct match_ctx *mctx)
740 {
741 int fres, cres;
742 struct expritem *ei;
743 char desc[DESCBUFSIZE];
744
745 fres = 0;
746 TAILQ_FOREACH(ei, r->expr, entry) {
747 cres = ei->match->match(mctx, ei);
748 if (cres == MATCH_ERROR)
749 return (-1);
750 cres = cres == MATCH_TRUE;
751 if (ei->inverted)
752 cres = !cres;
753 switch (ei->op) {
754 case OP_NONE:
755 case OP_OR:
756 fres = fres || cres;
757 break;
758 case OP_AND:
759 fres = fres && cres;
760 break;
761 }
762
763 ei->match->desc(ei, desc, sizeof desc);
764 log_debug2("%s: tried %s%s, got %d", mctx->account->name,
765 ei->inverted ? "not " : "", desc, cres);
766 }
767
768 return (fres);
769 }
770
771 int
772 do_deliver(struct rule *r, struct match_ctx *mctx)
773 {
774 struct account *a = mctx->account;
775 struct mail *m = mctx->mail;
776 struct action *t;
777 struct actions *ta;
778 u_int i, j, k;
779 char *s;
780 struct replstr *rs;
781 struct deliver_ctx *dctx;
782 struct strings *users;
783 int should_free;
784
785 for (i = 0; i < ARRAY_LENGTH(r->actions); i++) {
786 rs = &ARRAY_ITEM(r->actions, i, struct replstr);
787 s = replacestr(rs, m->tags, m, &m->rml);
788
789 log_debug2("%s: looking for actions matching: %s", a->name, s);
790 ta = match_actions(s);
791 if (ARRAY_EMPTY(ta))
792 goto empty;
793 xfree(s);
794
795 log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta));
796 for (j = 0; j < ARRAY_LENGTH(ta); j++) {
797 t = ARRAY_ITEM(ta, j, struct action *);
798 users = get_users(mctx, r, t, &should_free);
799
800 for (k = 0; k < ARRAY_LENGTH(users); k++) {
801 dctx = xmalloc(sizeof *dctx);
802 dctx->action = t;
803 dctx->account = a;
804 dctx->rule = r;
805 dctx->mail = m;
806 dctx->uid = ARRAY_ITEM(users, i, uid_t);
807 dctx->blocked = 0;
808
809 TAILQ_INSERT_TAIL(&mctx->dqueue, dctx, entry);
810 }
811
812 if (should_free)
813 ARRAY_FREEALL(users);
814 }
815
816 ARRAY_FREEALL(ta);
817 }
818
819 return (0);
820
821 empty:
822 xfree(s);
823 ARRAY_FREEALL(ta);
824 log_warnx("%s: no actions matching: %s (%s)", a->name, s, rs->str);
825 return (1);
826 }
827
828 struct strings *
829 get_users(struct match_ctx *mctx, struct rule *r, struct action *t,
830 int *should_free)
831 {
832 struct account *a = mctx->account;
833 struct mail *m = mctx->mail;
834 struct strings *users;
835
836 *should_free = 0;
837 users = NULL;
838 if (r->find_uid) { /* rule comes first */
839 *should_free = 1;
840 users = find_users(m);
841 } else if (r->users != NULL) {
842 *should_free = 0;
843 users = r->users;
844 } else if (t->find_uid) { /* then action */
845 *should_free = 1;
846 users = find_users(m);
847 } else if (t->users != NULL) {
848 *should_free = 0;
849 users = t->users;
850 } else if (a->find_uid) { /* then account */
851 *should_free = 1;
852 users = find_users(m);
853 } else if (a->users != NULL) {
854 *should_free = 0;
855 users = a->users;
856 }
857 if (users == NULL) {
858 *should_free = 1;
859 users = xmalloc(sizeof *users);
860 ARRAY_INIT(users);
861 ARRAY_ADD(users, conf.def_user, uid_t);
862 }
863
864 return (users);
865 }
866
867 int
868 start_action(struct io *io, struct deliver_ctx *dctx)
869 {
870 struct account *a = dctx->account;
871 struct action *t = dctx->action;
872 struct mail *m = dctx->mail;
873 struct msg msg;
874
875 if (t->deliver->deliver == NULL)
876 return (0);
877
878 log_debug2("%s: running action %s as user %lu", a->name, t->name,
879 (u_long) dctx->uid);
880 add_tag(&m->tags, "action", "%s", t->name);
881
882 /* just deliver now for in-child delivery */
883 if (t->deliver->type == DELIVER_INCHILD) {
884 dctx->blocked = 0;
885 if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS)
886 return (1);
887 return (0);
888 }
889
890 memset(&msg, 0, sizeof msg);
891 msg.type = MSG_ACTION;
892
893 msg.data.account = a;
894 msg.data.action = t;
895 msg.data.uid = dctx->uid;
896
897 mail_send(m, &msg);
898
899 log_debug3("%s: sending action to parent", a->name);
900 if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0)
901 fatalx("child: privsep_send error");
902 dctx->blocked = 1;
903
904 return (0);
905 }
906
907 int
908 finish_action(struct deliver_ctx *dctx, struct msg *msg, void *buf, size_t len)
909 {
910 struct account *a = dctx->account;
911 struct action *t = dctx->action;
912 struct mail *m = dctx->mail;
913 u_int lines;
914
915 if (buf == NULL || len == 0)
916 fatalx("child: bad tags");
917 strb_destroy(&m->tags);
918 m->tags = buf;
919 update_tags(&m->tags);
920
921 if (msg->data.error != 0)
922 return (1);
923
924 if (t->deliver->type != DELIVER_WRBACK)
925 return (0);
926
927 mail_receive(m, msg);
928 log_debug2("%s: received modified mail: size %zu, body %zd", a->name,
929 m->size, m->body);
930
931 /* trim from line */
932 trim_from(m);
933
934 /* and recreate the wrapped array */
935 lines = fill_wrapped(m);
936 log_debug2("%s: found %u wrapped lines", a->name, lines);
937
938 return (0);
939 }
1616 */
1717
1818 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/wait.h>
19 #include <sys/socket.h>
2220
23 #include <fcntl.h>
24 #include <fnmatch.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <time.h>
2821 #include <unistd.h>
2922
3023 #include "fdm.h"
31 #include "deliver.h"
32 #include "fetch.h"
33 #include "match.h"
34
35 int poll_account(struct io *, struct account *);
36 int fetch_account(struct io *, struct account *, double);
37 int fetch_transform(struct account *, struct mail *);
38
39 int run_done(struct match_queue *, int *, int *, const char **);
40 void flush_done(struct match_queue *);
41 int run_active(struct match_queue *, struct match_queue *, const char **);
42 void flush_active(struct match_queue *);
43
44 int fetch_rule(struct match_ctx *, const char **);
45
46 int do_expr(struct rule *, struct match_ctx *);
47 int do_deliver(struct rule *, struct match_ctx *);
48 int do_action(struct rule *, struct match_ctx *, struct action *);
49 int do_rules(struct match_ctx *, struct rules *, const char **);
5024
5125 void child_sighandler(int);
5226
9872 _exit(status);
9973 }
10074
101 int
102 do_child(int fd, enum fdmop op, struct account *a)
75 struct child *
76 child_start(struct children *children, uid_t uid, int (*start)(struct child *,
77 struct io *), int (*msg)(struct child *, struct msg *, void *, size_t),
78 void *data)
10379 {
80 struct child *child, *childp;
81 int fds[2], n;
82 u_int i;
10483 struct io *io;
105 struct msg msg;
106 int error = 1;
107 double tim;
10884
109 #ifdef DEBUG
110 xmalloc_clear();
111 COUNTFDS(a->name);
85 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) != 0)
86 fatal("socketpair");
87
88 child = xcalloc(1, sizeof *child);
89 child->io = io_create(fds[0], NULL, IO_CRLF, INFTIM);
90 child->data = data;
91 child->msg = msg;
92
93 if ((child->pid = child_fork()) == 0) {
94 for (i = 0; i < ARRAY_LENGTH(children); i++) {
95 childp = ARRAY_ITEM(children, i, struct child *);
96 io_close(childp->io);
97 io_free(childp->io);
98 xfree(childp);
99 }
100 io_close(child->io);
101 io_free(child->io);
102
103 if (geteuid() == 0) {
104 if (dropto(uid) != 0)
105 fatal("dropto");
106 }
107
108 io = io_create(fds[1], NULL, IO_LF, INFTIM);
109 n = start(child, io);
110 #ifdef PROFILE
111 /*
112 * We want to use _exit rather than exit in the child process,
113 * and it doesn't run atexit handlers, so run _mcleanup
114 * manually to force the profiler to dump its statistics.
115 *
116 * This is icky but it works, and is only for profiling.
117 */
118 extern void _mcleanup(void);
119 _mcleanup();
112120 #endif
121 child_exit(n);
122 }
123 close(fds[1]);
113124
114 io = io_create(fd, NULL, IO_LF, INFTIM);
115 log_debug2("%s: started, pid %ld", a->name, (long) getpid());
116
117 if (geteuid() != 0) {
118 log_debug2("%s: not root. not dropping privileges", a->name);
119 } else {
120 log_debug2("%s: changing to user %lu", a->name,
121 (u_long) conf.child_uid);
122 if (dropto(conf.child_uid) != 0)
123 fatal("dropto");
124 }
125 #ifndef NO_SETPROCTITLE
126 setproctitle("child: %s", a->name);
127 #endif
128
129 if (op == FDMOP_POLL && a->fetch->poll == NULL) {
130 log_info("%s: polling not supported", a->name);
131 goto out;
132 } else if (op == FDMOP_FETCH && a->fetch->fetch == NULL) {
133 log_info("%s: fetching not supported", a->name);
134 goto out;
135 }
136 tim = get_time();
137
138 /* start fetch */
139 if (a->fetch->start != NULL && a->fetch->start(a) != FETCH_SUCCESS) {
140 log_warnx("%s: start error. aborting", a->name);
141 goto out;
142 }
143
144 /* process fetch */
145 log_debug2("%s: started processing", a->name);
146 switch (op) {
147 case FDMOP_POLL:
148 error = poll_account(io, a);
149 break;
150 case FDMOP_FETCH:
151 error = fetch_account(io, a, tim);
152 break;
153 default:
154 fatalx("child: unexpected command");
155 }
156 log_debug2("%s: finished processing. exiting", a->name);
157
158 out:
159 /* finish fetch */
160 if (a->fetch->finish != NULL && a->fetch->finish(a) != FETCH_SUCCESS)
161 error = 1;
162
163 memset(&msg, 0, sizeof msg);
164 msg.type = MSG_EXIT;
165 log_debug3("%s: sending exit message to parent", a->name);
166 if (privsep_send(io, &msg, NULL, 0) != 0)
167 fatalx("child: privsep_send error");
168 log_debug3("%s: waiting for exit message from parent", a->name);
169 if (privsep_recv(io, &msg, NULL, 0) != 0)
170 fatalx("child: privsep_recv error");
171 if (msg.type != MSG_EXIT)
172 fatalx("child: unexpected message");
173
174 io_close(io);
175 io_free(io);
176
177 #ifdef DEBUG
178 COUNTFDS(a->name);
179 xmalloc_report(a->name);
180 #endif
181
182 return (error);
125 ARRAY_ADD(children, child, struct child *);
126 return (child);
183127 }
184128
185 int
186 poll_account(unused struct io *io, struct account *a)
187 {
188 u_int n;
189129
190 log_debug2("%s: polling", a->name);
191
192 if (a->fetch->poll(a, &n) == FETCH_ERROR) {
193 log_warnx("%s: polling error. aborted", a->name);
194 return (1);
195 }
196
197 log_info("%s: %u messages found", a->name, n);
198
199 return (0);
200 }
201
202 int
203 run_done(struct match_queue *dq, int *dropped, int *kept, const char **cause)
204 {
205 struct match_ctx *mctx;
206 struct account *a;
207 struct mail *m;
208 int error = 0;
209 const char *type;
210
211 if (TAILQ_EMPTY(dq))
212 return (0);
213
214 mctx = TAILQ_FIRST(dq);
215 a = mctx->account;
216 m = mctx->mail;
217 log_debug3("%s: running done queue", a->name);
218
219 TAILQ_REMOVE(dq, mctx, entry);
220 ARRAY_FREE(&mctx->stack);
221 xfree(mctx);
222
223 if (mctx->account->fetch->done != NULL) {
224 switch (mctx->mail->decision) {
225 case DECISION_DROP:
226 type = "deleting";
227 (*dropped)++;
228 break;
229 case DECISION_KEEP:
230 type = "keeping";
231 (*kept)++;
232 break;
233 default:
234 fatalx("invalid decision");
235 }
236 log_debug2("%s: %s message", a->name, type);
237
238 if (a->fetch->done(a, m) != FETCH_SUCCESS) {
239 *cause = type;
240 error = 1;
241 }
242 }
243
244 mail_destroy(m);
245 xfree(m);
246
247 return (error);
248 }
249
250 void
251 flush_done(struct match_queue *dq)
252 {
253 struct match_ctx *mctx;
254 struct mail *m;
255
256 while (!TAILQ_EMPTY(dq)) {
257 mctx = TAILQ_FIRST(dq);
258 m = mctx->mail;
259
260 TAILQ_REMOVE(dq, mctx, entry);
261 ARRAY_FREE(&mctx->stack);
262 xfree(mctx);
263
264 mail_destroy(m);
265 xfree(m);
266 }
267 }
268
269 int
270 run_active(struct match_queue *aq, struct match_queue *dq, const char **cause)
271 {
272 struct match_ctx *mctx;
273 struct account *a;
274
275 if (TAILQ_EMPTY(aq))
276 return (0);
277
278 mctx = TAILQ_FIRST(aq);
279 a = mctx->account;
280 log_debug3("%s: running active queue", a->name);
281
282 switch (fetch_rule(mctx, cause)) {
283 case FETCH_ERROR:
284 return (1);
285 case FETCH_COMPLETE:
286 TAILQ_REMOVE(aq, mctx, entry);
287 TAILQ_INSERT_TAIL(dq, mctx, entry);
288 break;
289 }
290
291 return (0);
292 }
293
294 void
295 flush_active(struct match_queue *aq)
296 {
297 struct match_ctx *mctx;
298 struct mail *m;
299
300 while (!TAILQ_EMPTY(aq)) {
301 mctx = TAILQ_FIRST(aq);
302 m = mctx->mail;
303
304 TAILQ_REMOVE(aq, mctx, entry);
305 ARRAY_FREE(&mctx->stack);
306 xfree(mctx);
307
308 mail_destroy(m);
309 xfree(m);
310 }
311 }
312
313 int
314 fetch_account(struct io *io, struct account *a, double tim)
315 {
316 struct mail *m;
317 u_int n, dropped, kept;
318 int error;
319 const char *cause = NULL;
320 struct match_queue activeq;
321 struct match_queue doneq;
322 struct match_ctx *mctx;
323
324 log_debug2("%s: fetching", a->name);
325
326 TAILQ_INIT(&activeq);
327 TAILQ_INIT(&doneq);
328
329 n = dropped = kept = 0;
330 for (;;) {
331 m = xcalloc(1, sizeof *m);
332 m->body = -1;
333 m->decision = DECISION_DROP;
334
335 /* fetch a message */
336 error = FETCH_AGAIN;
337 while (error == FETCH_AGAIN) {
338 if (TAILQ_EMPTY(&activeq)) {
339 log_debug3("%s: queue empty", a->name);
340 error = a->fetch->fetch(a, m, 0);
341 } else {
342 log_debug3("%s: queue non-empty", a->name);
343 error = a->fetch->fetch(a, m, FETCH_NOWAIT);
344 }
345 switch (error) {
346 case FETCH_ERROR:
347 cause = "fetching";
348 goto out;
349 case FETCH_COMPLETE:
350 goto out;
351 }
352
353 if (run_active(&activeq, &doneq, &cause) != 0)
354 goto out;
355 }
356
357 if (error != FETCH_OVERSIZE && error != FETCH_EMPTY) {
358 trim_from(m);
359 if (m->size == 0)
360 error = FETCH_EMPTY;
361 }
362
363 switch (error) {
364 case FETCH_EMPTY:
365 log_warnx("%s: empty message", a->name);
366 cause = "fetching";
367 goto out;
368 case FETCH_OVERSIZE:
369 log_warnx("%s: message too big: %zu bytes (limit %zu)",
370 a->name, m->size, conf.max_size);
371 if (conf.del_big)
372 break;
373 cause = "fetching";
374 goto out;
375 }
376
377 log_debug("%s: got message: size %zu, body %zd", a->name,
378 m->size, m->body);
379 fetch_transform(a, m);
380
381 /* construct mctx */
382 mctx = xcalloc(1, sizeof *mctx);
383 mctx->io = io;
384 mctx->account = a;
385 mctx->mail = m;
386 ARRAY_INIT(&mctx->stack);
387 mctx->rule = TAILQ_FIRST(&conf.rules);
388 mctx->matched = mctx->stopped = 0;
389 m = NULL; /* clear m to avoid double-free if out later */
390
391 /* and queue it */
392 TAILQ_INSERT_TAIL(&activeq, mctx, entry);
393
394 /* finish up a done mail */
395 if (run_done(&doneq, &dropped, &kept, &cause) != 0)
396 goto out;
397
398 if (conf.purge_after > 0 && a->fetch->purge != NULL) {
399 n++;
400 if (n >= conf.purge_after) {
401 log_debug("%s: %u mails, purging", a->name, n);
402
403 /*
404 * Must empty queues before purge to make sure
405 * eg POP3 indexing doesn't get ballsed up.
406 */
407 while (!TAILQ_EMPTY(&activeq)) {
408 if (run_active(&activeq, &doneq,
409 &cause) != 0)
410 break;
411 }
412 while (!TAILQ_EMPTY(&doneq)) {
413 if (run_done(&doneq, &dropped, &kept,
414 &cause) != 0)
415 break;
416 }
417
418 if (a->fetch->purge(a) != FETCH_SUCCESS) {
419 cause = "purging";
420 goto out;
421 }
422 n = 0;
423 }
424 }
425 }
426
427 out:
428 if (m != NULL) {
429 mail_destroy(m);
430 xfree(m);
431 }
432 if (cause == NULL) {
433 while (!TAILQ_EMPTY(&activeq)) {
434 if (run_active(&activeq, &doneq, &cause) != 0)
435 break;
436 }
437 while (!TAILQ_EMPTY(&doneq)) {
438 if (run_done(&doneq, &dropped, &kept, &cause) != 0)
439 break;
440 }
441 }
442 if (cause != NULL) {
443 flush_active(&activeq);
444 flush_done(&doneq);
445 }
446 if (cause != NULL)
447 log_warnx("%s: %s error. aborted", a->name, cause);
448
449 tim = get_time() - tim;
450 n = dropped + kept;
451 if (n > 0) {
452 log_info("%s: %u messages processed (%u kept) in %.3f seconds "
453 "(average %.3f)", a->name, n, kept, tim, tim / n);
454 } else {
455 log_info("%s: %u messages processed in %.3f seconds",
456 a->name, n, tim);
457 }
458
459 return (cause != NULL);
460 }
461
462 int
463 fetch_transform(struct account *a, struct mail *m)
464 {
465 char *hdr, rtm[64], *rnm;
466 u_int lines;
467 size_t len;
468 int error;
469
470 hdr = find_header(m, "message-id", &len, 1);
471 if (hdr == NULL || len == 0 || len > INT_MAX)
472 log_debug2("%s: message-id not found", a->name);
473 else {
474 log_debug2("%s: message-id is: %.*s", a->name, (int) len, hdr);
475 add_tag(&m->tags, "message_id", "%.*s", (int) len, hdr);
476 }
477
478 /*
479 * Insert received header.
480 *
481 * No header line must exceed 998 bytes. Limiting the user-supplied
482 * stuff to 900 bytes gives plenty of space for the other stuff, and if
483 * it gets truncated, who cares?
484 */
485 if (!conf.no_received) {
486 error = 1;
487 if (rfc822_time(time(NULL), rtm, sizeof rtm) != NULL) {
488 rnm = conf.info.fqdn;
489 if (rnm == NULL)
490 rnm = conf.info.host;
491
492 error = insert_header(m, "received",
493 "Received: by %.450s (%s " BUILD ", "
494 "account \"%.450s\");\n\t%s",
495 rnm, __progname, a->name, rtm);
496 }
497 if (error != 0)
498 log_debug3("%s: couldn't add received header", a->name);
499 }
500
501 /* fill wrapped line list */
502 lines = fill_wrapped(m);
503 log_debug2("%s: found %u wrapped lines", a->name, lines);
504
505 return (FETCH_SUCCESS);
506 }
507
508 int
509 fetch_rule(struct match_ctx *mctx, const char **cause)
510 {
511 struct account *a = mctx->account;
512 struct strings *aa;
513 struct mail *m = mctx->mail;
514 struct rule *r = mctx->rule;
515 u_int i;
516 int error;
517 char *tkey, *tvalue;
518
519 if (r == NULL) {
520 switch (conf.impl_act) {
521 case DECISION_NONE:
522 log_warnx("%s: reached end of ruleset. no "
523 "unmatched-mail option; keeping mail", a->name);
524 m->decision = DECISION_KEEP;
525 break;
526 case DECISION_KEEP:
527 log_debug2("%s: reached end of ruleset. keeping mail",
528 a->name);
529 m->decision = DECISION_KEEP;
530 break;
531 case DECISION_DROP:
532 log_debug2("%s: reached end of ruleset. dropping mail",
533 a->name);
534 m->decision = DECISION_DROP;
535 break;
536 }
537 goto done;
538 }
539
540 mctx->rule = TAILQ_NEXT(mctx->rule, entry);
541 while (mctx->rule == NULL) {
542 if (ARRAY_EMPTY(&mctx->stack))
543 break;
544 mctx->rule = ARRAY_LAST(&mctx->stack, struct rule *);
545 mctx->rule = TAILQ_NEXT(mctx->rule, entry);
546 ARRAY_TRUNC(&mctx->stack, 1, struct rule *);
547 }
548
549 aa = r->accounts;
550 if (!ARRAY_EMPTY(aa)) {
551 for (i = 0; i < ARRAY_LENGTH(aa); i++) {
552 if (name_match(ARRAY_ITEM(aa, i, char *), a->name))
553 break;
554 }
555 if (i == ARRAY_LENGTH(aa))
556 return (FETCH_SUCCESS);
557 }
558
559 /* match all the regexps */
560 switch (r->type) {
561 case RULE_EXPRESSION:
562 /* combine wrapped lines */
563 set_wrapped(m, ' ');
564
565 /* perform the expression */
566 if ((error = do_expr(r, mctx)) == -1) {
567 *cause = "matching";
568 return (FETCH_ERROR);
569 }
570
571 /* continue if no match */
572 if (!error)
573 return (FETCH_SUCCESS);
574 break;
575 case RULE_ALL:
576 break;
577 }
578
579 /* reset wrapped lines */
580 set_wrapped(m, '\n');
581
582 /* report rule number */
583 if (TAILQ_EMPTY(&r->rules))
584 log_debug2("%s: matched to rule %u", a->name, r->idx);
585 else
586 log_debug2("%s: matched to rule %u (nested)", a->name, r->idx);
587
588 /* tag mail if needed */
589 if (r->key.str != NULL) {
590 tkey = replacestr(&r->key, m->tags, m, &m->rml);
591 tvalue = replacestr(&r->value, m->tags, m, &m->rml);
592
593 if (tkey != NULL && *tkey != '\0' && tvalue != NULL) {
594 log_debug2("%s: tagging message: %s (%s)",
595 a->name, tkey, tvalue);
596 add_tag(&m->tags, tkey, "%s", tvalue);
597 }
598
599 if (tkey != NULL)
600 xfree(tkey);
601 if (tvalue != NULL)
602 xfree(tvalue);
603 }
604
605 /* handle delivery */
606 if (r->actions != NULL) {
607 log_debug2("%s: delivering message", a->name);
608 mctx->matched = 1;
609 if (do_deliver(r, mctx) != 0) {
610 *cause = "delivery";
611 return (FETCH_ERROR);
612 }
613 }
614
615 /* deal with nested rules */
616 if (!TAILQ_EMPTY(&r->rules)) {
617 log_debug2("%s: entering nested rules", a->name);
618 ARRAY_ADD(&mctx->stack, r, struct rule *);
619 mctx->rule = TAILQ_FIRST(&r->rules);
620 return (FETCH_SUCCESS);
621 }
622
623 /* if this rule is marked as stop, stop checking now */
624 if (r->stop)
625 goto done;
626 return (FETCH_SUCCESS);
627
628 done:
629 if (conf.keep_all || a->keep)
630 m->decision = DECISION_KEEP;
631 return (FETCH_COMPLETE);
632 }
633
634 int
635 do_expr(struct rule *r, struct match_ctx *mctx)
636 {
637 int fres, cres;
638 struct expritem *ei;
639 char desc[DESCBUFSIZE];
640
641 fres = 0;
642 TAILQ_FOREACH(ei, r->expr, entry) {
643 cres = ei->match->match(mctx, ei);
644 if (cres == MATCH_ERROR)
645 return (-1);
646 cres = cres == MATCH_TRUE;
647 if (ei->inverted)
648 cres = !cres;
649 switch (ei->op) {
650 case OP_NONE:
651 case OP_OR:
652 fres = fres || cres;
653 break;
654 case OP_AND:
655 fres = fres && cres;
656 break;
657 }
658
659 ei->match->desc(ei, desc, sizeof desc);
660 log_debug2("%s: tried %s%s, got %d", mctx->account->name,
661 ei->inverted ? "not " : "", desc, cres);
662 }
663
664 return (fres);
665 }
666
667 int
668 do_deliver(struct rule *r, struct match_ctx *mctx)
669 {
670 struct account *a = mctx->account;
671 struct mail *m = mctx->mail;
672 struct action *t;
673 struct actions *ta;
674 u_int i, j;
675 char *s;
676 struct replstr *rs;
677
678 if (r->actions == NULL)
679 return (0);
680
681 for (i = 0; i < ARRAY_LENGTH(r->actions); i++) {
682 rs = &ARRAY_ITEM(r->actions, i, struct replstr);
683 s = replacestr(rs, m->tags, m, &m->rml);
684
685 log_debug2("%s: looking for actions matching: %s", a->name, s);
686 ta = match_actions(s);
687 if (ARRAY_EMPTY(ta)) {
688 log_warnx("%s: no actions matching: %s (was %s)",
689 a->name, s, rs->str);
690 xfree(s);
691 ARRAY_FREEALL(ta);
692 return (1);
693 }
694 xfree(s);
695
696 log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta));
697 for (j = 0; j < ARRAY_LENGTH(ta); j++) {
698 t = ARRAY_ITEM(ta, j, struct action *);
699 log_debug2("%s: action %s", a->name, t->name);
700 if (do_action(r, mctx, t) != 0) {
701 ARRAY_FREEALL(ta);
702 return (1);
703 }
704 }
705
706 ARRAY_FREEALL(ta);
707 }
708
709 return (0);
710 }
711
712 int
713 do_action(struct rule *r, struct match_ctx *mctx, struct action *t)
714 {
715 struct account *a = mctx->account;
716 struct mail *m = mctx->mail;
717 struct msg msg;
718 struct deliver_ctx dctx;
719 u_int i, l;
720 int find;
721 struct strings *users;
722 void *buf;
723 size_t len;
724
725 if (t->deliver->deliver == NULL)
726 return (0);
727 add_tag(&m->tags, "action", "%s", t->name);
728
729 /* just deliver now for in-child delivery */
730 if (t->deliver->type == DELIVER_INCHILD) {
731 memset(&dctx, 0, sizeof dctx);
732 dctx.account = a;
733 dctx.mail = m;
734
735 if (t->deliver->deliver(&dctx, t) != DELIVER_SUCCESS)
736 return (1);
737 return (0);
738 }
739
740 /* figure out the users to use */
741 find = 0;
742 users = NULL;
743 if (r->find_uid) { /* rule comes first */
744 find = 1;
745 users = find_users(m);
746 } else if (r->users != NULL) {
747 find = 0;
748 users = r->users;
749 } else if (t->find_uid) { /* then action */
750 find = 1;
751 users = find_users(m);
752 } else if (t->users != NULL) {
753 find = 0;
754 users = t->users;
755 } else if (a->find_uid) { /* then account */
756 find = 1;
757 users = find_users(m);
758 } else if (a->users != NULL) {
759 find = 0;
760 users = a->users;
761 }
762 if (users == NULL) {
763 find = 1;
764 users = xmalloc(sizeof *users);
765 ARRAY_INIT(users);
766 ARRAY_ADD(users, conf.def_user, uid_t);
767 }
768
769 for (i = 0; i < ARRAY_LENGTH(users); i++) {
770 memset(&msg, 0, sizeof msg);
771 msg.type = MSG_ACTION;
772 msg.data.account = a;
773 msg.data.action = t;
774 msg.data.uid = ARRAY_ITEM(users, i, uid_t);
775
776 mail_send(m, &msg);
777
778 if (privsep_send(mctx->io, &msg, m->tags,
779 STRB_SIZE(m->tags)) != 0)
780 fatalx("child: privsep_send error");
781
782 if (privsep_recv(mctx->io, &msg, &buf, &len) != 0)
783 fatalx("child: privsep_recv error");
784 if (msg.type != MSG_DONE)
785 fatalx("child: unexpected message");
786
787 if (buf == NULL || len == 0)
788 fatalx("child: bad tags");
789 strb_destroy(&m->tags);
790 m->tags = buf;
791 update_tags(&m->tags);
792
793 if (msg.data.error != 0) {
794 ARRAY_FREEALL(users);
795 return (1);
796 }
797
798 if (t->deliver->type != DELIVER_WRBACK) {
799 /* check everything that should be is the same */
800 if (m->size != msg.data.mail.size ||
801 m->body != msg.data.mail.body)
802 fatalx("child: corrupted message");
803 continue;
804 }
805
806 mail_receive(m, &msg);
807 log_debug2("%s: received modified mail: size %zu, body %zd",
808 a->name, m->size, m->body);
809
810 /* trim from line */
811 trim_from(m);
812
813 /* and recreate the wrapped array */
814 l = fill_wrapped(m);
815 log_debug2("%s: found %u wrapped lines", a->name, l);
816 }
817
818 if (find)
819 ARRAY_FREEALL(users);
820
821 return (0);
822 }
7474 int exists, fd = -1, fd2;
7575 int res = DELIVER_FAILURE;
7676 gzFile gzf = NULL;
77 u_int n;
7778
7879 path = replacepath(&data->path, m->tags, m, &m->rml);
7980 if (path == NULL || *path == '\0') {
105106 goto out;
106107 }
107108
109 n = 0;
108110 do {
109111 fd = openlock(path, conf.lock_types, O_CREAT|O_WRONLY|O_APPEND,
110112 FILEMODE);
111113 if (fd < 0) {
112114 if (errno == EAGAIN) {
113 log_warnx("%s: %s: couldn't obtain lock. "
114 "sleeping", a->name, path);
115 sleep(LOCKSLEEPTIME);
115 usleep(LOCKSLEEPTIME);
116 n++;
117 if (n >= LOCKRETRIES) {
118 log_warnx("%s: %s: couldn't obtain lock"
119 " in %.2f seconds", a->name, path,
120 (LOCKSLEEPTIME * n) / 1000000.0);
121 goto out;
122 }
116123 } else {
117124 log_warn("%s: %s: open", a->name, path);
118125 goto out;
2424
2525 /* Deliver context. */
2626 struct deliver_ctx {
27 struct action *action;
28 struct rule *rule;
29
2730 struct account *account;
2831 struct mail *mail;
2932
33 uid_t uid;
34
3035 struct mail wr_mail;
36
37 int blocked; /* blocked waiting for parent */
38 TAILQ_ENTRY(deliver_ctx) entry;
3139 };
40 TAILQ_HEAD(deliver_queue, deliver_ctx);
3241
3342 /* Delivery types. */
3443 enum delivertype {
+39
-52
fdm.c less more
263263 int
264264 main(int argc, char **argv)
265265 {
266 int opt, fds[2], lockfd, status, res;
266 int opt, lockfd, status, res;
267267 u_int i;
268268 enum fdmop op = FDMOP_NONE;
269269 const char *errstr, *proxy = NULL, *s;
275275 time_t tt;
276276 struct account *a;
277277 pid_t pid;
278 struct children children;
278 struct children children, dead_children;
279279 struct child *child;
280 void *buf;
281 size_t len;
280282 struct io **ios, *io;
281283 double tim;
282284 struct sigaction act;
283285 struct msg msg;
284286 size_t off;
285287 struct macro *macro;
288 struct child_fetch_data *cfd;
286289 #ifdef DEBUG
287290 struct rule *r;
288291 struct action *t;
668671
669672 /* start the children and build the array */
670673 ARRAY_INIT(&children);
674 ARRAY_INIT(&dead_children);
675
671676 child = NULL;
672677 TAILQ_FOREACH(a, &conf.accounts, entry) {
673678 if (!use_account(a, NULL))
674679 continue;
675680
676 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) != 0)
677 fatal("socketpair");
678 if ((pid = child_fork()) == 0) {
679 for (i = 0; i < ARRAY_LENGTH(&children); i++) {
680 child = ARRAY_ITEM(&children, i,struct child *);
681 io_close(child->io);
682 io_free(child->io);
683 xfree(child);
684 }
685 close(fds[0]);
686
687 res = do_child(fds[1], op, a);
688 #ifdef PROFILE
689 /*
690 * We want to use _exit rather than exit in the child
691 * process, and it doesn't run atexit handlers, so run
692 * _mcleanup manually to force the profiler to dump its
693 * statistics.
694 *
695 * This is icky but it works, and is only for profiling.
696 */
697 extern void _mcleanup(void);
698 _mcleanup();
699 #endif
700 child_exit(res);
701 }
702
703 log_debug2("parent: child %ld (%s) started", (long) pid,
681 cfd = xmalloc(sizeof *cfd);
682 cfd->account = a;
683 cfd->op = op;
684 cfd->children = &children;
685 child = child_start(&children, conf.child_uid, child_fetch,
686 parent_fetch, cfd);
687
688 log_debug2("parent: child %ld (%s) started", (long) child->pid,
704689 a->name);
705
706 child = xcalloc(1, sizeof *child);
707 ARRAY_ADD(&children, child, struct child *);
708 close(fds[1]);
709 child->io = io_create(fds[0], NULL, IO_CRLF, INFTIM);
710 child->pid = pid;
711 child->account = a;
712690 }
713691
714692 if (ARRAY_EMPTY(&children)) {
724702 tim = get_time();
725703
726704 res = 0;
727 ios = xcalloc(ARRAY_LENGTH(&children), sizeof (struct io *));
705 ios = NULL;
728706 while (!ARRAY_EMPTY(&children)) {
729707 if (sigint || sigterm)
730708 break;
731709
732710 /* fill the io list */
711 ios = xrealloc(ios, ARRAY_LENGTH(&children), sizeof **ios);
733712 for (i = 0; i < ARRAY_LENGTH(&children); i++) {
734713 child = ARRAY_ITEM(&children, i, struct child *);
735714 ios[i] = child->io;
755734 break;
756735
757736 /* and handle them if necessary */
758 if (do_parent(child) == 0)
737 if (privsep_recv(child->io, &msg, &buf, &len) != 0)
738 fatalx("parent: privsep_recv error");
739 if (child->msg(child, &msg, buf, len) == 0)
759740 continue;
760741
761742 /* child has said it is ready to exit, tell it to */
769750 fatal("waitpid");
770751 if (WIFSIGNALED(status)) {
771752 res = 1;
772 log_debug2("parent: child %ld (%s) done, got"
773 "signal %d", (long) child->pid,
774 child->account->name, WTERMSIG(status));
753 log_debug2("parent: child %ld got signal %d",
754 (long) child->pid, WTERMSIG(status));
775755 } else if (!WIFEXITED(status)) {
776756 res = 1;
777 log_debug2("parent: child %ld (%s) done, didn't"
778 "exit normally", (long) child->pid,
779 child->account->name);
757 log_debug2("parent: child %ld didn't exit"
758 "normally", (long) child->pid);
780759 } else {
781760 if (WEXITSTATUS(status) != 0)
782761 res = 1;
783 log_debug2("parent: child %ld (%s) done, "
784 "returned %d", (long) child->pid,
785 child->account->name, WEXITSTATUS(status));
762 log_debug2("parent: child %ld returned %d",
763 (long) child->pid, WEXITSTATUS(status));
786764 }
787765
766 io_close(child->io);
767
788768 ARRAY_REMOVE(&children, i, struct child *);
789
790 io_close(child->io);
791 io_free(child->io);
792 xfree(child);
769 ARRAY_ADD(&dead_children, child, struct child *);
793770 }
794771 }
795772 xfree(ios);
773
774 /* free the dead children */
775 for (i = 0; i < ARRAY_LENGTH(&dead_children); i++) {
776 child = ARRAY_ITEM(&dead_children, i, struct child *);
777 io_free(child->io);
778 if (child->data != NULL)
779 xfree(child->data);
780 xfree(child);
781 }
782 ARRAY_FREE(&dead_children);
796783
797784 if (sigint || sigterm) {
798785 act.sa_handler = SIG_IGN;
+86
-37
fdm.h less more
4444 #define MAXMAILSIZE INT_MAX
4545 #define DEFMAILSIZE (1 * 1024 * 1024 * 1024) /* 1 GB */
4646 #define DEFTIMEOUT (900 * 1000)
47 #define LOCKSLEEPTIME 2
47 #define LOCKSLEEPTIME 10000
48 #define LOCKRETRIES 1000
4849 #define MAXNAMESIZE 64
4950 #define DEFUMASK (S_IRWXG|S_IRWXO)
5051 #define FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
351352
352353 /* A single mail. */
353354 struct mail {
355 u_int idx;
356
354357 struct strb *tags;
355358
356359 struct shm shm;
370373
371374 ssize_t body; /* offset of body */
372375
376 /* XXX move below into special struct and just cp it in mail_*? */
373377 struct rmlist rml; /* regexp matches */
378
379 int done; /* mail is finished with */
374380 enum decision decision; /* final deliver decision */
375381
376382 void (*auxfree)(void *);
392398 TAILQ_HEAD(, attach) children;
393399
394400 TAILQ_ENTRY(attach) entry;
401 };
402
403 /* Privsep message types. */
404 enum msgtype {
405 MSG_ACTION,
406 MSG_EXIT,
407 MSG_DONE,
408 MSG_COMMAND
409 };
410
411 /* Privsep message data. */
412 struct msgdata {
413 int error;
414 struct mail mail;
415
416 /* these only work so long as they aren't moved in either process */
417 struct account *account;
418 struct action *action;
419 struct match_command_data *cmddata;
420
421 uid_t uid;
422 };
423
424 /* Privsep message. */
425 struct msg {
426 enum msgtype type;
427 size_t size;
428
429 struct msgdata data;
395430 };
396431
397432 /* A single child. */
398433 struct child {
399434 pid_t pid;
400435 struct io *io;
401 struct account *account;
436
437 void *data;
438 int (*msg)(struct child *, struct msg *, void *, size_t);
402439 };
403440
404441 /* List of children. */
405442 ARRAY_DECL(children, struct child *);
406443
444 /* Fetch child data. */
445 struct child_fetch_data {
446 struct account *account;
447 enum fdmop op;
448 struct children *children;
449 };
450
451 /* Deliver child data. */
452 struct child_deliver_data {
453 void (*hook)(int, struct account *, struct msg *,
454 struct child_deliver_data *, int *);
455 struct child *child; /* the source of the request */
456 u_int msgid;
457 const char *name;
458 struct account *account;
459 struct action *action;
460 struct deliver_ctx *dctx;
461 struct mail *mail;
462 struct match_ctx *mctx;
463 struct match_command_data *cmddata;
464 };
465
407466 /* Account entry. */
408467 struct account {
468 u_int idx;
469
409470 char name[MAXNAMESIZE];
410471
411472 struct strings *users;
615676 int timeout;
616677 const char *eol;
617678 };
679 ARRAY_DECL(ios, struct io *);
618680
619681 /* Command flags. */
620682 #define CMD_IN 0x1
631693 struct io *io_in;
632694 struct io *io_out;
633695 struct io *io_err;
634 };
635
636 /* Privsep message types. */
637 enum msgtype {
638 MSG_ACTION,
639 MSG_EXIT,
640 MSG_DONE,
641 MSG_COMMAND
642 };
643
644 /* Privsep message data. */
645 struct msgdata {
646 int error;
647 struct mail mail;
648
649 /* these only work so long as they aren't moved in either process */
650 struct account *account;
651 struct action *action;
652 struct match_command_data *cmddata;
653
654 uid_t uid;
655 };
656
657 /* Privsep message. */
658 struct msg {
659 u_int n;
660
661 enum msgtype type;
662 size_t size;
663
664 struct msgdata data;
665696 };
666697
667698 /* Comparison operators. */
762793 /* child.c */
763794 int child_fork(void);
764795 __dead void child_exit(int);
765 int do_child(int, enum fdmop, struct account *);
766
767 /* parent.c */
768 int do_parent(struct child *);
796 struct child *child_start(struct children *, uid_t,
797 int (*)(struct child *, struct io *),
798 int (*)(struct child *, struct msg *, void *,
799 size_t), void *);
800
801 /* child-fetch.c */
802 int child_fetch(struct child *, struct io *);
803
804 /* child-deliver.c */
805 int child_deliver(struct child *, struct io *);
806 void child_deliver_action_hook(int, struct account *,
807 struct msg *, struct child_deliver_data *, int *);
808 void child_deliver_cmd_hook(int, struct account *,
809 struct msg *, struct child_deliver_data *, int *);
810
811 /* parent-fetch.c */
812 int parent_fetch(struct child *, struct msg *, void *,
813 size_t);
814
815 /* parent-deliver.c */
816 int parent_deliver(struct child *, struct msg *, void *,
817 size_t);
769818
770819 /* connect.c */
771820 struct proxy *getproxy(const char *);
2222 #include "fdm.h"
2323 #include "fetch.h"
2424
25 int fetch_imap_start(struct account *);
25 int fetch_imap_start(struct account *, struct ios *);
2626 int fetch_imap_finish(struct account *);
2727 void fetch_imap_desc(struct account *, char *, size_t);
2828
5656 }
5757
5858 int
59 fetch_imap_getln(struct account *a, int type, char **line, int flags)
59 fetch_imap_getln(struct account *a, int type, char **line, int flag)
6060 {
6161 struct fetch_imap_data *data = a->data;
6262 char **lbuf = &data->lbuf;
6464 char *cause;
6565 int tag;
6666
67 if (flags & FETCH_NOWAIT)
68 data->io->flags |= IO_NOWAIT;
69 else
70 data->io->flags &= ~IO_NOWAIT;
71
72
7367 restart:
74 switch (io_pollline2(data->io, line, lbuf, llen, &cause)) {
75 case 0:
76 log_warnx("%s: connection unexpectedly closed", a->name);
77 return (-1);
78 case -1:
79 if (errno == EAGAIN)
68 if (flag) {
69 *line = io_readline2(data->io, &data->lbuf, &data->llen);
70 if (*line == NULL)
8071 return (1);
81 log_warnx("%s: %s", a->name, cause);
82 xfree(cause);
83 return (-1);
72 } else {
73 switch (io_pollline2(data->io, line, lbuf, llen, &cause)) {
74 case 0:
75 log_warnx("%s: connection unexpectedly closed",a->name);
76 return (-1);
77 case -1:
78 log_warnx("%s: %s", a->name, cause);
79 xfree(cause);
80 return (-1);
81 }
8482 }
8583
8684 if (type == IMAP_RAW)
123121 }
124122
125123 int
126 fetch_imap_start(struct account *a)
124 fetch_imap_start(struct account *a, struct ios *ios)
127125 {
128126 struct fetch_imap_data *data = a->data;
129127 char *cause;
140138 }
141139 if (conf.debug > 3 && !conf.syslog)
142140 data->io->dup_fd = STDOUT_FILENO;
141 ARRAY_ADD(ios, data->io, struct io *);
143142
144143 data->getln = fetch_imap_getln;
145144 data->putln = fetch_imap_putln;
2727 void fetch_imappipe_desc(struct account *, char *, size_t);
2828
2929 int printflike2 fetch_imappipe_putln(struct account *, const char *, ...);
30 int fetch_imappipe_getln(struct account *, int, char **, int);
30 int fetch_imappipe_getln(struct account *, int, char **);
3131 void fetch_imappipe_flush(struct account *);
3232
3333 struct fetch fetch_imappipe = {
5656 }
5757
5858 int
59 fetch_imappipe_getln(struct account *a, int type, char **line, int flags)
59 fetch_imappipe_getln(struct account *a, int type, char **line)
6060 {
6161 struct fetch_imap_data *data = a->data;
6262 char **lbuf = &data->lbuf;
6363 size_t *llen = &data->llen;
6464 char *out, *err, *cause;
6565 int tag;
66
67 if (flags & FETCH_NOWAIT)
68 data->cmd->timeout = 0;
69 else
70 data->cmd->timeout = conf.timeout;
7166
7267 restart:
7368 switch (cmd_poll(data->cmd, &out, &err, lbuf, llen, &cause)) {
8479
8580 if (err != NULL)
8681 log_warnx("%s: %s: %s", a->name, data->pipecmd, err);
87 if (out == NULL) {
88 if (flags & FETCH_NOWAIT)
89 return (1);
82 if (out == NULL)
9083 goto restart;
91 }
9284 *line = out;
9385
9486 if (type == IMAP_RAW)
2626 #include "fdm.h"
2727 #include "fetch.h"
2828
29 int fetch_pop3_start(struct account *);
29 int fetch_pop3_start(struct account *, struct ios *);
3030 int fetch_pop3_finish(struct account *);
3131 int fetch_pop3_poll(struct account *, u_int *);
32 int fetch_pop3_fetch(struct account *, struct mail *, int);
33 int fetch_pop3_purge(struct account *);
32 int fetch_pop3_fetch(struct account *, struct mail *);
33 int fetch_pop3_purge(struct account *, struct ios *);
3434 int fetch_pop3_done(struct account *, struct mail *);
3535 void fetch_pop3_desc(struct account *, char *, size_t);
3636
3737 void fetch_pop3_free(void *);
3838
39 int fetch_pop3_connect(struct account *);
39 int fetch_pop3_connect(struct account *, struct ios *);
4040 int fetch_pop3_disconnect(struct account *);
4141
42 int fetch_pop3_line(struct account *, char **, int);
42 int fetch_pop3_line(struct account *, char **);
4343 int fetch_pop3_okay(char *);
4444 char *fetch_pop3_check(struct account *);
4545
6464 }
6565
6666 int
67 fetch_pop3_line(struct account *a, char **line, int flags)
67 fetch_pop3_line(struct account *a, char **line)
6868 {
6969 struct fetch_pop3_data *data = a->data;
7070 struct io *io = data->io;
7171 char *cause;
7272
73 if (flags & FETCH_NOWAIT)
74 io->flags |= IO_NOWAIT;
75 else
76 io->flags &= ~IO_NOWAIT;
7773 switch (io_pollline2(io, line, &data->lbuf, &data->llen, &cause)) {
7874 case 0:
7975 log_warnx("%s: connection unexpectedly closed", a->name);
80 return (-1);
76 return (1);
8177 case -1:
8278 if (errno == EAGAIN)
8379 return (1);
8480 log_warnx("%s: %s", a->name, cause);
8581 xfree(cause);
86 return (-1);
82 return (1);
8783 }
8884
8985 return (0);
10298 {
10399 char *line;
104100
105 if (fetch_pop3_line(a, &line, 0) != 0)
101 if (fetch_pop3_line(a, &line) != 0)
106102 return (NULL);
107103
108104 if (!fetch_pop3_okay(line)) {
114110 }
115111
116112 int
117 fetch_pop3_start(struct account *a)
113 fetch_pop3_start(struct account *a, struct ios *ios)
118114 {
119115 struct fetch_pop3_data *data = a->data;
120116
123119 data->llen = IO_LINESIZE;
124120 data->lbuf = xmalloc(data->llen);
125121
126 return (fetch_pop3_connect(a));
122 data->state = POP3_START;
123
124 return (fetch_pop3_connect(a, ios));
127125 }
128126
129127 int
148146 }
149147
150148 int
151 fetch_pop3_connect(struct account *a)
149 fetch_pop3_connect(struct account *a, struct ios *ios)
152150 {
153151 struct fetch_pop3_data *data = a->data;
154152 char *line, *cause;
162160 }
163161 if (conf.debug > 3 && !conf.syslog)
164162 data->io->dup_fd = STDOUT_FILENO;
163 ARRAY_ADD(ios, data->io, struct io *);
165164
166165 if (fetch_pop3_check(a) == NULL)
167166 return (FETCH_ERROR);
224223 }
225224
226225 int
227 fetch_pop3_fetch(struct account *a, struct mail *m, int flags)
226 fetch_pop3_fetch(struct account *a, struct mail *m)
228227 {
229228 struct fetch_pop3_data *data = a->data;
230229 struct fetch_pop3_mail *aux;
231230 char *line, *uid;
232231 size_t len;
233232 u_int n, i;
234 int error;
235233
236234 restart:
235 line = NULL;
236 if (data->state != POP3_START) {
237 line = io_readline2(data->io, &data->lbuf, &data->llen);
238 if (line == NULL)
239 return (FETCH_AGAIN);
240 }
241
237242 switch (data->state) {
238 case POP3_LISTDONE:
239 case POP3_UIDLDONE:
240 case POP3_RETRDONE:
241 case POP3_LINE:
242 error = fetch_pop3_line(a, &line, flags);
243 switch (error) {
244 case -1:
245 return (FETCH_ERROR);
246 case 1:
247 return (FETCH_AGAIN);
248 }
249 default:
250 break;
251 }
252
253 switch (data->state) {
254 case POP3_LIST:
243 case POP3_START:
255244 data->cur++;
256245 if (data->cur > data->num)
257246 return (FETCH_COMPLETE);
258247 io_writeline(data->io, "LIST %u", data->cur);
259 data->state = POP3_LISTDONE;
248 data->state = POP3_LIST;
260249 break;
261 case POP3_LISTDONE:
250 case POP3_LIST:
262251 if (!fetch_pop3_okay(line))
263252 goto bad;
264253 if (sscanf(line, "+OK %*u %zu", &data->size) != 1) {
271260 m->size = data->size;
272261 return (FETCH_OVERSIZE);
273262 }
263 io_writeline(data->io, "UIDL %u", data->cur);
274264 data->state = POP3_UIDL;
275265 break;
276266 case POP3_UIDL:
277 io_writeline(data->io, "UIDL %u", data->cur);
278 data->state = POP3_UIDLDONE;
279 break;
280 case POP3_UIDLDONE:
281267 if (!fetch_pop3_okay(line))
282268 goto bad;
283269 if (sscanf(line, "+OK %u ", &n) != 1)
308294 break;
309295 }
310296 }
297 io_writeline(data->io, "RETR %u", data->cur);
311298 data->state = POP3_RETR;
312299 break;
313300 case POP3_RETR:
314 io_writeline(data->io, "RETR %u", data->cur);
315 data->state = POP3_RETRDONE;
316 break;
317 case POP3_RETRDONE:
318301 if (!fetch_pop3_okay(line))
319302 goto bad;
320303 mail_open(m, IO_ROUND(data->size));
370353 goto restart;
371354
372355 complete:
373 data->state = POP3_LIST;
356 data->state = POP3_START;
374357
375358 add_tag(&m->tags, "lines", "%u", data->lines);
376359 if (data->bodylines == -1) {
390373
391374 if (data->flushing)
392375 return (FETCH_OVERSIZE);
376
393377 return (FETCH_SUCCESS);
394378
395379 bad:
398382 }
399383
400384 int
401 fetch_pop3_purge(struct account *a)
385 fetch_pop3_purge(struct account *a, struct ios *ios)
402386 {
403387 if (fetch_pop3_disconnect(a) != 0)
404388 return (FETCH_ERROR);
405 return (fetch_pop3_connect(a));
389 return (fetch_pop3_connect(a, ios));
406390 }
407391
408392 int
2727 #include "fdm.h"
2828 #include "fetch.h"
2929
30 int fetch_stdin_start(struct account *);
30 int fetch_stdin_start(struct account *, struct ios *);
3131 int fetch_stdin_finish(struct account *);
32 int fetch_stdin_fetch(struct account *, struct mail *, int);
32 int fetch_stdin_fetch(struct account *, struct mail *);
3333 int fetch_stdin_done(struct account *, struct mail *);
3434 void fetch_stdin_desc(struct account *, char *, size_t);
3535
4545 };
4646
4747 int
48 fetch_stdin_start(struct account *a)
48 fetch_stdin_start(struct account *a, struct ios *ios)
4949 {
5050 struct fetch_stdin_data *data = a->data;
5151
6767 data->io = io_create(STDIN_FILENO, NULL, IO_LF, conf.timeout);
6868 if (conf.debug > 3 && !conf.syslog)
6969 data->io->dup_fd = STDOUT_FILENO;
70 ARRAY_ADD(ios, data->io, struct io *);
7071
7172 data->complete = 0;
7273
108109 }
109110
110111 int
111 fetch_stdin_fetch(struct account *a, struct mail *m, int flags)
112 fetch_stdin_fetch(struct account *a, struct mail *m)
112113 {
113114 struct fetch_stdin_data *data = a->data;
114115 int error;
123124 m->size = 0;
124125 }
125126
126 if (flags & FETCH_NOWAIT)
127 data->io->flags |= IO_NOWAIT;
128 else
129 data->io->flags &= ~IO_NOWAIT;
130
131127 restart:
128 /*
129 * There can only ever be one mail on stdin, so the normal reentrancy
130 * becomes irrelevent. Which is good since we need to detect when the
131 * fd is closed.
132 */
132133 error = io_pollline2(data->io, &line, &data->lbuf, &data->llen, &cause);
133134 switch (error) {
134135 case 0:
141142 xfree(cause);
142143 return (FETCH_ERROR);
143144 }
144
145
145146 len = strlen(line);
146147 if (len == 0 && m->body == -1) {
147148 m->body = m->size + 1;
2626 #define FETCH_COMPLETE 4
2727 #define FETCH_AGAIN 5
2828
29 /* Fetch flags. */
30 #define FETCH_NOWAIT 0x1
31
3229 /* Fetch functions. */
3330 struct fetch {
3431 #define FETCHPORT_NORMAL 0
3532 #define FETCHPORT_SSL 1
3633 const char *ports[2]; /* normal port, ssl port */
3734
38 int (*start)(struct account *);
35 int (*start)(struct account *, struct ios *);
3936 int (*poll)(struct account *, u_int *);
40 int (*fetch)(struct account *, struct mail *, int);
41 int (*purge)(struct account *);
37 int (*fetch)(struct account *, struct mail *);
38 int (*purge)(struct account *, struct ios *);
4239 int (*done)(struct account *, struct mail *);
4340 int (*finish)(struct account *);
4441 void (*desc)(struct account *, char *, size_t);
106103 struct strings kept;
107104
108105 enum {
106 POP3_START,
109107 POP3_LIST,
110 POP3_LISTDONE,
111108 POP3_UIDL,
112 POP3_UIDLDONE,
113109 POP3_RETR,
114 POP3_RETRDONE,
115110 POP3_LINE
116111 } state;
117112 int flushing;
161156 ARRAY_DECL(, u_int) kept;
162157
163158 enum {
164 IMAP_UID,
165 IMAP_UIDDONE1,
166 IMAP_UIDDONE2,
159 IMAP_START,
160 IMAP_UID1,
161 IMAP_UID2,
167162 IMAP_FETCH,
168 IMAP_FETCHDONE,
169163 IMAP_LINE,
170 IMAP_LINEDONE1,
171 IMAP_LINEDONE2,
164 IMAP_END1,
165 IMAP_END2
172166 } state;
173167 int flushing;
174168 int bodylines;
219213 void imap_abort(struct account *);
220214 int imap_uid(struct account *);
221215 int imap_poll(struct account *, u_int *);
222 int imap_fetch(struct account *, struct mail *, int);
223 int imap_purge(struct account *);
216 int imap_fetch(struct account *, struct mail *);
217 int imap_purge(struct account *, struct ios *);
224218 int imap_done(struct account *, struct mail *);
225219
226220 #endif
208208 }
209209
210210 int
211 imap_fetch(struct account *a, struct mail *m, int flags)
211 imap_fetch(struct account *a, struct mail *m)
212212 {
213213 struct fetch_imap_data *data = a->data;
214214 struct fetch_imap_mail *aux = m->auxdata;
220220 restart:
221221 type = -1;
222222 switch (data->state) {
223 case IMAP_UIDDONE1:
224 case IMAP_FETCHDONE:
223 case IMAP_UID1:
224 case IMAP_FETCH:
225225 type = IMAP_UNTAGGED;
226226 break;
227 case IMAP_UIDDONE2:
228 case IMAP_LINEDONE2:
227 case IMAP_UID2:
228 case IMAP_END2:
229229 type = IMAP_TAGGED;
230230 break;
231231 case IMAP_LINE:
232 case IMAP_LINEDONE1:
232 case IMAP_END1:
233233 type = IMAP_RAW;
234234 break;
235235 default:
236236 break;
237237 }
238238 if (type != -1) {
239 error = data->getln(a, type, &line, flags);
239 error = data->getln(a, type, &line, 1);
240240 switch (error) {
241241 case -1:
242242 return (FETCH_ERROR);
246246 }
247247
248248 switch (data->state) {
249 case IMAP_UID:
249 case IMAP_START:
250250 data->cur++;
251251 if (data->cur > data->num)
252252 return (FETCH_COMPLETE);
255255 if (data->putln(a,
256256 "%u FETCH %u UID", ++data->tag, data->cur) != 0)
257257 return (FETCH_ERROR);
258 data->state = IMAP_UIDDONE1;
259 break;
260 case IMAP_UIDDONE1:
258 data->state = IMAP_UID1;
259 break;
260 case IMAP_UID1:
261261 if (sscanf(line, "* %u FETCH (UID %u)", &n, &data->uid) != 2) {
262262 log_warnx("%s: invalid response: %s", a->name, line);
263263 return (FETCH_ERROR);
266266 log_warnx("%s: bad message index: %s", a->name, line);
267267 return (FETCH_ERROR);
268268 }
269 data->state = IMAP_UIDDONE2;
270 break;
271 case IMAP_UIDDONE2:
269 data->state = IMAP_UID2;
270 break;
271 case IMAP_UID2:
272272 if (!imap_okay(a, line))
273273 return (FETCH_ERROR);
274274
275275 for (i = 0; i < ARRAY_LENGTH(&data->kept); i++) {
276276 if (ARRAY_ITEM(&data->kept, i, u_int) == data->uid) {
277277 /* had this message before and kept, so skip */
278 data->state = IMAP_UID;
278 data->state = IMAP_START;
279279 break;
280280 }
281281 }
282 data->state = IMAP_FETCH;
283 break;
284 case IMAP_FETCH:
285282 if (data->putln(a,
286283 "%u FETCH %u BODY[]", ++data->tag, data->cur) != 0)
287284 return (FETCH_ERROR);
288 data->state = IMAP_FETCHDONE;
289 break;
290 case IMAP_FETCHDONE:
285 data->state = IMAP_FETCH;
286 break;
287 case IMAP_FETCH:
291288 if (sscanf(line, "* %u FETCH (", &n) != 1) {
292289 log_warnx("%s: invalid response: %s", a->name, line);
293290 return (FETCH_ERROR);
351348 data->bodylines++;
352349 m->size += len + 1;
353350 if (m->size + data->lines >= data->size)
354 data->state = IMAP_LINEDONE1;
355 break;
356 case IMAP_LINEDONE1:
351 data->state = IMAP_END1;
352 break;
353 case IMAP_END1:
357354 if (strcmp(line, ")") != 0) {
358355 log_warnx("%s: invalid response: %s", a->name, line);
359356 return (FETCH_ERROR);
360357 }
361 data->state = IMAP_LINEDONE2;
362 break;
363 case IMAP_LINEDONE2:
358 data->state = IMAP_END2;
359 break;
360 case IMAP_END2:
364361 if (!imap_okay(a, line))
365362 return (FETCH_ERROR);
366363 goto complete;
369366 goto restart;
370367
371368 complete:
372 data->state = IMAP_UID;
369 data->state = IMAP_START;
373370
374371 if (m->size + data->lines != data->size) {
375372 log_warnx("%s: received too much data", a->name);
415412 }
416413
417414 int
418 imap_purge(struct account *a)
415 imap_purge(struct account *a, struct ios *ios)
419416 {
420417 struct fetch_imap_data *data = a->data;
421418 char *line;
430427 if (imap_select(a) != 0)
431428 return (FETCH_ERROR);
432429
433 return (FETCH_SUCCESS);
434 }
430 ARRAY_ADD(ios, data->io, struct io *);
431 return (FETCH_SUCCESS);
432 }
698698 int
699699 io_flush(struct io *io, char **cause)
700700 {
701 int flags;
702
703 flags = io->flags;
704 io->flags &= ~IO_NOWAIT;
705
701706 while (io->wsize > 0) {
702 if (io_poll(io, cause) != 1)
707 if (io_poll(io, cause) != 1) {
708 io->flags = flags;
703709 return (-1);
704 }
705
710 }
711 }
712
713 io->flags = flags;
706714 return (0);
707715 }
708716
710718 int
711719 io_wait(struct io *io, size_t len, char **cause)
712720 {
721 int flags;
722
723 flags = io->flags;
724 io->flags &= ~IO_NOWAIT;
725
713726 while (io->rsize < len) {
714 if (io_poll(io, cause) != 1)
727 if (io_poll(io, cause) != 1) {
728 io->flags = flags;
715729 return (-1);
716 }
717
730 }
731 }
732
733 io->flags = flags;
718734 return (0);
719735 }
3636 void
3737 mail_open(struct mail *m, size_t size)
3838 {
39 memset(m, 0, sizeof m);
40
4139 m->size = size;
4240 m->space = m->size;
4341 m->body = -1;
6866 mail_receive(struct mail *m, struct msg *msg)
6967 {
7068 struct mail *mm = &msg->data.mail;
69
70 mm->done = m->done;
71 mm->idx = m->idx;
7172
7273 mm->tags = m->tags;
7374 m->tags = NULL;
4545 * We are called as the child so to change uid this needs to be done
4646 * largely in the parent.
4747 */
48 memset(&msg, 0, sizeof msg);
4849 msg.type = MSG_COMMAND;
4950 msg.data.account = a;
5051 msg.data.cmddata = data;
1818 #ifndef MATCH_H
1919 #define MATCH_H
2020
21 #include "deliver.h"
22
2123 /* Match return codes. */
2224 #define MATCH_FALSE 0
2325 #define MATCH_TRUE 1
2527
2628 /* Match context. */
2729 struct match_ctx {
28 struct io *io;
29 struct account *account;
30 struct mail *mail;
30 struct io *io;
31 struct account *account;
32 struct mail *mail;
3133
32 int matched;
33 int stopped;
34 int matched;
35 int stopped;
3436
35 struct rule *rule;
36 ARRAY_DECL(, struct rule *) stack;
37
38 TAILQ_ENTRY(match_ctx) entry;
37 struct rule *rule;
38 ARRAY_DECL(, struct rule *) stack;
39
40 struct deliver_queue dqueue;
41
42 TAILQ_ENTRY(match_ctx) entry;
43 #define match_entry entry
44 #define deliver_entry entry
45 #define done_entry entry
3946 };
4047 TAILQ_HEAD(match_queue, match_ctx);
4148
0 /* $Id$ */
1
2 /*
3 * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
14 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
15 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/wait.h>
21
22 #include <fcntl.h>
23 #include <paths.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "fdm.h"
28 #include "deliver.h"
29 #include "match.h"
30
31 int
32 parent_deliver(struct child *child, struct msg *msg, void *buf, size_t len)
33 {
34 struct child_deliver_data *data = child->data;
35 struct account *a = data->account;
36 struct mail *m = data->mail;
37
38 log_debug2("parent_deliver: got message type %d from child %ld",
39 msg->type, (long) child->pid);
40
41 switch (msg->type) {
42 case MSG_DONE:
43 break;
44 default:
45 fatalx("parent_deliver: unexpected message");
46 }
47
48 if (buf == NULL || len == 0)
49 fatalx("parent_deliver: bad tags");
50 strb_destroy(&m->tags);
51 m->tags = buf;
52
53 /* call the hook */
54 data->hook(1, a, msg, data, &msg->data.error);
55
56 msg->type = MSG_DONE;
57
58 mail_send(m, msg);
59
60 child = data->child;
61 if (privsep_send(child->io, msg, m->tags, STRB_SIZE(m->tags)) != 0)
62 fatalx("parent_deliver: privsep_send error");
63
64 mail_close(m);
65 xfree(m);
66
67 return (1);
68 }
0 /* $Id$ */
1
2 /*
3 * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
14 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
15 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/wait.h>
21
22 #include <fcntl.h>
23 #include <paths.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "fdm.h"
28 #include "deliver.h"
29 #include "match.h"
30
31 void parent_fetch_action(struct child *, struct children *,
32 struct deliver_ctx *, struct msg *);
33 void parent_fetch_cmd(struct child *, struct children *, struct match_ctx *,
34 struct msg *);
35
36 int
37 parent_fetch(struct child *child, struct msg *msg, void *buf, size_t len)
38 {
39 struct child_fetch_data *data = child->data;
40 struct children *children = data->children;
41 struct deliver_ctx *dctx;
42 struct match_ctx *mctx;
43 struct mail *m;
44
45 log_debug2("parent_fetch: got message type %d from child %ld",
46 msg->type, (long) child->pid);
47
48 switch (msg->type) {
49 case MSG_ACTION:
50 if (buf == NULL || len == 0)
51 fatalx("parent_fetch: bad tags");
52 m = xcalloc(1, sizeof *m);
53 mail_receive(m, msg);
54 m->tags = buf;
55
56 dctx = xcalloc(1, sizeof *dctx);
57 dctx->account = msg->data.account;
58 dctx->mail = m;
59
60 parent_fetch_action(child, children, dctx, msg);
61 break;
62 case MSG_COMMAND:
63 if (buf == NULL || len == 0)
64 fatalx("parent_fetch: bad tags");
65 m = xcalloc(1, sizeof *m);
66 mail_receive(m, msg);
67 m->tags = buf;
68
69 mctx = xcalloc(1, sizeof *mctx);
70 mctx->account = msg->data.account;
71 mctx->mail = m;
72
73 parent_fetch_cmd(child, children, mctx, msg);
74 break;
75 case MSG_DONE:
76 fatalx("parent_fetch: unexpected message");
77 case MSG_EXIT:
78 return (1);
79 }
80
81 return (0);
82 }
83
84 void
85 parent_fetch_action(struct child *child, struct children *children,
86 struct deliver_ctx *dctx, struct msg *msg)
87 {
88 struct action *t = msg->data.action;
89 uid_t uid = msg->data.uid;
90 struct mail *m = dctx->mail;
91 struct mail *md = &dctx->wr_mail;
92 struct child_deliver_data *data;
93
94 memset(md, 0, sizeof *md);
95 /*
96 * If writing back, open a new mail now and set its ownership so it
97 * can be accessed by the child.
98 */
99 if (t->deliver->type == DELIVER_WRBACK) {
100 mail_open(md, IO_BLOCKSIZE);
101 if (geteuid() == 0 &&
102 fchown(md->shm.fd, conf.child_uid, conf.child_gid) != 0)
103 fatal("fchown");
104 md->decision = m->decision;
105 }
106
107 data = xmalloc(sizeof *data);
108 data->child = child;
109 data->account = dctx->account;
110 data->hook = child_deliver_action_hook;
111 data->action = t;
112 data->dctx = dctx;
113 data->mail = m;
114 data->name = "deliver";
115 child = child_start(children, uid, child_deliver, parent_deliver, data);
116 log_debug3("parent: deliver child %ld started", (long) child->pid);
117 }
118
119 void
120 parent_fetch_cmd(struct child *child, struct children *children,
121 struct match_ctx *mctx, struct msg *msg)
122 {
123 uid_t uid = msg->data.uid;
124 struct mail *m = mctx->mail;
125 struct child_deliver_data *data;
126
127 data = xmalloc(sizeof *data);
128 data->child = child;
129 data->account = mctx->account;
130 data->hook = child_deliver_cmd_hook;
131 data->mctx = mctx;
132 data->cmddata = msg->data.cmddata;
133 data->mail = m;
134 data->name = "command";
135 child = child_start(children, uid, child_deliver, parent_deliver, data);
136 log_debug2("parent: command child %ld started", (long) child->pid);
137 }
+0
-454
parent.c less more
0 /* $Id$ */
1
2 /*
3 * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
14 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
15 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/wait.h>
21
22 #include <fcntl.h>
23 #include <paths.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "fdm.h"
28 #include "deliver.h"
29 #include "match.h"
30
31 void parent_get(struct mail *, struct msg *, void *, struct deliver_ctx *,
32 struct match_ctx *);
33 void parent_done(struct io *, struct mail *, struct msg *, int);
34 int parent_child(struct account *, struct mail *, const char *, uid_t,
35 int (*)(pid_t, struct account *, struct msg *, void *, int *),
36 void *, int *);
37
38 struct parent_action_data {
39 struct action *action;
40 struct deliver_ctx *dctx;
41 struct mail *mail;
42 };
43
44 int parent_action(struct action *, struct deliver_ctx *, uid_t);
45 int parent_action_hook(int, struct account *, struct msg *, void *, int *);
46
47 struct parent_cmd_data {
48 struct match_ctx *mctx;
49 struct match_command_data *data;
50 struct mail *mail;
51 };
52
53 int parent_cmd(struct match_ctx *, struct match_command_data *, uid_t);
54 int parent_cmd_hook(int, struct account *, struct msg *, void *, int *);
55
56 void
57 parent_get(struct mail *m, struct msg *msg, void *buf, struct deliver_ctx *dctx,
58 struct match_ctx *mctx)
59 {
60 mail_receive(m, msg);
61 m->tags = buf;
62
63 if (dctx != NULL) {
64 memset(dctx, 0, sizeof dctx);
65 dctx->account = msg->data.account;
66 dctx->mail = m;
67 }
68
69 if (mctx != NULL) {
70 memset(mctx, 0, sizeof mctx);
71 mctx->account = msg->data.account;
72 mctx->mail = m;
73 }
74 }
75
76 void
77 parent_done(struct io *io, struct mail *m, struct msg *msg, int error)
78 {
79 memset(msg, 0, sizeof msg);
80
81 msg->type = MSG_DONE;
82 msg->data.error = error;
83
84 mail_send(m, msg);
85
86 if (privsep_send(io, msg, m->tags, STRB_SIZE(m->tags)) != 0)
87 fatalx("parent: privsep_send error");
88
89 mail_close(m);
90 }
91
92 int
93 parent_child(struct account *a, struct mail *m, const char *name, uid_t uid,
94 int (*hook)(pid_t, struct account *, struct msg *, void *, int *),
95 void *hookdata, int *result)
96 {
97 struct io *io;
98 struct msg msg;
99 int status, error = 0, fds[2];
100 pid_t pid;
101 void *buf;
102 size_t len;
103
104 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) != 0)
105 fatal("socketpair");
106 pid = child_fork();
107 if (pid != 0) {
108 /* create privsep io */
109 close(fds[1]);
110 io = io_create(fds[0], NULL, IO_LF, INFTIM);
111
112 /* parent process. wait for child */
113 log_debug2("%s: forked. child pid is %ld", a->name, (long) pid);
114
115 do {
116 if (privsep_recv(io, &msg, &buf, &len) != 0)
117 fatalx("parent2: privsep_recv error");
118 log_debug2("parent2: got message type %d", msg.type);
119
120 switch (msg.type) {
121 case MSG_DONE:
122 break;
123 default:
124 fatalx("parent2: unexpected message");
125 }
126 } while (msg.type != MSG_DONE);
127 *result = msg.data.error;
128
129 if (buf == NULL || len == 0)
130 fatalx("parent2: bad tags");
131 strb_destroy(&m->tags);
132 m->tags = buf;
133
134 /* call the hook */
135 if (hook(pid, a, &msg, hookdata, result) != 0)
136 error = 1;
137
138 /* free the io */
139 io_close(io);
140 io_free(io);
141
142 if (waitpid(pid, &status, 0) == -1)
143 fatal("waitpid");
144 if (WIFSIGNALED(status)) {
145 log_warnx("%s: child got signal: %d", a->name,
146 WTERMSIG(status));
147 return (1);
148 }
149 if (!WIFEXITED(status)) {
150 log_warnx("%s: child didn't exit normally", a->name);
151 return (1);
152 }
153 status = WEXITSTATUS(status);
154 if (status != 0) {
155 log_warnx("%s: child returned %d", a->name, status);
156 return (1);
157 }
158
159 return (error);
160 }
161
162 #ifdef DEBUG
163 xmalloc_clear();
164 COUNTFDS(a->name);
165 #endif
166
167 /* create privsep io */
168 close(fds[0]);
169 io = io_create(fds[1], NULL, IO_LF, INFTIM);
170
171 /* child process. change user and group */
172 if (geteuid() == 0) {
173 if (dropto(uid) != 0) {
174 log_warnx("%s: can't drop privileges", a->name);
175 child_exit(1);
176 }
177 } else {
178 log_debug2("%s: not root. using current user", a->name);
179 uid = geteuid();
180 }
181 #ifndef NO_SETPROCTITLE
182 setproctitle("%s[%lu]", name, (u_long) uid);
183 #endif
184
185 /* refresh user and home and fix tags */
186 fill_info(NULL);
187 update_tags(&m->tags);
188 log_debug2("%s: user is: %s, home is: %s", a->name, conf.info.user,
189 conf.info.home);
190
191 /* call the hook */
192 if (hook(pid, a, &msg, hookdata, result) != 0)
193 error = 1;
194
195 /* inform parent we're done */
196 msg.type = MSG_DONE;
197 msg.data.error = *result;
198 if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0)
199 fatalx("deliver: privsep_send error");
200
201 /* free the io */
202 io_close(io);
203 io_free(io);
204
205 #ifdef DEBUG
206 COUNTFDS(a->name);
207 xmalloc_report(a->name);
208 #endif
209
210 child_exit(error);
211 fatalx("child_exit: failed");
212 }
213
214 int
215 do_parent(struct child *child)
216 {
217 struct msg msg;
218 struct deliver_ctx dctx;
219 struct match_ctx mctx;
220 struct mail m;
221 int error;
222 void *buf;
223 size_t len;
224
225 memset(&m, 0, sizeof m);
226
227 if (privsep_recv(child->io, &msg, &buf, &len) != 0)
228 fatalx("parent: privsep_recv error");
229 log_debug2("parent: got message type %d from child %ld (%s)", msg.type,
230 (long) child->pid, child->account->name);
231
232 switch (msg.type) {
233 case MSG_ACTION:
234 if (buf == NULL || len == 0)
235 fatalx("parent: bad tags");
236 parent_get(&m, &msg, buf, &dctx, NULL);
237 error = parent_action(msg.data.action, &dctx, msg.data.uid);
238 parent_done(child->io, &m, &msg, error);
239 break;
240 case MSG_COMMAND:
241 if (buf == NULL || len == 0)
242 fatalx("parent: bad tags");
243 parent_get(&m, &msg, buf, NULL, &mctx);
244 error = parent_cmd(&mctx, msg.data.cmddata, msg.data.uid);
245 parent_done(child->io, &m, &msg, error);
246 break;
247 case MSG_DONE:
248 fatalx("parent: unexpected message");
249 case MSG_EXIT:
250 return (1);
251 }
252
253 return (0);
254 }
255
256 int
257 parent_action(struct action *t, struct deliver_ctx *dctx, uid_t uid)
258 {
259 struct account *a = dctx->account;
260 struct mail *m = dctx->mail;
261 struct mail *md = &dctx->wr_mail;
262 struct parent_action_data ad;
263 int result;
264
265 ad.action = t;
266 ad.dctx = dctx;
267 ad.mail = m;
268
269 memset(md, 0, sizeof *md);
270 /*
271 * If writing back, open a new mail now and set its ownership so it
272 * can be accessed by the child.
273 */
274 if (t->deliver->type == DELIVER_WRBACK) {
275 mail_open(md, IO_BLOCKSIZE);
276 if (geteuid() == 0 &&
277 fchown(md->shm.fd, conf.child_uid, conf.child_gid) != 0)
278 fatal("fchown");
279 md->decision = m->decision;
280 }
281
282 if (parent_child(a,
283 m, "deliver", uid, parent_action_hook, &ad, &result) != 0)
284 return (DELIVER_FAILURE);
285 return (result);
286 }
287
288 int
289 parent_action_hook(pid_t pid, struct account *a, struct msg *msg,
290 void *hookdata, int *result)
291 {
292 struct parent_action_data *ad = hookdata;
293 struct action *t = ad->action;
294 struct deliver_ctx *dctx = ad->dctx;
295 struct mail *m = ad->mail;
296 struct mail *md = &dctx->wr_mail;
297
298 /* check if this is the parent */
299 if (pid != 0) {
300 /* use new mail if necessary */
301 if (t->deliver->type != DELIVER_WRBACK)
302 return (0);
303
304 if (*result != DELIVER_SUCCESS) {
305 mail_destroy(md);
306 return (0);
307 }
308
309 mail_close(md);
310 mail_receive(m, msg);
311 log_debug2("%s: got new mail from delivery: size %zu, body %zd",
312 a->name, m->size, m->body);
313
314 return (0);
315 }
316
317 /* this is the child. do the delivery */
318 *result = t->deliver->deliver(dctx, t);
319 if (t->deliver->type != DELIVER_WRBACK || *result != DELIVER_SUCCESS)
320 return (0);
321
322 mail_send(md, msg);
323 log_debug2("%s: using new mail, size %zu", a->name, md->size);
324
325 return (0);
326 }
327
328 int
329 parent_cmd(struct match_ctx *mctx, struct match_command_data *data,
330 uid_t uid)
331 {
332 struct account *a = mctx->account;
333 struct mail *m = mctx->mail;
334 struct parent_cmd_data cd;
335 int result;
336
337 cd.mctx = mctx;
338 cd.data = data;
339
340 if (parent_child(a,
341 m, "command", uid, parent_cmd_hook, &cd, &result) != 0)
342 return (MATCH_ERROR);
343 return (result);
344 }
345
346 int
347 parent_cmd_hook(pid_t pid, struct account *a, unused struct msg *msg,
348 void *hookdata, int *result)
349 {
350 struct parent_cmd_data *cd = hookdata;
351 struct match_ctx *mctx = cd->mctx;
352 struct mail *m = mctx->mail;
353 struct match_command_data *data = cd->data;
354 int flags, status, found = 0;
355 char *s, *cause, *lbuf, *out, *err, tag[24];
356 size_t llen;
357 struct cmd *cmd = NULL;
358 struct rmlist rml;
359 u_int i;
360
361 /* if this is the parent, do nothing */
362 if (pid != 0)
363 return (0);
364
365 /* sort out the command */
366 s = replacepath(&data->cmd, m->tags, m, &m->rml);
367 if (s == NULL || *s == '\0') {
368 log_warnx("%s: empty command", a->name);
369 goto error;
370 }
371
372 log_debug2("%s: %s: started (ret=%d re=%s)", a->name,
373 s, data->ret, data->re.str == NULL ? "none" : data->re.str);
374 flags = CMD_ONCE;
375 if (data->pipe)
376 flags |= CMD_IN;
377 if (data->re.str != NULL)
378 flags |= CMD_OUT;
379 cmd = cmd_start(s, flags, conf.timeout, m->data, m->size, &cause);
380 if (cmd == NULL) {
381 log_warnx("%s: %s: %s", a->name, s, cause);
382 goto error;
383 }
384
385 llen = IO_LINESIZE;
386 lbuf = xmalloc(llen);
387
388 do {
389 status = cmd_poll(cmd, &out, &err, &lbuf, &llen, &cause);
390 if (status > 0) {
391 log_warnx("%s: %s: %s", a->name, s, cause);
392 goto error;
393 }
394 if (status < 0)
395 break;
396 if (err != NULL)
397 log_warnx("%s: %s: %s", a->name, s, err);
398 if (out == NULL)
399 continue;
400 log_debug3("%s: %s: out: %s", a->name, s, out);
401 if (found) /* XXX stop early? */
402 continue;
403
404 found = re_string(&data->re, out, &rml, &cause);
405 if (found == -1) {
406 log_warnx("%s: %s", a->name, cause);
407 goto error;
408 }
409 if (found != 1)
410 continue;
411 /* save the matches */
412 if (!rml.valid)
413 continue;
414 for (i = 0; i < NPMATCH; i++) {
415 if (!rml.list[i].valid)
416 break;
417 xsnprintf(tag, sizeof tag, "command%u", i);
418 add_tag(&m->tags, tag, "%.*s", (int) (rml.list[i].eo -
419 rml.list[i].so), out + rml.list[i].so);
420 }
421 } while (status >= 0);
422 status = -1 - status;
423
424 log_debug2("%s: %s: returned %d, found %d", a->name, s, status, found);
425
426 cmd_free(cmd);
427 xfree(s);
428 xfree(lbuf);
429
430 status = data->ret == status;
431 if (data->ret != -1 && data->re.str != NULL)
432 *result = (found && status) ? MATCH_TRUE : MATCH_FALSE;
433 else if (data->ret != -1 && data->re.str == NULL)
434 *result = status ? MATCH_TRUE : MATCH_FALSE;
435 else if (data->ret == -1 && data->re.str != NULL)
436 *result = found ? MATCH_TRUE : MATCH_FALSE;
437 else
438 *result = MATCH_ERROR;
439
440 return (0);
441
442 error:
443 if (cause != NULL)
444 xfree(cause);
445 if (cmd != NULL)
446 cmd_free(cmd);
447 if (s != NULL)
448 xfree(s);
449 if (lbuf != NULL)
450 xfree(lbuf);
451
452 return (1);
453 }
2222 int
2323 privsep_send(struct io *io, struct msg *msg, void *buf, size_t len)
2424 {
25 char *cause;
26
2527 if (buf != NULL && len > 0)
2628 msg->size = len;
2729 else
2830 msg->size = 0;
2931
3032 io_write(io, msg, sizeof *msg);
31 if (io_flush(io, NULL) != 0)
33 if (io_flush(io, &cause) != 0)
3234 return (1);
3335
3436 if (buf != NULL && len > 0) {
3537 io_write(io, buf, len);
36 if (io_flush(io, NULL) != 0)
38 if (io_flush(io, &cause) != 0)
3739 return (1);
3840 }
3941