Allow actions to chain other actions. Also clean up some replstr stuff and remove a few leftover bits.
Nicholas Marriott
17 years ago
0 | 0 | 04 May 2007 |
1 | 1 | |
2 | 2 | * "match tag ..." is now not supported. |
3 | * Permit actions to call other actions: | |
4 | ||
5 | action "one" ... | |
6 | action "two" ... | |
7 | action "three" { | |
8 | ... | |
9 | actions { "one" "two" } | |
10 | } | |
11 | ||
12 | A maximum of five levels is permitted, spoiling the | |
13 | ||
14 | action "x" { action "x" } | |
15 | ||
16 | fun. | |
3 | 17 | |
4 | 18 | 30 April 2007 |
5 | 19 |
30 | 30 | - fetch smallest/largest mails first. best pattern? probably 1 small, then in |
31 | 31 | decreasing size order, but with longer queue necessary |
32 | 32 | - document set verify-certificates/no-verify (http://cvs.mirbsd.de/src/etc/ssl.certs.shar) |
33 | - document chained actions |
126 | 126 | struct replstr value; |
127 | 127 | }; |
128 | 128 | |
129 | /* Deliver action data. */ | |
130 | struct deliver_action_data { | |
131 | struct replstrs *actions; | |
132 | }; | |
133 | ||
129 | 134 | /* deliver-smtp.c */ |
130 | 135 | extern struct deliver deliver_smtp; |
131 | 136 |
49 | 49 | #define DEFMAILQUEUE 2 |
50 | 50 | #define DEFMAILSIZE (32 * 1024 * 1024) /* 32 MB */ |
51 | 51 | #define MAXMAILSIZE IO_MAXBUFFERLEN |
52 | #define MAXACTIONCHAIN 5 | |
52 | 53 | #define DEFTIMEOUT (900 * 1000) |
53 | 54 | #define LOCKSLEEPTIME 10000 |
54 | 55 | #define LOCKRETRIES 1000 |
256 | 257 | */ |
257 | 258 | struct replstr { |
258 | 259 | char *str; |
259 | }; | |
260 | } __packed; | |
260 | 261 | ARRAY_DECL(replstrs, struct replstr); |
261 | 262 | |
262 | 263 | /* Similar to replstr but needs expand_path too. */ |
263 | 264 | struct replpath { |
264 | 265 | char *str; |
265 | }; | |
266 | } __packed; | |
266 | 267 | |
267 | 268 | /* Server description. */ |
268 | 269 | struct server { |
826 | 827 | extern struct macros macros; |
827 | 828 | struct users *weed_users(struct users *); |
828 | 829 | struct strings *weed_strings(struct strings *); |
830 | void free_replstrs(struct replstrs *); | |
831 | char *fmt_replstrs(const char *, struct replstrs *); | |
829 | 832 | void free_strings(struct strings *); |
830 | 833 | char *fmt_strings(const char *, struct strings *); |
831 | 834 | char *fmt_users(const char *, struct users *); |
28 | 28 | void apply_result(struct expritem *, int *, int); |
29 | 29 | |
30 | 30 | struct users *find_delivery_users(struct mail_ctx *, struct action *, int *); |
31 | int fill_delivery_queue(struct mail_ctx *, struct rule *); | |
32 | void fill_delivery_action(struct mail_ctx *, struct rule *, | |
31 | int fill_from_strings(struct mail_ctx *, struct rule *, | |
32 | struct replstrs *); | |
33 | int fill_from_string(struct mail_ctx *, struct rule *, | |
34 | struct replstr *); | |
35 | int fill_from_action(struct mail_ctx *, struct rule *, | |
33 | 36 | struct action *, struct users *); |
34 | 37 | |
35 | 38 | int start_action(struct mail_ctx *, struct deliver_ctx *); |
39 | 42 | #define ACTION_DONE 0 |
40 | 43 | #define ACTION_ERROR 1 |
41 | 44 | #define ACTION_PARENT 2 |
45 | ||
46 | /* | |
47 | * Number of chained actions. Limit on recursion with things like: | |
48 | * | |
49 | * action "name" { action "name" } | |
50 | */ | |
51 | u_int chained; | |
42 | 52 | |
43 | 53 | int |
44 | 54 | mail_match(struct mail_ctx *mctx, struct msg *msg, struct msgbuf *msgbuf) |
242 | 252 | if (mctx->rule->lambda != NULL) { |
243 | 253 | users = find_delivery_users(mctx, NULL, &should_free); |
244 | 254 | |
245 | fill_delivery_action(mctx, | |
246 | mctx->rule, mctx->rule->lambda, users); | |
255 | chained = MAXACTIONCHAIN; | |
256 | if (fill_from_action(mctx, | |
257 | mctx->rule, mctx->rule->lambda, users) != 0) { | |
258 | if (should_free) | |
259 | ARRAY_FREEALL(users); | |
260 | return (MAIL_ERROR); | |
261 | } | |
247 | 262 | |
248 | 263 | if (should_free) |
249 | 264 | ARRAY_FREEALL(users); |
254 | 269 | * Fill the delivery action queue. |
255 | 270 | */ |
256 | 271 | if (!ARRAY_EMPTY(mctx->rule->actions)) { |
257 | if (fill_delivery_queue(mctx, mctx->rule) != 0) | |
272 | chained = MAXACTIONCHAIN; | |
273 | if (fill_from_strings(mctx, | |
274 | mctx->rule, mctx->rule->actions) != 0) | |
258 | 275 | return (MAIL_ERROR); |
259 | 276 | error = MAIL_DELIVER; |
260 | 277 | } |
397 | 414 | } |
398 | 415 | |
399 | 416 | int |
400 | fill_delivery_queue(struct mail_ctx *mctx, struct rule *r) | |
401 | { | |
402 | struct account *a = mctx->account; | |
403 | struct mail *m = mctx->mail; | |
404 | struct action *t; | |
405 | struct actions *ta; | |
406 | u_int i, j; | |
407 | char *s; | |
408 | struct replstr *rs; | |
409 | struct users *users; | |
410 | int should_free; | |
411 | ||
412 | for (i = 0; i < ARRAY_LENGTH(r->actions); i++) { | |
413 | rs = &ARRAY_ITEM(r->actions, i, struct replstr); | |
414 | s = replacestr(rs, m->tags, m, &m->rml); | |
415 | ||
416 | log_debug2("%s: looking for actions matching: %s", a->name, s); | |
417 | ta = match_actions(s); | |
418 | if (ARRAY_EMPTY(ta)) | |
419 | goto empty; | |
420 | xfree(s); | |
421 | ||
422 | log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta)); | |
423 | for (j = 0; j < ARRAY_LENGTH(ta); j++) { | |
424 | t = ARRAY_ITEM(ta, j, struct action *); | |
425 | users = find_delivery_users(mctx, t, &should_free); | |
426 | ||
427 | fill_delivery_action(mctx, r, t, users); | |
428 | ||
417 | fill_from_strings(struct mail_ctx *mctx, struct rule *r, struct replstrs *rsa) | |
418 | { | |
419 | struct account *a = mctx->account; | |
420 | u_int i; | |
421 | struct replstr *rs; | |
422 | ||
423 | chained--; | |
424 | if (chained == 0) { | |
425 | log_warnx("%s: too many chained actions", a->name); | |
426 | return (1); | |
427 | } | |
428 | ||
429 | for (i = 0; i < ARRAY_LENGTH(rsa); i++) { | |
430 | rs = &ARRAY_ITEM(rsa, i, struct replstr); | |
431 | if (fill_from_string(mctx, r, rs) != 0) | |
432 | return (1); | |
433 | } | |
434 | ||
435 | return (0); | |
436 | } | |
437 | ||
438 | int | |
439 | fill_from_string(struct mail_ctx *mctx, struct rule *r, struct replstr *rs) | |
440 | { | |
441 | struct account *a = mctx->account; | |
442 | struct mail *m = mctx->mail; | |
443 | struct action *t; | |
444 | struct actions *ta; | |
445 | u_int i; | |
446 | char *s; | |
447 | struct users *users; | |
448 | int should_free; | |
449 | ||
450 | s = replacestr(rs, m->tags, m, &m->rml); | |
451 | ||
452 | log_debug2("%s: looking for actions matching: %s", a->name, s); | |
453 | ta = match_actions(s); | |
454 | if (ARRAY_EMPTY(ta)) | |
455 | goto empty; | |
456 | xfree(s); | |
457 | ||
458 | log_debug2("%s: found %u actions", a->name, ARRAY_LENGTH(ta)); | |
459 | for (i = 0; i < ARRAY_LENGTH(ta); i++) { | |
460 | t = ARRAY_ITEM(ta, i, struct action *); | |
461 | users = find_delivery_users(mctx, t, &should_free); | |
462 | ||
463 | if (fill_from_action(mctx, r, t, users) != 0) { | |
429 | 464 | if (should_free) |
430 | 465 | ARRAY_FREEALL(users); |
431 | } | |
432 | ||
433 | ARRAY_FREEALL(ta); | |
434 | } | |
435 | ||
466 | ARRAY_FREEALL(ta); | |
467 | return (1); | |
468 | } | |
469 | ||
470 | if (should_free) | |
471 | ARRAY_FREEALL(users); | |
472 | } | |
473 | ||
474 | ARRAY_FREEALL(ta); | |
436 | 475 | return (0); |
437 | 476 | |
438 | 477 | empty: |
478 | log_warnx("%s: no actions matching: %s (%s)", a->name, s, rs->str); | |
439 | 479 | xfree(s); |
440 | 480 | ARRAY_FREEALL(ta); |
441 | log_warnx("%s: no actions matching: %s (%s)", a->name, s, rs->str); | |
442 | 481 | return (1); |
443 | ||
444 | } | |
445 | ||
446 | void | |
447 | fill_delivery_action(struct mail_ctx *mctx, struct rule *r, struct action *t, | |
482 | } | |
483 | ||
484 | int | |
485 | fill_from_action(struct mail_ctx *mctx, struct rule *r, struct action *t, | |
448 | 486 | struct users *users) |
449 | 487 | { |
450 | struct account *a = mctx->account; | |
451 | struct mail *m = mctx->mail; | |
452 | struct actitem *ti; | |
453 | struct deliver_ctx *dctx; | |
454 | u_int i; | |
488 | struct account *a = mctx->account; | |
489 | struct mail *m = mctx->mail; | |
490 | struct deliver_action_data *data; | |
491 | struct actitem *ti; | |
492 | struct deliver_ctx *dctx; | |
493 | u_int i; | |
455 | 494 | |
456 | 495 | for (i = 0; i < ARRAY_LENGTH(users); i++) { |
457 | 496 | TAILQ_FOREACH(ti, t->list, entry) { |
497 | if (ti->deliver == NULL) { | |
498 | data = ti->data; | |
499 | if (fill_from_strings(mctx, r, | |
500 | data->actions) != 0) | |
501 | return (1); | |
502 | continue; | |
503 | } | |
504 | ||
458 | 505 | dctx = xcalloc(1, sizeof *dctx); |
459 | 506 | dctx->action = t; |
460 | 507 | dctx->actitem = ti; |
469 | 516 | TAILQ_INSERT_TAIL(&mctx->dqueue, dctx, entry); |
470 | 517 | } |
471 | 518 | } |
519 | ||
520 | return (0); | |
472 | 521 | } |
473 | 522 | |
474 | 523 | int |
186 | 186 | } |
187 | 187 | |
188 | 188 | return (up); |
189 | } | |
190 | ||
191 | char * | |
192 | fmt_replstrs(const char *prefix, struct replstrs *rsp) | |
193 | { | |
194 | return (fmt_strings(prefix, (struct strings *) rsp)); /* XXX */ | |
195 | } | |
196 | ||
197 | void | |
198 | free_replstrs(struct replstrs *rsp) | |
199 | { | |
200 | return (free_strings((struct strings *) rsp)); /* XXX */ | |
189 | 201 | } |
190 | 202 | |
191 | 203 | char * |
389 | 401 | log_debug2("added rule %u:%s%s matches=%slambda=%s", r->idx, |
390 | 402 | sa, su, s, desc); |
391 | 403 | } else if (r->actions != NULL) { |
392 | ss = fmt_strings(NULL, (struct strings *) r->actions); | |
404 | ss = fmt_replstrs(NULL, r->actions); | |
393 | 405 | log_debug2("added rule %u:%s%s matches=%sactions=%s", r->idx, |
394 | 406 | sa, su, s, ss); |
395 | 407 | xfree(ss); |
420 | 432 | void |
421 | 433 | make_actlist(struct actlist *tl, char *buf, size_t len) |
422 | 434 | { |
423 | struct actitem *ti; | |
424 | char desc[DESCBUFSIZE]; | |
425 | size_t off; | |
435 | struct actitem *ti; | |
436 | struct deliver_action_data *data; | |
437 | char desc[DESCBUFSIZE], *s; | |
438 | size_t off; | |
426 | 439 | |
427 | 440 | off = 0; |
428 | 441 | TAILQ_FOREACH(ti, tl, entry) { |
429 | ti->deliver->desc(ti, desc, sizeof desc); | |
442 | if (ti->deliver != NULL) | |
443 | ti->deliver->desc(ti, desc, sizeof desc); | |
444 | else { | |
445 | data = ti->data; | |
446 | s = fmt_replstrs(NULL, data->actions); | |
447 | xsnprintf(desc, sizeof desc, "action %s", s); | |
448 | xfree(s); | |
449 | } | |
430 | 450 | off += xsnprintf(buf + off, len - off, "%u:%s ", ti->idx, desc); |
431 | 451 | if (off >= len) |
432 | 452 | break; |
493 | 513 | xfree(data->server.port); |
494 | 514 | if (data->server.ai != NULL) |
495 | 515 | freeaddrinfo(data->server.ai); |
516 | } else if (ti->deliver == NULL) { | |
517 | struct deliver_action_data *data = ti->data; | |
518 | free_replstrs(data->actions); | |
519 | ARRAY_FREEALL(data->actions); | |
496 | 520 | } |
497 | 521 | if (ti->data != NULL) |
498 | 522 | xfree(ti->data); |
513 | 537 | if (r->users != NULL) |
514 | 538 | ARRAY_FREEALL(r->users); |
515 | 539 | if (r->actions != NULL) { |
516 | free_strings((struct strings *) r->actions); | |
540 | free_replstrs(r->actions); | |
517 | 541 | ARRAY_FREEALL(r->actions); |
518 | 542 | } |
519 | 543 | |
1772 | 1796 | data->hdr.str = $2; |
1773 | 1797 | data->value.str = $3; |
1774 | 1798 | } |
1775 | | TOKAPPENDSTRING strv | |
1799 | | TOKAPPENDSTRING strv | |
1776 | 1800 | /** [$2: strv (char *)] */ |
1777 | 1801 | { |
1778 | 1802 | struct deliver_append_string_data *data; |
1871 | 1895 | data->key.str = $2; |
1872 | 1896 | data->value.str = $4; |
1873 | 1897 | } |
1898 | | actions | |
1899 | { | |
1900 | struct deliver_action_data *data; | |
1901 | ||
1902 | /* | |
1903 | * This is a special-case, handled when the list of delivery | |
1904 | * targets is resolved rather than by calling a deliver | |
1905 | * function, so the deliver pointer is NULL. | |
1906 | */ | |
1907 | $$ = xcalloc(1, sizeof *$$); | |
1908 | $$->deliver = NULL; | |
1909 | ||
1910 | data = xcalloc(1, sizeof *data); | |
1911 | $$->data = data; | |
1912 | ||
1913 | data->actions = $1; | |
1914 | } | |
1874 | 1915 | | TOKDROP |
1875 | 1916 | { |
1876 | 1917 | $$ = xcalloc(1, sizeof *$$); |
2010 | 2051 | } |
2011 | 2052 | |
2012 | 2053 | /** ACTIONS: <replstrs> (struct replstrs *) */ |
2013 | actions: actionp TOKNONE | |
2014 | { | |
2015 | $$ = NULL; | |
2016 | } | |
2017 | | actionp strv | |
2054 | actions: actionp strv | |
2018 | 2055 | /** [$2: strv (char *)] */ |
2019 | 2056 | { |
2020 | 2057 | if (*$2 == '\0') |