Yet another reorganisation of the fetch code. Not quite finished, still some memory leaks in the error path. This works in much the same way as the last version, except it is a bit cleaner and tidier and I didn't forget that matching may block for the parent too (match pipe/exec) and so break that.
Nicholas Marriott
17 years ago
36 | 36 | cleanup.c imap-common.c fetch-imappipe.c deliver-remove-header.c \ |
37 | 37 | deliver-stdout.c deliver-append-string.c strb.c deliver-add-header.c \ |
38 | 38 | deliver-exec.c child-fetch.c parent-fetch.c child-deliver.c \ |
39 | parent-deliver.c \ | |
39 | parent-deliver.c mail-state.c \ | |
40 | 40 | y.tab.c lex.yy.c |
41 | 41 | |
42 | 42 | DEFS= -DBUILD="\"$(VERSION) ($(DATE))\"" |
20 | 20 | cleanup.c imap-common.c fetch-imappipe.c deliver-remove-header.c \ |
21 | 21 | deliver-stdout.c deliver-append-string.c strb.c deliver-add-header.c \ |
22 | 22 | deliver-exec.c child-fetch.c parent-fetch.c child-deliver.c \ |
23 | parent-deliver.c \ | |
23 | parent-deliver.c mail-state.c \ | |
24 | 24 | parse.y lex.l |
25 | 25 | |
26 | 26 | LEX= lex |
32 | 32 | struct account *a = data->account; |
33 | 33 | struct mail *m = data->mail; |
34 | 34 | struct msg msg; |
35 | struct msgbuf msgbuf; | |
35 | 36 | int error = 0; |
36 | 37 | |
37 | 38 | #ifndef NO_SETPROCTITLE |
49 | 50 | |
50 | 51 | /* inform parent we're done */ |
51 | 52 | msg.type = MSG_DONE; |
52 | if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0) | |
53 | msg.id = 0; | |
54 | ||
55 | msgbuf.buf = m->tags; | |
56 | msgbuf.len = STRB_SIZE(m->tags); | |
57 | ||
58 | if (privsep_send(io, &msg, &msgbuf) != 0) | |
53 | 59 | fatalx("deliver: privsep_send error"); |
54 | if (privsep_recv(io, &msg, NULL, 0) != 0) | |
60 | if (privsep_recv(io, &msg, NULL) != 0) | |
55 | 61 | fatalx("deliver: privsep_recv error"); |
56 | 62 | if (msg.type != MSG_EXIT) |
57 | 63 | fatalx("deliver: unexpected message"); |
103 | 109 | child_deliver_cmd_hook(pid_t pid, struct account *a, unused struct msg *msg, |
104 | 110 | struct child_deliver_data *data, int *result) |
105 | 111 | { |
106 | struct match_ctx *mctx = data->mctx; | |
107 | struct mail *m = mctx->mail; | |
112 | struct mail_ctx *mctx = data->mctx; | |
113 | struct mail *m = data->mail; | |
108 | 114 | struct match_command_data *cmddata = data->cmddata; |
109 | 115 | int flags, status, found = 0; |
110 | 116 | char *s, *cause, *lbuf, *out, *err, tag[24]; |
35 | 35 | int poll_account(struct io *, struct account *); |
36 | 36 | int fetch_account(struct io *, struct account *, double); |
37 | 37 | |
38 | int fetch_flush(struct account *, struct io *, int *, int *, int *, | |
39 | const char **); | |
40 | int fetch_poll(struct account *a, int, struct io *, struct io **); | |
38 | int fetch_drain(u_int *, u_int *); | |
39 | int fetch_done(struct mail_ctx *, u_int *, u_int *); | |
40 | int fetch_match(int *, u_int *, struct msg *, struct msgbuf *); | |
41 | int fetch_deliver(int *, struct msg *, struct msgbuf *); | |
42 | int fetch_poll(struct io *, struct mail_ctx *, int, u_int); | |
41 | 43 | 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; | |
44 | ||
45 | struct mail_queue matchq; | |
46 | struct mail_queue deliverq; | |
47 | struct mail_queue doneq; | |
48 | ||
49 | #define ACTION_DONE 0 | |
50 | #define ACTION_ERROR 1 | |
51 | #define ACTION_PARENT 2 | |
65 | 52 | |
66 | 53 | int |
67 | 54 | child_fetch(struct child *child, struct io *io) |
120 | 107 | |
121 | 108 | out: |
122 | 109 | /* finish fetch */ |
123 | if (a->fetch->finish != NULL && a->fetch->finish(a) != FETCH_SUCCESS) | |
124 | error = 1; | |
110 | if (a->fetch->finish != NULL) { | |
111 | if (a->fetch->finish(a, error) != FETCH_SUCCESS) | |
112 | error = 1; | |
113 | } | |
125 | 114 | |
126 | 115 | io->flags &= ~IO_NOWAIT; |
127 | 116 | memset(&msg, 0, sizeof msg); |
128 | 117 | |
129 | 118 | msg.type = MSG_EXIT; |
130 | 119 | log_debug3("%s: sending exit message to parent", a->name); |
131 | if (privsep_send(io, &msg, NULL, 0) != 0) | |
120 | if (privsep_send(io, &msg, NULL) != 0) | |
132 | 121 | fatalx("child: privsep_send error"); |
133 | 122 | log_debug3("%s: waiting for exit message from parent", a->name); |
134 | if (privsep_recv(io, &msg, NULL, 0) != 0) | |
123 | if (privsep_recv(io, &msg, NULL) != 0) | |
135 | 124 | fatalx("child: privsep_recv error"); |
136 | 125 | if (msg.type != MSG_EXIT) |
137 | 126 | fatalx("child: unexpected message"); |
165 | 154 | } |
166 | 155 | |
167 | 156 | 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, entry); | |
185 | TAILQ_INSERT_TAIL(&deliverq, mctx, 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, entry); | |
191 | TAILQ_INSERT_TAIL(&doneq, mctx, entry); | |
192 | ||
193 | /* | |
194 | * Destroy mail data now it is finished, just keep the mail | |
195 | * structure. | |
196 | */ | |
197 | shm_destroy(&mctx->mail->shm); | |
198 | break; | |
199 | } | |
200 | ||
201 | return (0); | |
202 | } | |
203 | ||
204 | int | |
205 | run_deliver(struct account *a, struct io *io, int *blocked, const char **cause) | |
206 | { | |
207 | struct match_ctx *mctx; | |
208 | struct deliver_ctx *dctx; | |
209 | struct msg msg; | |
210 | void *buf; | |
211 | size_t len; | |
212 | ||
213 | *blocked = 0; | |
214 | if (TAILQ_EMPTY(&deliverq)) | |
215 | return (0); | |
216 | ||
217 | mctx = TAILQ_FIRST(&deliverq); | |
218 | if (TAILQ_EMPTY(&mctx->dqueue)) { | |
219 | /* delivery done. return to match queue */ | |
220 | log_debug3("%s: returning to match queue", a->name); | |
221 | TAILQ_REMOVE(&deliverq, mctx, entry); | |
222 | TAILQ_INSERT_HEAD(&matchq, mctx, entry); | |
223 | return (0); | |
224 | } | |
225 | ||
226 | /* start the first action */ | |
227 | log_debug3("%s: running deliver queue", a->name); | |
228 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
229 | ||
230 | if (dctx->blocked) { | |
231 | /* check for reply from parent and finish */ | |
232 | if (!privsep_check(io)) { | |
233 | *blocked = 1; | |
234 | return (0); | |
235 | } | |
236 | ||
237 | if (privsep_recv(io, &msg, &buf, &len) != 0) | |
238 | fatalx("child: privsep_recv error"); | |
239 | if (msg.type != MSG_DONE) | |
240 | fatalx("child: unexpected message"); | |
241 | ||
242 | if (finish_action(dctx, &msg, buf, len) != 0) { | |
243 | *cause = "delivery"; | |
244 | return (1); | |
245 | } | |
246 | } else { | |
247 | if (start_action(mctx->io, dctx) != 0) { | |
248 | *cause = "delivery"; | |
249 | return (1); | |
250 | } | |
251 | if (dctx->blocked) { | |
252 | *blocked = 1; | |
253 | return (0); | |
254 | } | |
255 | } | |
256 | ||
257 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); | |
258 | log_debug("%s: message %u delivered (rule %u, %s) after %.3f seconds", | |
259 | a->name, mctx->mail->idx, dctx->rule->idx, | |
260 | dctx->action->deliver->name, get_time() - dctx->tim); | |
261 | xfree(dctx); | |
262 | return (0); | |
263 | } | |
264 | ||
265 | int | |
266 | run_done(struct account *a, int *dropped, int *kept, const char **cause) | |
267 | { | |
268 | struct match_ctx *mctx; | |
269 | struct mail *m; | |
270 | int error = 0; | |
271 | const char *type; | |
272 | ||
273 | if (TAILQ_EMPTY(&doneq)) | |
274 | return (0); | |
275 | ||
276 | mctx = TAILQ_FIRST(&doneq); | |
277 | m = mctx->mail; | |
278 | log_debug3("%s: running done queue", a->name); | |
279 | ||
280 | TAILQ_REMOVE(&doneq, mctx, entry); | |
281 | ARRAY_FREE(&mctx->stack); | |
282 | log_debug("%s: message %u done after %.3f seconds", a->name, m->idx, | |
283 | get_time() - mctx->tim); | |
284 | xfree(mctx); | |
285 | ||
286 | if (a->fetch->done != NULL) { | |
287 | switch (m->decision) { | |
288 | case DECISION_DROP: | |
289 | type = "deleting"; | |
290 | (*dropped)++; | |
291 | break; | |
292 | case DECISION_KEEP: | |
293 | type = "keeping"; | |
294 | (*kept)++; | |
157 | fetch_poll(struct io *pio, struct mail_ctx *mctx, int blocked, u_int queued) | |
158 | { | |
159 | struct account *a = mctx->account; | |
160 | static int finished; /* fetch is complete */ | |
161 | static int holding; /* holding for queue to drop */ | |
162 | struct io *rio, *iop[NFDS]; | |
163 | char *cause; | |
164 | u_int n; | |
165 | int timeout, error; | |
166 | ||
167 | n = 1; | |
168 | iop[0] = pio; | |
169 | ||
170 | /* | |
171 | * If the queue is empty and the fetch finished, must be all done. | |
172 | */ | |
173 | if (queued == 0 && finished) | |
174 | return (FETCH_COMPLETE); | |
175 | ||
176 | /* | |
177 | * Update the holding flag. | |
178 | */ | |
179 | if (queued >= MAXMAILQUEUED) | |
180 | holding = 1; | |
181 | if (queued < MINMAILQUEUED) | |
182 | holding = 0; | |
183 | ||
184 | ||
185 | /* | |
186 | * If not finished, try to get a mail. | |
187 | */ | |
188 | if (!finished && !holding) { | |
189 | switch ((error = a->fetch->fetch(a, mctx->mail))) { | |
190 | case FETCH_COMPLETE: | |
191 | finished = 1; | |
192 | return (FETCH_AGAIN); | |
193 | case FETCH_AGAIN: | |
295 | 194 | break; |
296 | 195 | default: |
297 | fatalx("invalid decision"); | |
298 | } | |
299 | log_debug("%s: %s message %u", a->name, type, m->idx); | |
300 | ||
301 | if (a->fetch->done(a, m) != FETCH_SUCCESS) { | |
302 | *cause = type; | |
303 | error = 1; | |
304 | } | |
305 | } | |
306 | ||
307 | mail_destroy(m); | |
308 | xfree(m); | |
309 | ||
310 | return (error); | |
311 | } | |
312 | ||
313 | void | |
314 | flush_queue(struct match_queue *mq) | |
315 | { | |
316 | struct match_ctx *mctx; | |
317 | struct deliver_ctx *dctx; | |
318 | struct mail *m; | |
319 | ||
320 | while (!TAILQ_EMPTY(mq)) { | |
321 | mctx = TAILQ_FIRST(mq); | |
322 | m = mctx->mail; | |
323 | ||
324 | TAILQ_REMOVE(mq, mctx, entry); | |
325 | while (!TAILQ_EMPTY(&mctx->dqueue)) { | |
326 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
327 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); | |
328 | xfree(dctx); | |
329 | } | |
330 | ARRAY_FREE(&mctx->stack); | |
331 | xfree(mctx); | |
332 | ||
333 | mail_destroy(m); | |
334 | xfree(m); | |
335 | } | |
336 | } | |
337 | ||
338 | u_int | |
339 | queue_length(struct match_queue *mq) | |
340 | { | |
341 | struct match_ctx *mctx; | |
342 | u_int n; | |
343 | ||
344 | n = 0; | |
345 | TAILQ_FOREACH(mctx, mq, entry) | |
346 | n++; | |
347 | ||
348 | return (n); | |
349 | } | |
350 | ||
351 | int | |
352 | fetch_poll(struct account *a, int blocked, struct io *pio, struct io **rio) | |
353 | { | |
354 | int timeout; | |
355 | char *cause; | |
356 | struct io *iop[NFDS]; | |
357 | u_int n; | |
358 | ||
359 | n = 1; | |
360 | iop[0] = pio; | |
361 | ||
362 | if (a->fetch->fill != NULL) | |
196 | return (error); | |
197 | } | |
198 | } | |
199 | ||
200 | /* | |
201 | * If the fetch itself not finished, fill in its io list. | |
202 | */ | |
203 | if (!finished && a->fetch->fill != NULL) | |
363 | 204 | a->fetch->fill(a, iop, &n); |
205 | ||
206 | /* | |
207 | * If that didn't add any fds, and we're not blocked for the parent | |
208 | * then skip the poll entirely and tell the caller not to loop to | |
209 | * us again immediately. | |
210 | */ | |
364 | 211 | if (n == 1 && !blocked) |
365 | return (0); | |
366 | ||
212 | return (FETCH_NONE); | |
213 | ||
214 | /* | |
215 | * If the queues are empty, or blocked waiting for the parent, then | |
216 | * let poll block. | |
217 | */ | |
367 | 218 | timeout = 0; |
368 | if (TAILQ_EMPTY(&matchq) && (TAILQ_EMPTY(&deliverq) || blocked)) | |
219 | if (blocked || queued == 0) | |
369 | 220 | timeout = conf.timeout; |
370 | 221 | |
371 | 222 | log_debug3("%s: polling %u fds, timeout=%d", a->name, n, timeout); |
372 | switch (io_polln(iop, n, rio, timeout, &cause)) { | |
223 | switch (io_polln(iop, n, &rio, timeout, &cause)) { | |
373 | 224 | case 0: |
225 | if (rio == pio) | |
226 | fatalx("child: parent socket closed"); | |
374 | 227 | log_warnx("%s: connection unexpectedly closed", a->name); |
375 | 228 | return (1); |
376 | 229 | case -1: |
230 | if (rio == pio) | |
231 | fatalx("child: parent socket error"); | |
377 | 232 | if (errno == EAGAIN) |
378 | return (0); | |
233 | break; | |
379 | 234 | log_warnx("%s: %s", a->name, cause); |
380 | 235 | xfree(cause); |
381 | 236 | return (1); |
382 | 237 | } |
383 | 238 | |
239 | return (FETCH_AGAIN); | |
240 | } | |
241 | ||
242 | int | |
243 | fetch_done(struct mail_ctx *mctx, u_int *dropped, u_int *kept) | |
244 | { | |
245 | struct account *a = mctx->account; | |
246 | struct mail *m = mctx->mail; | |
247 | ||
248 | if (a->fetch->done == NULL) | |
249 | return (0); | |
250 | ||
251 | switch (m->decision) { | |
252 | case DECISION_DROP: | |
253 | (*dropped)++; | |
254 | log_debug("%s: dropping message %u", a->name, m->idx); | |
255 | break; | |
256 | case DECISION_KEEP: | |
257 | (*kept)++; | |
258 | log_debug("%s: keeping message %u", a->name, m->idx); | |
259 | break; | |
260 | default: | |
261 | fatalx("invalid decision"); | |
262 | } | |
263 | ||
264 | if (a->fetch->done(a, m) != FETCH_SUCCESS) | |
265 | return (1); | |
266 | ||
384 | 267 | return (0); |
385 | 268 | } |
386 | 269 | |
387 | 270 | int |
388 | fetch_flush(struct account *a, struct io *pio, int *blocked, int *dropped, | |
389 | int *kept, const char **cause) | |
390 | { | |
391 | while (!TAILQ_EMPTY(&matchq) || !TAILQ_EMPTY(&deliverq)) { | |
392 | if (run_match(a, cause) != 0) | |
271 | fetch_drain(u_int *dropped, u_int *kept) | |
272 | { | |
273 | struct mail_ctx *mctx; | |
274 | ||
275 | while (!TAILQ_EMPTY(&doneq)) { | |
276 | mctx = TAILQ_FIRST(&doneq); | |
277 | if (fetch_done(mctx, dropped, kept) != 0) | |
393 | 278 | return (1); |
394 | if (run_deliver(a, pio, blocked, cause) != 0) | |
395 | return (1); | |
396 | ||
397 | if (!TAILQ_EMPTY(&deliverq) && *blocked) { | |
398 | pio->flags &= ~IO_NOWAIT; | |
399 | if (!TAILQ_EMPTY(&matchq)) | |
400 | pio->flags |= IO_NOWAIT; | |
401 | switch (io_poll(pio, NULL)) { | |
402 | case 0: | |
403 | fatalx("child: parent socket closed"); | |
404 | case -1: | |
405 | if (errno == EAGAIN) | |
406 | break; | |
407 | fatalx("child: parent socket error"); | |
279 | ||
280 | TAILQ_REMOVE(&doneq, mctx, entry); | |
281 | ||
282 | mail_destroy(mctx->mail); | |
283 | xfree(mctx->mail); | |
284 | ||
285 | xfree(mctx); | |
286 | } | |
287 | ||
288 | return (0); | |
289 | } | |
290 | ||
291 | int | |
292 | fetch_match(int *blocked, u_int *queued, struct msg *msg, struct msgbuf *msgbuf) | |
293 | { | |
294 | struct mail_ctx *mctx; | |
295 | ||
296 | if (TAILQ_EMPTY(&matchq)) | |
297 | return (0); | |
298 | ||
299 | mctx = TAILQ_FIRST(&matchq); | |
300 | switch (mail_match(mctx, msg, msgbuf)) { | |
301 | case MAIL_ERROR: | |
302 | return (1); | |
303 | case MAIL_DELIVER: | |
304 | TAILQ_REMOVE(&matchq, mctx, entry); | |
305 | TAILQ_INSERT_TAIL(&deliverq, mctx, entry); | |
306 | break; | |
307 | case MAIL_DONE: | |
308 | TAILQ_REMOVE(&matchq, mctx, entry); | |
309 | TAILQ_INSERT_TAIL(&doneq, mctx, entry); | |
310 | (*queued)--; | |
311 | break; | |
312 | case MAIL_BLOCKED: | |
313 | *blocked = 1; | |
314 | break; | |
315 | } | |
316 | ||
317 | return (0); | |
318 | } | |
319 | ||
320 | int | |
321 | fetch_deliver(int *blocked, struct msg *msg, struct msgbuf *msgbuf) | |
322 | { | |
323 | struct mail_ctx *mctx; | |
324 | ||
325 | if (TAILQ_EMPTY(&deliverq)) | |
326 | return (0); | |
327 | ||
328 | mctx = TAILQ_FIRST(&deliverq); | |
329 | switch (mail_deliver(mctx, msg, msgbuf)) { | |
330 | case MAIL_ERROR: | |
331 | return (1); | |
332 | case MAIL_MATCH: | |
333 | TAILQ_REMOVE(&deliverq, mctx, entry); | |
334 | TAILQ_INSERT_TAIL(&matchq, mctx, entry); | |
335 | break; | |
336 | case MAIL_BLOCKED: | |
337 | *blocked = 1; | |
338 | break; | |
339 | } | |
340 | ||
341 | return (0); | |
342 | } | |
343 | ||
344 | int | |
345 | fetch_account(struct io *pio, struct account *a, double tim) | |
346 | { | |
347 | struct mail *m; | |
348 | struct mail_ctx *mctx; | |
349 | struct msg msg, *msgp; | |
350 | struct msgbuf msgbuf; | |
351 | u_int n, queued, dropped, kept; | |
352 | int error, blocked; | |
353 | ||
354 | log_debug2("%s: fetching", a->name); | |
355 | TAILQ_INIT(&matchq); | |
356 | TAILQ_INIT(&deliverq); | |
357 | TAILQ_INIT(&doneq); | |
358 | ||
359 | mctx = NULL; | |
360 | m = NULL; | |
361 | n = queued = dropped = kept = 0; | |
362 | for (;;) { | |
363 | /* | |
364 | * If the last context was queued (mail received successfully), | |
365 | * make a new one. | |
366 | */ | |
367 | if (mctx == NULL) { | |
368 | m = xcalloc(1, sizeof *m); | |
369 | m->body = -1; | |
370 | m->decision = DECISION_DROP; | |
371 | m->idx = ++a->idx; | |
372 | m->tim = get_time(); | |
373 | ||
374 | mctx = xcalloc(1, sizeof *mctx); | |
375 | mctx->account = a; | |
376 | mctx->mail = m; | |
377 | mctx->msgid = 0; | |
378 | mctx->done = 0; | |
379 | ||
380 | mctx->matched = 0; | |
381 | ||
382 | mctx->account = a; | |
383 | mctx->io = pio; | |
384 | ||
385 | mctx->rule = TAILQ_FIRST(&conf.rules); | |
386 | TAILQ_INIT(&mctx->dqueue); | |
387 | ARRAY_INIT(&mctx->stack); | |
388 | } | |
389 | ||
390 | /* | |
391 | * Loop handling mails. | |
392 | */ | |
393 | error = FETCH_AGAIN; | |
394 | msgp = NULL; | |
395 | while (error == FETCH_AGAIN) { | |
396 | blocked = 0; | |
397 | ||
398 | /* | |
399 | * Match a mail. | |
400 | */ | |
401 | if (fetch_match(&blocked, &queued, msgp,&msgbuf) != 0) { | |
402 | error = FETCH_ERROR; | |
403 | goto out; | |
408 | 404 | } |
409 | } | |
410 | ||
411 | if (run_done(a, dropped, kept, cause) != 0) | |
412 | return (1); | |
413 | } | |
414 | ||
415 | while (!TAILQ_EMPTY(&doneq)) { | |
416 | if (run_done(a, dropped, kept, cause) != 0) | |
417 | return (1); | |
418 | } | |
419 | ||
420 | return (0); | |
421 | } | |
422 | ||
423 | int | |
424 | fetch_account(struct io *pio, struct account *a, double tim) | |
425 | { | |
426 | struct mail *m; | |
427 | u_int n, dropped, kept, total; | |
428 | int error, blocked, holding; | |
429 | const char *cause = NULL; | |
430 | struct match_ctx *mctx; | |
431 | struct io *rio; | |
432 | ||
433 | log_debug2("%s: fetching", a->name); | |
434 | ||
435 | TAILQ_INIT(&matchq); | |
436 | TAILQ_INIT(&deliverq); | |
437 | TAILQ_INIT(&doneq); | |
438 | ||
439 | n = dropped = kept = 0; | |
440 | m = NULL; | |
441 | blocked = 0; | |
442 | for (;;) { | |
443 | m = xcalloc(1, sizeof *m); | |
444 | m->body = -1; | |
445 | m->decision = DECISION_DROP; | |
446 | m->done = 0; | |
447 | m->idx = ++a->idx; | |
448 | m->tim = get_time(); | |
449 | ||
450 | /* fetch a message */ | |
451 | error = FETCH_AGAIN; | |
452 | rio = NULL; | |
453 | holding = 0; | |
454 | while (error == FETCH_AGAIN) { | |
455 | total = queue_length(&matchq) + queue_length(&deliverq); | |
456 | if (total >= MAXMAILQUEUED) | |
457 | holding = 1; | |
458 | if (total < MINMAILQUEUED) | |
459 | holding = 0; | |
460 | ||
461 | log_debug3("%s: queue %u; blocked=%d; holding=%d", | |
462 | a->name, total, blocked, holding); | |
463 | ||
464 | if (!holding) { | |
465 | if (rio != pio) { | |
466 | error = a->fetch->fetch(a, m); | |
467 | switch (error) { | |
468 | case FETCH_ERROR: | |
469 | if (rio != pio) { | |
470 | cause = "fetching"; | |
471 | goto out; | |
472 | } | |
473 | fatalx("child: lost parent"); | |
474 | case FETCH_COMPLETE: | |
475 | goto out; | |
476 | } | |
477 | } | |
405 | ||
406 | /* | |
407 | * Deliver a mail. | |
408 | */ | |
409 | if (fetch_deliver(&blocked, msgp, &msgbuf) != 0) { | |
410 | error = FETCH_ERROR; | |
411 | goto out; | |
478 | 412 | } |
479 | if (error == FETCH_AGAIN) { | |
480 | if (fetch_poll(a, blocked, pio,&rio) != 0) | |
481 | goto out; | |
482 | } | |
483 | ||
484 | if (run_match(a, &cause) != 0) | |
413 | ||
414 | /* | |
415 | * Poll for new mails. | |
416 | */ | |
417 | log_debug3("%s: queued %u; blocked=%d", a->name, queued, | |
418 | blocked); | |
419 | error = fetch_poll(pio, mctx, blocked, queued); | |
420 | if (error == FETCH_ERROR || error == FETCH_COMPLETE) | |
485 | 421 | goto out; |
486 | if (run_deliver(a, pio, &blocked, &cause) != 0) | |
487 | goto out; | |
488 | } | |
489 | ||
490 | log_debug("%s: message %u fetched after %.3f seconds", a->name, | |
491 | m->idx, get_time() - m->tim); | |
492 | ||
493 | if (error != FETCH_OVERSIZE && error != FETCH_EMPTY) { | |
422 | ||
423 | /* | |
424 | * Check for new privsep messages. | |
425 | */ | |
426 | msgp = NULL; | |
427 | if (!privsep_check(pio)) | |
428 | continue; | |
429 | if (privsep_recv(pio, &msg, &msgbuf) != 0) | |
430 | fatalx("child: privsep_recv error"); | |
431 | log_debug3("%s: got message type %d, id %u", a->name, | |
432 | msg.type, msg.id); | |
433 | msgp = &msg; | |
434 | } | |
435 | ||
436 | /* | |
437 | * Trim "From " line. | |
438 | */ | |
439 | if (error == FETCH_SUCCESS) { | |
494 | 440 | trim_from(m); |
495 | 441 | if (m->size == 0) |
496 | 442 | error = FETCH_EMPTY; |
497 | 443 | } |
498 | 444 | |
445 | /* | |
446 | * And handle the return code. | |
447 | */ | |
499 | 448 | switch (error) { |
500 | 449 | case FETCH_EMPTY: |
501 | 450 | log_warnx("%s: empty message", a->name); |
502 | cause = "fetching"; | |
451 | error = FETCH_ERROR; | |
503 | 452 | goto out; |
504 | 453 | case FETCH_OVERSIZE: |
505 | 454 | log_warnx("%s: message too big: %zu bytes (limit %zu)", |
506 | 455 | a->name, m->size, conf.max_size); |
507 | if (conf.del_big) | |
456 | if (conf.del_big) { | |
457 | /* | |
458 | * Queue on the done queue and destroy the | |
459 | * mail file. | |
460 | */ | |
461 | TAILQ_INSERT_TAIL(&doneq, mctx, entry); | |
462 | shm_destroy(&mctx->mail->shm); | |
463 | ||
464 | /* | |
465 | * Set error to success to allocate a new | |
466 | * context at the start of the loop. | |
467 | */ | |
468 | mctx = NULL; | |
508 | 469 | break; |
509 | cause = "fetching"; | |
470 | } | |
471 | error = FETCH_ERROR; | |
510 | 472 | goto out; |
511 | } | |
512 | ||
513 | log_debug("%s: got message %u: size %zu, body %zd", a->name, | |
514 | m->idx, m->size, m->body); | |
515 | fetch_transform(a, m); | |
516 | ||
517 | /* construct mctx */ | |
518 | mctx = xcalloc(1, sizeof *mctx); | |
519 | mctx->tim = get_time(); | |
520 | mctx->io = pio; | |
521 | mctx->account = a; | |
522 | mctx->mail = m; | |
523 | ARRAY_INIT(&mctx->stack); | |
524 | mctx->rule = TAILQ_FIRST(&conf.rules); | |
525 | mctx->matched = mctx->stopped = 0; | |
526 | TAILQ_INIT(&mctx->dqueue); | |
527 | m = NULL; /* clear m to avoid double-free if out later */ | |
528 | ||
529 | /* and queue it */ | |
530 | log_debug3("%s: adding to match queue", a->name); | |
531 | TAILQ_INSERT_TAIL(&matchq, mctx, entry); | |
532 | ||
533 | /* finish up a done mail */ | |
534 | if (run_done(a, &dropped, &kept, &cause) != 0) | |
473 | case FETCH_SUCCESS: | |
474 | /* | |
475 | * Got a mail: modify it and queue it. | |
476 | */ | |
477 | log_debug("%s: got message %u after %.3f seconds: " | |
478 | "size %zu, body %zd", a->name, m->idx, | |
479 | get_time() - m->tim, m->size, m->body); | |
480 | fetch_transform(a, m); | |
481 | TAILQ_INSERT_TAIL(&matchq, mctx, entry); | |
482 | mctx = NULL; | |
483 | queued++; | |
484 | break; | |
485 | } | |
486 | ||
487 | /* | |
488 | * Empty the done queue. Can get here either from FETCH_SUCCESS | |
489 | * or FETCH_NONE. | |
490 | */ | |
491 | if (fetch_drain(&dropped, &kept) != 0) { | |
492 | error = FETCH_ERROR; | |
535 | 493 | goto out; |
536 | if (queue_length(&doneq) > MAXMAILQUEUED) { | |
537 | while (queue_length(&doneq) > MINMAILQUEUED) { | |
538 | if (run_done(a, &dropped, &kept, &cause) != 0) | |
539 | goto out; | |
540 | } | |
541 | } | |
542 | ||
494 | } | |
495 | ||
496 | /* | |
497 | * Purge if necessary. | |
498 | */ | |
543 | 499 | if (conf.purge_after == 0 || a->fetch->purge == NULL) |
544 | 500 | continue; |
545 | 501 | |
502 | #if 0 | |
546 | 503 | n++; |
547 | 504 | if (n >= conf.purge_after) { |
548 | 505 | log_debug("%s: got %u mails, purging", a->name, n); |
506 | n = 0; | |
549 | 507 | |
550 | 508 | /* |
551 | 509 | * Must empty queues before purge to make sure things |
552 | 510 | * like POP3 indexing don't get ballsed up. |
553 | 511 | */ |
554 | if (fetch_flush(a, pio, &blocked, &dropped, &kept, | |
555 | &cause) != 0) | |
556 | goto out; | |
557 | ||
558 | if (a->fetch->purge(a) != FETCH_SUCCESS) { | |
559 | cause = "purging"; | |
560 | goto out; | |
561 | } | |
562 | ||
563 | n = 0; | |
564 | } | |
512 | if (fetch_flush(void) != 0) | |
513 | break; | |
514 | if (a->fetch->purge(a) != FETCH_SUCCESS) | |
515 | break; | |
516 | } | |
517 | #endif | |
565 | 518 | } |
566 | 519 | |
567 | 520 | out: |
568 | if (m != NULL) { | |
521 | if (mctx != NULL) { | |
569 | 522 | mail_destroy(m); |
570 | 523 | xfree(m); |
571 | } | |
572 | ||
573 | if (cause == NULL) | |
574 | fetch_flush(a, pio, &blocked, &dropped, &kept, &cause); | |
575 | if (cause != NULL) { | |
576 | flush_queue(&matchq); | |
577 | flush_queue(&deliverq); | |
578 | flush_queue(&doneq); | |
579 | ||
580 | log_warnx("%s: %s error. aborted", a->name, cause); | |
581 | } | |
582 | ||
524 | ||
525 | xfree(mctx); | |
526 | } | |
527 | ||
528 | /* | |
529 | * Drain the done queue if not an error. | |
530 | */ | |
531 | if (error != FETCH_ERROR) { | |
532 | if (fetch_drain(&dropped, &kept) != 0) | |
533 | error = FETCH_ERROR; | |
534 | } | |
535 | ||
536 | /* | |
537 | * Report error and free queues. | |
538 | */ | |
539 | if (error == FETCH_ERROR) { | |
540 | log_warnx("%s: fetching error. aborted", a->name); | |
541 | /* XXX fetch_free(); */ | |
542 | } | |
543 | ||
583 | 544 | tim = get_time() - tim; |
584 | 545 | n = dropped + kept; |
585 | 546 | if (n > 0) { |
586 | 547 | log_info("%s: %u messages processed (%u kept) in %.3f seconds " |
587 | 548 | "(average %.3f)", a->name, n, kept, tim, tim / n); |
588 | } else { | |
589 | log_info("%s: %u messages processed in %.3f seconds", | |
590 | a->name, n, tim); | |
591 | } | |
592 | ||
593 | return (cause != NULL); | |
549 | return (error != 1); | |
550 | } | |
551 | ||
552 | log_info("%s: %u messages processed in %.3f seconds", a->name, n, tim); | |
553 | return (error == FETCH_ERROR); | |
594 | 554 | } |
595 | 555 | |
596 | 556 | int |
637 | 597 | |
638 | 598 | return (FETCH_SUCCESS); |
639 | 599 | } |
640 | ||
641 | int | |
642 | fetch_rule(struct match_ctx *mctx, const char **cause) | |
643 | { | |
644 | struct account *a = mctx->account; | |
645 | struct strings *aa; | |
646 | struct mail *m = mctx->mail; | |
647 | struct rule *r = mctx->rule; | |
648 | u_int i; | |
649 | int error; | |
650 | char *tkey, *tvalue; | |
651 | ||
652 | /* matching finished */ | |
653 | if (m->done) { | |
654 | if (conf.keep_all || a->keep) | |
655 | m->decision = DECISION_KEEP; | |
656 | return (FETCH_COMPLETE); | |
657 | } | |
658 | ||
659 | /* end of ruleset reached */ | |
660 | if (r == NULL) { | |
661 | switch (conf.impl_act) { | |
662 | case DECISION_NONE: | |
663 | log_warnx("%s: reached end of ruleset. no " | |
664 | "unmatched-mail option; keeping mail", a->name); | |
665 | m->decision = DECISION_KEEP; | |
666 | break; | |
667 | case DECISION_KEEP: | |
668 | log_debug2("%s: reached end of ruleset. keeping mail", | |
669 | a->name); | |
670 | m->decision = DECISION_KEEP; | |
671 | break; | |
672 | case DECISION_DROP: | |
673 | log_debug2("%s: reached end of ruleset. dropping mail", | |
674 | a->name); | |
675 | m->decision = DECISION_DROP; | |
676 | break; | |
677 | } | |
678 | m->done = 1; | |
679 | return (FETCH_SUCCESS); | |
680 | } | |
681 | ||
682 | mctx->rule = TAILQ_NEXT(mctx->rule, entry); | |
683 | while (mctx->rule == NULL) { | |
684 | if (ARRAY_EMPTY(&mctx->stack)) | |
685 | break; | |
686 | mctx->rule = ARRAY_LAST(&mctx->stack, struct rule *); | |
687 | mctx->rule = TAILQ_NEXT(mctx->rule, entry); | |
688 | ARRAY_TRUNC(&mctx->stack, 1, struct rule *); | |
689 | } | |
690 | ||
691 | aa = r->accounts; | |
692 | if (!ARRAY_EMPTY(aa)) { | |
693 | for (i = 0; i < ARRAY_LENGTH(aa); i++) { | |
694 | if (name_match(ARRAY_ITEM(aa, i, char *), a->name)) | |
695 | break; | |
696 | } | |
697 | if (i == ARRAY_LENGTH(aa)) | |
698 | return (FETCH_SUCCESS); | |
699 | } | |
700 | ||
701 | /* match all the regexps */ | |
702 | switch (r->type) { | |
703 | case RULE_EXPRESSION: | |
704 | /* combine wrapped lines */ | |
705 | set_wrapped(m, ' '); | |
706 | ||
707 | /* perform the expression */ | |
708 | if ((error = do_expr(r, mctx)) == -1) { | |
709 | *cause = "matching"; | |
710 | return (FETCH_ERROR); | |
711 | } | |
712 | ||
713 | /* continue if no match */ | |
714 | if (!error) | |
715 | return (FETCH_SUCCESS); | |
716 | break; | |
717 | case RULE_ALL: | |
718 | break; | |
719 | } | |
720 | ||
721 | /* reset wrapped lines */ | |
722 | set_wrapped(m, '\n'); | |
723 | ||
724 | /* report rule number */ | |
725 | if (TAILQ_EMPTY(&r->rules)) | |
726 | log_debug2("%s: matched to rule %u", a->name, r->idx); | |
727 | else | |
728 | log_debug2("%s: matched to rule %u (nested)", a->name, r->idx); | |
729 | ||
730 | /* deal with nested rules */ | |
731 | if (!TAILQ_EMPTY(&r->rules)) { | |
732 | log_debug2("%s: entering nested rules", a->name); | |
733 | ARRAY_ADD(&mctx->stack, r, struct rule *); | |
734 | mctx->rule = TAILQ_FIRST(&r->rules); | |
735 | return (FETCH_SUCCESS); | |
736 | } | |
737 | ||
738 | /* tag mail if needed */ | |
739 | if (r->key.str != NULL) { | |
740 | tkey = replacestr(&r->key, m->tags, m, &m->rml); | |
741 | tvalue = replacestr(&r->value, m->tags, m, &m->rml); | |
742 | ||
743 | if (tkey != NULL && *tkey != '\0' && tvalue != NULL) { | |
744 | log_debug2("%s: tagging message: %s (%s)", | |
745 | a->name, tkey, tvalue); | |
746 | add_tag(&m->tags, tkey, "%s", tvalue); | |
747 | } | |
748 | ||
749 | if (tkey != NULL) | |
750 | xfree(tkey); | |
751 | if (tvalue != NULL) | |
752 | xfree(tvalue); | |
753 | } | |
754 | ||
755 | /* if this rule is marked as stop, mark the mail as done */ | |
756 | if (r->stop) | |
757 | m->done = 1; | |
758 | ||
759 | /* handle delivery */ | |
760 | if (r->actions != NULL) { | |
761 | log_debug2("%s: delivering message", a->name); | |
762 | mctx->matched = 1; | |
763 | if (do_deliver(r, mctx) != 0) { | |
764 | *cause = "delivery"; | |
765 | return (FETCH_ERROR); | |
766 | } | |
767 | return (FETCH_AGAIN); | |
768 | } | |
769 | ||
770 | return (FETCH_SUCCESS); | |
771 | } | |
772 | ||
773 | int | |
774 | do_expr(struct rule *r, struct match_ctx *mctx) | |
775 | { | |
776 | int fres, cres; | |
777 | struct expritem *ei; | |
778 | char desc[DESCBUFSIZE]; | |
779 | ||
780 | fres = 0; | |
781 | TAILQ_FOREACH(ei, r->expr, entry) { | |
782 | cres = ei->match->match(mctx, ei); | |
783 | if (cres == MATCH_ERROR) | |
784 | return (-1); | |
785 | cres = cres == MATCH_TRUE; | |
786 | if (ei->inverted) | |
787 | cres = !cres; | |
788 | switch (ei->op) { | |
789 | case OP_NONE: | |
790 | case OP_OR: | |
791 | fres = fres || cres; | |
792 | break; | |
793 | case OP_AND: | |
794 | fres = fres && cres; | |
795 | break; | |
796 | } | |
797 | ||
798 | ei->match->desc(ei, desc, sizeof desc); | |
799 | log_debug2("%s: tried %s%s, got %d", mctx->account->name, | |
800 | ei->inverted ? "not " : "", desc, cres); | |
801 | } | |
802 | ||
803 | return (fres); | |
804 | } | |
805 | ||
806 | int | |
807 | do_deliver(struct rule *r, struct match_ctx *mctx) | |
808 | { | |
809 | struct account *a = mctx->account; | |
810 | struct mail *m = mctx->mail; | |
811 | struct action *t; | |
812 | struct actions *ta; | |
813 | u_int i, j, k; | |
814 | char *s; | |
815 | struct replstr *rs; | |
816 | struct deliver_ctx *dctx; | |
817 | struct strings *users; | |
818 | int should_free; | |
819 | ||
820 | for (i = 0; i < ARRAY_LENGTH(r->actions); i++) { | |
821 | rs = &ARRAY_ITEM(r->actions, i, struct replstr); | |
822 | s = replacestr(rs, m->tags, m, &m->rml); | |
823 | ||
824 | log_debug2("%s: looking for actions matching: %s", a->name, s); | |
825 | ta = match_actions(s); | |
826 | if (ARRAY_EMPTY(ta)) | |
827 | goto empty; | |
828 | xfree(s); | |
829 | ||
830 | log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta)); | |
831 | for (j = 0; j < ARRAY_LENGTH(ta); j++) { | |
832 | t = ARRAY_ITEM(ta, j, struct action *); | |
833 | users = get_users(mctx, r, t, &should_free); | |
834 | ||
835 | for (k = 0; k < ARRAY_LENGTH(users); k++) { | |
836 | dctx = xmalloc(sizeof *dctx); | |
837 | dctx->action = t; | |
838 | dctx->account = a; | |
839 | dctx->rule = r; | |
840 | dctx->mail = m; | |
841 | dctx->uid = ARRAY_ITEM(users, k, uid_t); | |
842 | dctx->blocked = 0; | |
843 | ||
844 | TAILQ_INSERT_TAIL(&mctx->dqueue, dctx, entry); | |
845 | } | |
846 | ||
847 | if (should_free) | |
848 | ARRAY_FREEALL(users); | |
849 | } | |
850 | ||
851 | ARRAY_FREEALL(ta); | |
852 | } | |
853 | ||
854 | return (0); | |
855 | ||
856 | empty: | |
857 | xfree(s); | |
858 | ARRAY_FREEALL(ta); | |
859 | log_warnx("%s: no actions matching: %s (%s)", a->name, s, rs->str); | |
860 | return (1); | |
861 | } | |
862 | ||
863 | struct strings * | |
864 | get_users(struct match_ctx *mctx, struct rule *r, struct action *t, | |
865 | int *should_free) | |
866 | { | |
867 | struct account *a = mctx->account; | |
868 | struct mail *m = mctx->mail; | |
869 | struct strings *users; | |
870 | ||
871 | *should_free = 0; | |
872 | users = NULL; | |
873 | if (r->find_uid) { /* rule comes first */ | |
874 | *should_free = 1; | |
875 | users = find_users(m); | |
876 | } else if (r->users != NULL) { | |
877 | *should_free = 0; | |
878 | users = r->users; | |
879 | } else if (t->find_uid) { /* then action */ | |
880 | *should_free = 1; | |
881 | users = find_users(m); | |
882 | } else if (t->users != NULL) { | |
883 | *should_free = 0; | |
884 | users = t->users; | |
885 | } else if (a->find_uid) { /* then account */ | |
886 | *should_free = 1; | |
887 | users = find_users(m); | |
888 | } else if (a->users != NULL) { | |
889 | *should_free = 0; | |
890 | users = a->users; | |
891 | } | |
892 | if (users == NULL) { | |
893 | *should_free = 1; | |
894 | users = xmalloc(sizeof *users); | |
895 | ARRAY_INIT(users); | |
896 | ARRAY_ADD(users, conf.def_user, uid_t); | |
897 | } | |
898 | ||
899 | return (users); | |
900 | } | |
901 | ||
902 | int | |
903 | start_action(struct io *io, struct deliver_ctx *dctx) | |
904 | { | |
905 | struct account *a = dctx->account; | |
906 | struct action *t = dctx->action; | |
907 | struct mail *m = dctx->mail; | |
908 | struct mail *md = &dctx->wr_mail; | |
909 | struct msg msg; | |
910 | u_int lines; | |
911 | ||
912 | dctx->tim = get_time(); | |
913 | if (t->deliver->deliver == NULL) | |
914 | return (0); | |
915 | ||
916 | log_debug2("%s: message %u, running action %s as user %lu", | |
917 | a->name, m->idx, t->name, (u_long) dctx->uid); | |
918 | add_tag(&m->tags, "action", "%s", t->name); | |
919 | ||
920 | /* just deliver now for in-child delivery */ | |
921 | if (t->deliver->type == DELIVER_INCHILD) { | |
922 | dctx->blocked = 0; | |
923 | if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS) | |
924 | return (1); | |
925 | return (0); | |
926 | } | |
927 | ||
928 | #if 0 | |
929 | /* if the current user is the same as the deliver user, don't bother | |
930 | passing up either */ | |
931 | if (t->deliver->type == DELIVER_ASUSER && dctx->uid == geteuid()) { | |
932 | dctx->blocked = 0; | |
933 | if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS) | |
934 | return (1); | |
935 | return (0); | |
936 | } | |
937 | if (t->deliver->type == DELIVER_WRBACK && dctx->uid == geteuid()) { | |
938 | dctx->blocked = 0; | |
939 | ||
940 | mail_open(md, IO_BLOCKSIZE); | |
941 | md->decision = m->decision; | |
942 | ||
943 | if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS) { | |
944 | mail_destroy(md); | |
945 | return (1); | |
946 | } | |
947 | ||
948 | memcpy(&msg.data.mail, md, sizeof msg.data.mail); | |
949 | cleanup_deregister(md->shm.name); | |
950 | strb_destroy(&md->tags); | |
951 | ||
952 | mail_receive(m, &msg); | |
953 | log_debug2("%s: received modified mail: size %zu, body %zd", | |
954 | a->name, m->size, m->body); | |
955 | ||
956 | /* trim from line */ | |
957 | trim_from(m); | |
958 | ||
959 | /* and recreate the wrapped array */ | |
960 | lines = fill_wrapped(m); | |
961 | log_debug2("%s: found %u wrapped lines", a->name, lines); | |
962 | ||
963 | return (0); | |
964 | } | |
965 | #endif | |
966 | ||
967 | memset(&msg, 0, sizeof msg); | |
968 | msg.type = MSG_ACTION; | |
969 | ||
970 | msg.data.account = a; | |
971 | msg.data.action = t; | |
972 | msg.data.uid = dctx->uid; | |
973 | ||
974 | mail_send(m, &msg); | |
975 | ||
976 | log_debug3("%s: sending action to parent", a->name); | |
977 | if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0) | |
978 | fatalx("child: privsep_send error"); | |
979 | dctx->blocked = 1; | |
980 | ||
981 | return (0); | |
982 | } | |
983 | ||
984 | int | |
985 | finish_action(struct deliver_ctx *dctx, struct msg *msg, void *buf, size_t len) | |
986 | { | |
987 | struct account *a = dctx->account; | |
988 | struct action *t = dctx->action; | |
989 | struct mail *m = dctx->mail; | |
990 | u_int lines; | |
991 | ||
992 | if (buf == NULL || len == 0) | |
993 | fatalx("child: bad tags"); | |
994 | strb_destroy(&m->tags); | |
995 | m->tags = buf; | |
996 | update_tags(&m->tags); | |
997 | ||
998 | if (msg->data.error != 0) | |
999 | return (1); | |
1000 | ||
1001 | if (t->deliver->type != DELIVER_WRBACK) | |
1002 | return (0); | |
1003 | ||
1004 | mail_receive(m, msg); | |
1005 | log_debug2("%s: message %u, received modified mail: size %zu, body %zd", | |
1006 | a->name, m->idx, m->size, m->body); | |
1007 | ||
1008 | /* trim from line */ | |
1009 | trim_from(m); | |
1010 | ||
1011 | /* and recreate the wrapped array */ | |
1012 | lines = fill_wrapped(m); | |
1013 | log_debug2("%s: found %u wrapped lines", a->name, lines); | |
1014 | ||
1015 | return (0); | |
1016 | } |
74 | 74 | |
75 | 75 | struct child * |
76 | 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), | |
77 | struct io *), int (*msg)(struct child *, struct msg *, struct msgbuf *), | |
78 | 78 | void *data) |
79 | 79 | { |
80 | 80 | struct child *child, *childp; |
24 | 24 | |
25 | 25 | /* Deliver context. */ |
26 | 26 | struct deliver_ctx { |
27 | double tim; | |
27 | double tim; | |
28 | 28 | |
29 | struct action *action; | |
30 | struct rule *rule; | |
29 | struct action *action; | |
30 | struct rule *rule; | |
31 | 31 | |
32 | struct account *account; | |
33 | struct mail *mail; | |
32 | struct account *account; | |
33 | struct mail *mail; | |
34 | 34 | |
35 | uid_t uid; | |
35 | uid_t uid; | |
36 | 36 | |
37 | struct mail wr_mail; | |
37 | struct mail wr_mail; | |
38 | 38 | |
39 | int blocked; /* blocked waiting for parent */ | |
40 | TAILQ_ENTRY(deliver_ctx) entry; | |
39 | TAILQ_ENTRY(deliver_ctx) entry; | |
41 | 40 | }; |
42 | TAILQ_HEAD(deliver_queue, deliver_ctx); | |
43 | 41 | |
44 | 42 | /* Delivery types. */ |
45 | 43 | enum delivertype { |
277 | 277 | pid_t pid; |
278 | 278 | struct children children, dead_children; |
279 | 279 | struct child *child; |
280 | void *buf; | |
281 | size_t len; | |
282 | 280 | struct io **ios, *io; |
283 | 281 | double tim; |
284 | 282 | struct sigaction act; |
285 | 283 | struct msg msg; |
284 | struct msgbuf msgbuf; | |
286 | 285 | size_t off; |
287 | 286 | struct macro *macro; |
288 | 287 | struct child_fetch_data *cfd; |
734 | 733 | break; |
735 | 734 | |
736 | 735 | /* and handle them if necessary */ |
737 | if (privsep_recv(child->io, &msg, &buf, &len) != 0) | |
736 | if (privsep_recv(child->io, &msg, &msgbuf) != 0) | |
738 | 737 | fatalx("parent: privsep_recv error"); |
739 | if (child->msg(child, &msg, buf, len) == 0) | |
738 | log_debug3("parent: got message type %d, id %u from " | |
739 | "child %ld", msg.type, msg.id, (long) child->pid); | |
740 | ||
741 | if (child->msg(child, &msg, &msgbuf) == 0) | |
740 | 742 | continue; |
741 | 743 | |
742 | 744 | /* child has said it is ready to exit, tell it to */ |
743 | 745 | memset(&msg, 0, sizeof msg); |
744 | 746 | msg.type = MSG_EXIT; |
745 | if (privsep_send(child->io, &msg, NULL, 0) != 0) | |
747 | if (privsep_send(child->io, &msg, NULL) != 0) | |
746 | 748 | fatalx("parent: privsep_send error"); |
747 | 749 | |
748 | /* wait for the child */ | |
750 | /* wait for the child */ | |
749 | 751 | if (waitpid(child->pid, &status, 0) == -1) |
750 | 752 | fatal("waitpid"); |
751 | 753 | if (WIFSIGNALED(status)) { |
354 | 354 | |
355 | 355 | /* A single mail. */ |
356 | 356 | struct mail { |
357 | u_int idx; | |
357 | 358 | double tim; |
358 | u_int idx; | |
359 | 359 | |
360 | 360 | struct strb *tags; |
361 | 361 | |
379 | 379 | /* XXX move below into special struct and just cp it in mail_*? */ |
380 | 380 | struct rmlist rml; /* regexp matches */ |
381 | 381 | |
382 | int done; /* mail is finished with */ | |
383 | 382 | enum decision decision; /* final deliver decision */ |
384 | 383 | |
385 | 384 | void (*auxfree)(void *); |
386 | 385 | void *auxdata; |
387 | 386 | }; |
387 | ||
388 | /* Mail fetch/delivery return codes. */ | |
389 | #define MAIL_CONTINUE 0 | |
390 | #define MAIL_DELIVER 1 | |
391 | #define MAIL_MATCH 2 | |
392 | #define MAIL_ERROR 3 | |
393 | #define MAIL_BLOCKED 4 | |
394 | #define MAIL_DONE 5 | |
395 | ||
396 | /* Mail fetch/delivery context. */ | |
397 | struct mail_ctx { | |
398 | int done; | |
399 | u_int msgid; | |
400 | ||
401 | struct account *account; | |
402 | struct io *io; | |
403 | struct mail *mail; | |
404 | ||
405 | struct rule *rule; | |
406 | ARRAY_DECL(, struct rule *) stack; | |
407 | struct expritem *expritem; | |
408 | int result; | |
409 | int matched; | |
410 | ||
411 | TAILQ_HEAD(, deliver_ctx) dqueue; | |
412 | ||
413 | TAILQ_ENTRY(mail_ctx) entry; | |
414 | }; | |
415 | TAILQ_HEAD(mail_queue, mail_ctx); | |
416 | extern struct mail_queue mail_queue; | |
388 | 417 | |
389 | 418 | /* An attachment. */ |
390 | 419 | struct attach { |
424 | 453 | uid_t uid; |
425 | 454 | }; |
426 | 455 | |
456 | /* Privsep message buffer. */ | |
457 | struct msgbuf { | |
458 | void *buf; | |
459 | size_t len; | |
460 | }; | |
461 | ||
427 | 462 | /* Privsep message. */ |
428 | 463 | struct msg { |
464 | u_int id; | |
429 | 465 | enum msgtype type; |
430 | 466 | size_t size; |
431 | 467 | |
438 | 474 | struct io *io; |
439 | 475 | |
440 | 476 | void *data; |
441 | int (*msg)(struct child *, struct msg *, void *, size_t); | |
477 | int (*msg)(struct child *, struct msg *, struct msgbuf *); | |
478 | ||
479 | void *buf; | |
480 | size_t len; | |
442 | 481 | }; |
443 | 482 | |
444 | 483 | /* List of children. */ |
455 | 494 | struct child_deliver_data { |
456 | 495 | void (*hook)(int, struct account *, struct msg *, |
457 | 496 | struct child_deliver_data *, int *); |
497 | ||
458 | 498 | struct child *child; /* the source of the request */ |
499 | ||
459 | 500 | u_int msgid; |
460 | 501 | const char *name; |
502 | ||
461 | 503 | struct account *account; |
504 | struct mail *mail; | |
462 | 505 | struct action *action; |
506 | ||
463 | 507 | struct deliver_ctx *dctx; |
464 | struct mail *mail; | |
465 | struct match_ctx *mctx; | |
508 | struct mail_ctx *mctx; | |
509 | ||
466 | 510 | struct match_command_data *cmddata; |
467 | 511 | }; |
468 | 512 | |
779 | 823 | void attach_free(struct attach *); |
780 | 824 | |
781 | 825 | /* privsep.c */ |
782 | int privsep_send(struct io *, struct msg *, void *, | |
783 | size_t); | |
826 | int privsep_send(struct io *, struct msg *, | |
827 | struct msgbuf *); | |
784 | 828 | int privsep_check(struct io *); |
785 | int privsep_recv(struct io *, struct msg *, void **, | |
786 | size_t *); | |
829 | int privsep_recv(struct io *, struct msg *, | |
830 | struct msgbuf *); | |
787 | 831 | |
788 | 832 | /* command.c */ |
789 | 833 | struct cmd *cmd_start(const char *, int, int, char *, size_t, |
797 | 841 | __dead void child_exit(int); |
798 | 842 | struct child *child_start(struct children *, uid_t, |
799 | 843 | int (*)(struct child *, struct io *), |
800 | int (*)(struct child *, struct msg *, void *, | |
801 | size_t), void *); | |
844 | int (*)(struct child *, struct msg *, | |
845 | struct msgbuf *), void *); | |
802 | 846 | |
803 | 847 | /* child-fetch.c */ |
804 | 848 | int child_fetch(struct child *, struct io *); |
811 | 855 | struct msg *, struct child_deliver_data *, int *); |
812 | 856 | |
813 | 857 | /* parent-fetch.c */ |
814 | int parent_fetch(struct child *, struct msg *, void *, | |
815 | size_t); | |
858 | int parent_fetch(struct child *, struct msg *, | |
859 | struct msgbuf *); | |
816 | 860 | |
817 | 861 | /* parent-deliver.c */ |
818 | int parent_deliver(struct child *, struct msg *, void *, | |
819 | size_t); | |
862 | int parent_deliver(struct child *, struct msg *, | |
863 | struct msgbuf *); | |
820 | 864 | |
821 | 865 | /* connect.c */ |
822 | 866 | struct proxy *getproxy(const char *); |
851 | 895 | u_int fill_wrapped(struct mail *); |
852 | 896 | void set_wrapped(struct mail *, char); |
853 | 897 | |
898 | /* mail-state.c */ | |
899 | int mail_match(struct mail_ctx *, struct msg *, struct msgbuf *); | |
900 | int mail_deliver(struct mail_ctx *, struct msg *, struct msgbuf *); | |
901 | ||
854 | 902 | /* cleanup.c */ |
855 | 903 | void cleanup_check(void); |
856 | 904 | void cleanup_flush(void); |
28 | 28 | |
29 | 29 | int fetch_pop3_start(struct account *); |
30 | 30 | void fetch_pop3_fill(struct account *, struct io **, u_int *); |
31 | int fetch_pop3_finish(struct account *); | |
31 | int fetch_pop3_finish(struct account *, int); | |
32 | 32 | int fetch_pop3_poll(struct account *, u_int *); |
33 | 33 | int fetch_pop3_fetch(struct account *, struct mail *); |
34 | 34 | int fetch_pop3_purge(struct account *); |
38 | 38 | void fetch_pop3_free(void *); |
39 | 39 | |
40 | 40 | int fetch_pop3_connect(struct account *); |
41 | int fetch_pop3_disconnect(struct account *); | |
41 | int fetch_pop3_disconnect(struct account *, int); | |
42 | 42 | |
43 | 43 | int fetch_pop3_line(struct account *, char **); |
44 | 44 | int fetch_pop3_okay(char *); |
136 | 136 | } |
137 | 137 | |
138 | 138 | int |
139 | fetch_pop3_finish(struct account *a) | |
139 | fetch_pop3_finish(struct account *a, int aborted) | |
140 | 140 | { |
141 | 141 | struct fetch_pop3_data *data = a->data; |
142 | 142 | u_int i; |
143 | 143 | |
144 | 144 | if (data->io != NULL) |
145 | fetch_pop3_disconnect(a); | |
145 | fetch_pop3_disconnect(a, aborted); | |
146 | 146 | |
147 | 147 | if (data->uid != NULL) |
148 | 148 | xfree(data->uid); |
199 | 199 | } |
200 | 200 | |
201 | 201 | int |
202 | fetch_pop3_disconnect(struct account *a) | |
202 | fetch_pop3_disconnect(struct account *a, int aborted) | |
203 | 203 | { |
204 | 204 | struct fetch_pop3_data *data = a->data; |
205 | 205 | |
206 | 206 | io_writeline(data->io, "QUIT"); |
207 | if (fetch_pop3_check(a) == NULL) | |
207 | if (!aborted && fetch_pop3_check(a) == NULL) | |
208 | 208 | goto error; |
209 | 209 | |
210 | 210 | io_close(data->io); |
391 | 391 | int |
392 | 392 | fetch_pop3_purge(struct account *a) |
393 | 393 | { |
394 | if (fetch_pop3_disconnect(a) != 0) | |
394 | if (fetch_pop3_disconnect(a, 0) != 0) | |
395 | 395 | return (FETCH_ERROR); |
396 | 396 | return (fetch_pop3_connect(a)); |
397 | 397 | } |
25 | 25 | #define FETCH_EMPTY 3 |
26 | 26 | #define FETCH_COMPLETE 4 |
27 | 27 | #define FETCH_AGAIN 5 |
28 | #define FETCH_NONE 6 | |
28 | 29 | |
29 | 30 | /* Fetch functions. */ |
30 | 31 | struct fetch { |
40 | 41 | int (*fetch)(struct account *, struct mail *); |
41 | 42 | int (*purge)(struct account *); |
42 | 43 | int (*done)(struct account *, struct mail *); |
43 | int (*finish)(struct account *); | |
44 | int (*finish)(struct account *, int); | |
44 | 45 | void (*desc)(struct account *, char *, size_t); |
45 | 46 | }; |
46 | 47 |
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 <fnmatch.h> | |
21 | #include <string.h> | |
22 | ||
23 | #include "fdm.h" | |
24 | #include "fetch.h" | |
25 | #include "match.h" | |
26 | ||
27 | struct strings *find_delivery_users(struct mail_ctx *, struct action *, int *); | |
28 | int fill_delivery_queue(struct mail_ctx *, struct rule *); | |
29 | ||
30 | int start_action(struct mail_ctx *, struct deliver_ctx *); | |
31 | int finish_action(struct deliver_ctx *, struct msg *, | |
32 | struct msgbuf *); | |
33 | ||
34 | #define ACTION_DONE 0 | |
35 | #define ACTION_ERROR 1 | |
36 | #define ACTION_PARENT 2 | |
37 | ||
38 | int | |
39 | mail_match(struct mail_ctx *mctx, struct msg *msg, struct msgbuf *msgbuf) | |
40 | { | |
41 | struct account *a = mctx->account; | |
42 | struct mail *m = mctx->mail; | |
43 | struct strings *aa; | |
44 | struct expritem *ei; | |
45 | u_int i; | |
46 | int error = MAIL_CONTINUE; | |
47 | char *an, *tkey, *tvalue; | |
48 | ||
49 | /* | |
50 | * If blocked, check for msgs from parent. | |
51 | */ | |
52 | if (mctx->msgid != 0) { | |
53 | if (msg == NULL || msg->id != mctx->msgid) | |
54 | return (MAIL_BLOCKED); | |
55 | mctx->msgid = 0; | |
56 | ||
57 | if (msg->type != MSG_DONE) | |
58 | fatalx("child: unexpected message"); | |
59 | if (msgbuf->buf == NULL || msgbuf->len == 0) | |
60 | fatalx("child: bad tags"); | |
61 | strb_destroy(&m->tags); | |
62 | m->tags = msgbuf->buf; | |
63 | ||
64 | ei = mctx->expritem; | |
65 | switch (msg->data.error) { | |
66 | case MATCH_ERROR: | |
67 | return (MAIL_ERROR); | |
68 | case MATCH_TRUE: | |
69 | if (ei->op == OP_NONE || ei->op == OP_OR) | |
70 | mctx->result = 1; | |
71 | break; | |
72 | case MATCH_FALSE: | |
73 | if (ei->op == OP_AND) | |
74 | mctx->result = 0; | |
75 | break; | |
76 | default: | |
77 | fatalx("child: unexpected response"); | |
78 | } | |
79 | ||
80 | goto next_expritem; | |
81 | } | |
82 | ||
83 | /* | |
84 | * Check for completion and end of ruleset. | |
85 | */ | |
86 | if (mctx->done) | |
87 | return (MAIL_DONE); | |
88 | if (mctx->rule == NULL) { | |
89 | switch (conf.impl_act) { | |
90 | case DECISION_NONE: | |
91 | log_warnx("%s: reached end of ruleset. no " | |
92 | "unmatched-mail option; keeping mail", a->name); | |
93 | m->decision = DECISION_KEEP; | |
94 | break; | |
95 | case DECISION_KEEP: | |
96 | log_debug2("%s: reached end of ruleset. keeping mail", | |
97 | a->name); | |
98 | m->decision = DECISION_KEEP; | |
99 | break; | |
100 | case DECISION_DROP: | |
101 | log_debug2("%s: reached end of ruleset. dropping mail", | |
102 | a->name); | |
103 | m->decision = DECISION_DROP; | |
104 | break; | |
105 | } | |
106 | return (MAIL_DONE); | |
107 | } | |
108 | ||
109 | /* | |
110 | * Expression not started. Start it. | |
111 | */ | |
112 | if (mctx->expritem == NULL) { | |
113 | /* | |
114 | * Check rule account list. | |
115 | */ | |
116 | aa = mctx->rule->accounts; | |
117 | if (aa != NULL && !ARRAY_EMPTY(aa)) { | |
118 | for (i = 0; i < ARRAY_LENGTH(aa); i++) { | |
119 | an = ARRAY_ITEM(aa, i, char *); | |
120 | if (name_match(an, a->name)) | |
121 | break; | |
122 | } | |
123 | if (i == ARRAY_LENGTH(aa)) { | |
124 | mctx->result = 0; | |
125 | goto skip; | |
126 | } | |
127 | } | |
128 | ||
129 | /* | |
130 | * No expression. Must be an "all" rule, treat it as always | |
131 | * true. | |
132 | */ | |
133 | if (mctx->rule->expr == NULL || TAILQ_EMPTY(mctx->rule->expr)) { | |
134 | mctx->result = 1; | |
135 | goto skip; | |
136 | } | |
137 | ||
138 | /* | |
139 | * Start the expression. | |
140 | */ | |
141 | mctx->result = 0; | |
142 | mctx->expritem = TAILQ_FIRST(mctx->rule->expr); | |
143 | } | |
144 | ||
145 | /* | |
146 | * Check this expression item and adjust the result. | |
147 | */ | |
148 | ei = mctx->expritem; | |
149 | switch (ei->match->match(mctx, ei)) { | |
150 | case MATCH_ERROR: | |
151 | return (MAIL_ERROR); | |
152 | case MATCH_PARENT: | |
153 | return (MAIL_BLOCKED); | |
154 | case MATCH_TRUE: | |
155 | if (ei->op == OP_NONE || ei->op == OP_OR) | |
156 | mctx->result = 1; | |
157 | break; | |
158 | case MATCH_FALSE: | |
159 | if (ei->op == OP_AND) | |
160 | mctx->result = 0; | |
161 | break; | |
162 | } | |
163 | ||
164 | next_expritem: | |
165 | /* | |
166 | * Move to the next item. If there is one, then return. | |
167 | */ | |
168 | mctx->expritem = TAILQ_NEXT(mctx->expritem, entry); | |
169 | if (mctx->expritem != NULL) | |
170 | return (MAIL_CONTINUE); | |
171 | ||
172 | skip: | |
173 | /* | |
174 | * If the result was false, skip to find the next rule. | |
175 | */ | |
176 | if (!mctx->result) | |
177 | goto next_rule; | |
178 | mctx->matched = 1; | |
179 | log_debug2("%s: matched to rule %u", a->name, mctx->rule->idx); | |
180 | ||
181 | /* | |
182 | * If this rule is stop, mark the context so when we get back after | |
183 | * delivery we know to stop. | |
184 | */ | |
185 | if (mctx->rule->stop) | |
186 | mctx->done = 1; | |
187 | ||
188 | /* | |
189 | * Handle nested rules. | |
190 | */ | |
191 | if (!TAILQ_EMPTY(&mctx->rule->rules)) { | |
192 | log_debug2("%s: entering nested rules", a->name); | |
193 | ||
194 | /* | |
195 | * Stack the current rule (we are at the end of it so the | |
196 | * the expritem must be NULL already). | |
197 | */ | |
198 | ARRAY_ADD(&mctx->stack, mctx->rule, struct rule *); | |
199 | ||
200 | /* | |
201 | * Continue with the first rule of the nested list. | |
202 | */ | |
203 | mctx->rule = TAILQ_FIRST(&mctx->rule->rules); | |
204 | return (MAIL_CONTINUE); | |
205 | } | |
206 | ||
207 | /* | |
208 | * Tag mail if necessary. | |
209 | */ | |
210 | if (mctx->rule->key.str != NULL) { | |
211 | tkey = replacestr(&mctx->rule->key, m->tags, m, &m->rml); | |
212 | tvalue = replacestr(&mctx->rule->value, m->tags, m, &m->rml); | |
213 | ||
214 | if (tkey != NULL && *tkey != '\0' && tvalue != NULL) { | |
215 | log_debug2("%s: tagging message: %s (%s)", a->name, | |
216 | tkey, tvalue); | |
217 | add_tag(&m->tags, tkey, "%s", tvalue); | |
218 | } | |
219 | ||
220 | if (tkey != NULL) | |
221 | xfree(tkey); | |
222 | if (tvalue != NULL) | |
223 | xfree(tvalue); | |
224 | } | |
225 | ||
226 | /* | |
227 | * Fill the delivery action queue. | |
228 | */ | |
229 | if (!ARRAY_EMPTY(mctx->rule->actions)) { | |
230 | if (fill_delivery_queue(mctx, mctx->rule) != 0) | |
231 | return (MAIL_ERROR); | |
232 | error = MAIL_DELIVER; | |
233 | } | |
234 | ||
235 | next_rule: | |
236 | /* | |
237 | * Move to the next rule. | |
238 | */ | |
239 | mctx->rule = TAILQ_NEXT(mctx->rule, entry); | |
240 | ||
241 | /* | |
242 | * If no more rules, try to move up the stack. | |
243 | */ | |
244 | while (mctx->rule == NULL) { | |
245 | if (ARRAY_EMPTY(&mctx->stack)) | |
246 | break; | |
247 | mctx->rule = ARRAY_LAST(&mctx->stack, struct rule *); | |
248 | mctx->rule = TAILQ_NEXT(mctx->rule, entry); | |
249 | ARRAY_TRUNC(&mctx->stack, 1, struct rule *); | |
250 | } | |
251 | ||
252 | return (error); | |
253 | } | |
254 | ||
255 | int | |
256 | mail_deliver(struct mail_ctx *mctx, struct msg *msg, struct msgbuf *msgbuf) | |
257 | { | |
258 | struct account *a = mctx->account; | |
259 | struct mail *m = mctx->mail; | |
260 | struct deliver_ctx *dctx; | |
261 | ||
262 | /* | |
263 | * If blocked, check for msgs from parent. | |
264 | */ | |
265 | if (mctx->msgid != 0) { | |
266 | if (msg == NULL || msg->id != mctx->msgid) | |
267 | return (MAIL_BLOCKED); | |
268 | mctx->msgid = 0; | |
269 | ||
270 | /* | |
271 | * Got message. Finish delivery. | |
272 | */ | |
273 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
274 | if (finish_action(dctx, msg, msgbuf) == ACTION_ERROR) | |
275 | return (MAIL_ERROR); | |
276 | ||
277 | /* | |
278 | * Move on to dequeue this delivery action. | |
279 | */ | |
280 | goto done; | |
281 | } | |
282 | ||
283 | /* | |
284 | * Check if delivery is complete. | |
285 | */ | |
286 | if (TAILQ_EMPTY(&mctx->dqueue)) | |
287 | return (MAIL_MATCH); | |
288 | ||
289 | /* | |
290 | * Get the first delivery action and start it. | |
291 | */ | |
292 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
293 | switch (start_action(mctx, dctx)) { | |
294 | case ACTION_ERROR: | |
295 | return (MAIL_ERROR); | |
296 | case ACTION_PARENT: | |
297 | return (MAIL_BLOCKED); | |
298 | } | |
299 | ||
300 | done: | |
301 | /* | |
302 | * Remove completed action from queue. | |
303 | */ | |
304 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); | |
305 | log_debug("%s: message %u delivered (rule %u, %s) after %.3f seconds", | |
306 | a->name, m->idx, dctx->rule->idx, | |
307 | dctx->action->deliver->name, get_time() - dctx->tim); | |
308 | xfree(dctx); | |
309 | return (MAIL_CONTINUE); | |
310 | } | |
311 | ||
312 | struct strings * | |
313 | find_delivery_users(struct mail_ctx *mctx, struct action *t, int *should_free) | |
314 | { | |
315 | struct account *a = mctx->account; | |
316 | struct mail *m = mctx->mail; | |
317 | struct rule *r = mctx->rule; | |
318 | struct strings *users; | |
319 | ||
320 | *should_free = 0; | |
321 | users = NULL; | |
322 | if (r->find_uid) { /* rule comes first */ | |
323 | *should_free = 1; | |
324 | users = find_users(m); | |
325 | } else if (r->users != NULL) { | |
326 | *should_free = 0; | |
327 | users = r->users; | |
328 | } else if (t->find_uid) { /* then action */ | |
329 | *should_free = 1; | |
330 | users = find_users(m); | |
331 | } else if (t->users != NULL) { | |
332 | *should_free = 0; | |
333 | users = t->users; | |
334 | } else if (a->find_uid) { /* then account */ | |
335 | *should_free = 1; | |
336 | users = find_users(m); | |
337 | } else if (a->users != NULL) { | |
338 | *should_free = 0; | |
339 | users = a->users; | |
340 | } | |
341 | if (users == NULL) { | |
342 | *should_free = 1; | |
343 | users = xmalloc(sizeof *users); | |
344 | ARRAY_INIT(users); | |
345 | ARRAY_ADD(users, conf.def_user, uid_t); | |
346 | } | |
347 | ||
348 | return (users); | |
349 | } | |
350 | ||
351 | int | |
352 | fill_delivery_queue(struct mail_ctx *mctx, struct rule *r) | |
353 | { | |
354 | struct account *a = mctx->account; | |
355 | struct mail *m = mctx->mail; | |
356 | struct action *t; | |
357 | struct actions *ta; | |
358 | u_int i, j, k; | |
359 | char *s; | |
360 | struct replstr *rs; | |
361 | struct deliver_ctx *dctx; | |
362 | struct strings *users; | |
363 | int should_free; | |
364 | ||
365 | for (i = 0; i < ARRAY_LENGTH(r->actions); i++) { | |
366 | rs = &ARRAY_ITEM(r->actions, i, struct replstr); | |
367 | s = replacestr(rs, m->tags, m, &m->rml); | |
368 | ||
369 | log_debug2("%s: looking for actions matching: %s", a->name, s); | |
370 | ta = match_actions(s); | |
371 | if (ARRAY_EMPTY(ta)) | |
372 | goto empty; | |
373 | xfree(s); | |
374 | ||
375 | log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta)); | |
376 | for (j = 0; j < ARRAY_LENGTH(ta); j++) { | |
377 | t = ARRAY_ITEM(ta, j, struct action *); | |
378 | users = find_delivery_users(mctx, t, &should_free); | |
379 | ||
380 | for (k = 0; k < ARRAY_LENGTH(users); k++) { | |
381 | dctx = xcalloc(1, sizeof *dctx); | |
382 | dctx->action = t; | |
383 | dctx->account = a; | |
384 | dctx->rule = r; | |
385 | dctx->mail = m; | |
386 | dctx->uid = ARRAY_ITEM(users, k, uid_t); | |
387 | ||
388 | log_debug3("%s: action %s, uid %lu", a->name, | |
389 | t->name, (u_long) dctx->uid); | |
390 | TAILQ_INSERT_TAIL(&mctx->dqueue, dctx, entry); | |
391 | } | |
392 | ||
393 | if (should_free) | |
394 | ARRAY_FREEALL(users); | |
395 | } | |
396 | ||
397 | ARRAY_FREEALL(ta); | |
398 | } | |
399 | ||
400 | return (0); | |
401 | ||
402 | empty: | |
403 | xfree(s); | |
404 | ARRAY_FREEALL(ta); | |
405 | log_warnx("%s: no actions matching: %s (%s)", a->name, s, rs->str); | |
406 | return (1); | |
407 | ||
408 | } | |
409 | ||
410 | int | |
411 | start_action(struct mail_ctx *mctx, struct deliver_ctx *dctx) | |
412 | { | |
413 | struct account *a = dctx->account; | |
414 | struct action *t = dctx->action; | |
415 | struct mail *m = dctx->mail; | |
416 | struct mail *md = &dctx->wr_mail; | |
417 | struct msg msg; | |
418 | struct msgbuf msgbuf; | |
419 | u_int lines; | |
420 | ||
421 | dctx->tim = get_time(); | |
422 | if (t->deliver->deliver == NULL) | |
423 | return (0); | |
424 | ||
425 | log_debug2("%s: message %u, running action %s as user %lu", | |
426 | a->name, m->idx, t->name, (u_long) dctx->uid); | |
427 | add_tag(&m->tags, "action", "%s", t->name); | |
428 | ||
429 | /* just deliver now for in-child delivery */ | |
430 | if (t->deliver->type == DELIVER_INCHILD) { | |
431 | if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS) | |
432 | return (ACTION_ERROR); | |
433 | return (ACTION_DONE); | |
434 | } | |
435 | ||
436 | #if 0 | |
437 | /* if the current user is the same as the deliver user, don't bother | |
438 | passing up either */ | |
439 | if (t->deliver->type == DELIVER_ASUSER && dctx->uid == geteuid()) { | |
440 | dctx->blocked = 0; | |
441 | if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS) | |
442 | return (ACTION_ERROR); | |
443 | return (ACTION_DONE); | |
444 | } | |
445 | if (t->deliver->type == DELIVER_WRBACK && dctx->uid == geteuid()) { | |
446 | dctx->blocked = 0; | |
447 | ||
448 | mail_open(md, IO_BLOCKSIZE); | |
449 | md->decision = m->decision; | |
450 | ||
451 | if (t->deliver->deliver(dctx, t) != DELIVER_SUCCESS) { | |
452 | mail_destroy(md); | |
453 | return (ACTION_ERROR); | |
454 | } | |
455 | ||
456 | memcpy(&msg.data.mail, md, sizeof msg.data.mail); | |
457 | cleanup_deregister(md->shm.name); | |
458 | strb_destroy(&md->tags); | |
459 | ||
460 | mail_receive(m, &msg); | |
461 | log_debug2("%s: received modified mail: size %zu, body %zd", | |
462 | a->name, m->size, m->body); | |
463 | ||
464 | /* trim from line */ | |
465 | trim_from(m); | |
466 | ||
467 | /* and recreate the wrapped array */ | |
468 | lines = fill_wrapped(m); | |
469 | log_debug2("%s: found %u wrapped lines", a->name, lines); | |
470 | ||
471 | return (ACTION_DONE); | |
472 | } | |
473 | #endif | |
474 | ||
475 | memset(&msg, 0, sizeof msg); | |
476 | msg.type = MSG_ACTION; | |
477 | msg.id = m->idx; | |
478 | ||
479 | msg.data.account = a; | |
480 | msg.data.action = t; | |
481 | msg.data.uid = dctx->uid; | |
482 | ||
483 | msgbuf.buf = m->tags; | |
484 | msgbuf.len = STRB_SIZE(m->tags); | |
485 | ||
486 | mail_send(m, &msg); | |
487 | ||
488 | log_debug3("%s: sending action to parent", a->name); | |
489 | if (privsep_send(mctx->io, &msg, &msgbuf) != 0) | |
490 | fatalx("child: privsep_send error"); | |
491 | ||
492 | mctx->msgid = msg.id; | |
493 | return (ACTION_PARENT); | |
494 | } | |
495 | ||
496 | int | |
497 | finish_action(struct deliver_ctx *dctx, struct msg *msg, struct msgbuf *msgbuf) | |
498 | { | |
499 | struct account *a = dctx->account; | |
500 | struct action *t = dctx->action; | |
501 | struct mail *m = dctx->mail; | |
502 | u_int lines; | |
503 | ||
504 | if (msgbuf->buf == NULL || msgbuf->len == 0) | |
505 | fatalx("child: bad tags"); | |
506 | strb_destroy(&m->tags); | |
507 | m->tags = msgbuf->buf; | |
508 | update_tags(&m->tags); | |
509 | ||
510 | if (msg->data.error != 0) | |
511 | return (ACTION_ERROR); | |
512 | ||
513 | if (t->deliver->type != DELIVER_WRBACK) | |
514 | return (ACTION_DONE); | |
515 | ||
516 | mail_receive(m, msg); | |
517 | log_debug2("%s: message %u, received modified mail: size %zu, body %zd", | |
518 | a->name, m->idx, m->size, m->body); | |
519 | ||
520 | /* trim from line */ | |
521 | trim_from(m); | |
522 | ||
523 | /* and recreate the wrapped array */ | |
524 | lines = fill_wrapped(m); | |
525 | log_debug2("%s: found %u wrapped lines", a->name, lines); | |
526 | ||
527 | return (ACTION_DONE); | |
528 | } | |
529 | ||
530 | /* -------------------------------------------------------------------------- */ | |
531 | #if 0 | |
532 | int | |
533 | run_match(struct account *a, const char **cause) | |
534 | { | |
535 | switch (fetch_rule(mctx, cause)) { | |
536 | case FETCH_ERROR: | |
537 | return (1); | |
538 | case FETCH_AGAIN: | |
539 | /* delivering mail, queue for delivery */ | |
540 | log_debug3("%s: %u, adding to deliver queue", a->name, m->idx); | |
541 | TAILQ_REMOVE(&matchq, mctx, entry); | |
542 | TAILQ_INSERT_TAIL(&deliverq, mctx, entry); | |
543 | break; | |
544 | case FETCH_COMPLETE: | |
545 | /* finished with mail, queue on done queue */ | |
546 | log_debug3("%s: %u, adding to done queue", a->name, m->idx); | |
547 | TAILQ_REMOVE(&matchq, mctx, entry); | |
548 | TAILQ_INSERT_TAIL(&doneq, mctx, entry); | |
549 | ||
550 | /* | |
551 | * Destroy mail data now it is finished, just keep the mail | |
552 | * structure. | |
553 | */ | |
554 | shm_destroy(&mctx->mail->shm); | |
555 | break; | |
556 | } | |
557 | ||
558 | return (0); | |
559 | } | |
560 | ||
561 | int | |
562 | run_done(struct account *a, const char **cause) | |
563 | { | |
564 | struct match_ctx *mctx; | |
565 | struct mail *m; | |
566 | int error = 0; | |
567 | const char *type; | |
568 | ||
569 | if (TAILQ_EMPTY(&doneq)) | |
570 | return (0); | |
571 | ||
572 | mctx = TAILQ_FIRST(&doneq); | |
573 | m = mctx->mail; | |
574 | log_debug3("%s: running done queue", a->name); | |
575 | ||
576 | TAILQ_REMOVE(&doneq, mctx, entry); | |
577 | ARRAY_FREE(&mctx->stack); | |
578 | log_debug("%s: message %u done after %.3f seconds", a->name, m->idx, | |
579 | get_time() - mctx->tim); | |
580 | xfree(mctx); | |
581 | ||
582 | if (a->fetch->done != NULL) { | |
583 | switch (m->decision) { | |
584 | case DECISION_DROP: | |
585 | type = "deleting"; | |
586 | dropped++; | |
587 | break; | |
588 | case DECISION_KEEP: | |
589 | type = "keeping"; | |
590 | kept++; | |
591 | break; | |
592 | default: | |
593 | fatalx("invalid decision"); | |
594 | } | |
595 | log_debug("%s: %s message %u", a->name, type, m->idx); | |
596 | ||
597 | if (a->fetch->done(a, m) != FETCH_SUCCESS) { | |
598 | *cause = type; | |
599 | error = 1; | |
600 | } | |
601 | } | |
602 | ||
603 | mail_destroy(m); | |
604 | xfree(m); | |
605 | ||
606 | return (error); | |
607 | } | |
608 | ||
609 | void | |
610 | flush_queue(struct match_queue *mq) | |
611 | { | |
612 | struct match_ctx *mctx; | |
613 | struct deliver_ctx *dctx; | |
614 | struct mail *m; | |
615 | ||
616 | while (!TAILQ_EMPTY(mq)) { | |
617 | mctx = TAILQ_FIRST(mq); | |
618 | m = mctx->mail; | |
619 | ||
620 | TAILQ_REMOVE(mq, mctx, entry); | |
621 | while (!TAILQ_EMPTY(&mctx->dqueue)) { | |
622 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
623 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); | |
624 | xfree(dctx); | |
625 | } | |
626 | ARRAY_FREE(&mctx->stack); | |
627 | xfree(mctx); | |
628 | ||
629 | mail_destroy(m); | |
630 | xfree(m); | |
631 | } | |
632 | } | |
633 | ||
634 | int | |
635 | run_deliver(struct account *a, const char **cause) | |
636 | { | |
637 | struct match_ctx *mctx; | |
638 | struct mail *m; | |
639 | struct deliver_ctx *dctx; | |
640 | ||
641 | if (TAILQ_EMPTY(&deliverq)) | |
642 | return (0); | |
643 | ||
644 | mctx = TAILQ_FIRST(&deliverq); | |
645 | m = mctx->mail; | |
646 | ||
647 | if (TAILQ_EMPTY(&mctx->dqueue)) { | |
648 | /* delivery done. return to match queue */ | |
649 | log_debug3("%s: %u, returning to match queue", a->name, m->idx); | |
650 | TAILQ_REMOVE(&deliverq, mctx, entry); | |
651 | TAILQ_INSERT_HEAD(&matchq, mctx, entry); | |
652 | return (0); | |
653 | } | |
654 | ||
655 | /* start the first action */ | |
656 | log_debug3("%s: running deliver queue", a->name); | |
657 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
658 | ||
659 | switch (start_action(mctx, dctx)) { | |
660 | case ACTION_ERROR: | |
661 | *cause = "delivery"; | |
662 | return (1); | |
663 | case ACTION_PARENT: | |
664 | log_debug3("%s: %u, adding to blocked queue", a->name, m->idx); | |
665 | TAILQ_REMOVE(&deliverq, mctx, entry); | |
666 | TAILQ_INSERT_HEAD(&blockedq, mctx, entry); | |
667 | return (0); | |
668 | } | |
669 | ||
670 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); | |
671 | log_debug("%s: message %u delivered (rule %u, %s) after %.3f seconds", | |
672 | a->name, mctx->mail->idx, dctx->rule->idx, | |
673 | dctx->action->deliver->name, get_time() - dctx->tim); | |
674 | xfree(dctx); | |
675 | return (0); | |
676 | } | |
677 | ||
678 | int | |
679 | recvd_deliver(struct msg *msg, struct msgbuf *msgbuf, void *data, | |
680 | const char **cause) | |
681 | { | |
682 | struct match_ctx *mctx = data; | |
683 | struct account *a = mctx->account; | |
684 | struct mail *m = mctx->mail; | |
685 | struct deliver_ctx *dctx; | |
686 | ||
687 | if (msg->type != MSG_DONE) | |
688 | fatalx("child: unexpected message"); | |
689 | ||
690 | log_debug3("%s: %u, returning to deliver queue", a->name, m->idx); | |
691 | TAILQ_REMOVE(&blockedq, mctx, entry); | |
692 | TAILQ_INSERT_HEAD(&deliverq, mctx, entry); | |
693 | ||
694 | dctx = TAILQ_FIRST(&mctx->dqueue); | |
695 | if (finish_action(dctx, msg, msgbuf) != ACTION_DONE) { | |
696 | *cause = "delivery"; | |
697 | return (1); | |
698 | } | |
699 | ||
700 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); | |
701 | log_debug("%s: message %u delivered (rule %u, %s) after %.3f seconds", | |
702 | a->name, mctx->mail->idx, dctx->rule->idx, | |
703 | dctx->action->deliver->name, get_time() - dctx->tim); | |
704 | xfree(dctx); | |
705 | return (0); | |
706 | } | |
707 | ||
708 | int | |
709 | fetch_rule(struct match_ctx *mctx, const char **cause) | |
710 | { | |
711 | struct account *a = mctx->account; | |
712 | struct strings *aa; | |
713 | struct mail *m = mctx->mail; | |
714 | struct rule *r = mctx->rule; | |
715 | u_int i; | |
716 | int error; | |
717 | char *tkey, *tvalue; | |
718 | ||
719 | /* matching finished */ | |
720 | if (m->done) { | |
721 | if (conf.keep_all || a->keep) | |
722 | m->decision = DECISION_KEEP; | |
723 | return (FETCH_COMPLETE); | |
724 | } | |
725 | ||
726 | /* end of ruleset reached */ | |
727 | if (r == NULL) { | |
728 | switch (conf.impl_act) { | |
729 | case DECISION_NONE: | |
730 | log_warnx("%s: reached end of ruleset. no " | |
731 | "unmatched-mail option; keeping mail", a->name); | |
732 | m->decision = DECISION_KEEP; | |
733 | break; | |
734 | case DECISION_KEEP: | |
735 | log_debug2("%s: reached end of ruleset. keeping mail", | |
736 | a->name); | |
737 | m->decision = DECISION_KEEP; | |
738 | break; | |
739 | case DECISION_DROP: | |
740 | log_debug2("%s: reached end of ruleset. dropping mail", | |
741 | a->name); | |
742 | m->decision = DECISION_DROP; | |
743 | break; | |
744 | } | |
745 | m->done = 1; | |
746 | return (FETCH_SUCCESS); | |
747 | } | |
748 | ||
749 | mctx->rule = TAILQ_NEXT(mctx->rule, entry); | |
750 | while (mctx->rule == NULL) { | |
751 | if (ARRAY_EMPTY(&mctx->stack)) | |
752 | break; | |
753 | mctx->rule = ARRAY_LAST(&mctx->stack, struct rule *); | |
754 | mctx->rule = TAILQ_NEXT(mctx->rule, entry); | |
755 | ARRAY_TRUNC(&mctx->stack, 1, struct rule *); | |
756 | } | |
757 | ||
758 | aa = r->accounts; | |
759 | if (!ARRAY_EMPTY(aa)) { | |
760 | for (i = 0; i < ARRAY_LENGTH(aa); i++) { | |
761 | if (name_match(ARRAY_ITEM(aa, i, char *), a->name)) | |
762 | break; | |
763 | } | |
764 | if (i == ARRAY_LENGTH(aa)) | |
765 | return (FETCH_SUCCESS); | |
766 | } | |
767 | ||
768 | /* match all the regexps */ | |
769 | switch (r->type) { | |
770 | case RULE_EXPRESSION: | |
771 | /* combine wrapped lines */ | |
772 | set_wrapped(m, ' '); | |
773 | ||
774 | /* perform the expression */ | |
775 | if ((error = do_expr(r, mctx)) == -1) { | |
776 | *cause = "matching"; | |
777 | return (FETCH_ERROR); | |
778 | } | |
779 | ||
780 | /* continue if no match */ | |
781 | if (!error) | |
782 | return (FETCH_SUCCESS); | |
783 | break; | |
784 | case RULE_ALL: | |
785 | break; | |
786 | } | |
787 | ||
788 | /* reset wrapped lines */ | |
789 | set_wrapped(m, '\n'); | |
790 | ||
791 | /* report rule number */ | |
792 | if (TAILQ_EMPTY(&r->rules)) | |
793 | log_debug2("%s: matched to rule %u", a->name, r->idx); | |
794 | else | |
795 | log_debug2("%s: matched to rule %u (nested)", a->name, r->idx); | |
796 | ||
797 | /* deal with nested rules */ | |
798 | if (!TAILQ_EMPTY(&r->rules)) { | |
799 | log_debug2("%s: entering nested rules", a->name); | |
800 | ARRAY_ADD(&mctx->stack, r, struct rule *); | |
801 | mctx->rule = TAILQ_FIRST(&r->rules); | |
802 | return (FETCH_SUCCESS); | |
803 | } | |
804 | ||
805 | /* tag mail if needed */ | |
806 | if (r->key.str != NULL) { | |
807 | tkey = replacestr(&r->key, m->tags, m, &m->rml); | |
808 | tvalue = replacestr(&r->value, m->tags, m, &m->rml); | |
809 | ||
810 | if (tkey != NULL && *tkey != '\0' && tvalue != NULL) { | |
811 | log_debug2("%s: tagging message: %s (%s)", | |
812 | a->name, tkey, tvalue); | |
813 | add_tag(&m->tags, tkey, "%s", tvalue); | |
814 | } | |
815 | ||
816 | if (tkey != NULL) | |
817 | xfree(tkey); | |
818 | if (tvalue != NULL) | |
819 | xfree(tvalue); | |
820 | } | |
821 | 1 /* if this rule is marked as stop, mark the mail as done */ | |
822 | if (r->stop) | |
823 | m->done = 1; | |
824 | ||
825 | /* handle delivery */ | |
826 | if (r->actions != NULL) { | |
827 | log_debug2("%s: delivering message", a->name); | |
828 | mctx->matched = 1; | |
829 | if (do_deliver(r, mctx) != 0) { | |
830 | *cause = "delivery"; | |
831 | return (FETCH_ERROR); | |
832 | } | |
833 | return (FETCH_AGAIN); | |
834 | } | |
835 | ||
836 | return (FETCH_SUCCESS); | |
837 | } | |
838 | ||
839 | int | |
840 | do_expr(struct rule *r, struct match_ctx *mctx) | |
841 | { | |
842 | int fres, cres; | |
843 | struct expritem *ei; | |
844 | char desc[DESCBUFSIZE]; | |
845 | ||
846 | fres = 0; | |
847 | TAILQ_FOREACH(ei, r->expr, entry) { | |
848 | cres = ei->match->match(mctx, ei); | |
849 | if (cres == MATCH_ERROR) | |
850 | return (-1); | |
851 | cres = cres == MATCH_TRUE; | |
852 | if (ei->inverted) | |
853 | cres = !cres; | |
854 | switch (ei->op) { | |
855 | case OP_NONE: | |
856 | case OP_OR: | |
857 | fres = fres || cres; | |
858 | break; | |
859 | case OP_AND: | |
860 | fres = fres && cres; | |
861 | break; | |
862 | } | |
863 | ||
864 | ei->match->desc(ei, desc, sizeof desc); | |
865 | log_debug2("%s: tried %s%s, got %d", mctx->account->name, | |
866 | ei->inverted ? "not " : "", desc, cres); | |
867 | } | |
868 | ||
869 | return (fres); | |
870 | } | |
871 | ||
872 | int | |
873 | do_deliver(struct rule *r, struct match_ctx *mctx) | |
874 | { | |
875 | struct account *a = mctx->account; | |
876 | struct mail *m = mctx->mail; | |
877 | struct action *t; | |
878 | struct actions *ta; | |
879 | u_int i, j, k; | |
880 | char *s; | |
881 | struct replstr *rs; | |
882 | struct deliver_ctx *dctx; | |
883 | struct strings *users; | |
884 | int should_free; | |
885 | ||
886 | for (i = 0; i < ARRAY_LENGTH(r->actions); i++) { | |
887 | rs = &ARRAY_ITEM(r->actions, i, struct replstr); | |
888 | s = replacestr(rs, m->tags, m, &m->rml); | |
889 | ||
890 | log_debug2("%s: looking for actions matching: %s", a->name, s); | |
891 | ta = match_actions(s); | |
892 | if (ARRAY_EMPTY(ta)) | |
893 | goto empty; | |
894 | xfree(s); | |
895 | ||
896 | log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta)); | |
897 | for (j = 0; j < ARRAY_LENGTH(ta); j++) { | |
898 | t = ARRAY_ITEM(ta, j, struct action *); | |
899 | users = get_users(mctx, r, t, &should_free); | |
900 | ||
901 | for (k = 0; k < ARRAY_LENGTH(users); k++) { | |
902 | dctx = xmalloc(sizeof *dctx); | |
903 | dctx->action = t; | |
904 | dctx->account = a; | |
905 | dctx->rule = r; | |
906 | dctx->mail = m; | |
907 | dctx->uid = ARRAY_ITEM(users, k, uid_t); | |
908 | ||
909 | TAILQ_INSERT_TAIL(&mctx->dqueue, dctx, entry); | |
910 | } | |
911 | ||
912 | if (should_free) | |
913 | ARRAY_FREEALL(users); | |
914 | } | |
915 | ||
916 | ARRAY_FREEALL(ta); | |
917 | } | |
918 | ||
919 | return (0); | |
920 | ||
921 | empty: | |
922 | xfree(s); | |
923 | ARRAY_FREEALL(ta); | |
924 | log_warnx("%s: no actions matching: %s (%s)", a->name, s, rs->str); | |
925 | return (1); | |
926 | } | |
927 | #endif |
67 | 67 | { |
68 | 68 | struct mail *mm = &msg->data.mail; |
69 | 69 | |
70 | mm->done = m->done; | |
71 | 70 | mm->idx = m->idx; |
72 | 71 | |
73 | 72 | mm->tags = m->tags; |
24 | 24 | #include "fdm.h" |
25 | 25 | #include "match.h" |
26 | 26 | |
27 | int match_age_match(struct match_ctx *, struct expritem *); | |
27 | int match_age_match(struct mail_ctx *, struct expritem *); | |
28 | 28 | void match_age_desc(struct expritem *, char *, size_t); |
29 | 29 | |
30 | 30 | int match_age_tzlookup(const char *, int *); |
79 | 79 | } |
80 | 80 | |
81 | 81 | int |
82 | match_age_match(struct match_ctx *mctx, struct expritem *ei) | |
82 | match_age_match(struct mail_ctx *mctx, struct expritem *ei) | |
83 | 83 | { |
84 | 84 | struct match_age_data *data = ei->data; |
85 | 85 | struct account *a = mctx->account; |
22 | 22 | #include "fdm.h" |
23 | 23 | #include "match.h" |
24 | 24 | |
25 | int match_attachment_match(struct match_ctx *, struct expritem *); | |
25 | int match_attachment_match(struct mail_ctx *, struct expritem *); | |
26 | 26 | void match_attachment_desc(struct expritem *, char *, size_t); |
27 | 27 | |
28 | 28 | struct match match_attachment = { |
32 | 32 | }; |
33 | 33 | |
34 | 34 | int |
35 | match_attachment_match(struct match_ctx *mctx, struct expritem *ei) | |
35 | match_attachment_match(struct mail_ctx *mctx, struct expritem *ei) | |
36 | 36 | { |
37 | 37 | struct match_attachment_data *data = ei->data; |
38 | 38 | struct account *a = mctx->account; |
22 | 22 | #include "fdm.h" |
23 | 23 | #include "match.h" |
24 | 24 | |
25 | int match_command_match(struct match_ctx *, struct expritem *); | |
25 | int match_command_match(struct mail_ctx *, struct expritem *); | |
26 | 26 | void match_command_desc(struct expritem *, char *, size_t); |
27 | 27 | |
28 | 28 | struct match match_command = { |
32 | 32 | }; |
33 | 33 | |
34 | 34 | int |
35 | match_command_match(struct match_ctx *mctx, struct expritem *ei) | |
35 | match_command_match(struct mail_ctx *mctx, struct expritem *ei) | |
36 | 36 | { |
37 | 37 | struct match_command_data *data = ei->data; |
38 | 38 | struct account *a = mctx->account; |
39 | 39 | struct mail *m = mctx->mail; |
40 | 40 | struct io *io = mctx->io; |
41 | 41 | struct msg msg; |
42 | void *buf; | |
43 | size_t len; | |
42 | struct msgbuf msgbuf; | |
44 | 43 | |
45 | 44 | /* |
46 | 45 | * We are called as the child so to change uid this needs to be done |
48 | 47 | */ |
49 | 48 | memset(&msg, 0, sizeof msg); |
50 | 49 | msg.type = MSG_COMMAND; |
50 | msg.id = m->idx; | |
51 | ||
51 | 52 | msg.data.account = a; |
52 | 53 | msg.data.cmddata = data; |
53 | 54 | msg.data.uid = data->uid; |
54 | 55 | |
56 | msgbuf.buf = m->tags; | |
57 | msgbuf.len = STRB_SIZE(m->tags); | |
58 | ||
55 | 59 | mail_send(m, &msg); |
56 | 60 | |
57 | if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0) | |
61 | if (privsep_send(io, &msg, &msgbuf) != 0) | |
58 | 62 | fatalx("child: privsep_send error"); |
59 | 63 | |
60 | if (privsep_recv(io, &msg, &buf, &len) != 0) | |
61 | fatalx("child: privsep_recv error"); | |
62 | if (msg.type != MSG_DONE) | |
63 | fatalx("child: unexpected message"); | |
64 | ||
65 | if (buf == NULL || len == 0) | |
66 | fatalx("child: bad tags"); | |
67 | strb_destroy(&m->tags); | |
68 | m->tags = buf; | |
69 | ||
70 | return (msg.data.error); | |
64 | mctx->msgid = msg.id; | |
65 | return (MATCH_PARENT); | |
71 | 66 | } |
72 | 67 | |
73 | 68 | void |
22 | 22 | #include "fdm.h" |
23 | 23 | #include "match.h" |
24 | 24 | |
25 | int match_matched_match(struct match_ctx *, struct expritem *); | |
25 | int match_matched_match(struct mail_ctx *, struct expritem *); | |
26 | 26 | void match_matched_desc(struct expritem *, char *, size_t); |
27 | 27 | |
28 | 28 | struct match match_matched = { |
32 | 32 | }; |
33 | 33 | |
34 | 34 | int |
35 | match_matched_match(struct match_ctx *mctx, unused struct expritem *ei) | |
35 | match_matched_match(struct mail_ctx *mctx, unused struct expritem *ei) | |
36 | 36 | { |
37 | 37 | if (mctx->matched) |
38 | 38 | return (MATCH_TRUE); |
22 | 22 | #include "fdm.h" |
23 | 23 | #include "match.h" |
24 | 24 | |
25 | int match_regexp_match(struct match_ctx *, struct expritem *); | |
25 | int match_regexp_match(struct mail_ctx *, struct expritem *); | |
26 | 26 | void match_regexp_desc(struct expritem *, char *, size_t); |
27 | 27 | |
28 | 28 | struct match match_regexp = { |
32 | 32 | }; |
33 | 33 | |
34 | 34 | int |
35 | match_regexp_match(struct match_ctx *mctx, struct expritem *ei) | |
35 | match_regexp_match(struct mail_ctx *mctx, struct expritem *ei) | |
36 | 36 | { |
37 | 37 | struct match_regexp_data *data = ei->data; |
38 | 38 | struct account *a = mctx->account; |
22 | 22 | #include "fdm.h" |
23 | 23 | #include "match.h" |
24 | 24 | |
25 | int match_size_match(struct match_ctx *, struct expritem *); | |
25 | int match_size_match(struct mail_ctx *, struct expritem *); | |
26 | 26 | void match_size_desc(struct expritem *, char *, size_t); |
27 | 27 | |
28 | 28 | struct match match_size = { |
32 | 32 | }; |
33 | 33 | |
34 | 34 | int |
35 | match_size_match(struct match_ctx *mctx, struct expritem *ei) | |
35 | match_size_match(struct mail_ctx *mctx, struct expritem *ei) | |
36 | 36 | { |
37 | 37 | struct match_size_data *data = ei->data; |
38 | 38 | struct mail *m = mctx->mail; |
23 | 23 | #include "fdm.h" |
24 | 24 | #include "match.h" |
25 | 25 | |
26 | int match_string_match(struct match_ctx *, struct expritem *); | |
26 | int match_string_match(struct mail_ctx *, struct expritem *); | |
27 | 27 | void match_string_desc(struct expritem *, char *, size_t); |
28 | 28 | |
29 | 29 | struct match match_string = { |
33 | 33 | }; |
34 | 34 | |
35 | 35 | int |
36 | match_string_match(struct match_ctx *mctx, struct expritem *ei) | |
36 | match_string_match(struct mail_ctx *mctx, struct expritem *ei) | |
37 | 37 | { |
38 | 38 | struct match_string_data *data = ei->data; |
39 | 39 | struct account *a = mctx->account; |
23 | 23 | #include "fdm.h" |
24 | 24 | #include "match.h" |
25 | 25 | |
26 | int match_tagged_match(struct match_ctx *, struct expritem *); | |
26 | int match_tagged_match(struct mail_ctx *, struct expritem *); | |
27 | 27 | void match_tagged_desc(struct expritem *, char *, size_t); |
28 | 28 | |
29 | 29 | struct match match_tagged = { |
33 | 33 | }; |
34 | 34 | |
35 | 35 | int |
36 | match_tagged_match(struct match_ctx *mctx, struct expritem *ei) | |
36 | match_tagged_match(struct mail_ctx *mctx, struct expritem *ei) | |
37 | 37 | { |
38 | 38 | struct match_tagged_data *data = ei->data; |
39 | 39 | struct mail *m = mctx->mail; |
22 | 22 | #include "fdm.h" |
23 | 23 | #include "match.h" |
24 | 24 | |
25 | int match_unmatched_match(struct match_ctx *, struct expritem *); | |
25 | int match_unmatched_match(struct mail_ctx *, struct expritem *); | |
26 | 26 | void match_unmatched_desc(struct expritem *, char *, size_t); |
27 | 27 | |
28 | 28 | struct match match_unmatched = { |
32 | 32 | }; |
33 | 33 | |
34 | 34 | int |
35 | match_unmatched_match(struct match_ctx *mctx, unused struct expritem *ei) | |
35 | match_unmatched_match(struct mail_ctx *mctx, unused struct expritem *ei) | |
36 | 36 | { |
37 | 37 | if (mctx->matched) |
38 | 38 | return (MATCH_FALSE); |
24 | 24 | #define MATCH_FALSE 0 |
25 | 25 | #define MATCH_TRUE 1 |
26 | 26 | #define MATCH_ERROR 2 |
27 | ||
28 | /* Match context. */ | |
29 | struct match_ctx { | |
30 | double tim; | |
31 | ||
32 | struct io *io; | |
33 | struct account *account; | |
34 | struct mail *mail; | |
35 | ||
36 | int matched; | |
37 | int stopped; | |
38 | ||
39 | struct rule *rule; | |
40 | ARRAY_DECL(, struct rule *) stack; | |
41 | ||
42 | struct deliver_queue dqueue; | |
43 | ||
44 | TAILQ_ENTRY(match_ctx) entry; | |
45 | }; | |
46 | /* XXX should this be an array since we need to know the length? */ | |
47 | TAILQ_HEAD(match_queue, match_ctx); | |
27 | #define MATCH_PARENT 3 | |
48 | 28 | |
49 | 29 | /* Match functions. */ |
50 | 30 | struct match { |
51 | 31 | const char *name; |
52 | 32 | |
53 | int (*match)(struct match_ctx *, struct expritem *); | |
33 | int (*match)(struct mail_ctx *, struct expritem *); | |
54 | 34 | void (*desc)(struct expritem *, char *, size_t); |
55 | 35 | }; |
56 | 36 |
29 | 29 | #include "match.h" |
30 | 30 | |
31 | 31 | int |
32 | parent_deliver(struct child *child, struct msg *msg, void *buf, size_t len) | |
32 | parent_deliver(struct child *child, struct msg *msg, struct msgbuf *msgbuf) | |
33 | 33 | { |
34 | 34 | struct child_deliver_data *data = child->data; |
35 | 35 | struct account *a = data->account; |
36 | 36 | struct mail *m = data->mail; |
37 | ||
38 | log_debug3("parent_deliver: got message type %d from child %ld", | |
39 | msg->type, (long) child->pid); | |
40 | 37 | |
41 | 38 | switch (msg->type) { |
42 | 39 | case MSG_DONE: |
45 | 42 | fatalx("parent_deliver: unexpected message"); |
46 | 43 | } |
47 | 44 | |
48 | if (buf == NULL || len == 0) | |
45 | if (msgbuf->buf == NULL || msgbuf->len == 0) | |
49 | 46 | fatalx("parent_deliver: bad tags"); |
50 | 47 | strb_destroy(&m->tags); |
51 | m->tags = buf; | |
48 | m->tags = msgbuf->buf; | |
52 | 49 | |
53 | 50 | /* call the hook */ |
54 | 51 | data->hook(1, a, msg, data, &msg->data.error); |
55 | 52 | |
56 | 53 | msg->type = MSG_DONE; |
54 | msg->id = data->msgid; | |
55 | ||
56 | msgbuf->buf = m->tags; | |
57 | msgbuf->len = STRB_SIZE(m->tags); | |
57 | 58 | |
58 | 59 | mail_send(m, msg); |
59 | 60 | |
60 | 61 | child = data->child; |
61 | if (privsep_send(child->io, msg, m->tags, STRB_SIZE(m->tags)) != 0) | |
62 | if (privsep_send(child->io, msg, msgbuf) != 0) | |
62 | 63 | fatalx("parent_deliver: privsep_send error"); |
63 | 64 | |
64 | 65 | mail_close(m); |
30 | 30 | |
31 | 31 | void parent_fetch_action(struct child *, struct children *, |
32 | 32 | struct deliver_ctx *, struct msg *); |
33 | void parent_fetch_cmd(struct child *, struct children *, struct match_ctx *, | |
33 | void parent_fetch_cmd(struct child *, struct children *, struct mail_ctx *, | |
34 | 34 | struct msg *); |
35 | 35 | |
36 | 36 | int |
37 | parent_fetch(struct child *child, struct msg *msg, void *buf, size_t len) | |
37 | parent_fetch(struct child *child, struct msg *msg, struct msgbuf *msgbuf) | |
38 | 38 | { |
39 | 39 | struct child_fetch_data *data = child->data; |
40 | 40 | struct children *children = data->children; |
41 | 41 | struct deliver_ctx *dctx; |
42 | struct match_ctx *mctx; | |
42 | struct mail_ctx *mctx; | |
43 | 43 | struct mail *m; |
44 | ||
45 | log_debug3("parent_fetch: got message type %d from child %ld", | |
46 | msg->type, (long) child->pid); | |
47 | 44 | |
48 | 45 | switch (msg->type) { |
49 | 46 | case MSG_ACTION: |
50 | if (buf == NULL || len == 0) | |
47 | if (msgbuf->buf == NULL || msgbuf->len == 0) | |
51 | 48 | fatalx("parent_fetch: bad tags"); |
52 | 49 | m = xcalloc(1, sizeof *m); |
53 | 50 | mail_receive(m, msg); |
54 | m->tags = buf; | |
51 | m->tags = msgbuf->buf; | |
55 | 52 | |
56 | 53 | dctx = xcalloc(1, sizeof *dctx); |
57 | 54 | dctx->account = msg->data.account; |
60 | 57 | parent_fetch_action(child, children, dctx, msg); |
61 | 58 | break; |
62 | 59 | case MSG_COMMAND: |
63 | if (buf == NULL || len == 0) | |
60 | if (msgbuf->buf == NULL || msgbuf->len == 0) | |
64 | 61 | fatalx("parent_fetch: bad tags"); |
65 | 62 | m = xcalloc(1, sizeof *m); |
66 | 63 | mail_receive(m, msg); |
67 | m->tags = buf; | |
64 | m->tags = msgbuf->buf; | |
68 | 65 | |
69 | 66 | mctx = xcalloc(1, sizeof *mctx); |
70 | 67 | mctx->account = msg->data.account; |
106 | 103 | |
107 | 104 | data = xmalloc(sizeof *data); |
108 | 105 | data->child = child; |
106 | data->msgid = msg->id; | |
109 | 107 | data->account = dctx->account; |
110 | 108 | data->hook = child_deliver_action_hook; |
111 | 109 | data->action = t; |
118 | 116 | |
119 | 117 | void |
120 | 118 | parent_fetch_cmd(struct child *child, struct children *children, |
121 | struct match_ctx *mctx, struct msg *msg) | |
119 | struct mail_ctx *mctx, struct msg *msg) | |
122 | 120 | { |
123 | 121 | uid_t uid = msg->data.uid; |
124 | 122 | struct mail *m = mctx->mail; |
126 | 124 | |
127 | 125 | data = xmalloc(sizeof *data); |
128 | 126 | data->child = child; |
127 | data->msgid = msg->id; | |
129 | 128 | data->account = mctx->account; |
130 | 129 | data->hook = child_deliver_cmd_hook; |
131 | 130 | data->mctx = mctx; |
20 | 20 | #include "fdm.h" |
21 | 21 | |
22 | 22 | int |
23 | privsep_send(struct io *io, struct msg *msg, void *buf, size_t len) | |
23 | privsep_send(struct io *io, struct msg *msg, struct msgbuf *msgbuf) | |
24 | 24 | { |
25 | 25 | char *cause; |
26 | 26 | |
27 | if (buf != NULL && len > 0) | |
28 | msg->size = len; | |
29 | else | |
30 | msg->size = 0; | |
27 | msg->size = 0; | |
28 | if (msgbuf != NULL && msgbuf->buf != NULL && msgbuf->len > 0) | |
29 | msg->size = msgbuf->len; | |
31 | 30 | |
32 | 31 | io_write(io, msg, sizeof *msg); |
33 | 32 | if (io_flush(io, &cause) != 0) |
34 | 33 | return (1); |
35 | 34 | |
36 | if (buf != NULL && len > 0) { | |
37 | io_write(io, buf, len); | |
35 | if (msg->size != 0) { | |
36 | io_write(io, msgbuf->buf, msgbuf->len); | |
38 | 37 | if (io_flush(io, &cause) != 0) |
39 | 38 | return (1); |
40 | 39 | } |
49 | 48 | } |
50 | 49 | |
51 | 50 | int |
52 | privsep_recv(struct io *io, struct msg *msg, void **buf, size_t *len) | |
51 | privsep_recv(struct io *io, struct msg *msg, struct msgbuf *msgbuf) | |
53 | 52 | { |
54 | if (len != NULL) | |
55 | *len = 0; | |
56 | if (buf != NULL) | |
57 | *buf = NULL; | |
58 | ||
59 | 53 | if (io_wait(io, sizeof *msg, NULL) != 0) |
60 | 54 | return (1); |
61 | 55 | if (io_read2(io, msg, sizeof *msg) != 0) |
63 | 57 | |
64 | 58 | if (msg->size == 0) |
65 | 59 | return (0); |
66 | if (buf == NULL || len == NULL) | |
60 | if (msgbuf == NULL) | |
67 | 61 | return (1); |
68 | 62 | |
69 | *len = msg->size; | |
70 | if (*len == 0) { | |
71 | *buf = NULL; | |
63 | msgbuf->len = msg->size; | |
64 | if (msgbuf->len == 0) { | |
65 | msgbuf->buf = NULL; | |
72 | 66 | return (0); |
73 | 67 | } |
74 | if (io_wait(io, *len, NULL) != 0) | |
68 | if (io_wait(io, msgbuf->len, NULL) != 0) | |
75 | 69 | return (1); |
76 | if ((*buf = io_read(io, *len)) == NULL) | |
70 | if ((msgbuf->buf = io_read(io, msgbuf->len)) == NULL) | |
77 | 71 | return (1); |
78 | 72 | |
79 | 73 | return (0); |