%{ /* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999-2021 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include mu_sieve_machine_t mu_sieve_machine; int mu_sieve_error_count; static struct mu_sieve_node *sieve_tree; static struct mu_sieve_node *node_alloc (enum mu_sieve_node_type, struct mu_locus_range *); static void node_list_add (struct mu_sieve_node_list *list, struct mu_sieve_node *node); %} %error-verbose %locations %union { char *string; size_t number; size_t idx; struct mu_sieve_slice slice; struct { char *ident; struct mu_locus_range idloc; size_t first; size_t count; } command; struct mu_sieve_node_list node_list; struct mu_sieve_node *node; } %token IDENT TAG %token NUMBER %token STRING MULTILINE %token REQUIRE IF ELSIF ELSE ANYOF ALLOF NOT FALSE TRUE %type arg %type arglist maybe_arglist slist stringlist stringorlist %type command %type action test statement block cond %type else_part %type list testlist elsif_branch maybe_elsif %% input : /* empty */ { sieve_tree = NULL; } | list { struct mu_locus_range lr; lr.beg = lr.end = @1.end; node_list_add (&$1, node_alloc (mu_sieve_node_end, &lr)); sieve_tree = $1.head; } ; list : statement { $$.head = $$.tail = $1; } | list statement { node_list_add (&$1, $2); $$ = $1; } ; statement : REQUIRE stringorlist ';' { mu_sieve_require (mu_sieve_machine, &$2); /* Reclaim string slots. The string data referred to by $2 are registered in memory_pool, so we don't free them */ mu_sieve_machine->stringcount -= $2.count; $$ = NULL; } | action ';' /* 1 2 3 4 */ | IF cond block else_part { $$ = node_alloc (mu_sieve_node_cond, &@1); $$->v.cond.expr = $2; $$->v.cond.iftrue = $3; $$->v.cond.iffalse = $4; } ; else_part : maybe_elsif { $$ = $1.head; } | maybe_elsif ELSE block { if ($1.tail) { $1.tail->v.cond.iffalse = $3; $$ = $1.head; } else $$ = $3; } ; maybe_elsif : /* empty */ { $$.head = $$.tail = NULL; } | elsif_branch ; /* elsif branches form a singly-linked version of node_list. Nodes in this list are linked by v.cond.iffalse pointer */ elsif_branch : ELSIF cond block { struct mu_sieve_node *node = node_alloc (mu_sieve_node_cond, &@1); node->v.cond.expr = $2; node->v.cond.iftrue = $3; node->v.cond.iffalse = NULL; $$.head = $$.tail = node; } | elsif_branch ELSIF cond block { struct mu_sieve_node *node = node_alloc (mu_sieve_node_cond, &@2); node->v.cond.expr = $3; node->v.cond.iftrue = $4; node->v.cond.iffalse = NULL; $1.tail->v.cond.iffalse = node; $1.tail = node; $$ = $1; } ; block : '{' list '}' { $$ = $2.head; } ; testlist : cond { $$.head = $$.tail = $1; } | testlist ',' cond { $3->prev = $1.tail; $1.tail->next = $3; $1.tail = $3; $$ = $1; } ; cond : test | ANYOF '(' testlist ')' { $$ = node_alloc (mu_sieve_node_anyof, &@1); $$->v.node = $3.head; } | ALLOF '(' testlist ')' { $$ = node_alloc (mu_sieve_node_allof, &@1); $$->v.node = $3.head; } | NOT cond { $$ = node_alloc (mu_sieve_node_not, &@1); $$->v.node = $2; } ; test : command { mu_sieve_registry_t *reg; mu_locus_range_copy (&mu_sieve_machine->locus, &@1); reg = mu_sieve_registry_lookup (mu_sieve_machine, $1.ident, mu_sieve_record_test); if (!reg) { mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc, _("unknown test: %s"), $1.ident); mu_i_sv_error (mu_sieve_machine); } else if (!reg->required) { mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc, _("test `%s' has not been required"), $1.ident); mu_i_sv_error (mu_sieve_machine); } $$ = node_alloc (mu_sieve_node_test, &@1); $$->v.command.reg = reg; $$->v.command.argstart = $1.first; $$->v.command.argcount = $1.count; $$->v.command.tagcount = 0; $$->v.command.comparator = NULL; mu_i_sv_lint_command (mu_sieve_machine, $$); } | TRUE { $$ = node_alloc (mu_sieve_node_true, &@1); } | FALSE { $$ = node_alloc (mu_sieve_node_false, &@1); } ; command : IDENT maybe_arglist { $$.ident = $1; $$.idloc = @1; $$.first = $2.first; $$.count = $2.count; } ; action : command { mu_sieve_registry_t *reg; mu_locus_range_copy (&mu_sieve_machine->locus, &@1); reg = mu_sieve_registry_lookup (mu_sieve_machine, $1.ident, mu_sieve_record_action); if (!reg) { mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc, _("unknown action: %s"), $1.ident); mu_i_sv_error (mu_sieve_machine); } else if (!reg->required) { mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc, _("action `%s' has not been required"), $1.ident); mu_i_sv_error (mu_sieve_machine); } $$ = node_alloc(mu_sieve_node_action, &@1); $$->v.command.reg = reg; $$->v.command.argstart = $1.first; $$->v.command.argcount = $1.count; $$->v.command.tagcount = 0; mu_i_sv_lint_command (mu_sieve_machine, $$); } ; maybe_arglist: /* empty */ { $$.first = 0; $$.count = 0; } | arglist ; arglist : arg { $$.first = $1; $$.count = 1; } | arglist arg { $1.count++; $$ = $1; } ; arg : stringlist { $$ = mu_sieve_value_create (mu_sieve_machine, SVT_STRING_LIST, &@1, &$1); } | STRING { $$ = mu_sieve_value_create (mu_sieve_machine, SVT_STRING, &@1, $1); } | MULTILINE { $$ = mu_sieve_value_create (mu_sieve_machine, SVT_STRING, &@1, $1); } | NUMBER { $$ = mu_sieve_value_create (mu_sieve_machine, SVT_NUMBER, &@1, &$1); } | TAG { $$ = mu_sieve_value_create (mu_sieve_machine, SVT_TAG, &@1, $1); } ; stringorlist : STRING { $$.first = mu_i_sv_string_create (mu_sieve_machine, $1); $$.count = 1; } | stringlist ; stringlist : '[' slist ']' { $$ = $2; } ; slist : STRING { $$.first = mu_i_sv_string_create (mu_sieve_machine, $1); $$.count = 1; } | slist ',' STRING { mu_i_sv_string_create (mu_sieve_machine, $3); $1.count++; $$ = $1; } ; %% int yyerror (const char *s) { mu_error ("%s", s); mu_i_sv_error (mu_sieve_machine); return 0; } static void node_list_add (struct mu_sieve_node_list *list, struct mu_sieve_node *node) { if (!node) return; node->prev = list->tail; if (list->tail) list->tail->next = node; else list->head = node; list->tail = node; } static struct mu_sieve_node * node_alloc (enum mu_sieve_node_type type, struct mu_locus_range *lr) { struct mu_sieve_node *node = malloc (sizeof (*node)); if (node) { node->prev = node->next = NULL; node->type = type; mu_locus_range_init (&node->locus); mu_locus_range_copy (&node->locus, lr); } return node; } static void node_optimize (struct mu_sieve_node *node); static void node_free (struct mu_sieve_node *node); static void node_replace (struct mu_sieve_node *node, struct mu_sieve_node *repl); static void node_code (struct mu_sieve_machine *mach, struct mu_sieve_node *node); static void node_dump (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach); static void tree_free (struct mu_sieve_node **tree); static void tree_optimize (struct mu_sieve_node *tree); static void tree_code (struct mu_sieve_machine *mach, struct mu_sieve_node *tree); static void tree_dump (mu_stream_t str, struct mu_sieve_node *tree, unsigned level, struct mu_sieve_machine *mach); static void indent (mu_stream_t str, unsigned level) { #define tab " " #define tablen (sizeof (tab) - 1) while (level--) mu_stream_write (str, tab, tablen, NULL); } /* mu_sieve_node_noop */ static void dump_node_noop (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { indent (str, level); mu_stream_printf (str, "NOOP\n"); } /* mu_sieve_node_false */ static void dump_node_false (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { indent (str, level); mu_stream_printf (str, "FALSE\n"); } /* mu_sieve_node_true */ static void dump_node_true (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { indent (str, level); mu_stream_printf (str, "TRUE\n"); } /* mu_sieve_node_test & mu_sieve_node_action */ static void free_node_command (struct mu_sieve_node *node) { /* nothing */ } static void code_node_test (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { mu_i_sv_code_test (mach, node); } static void code_node_action (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { mu_i_sv_code_action (mach, node); } void mu_i_sv_valf (mu_sieve_machine_t mach, mu_stream_t str, mu_sieve_value_t *val) { mu_stream_printf (str, " "); if (val->tag) { mu_stream_printf (str, ":%s", val->tag); if (val->type == SVT_VOID) return; mu_stream_printf (str, " "); } switch (val->type) { case SVT_VOID: mu_stream_printf (str, "(void)"); break; case SVT_NUMBER: mu_stream_printf (str, "%zu", val->v.number); break; case SVT_STRING: mu_stream_printf (str, "\"%s\"", mu_sieve_string_raw (mach, &val->v.list, 0)->orig); break; case SVT_STRING_LIST: { size_t i; mu_stream_printf (str, "["); for (i = 0; i < val->v.list.count; i++) { if (i) mu_stream_printf (str, ", "); mu_stream_printf (str, "\"%s\"", mu_sieve_string_raw (mach, &val->v.list, i)->orig); } mu_stream_printf (str, "]"); } break; case SVT_TAG: mu_stream_printf (str, ":%s", val->v.string); break; default: abort (); } } static void dump_node_command (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { size_t i; indent (str, level); mu_stream_printf (str, "COMMAND %s", node->v.command.reg->name); for (i = 0; i < node->v.command.argcount + node->v.command.tagcount; i++) mu_i_sv_valf (mach, str, &mach->valspace[node->v.command.argstart + i]); mu_stream_printf (str, "\n"); } /* mu_sieve_node_cond */ static void free_node_cond (struct mu_sieve_node *node) { tree_free (&node->v.cond.expr); tree_free (&node->v.cond.iftrue); tree_free (&node->v.cond.iffalse); } static void optimize_node_cond (struct mu_sieve_node *node) { tree_optimize (node->v.cond.expr); switch (node->v.cond.expr->type) { case mu_sieve_node_true: tree_optimize (node->v.cond.iftrue); node_replace (node, node->v.cond.iftrue); break; case mu_sieve_node_false: tree_optimize (node->v.cond.iffalse); node_replace (node, node->v.cond.iffalse); break; default: tree_optimize (node->v.cond.iftrue); tree_optimize (node->v.cond.iffalse); } } static void code_node_cond (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { size_t br1; tree_code (mach, node->v.cond.expr); mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_brz); br1 = mach->pc; mu_i_sv_code (mach, (sieve_op_t) 0); tree_code (mach, node->v.cond.iftrue); if (node->v.cond.iffalse) { size_t br2; mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_branch); br2 = mach->pc; mu_i_sv_code (mach, (sieve_op_t) 0); mach->prog[br1].pc = mach->pc - br1 - 1; tree_code (mach, node->v.cond.iffalse); mach->prog[br2].pc = mach->pc - br2 - 1; } else mach->prog[br1].pc = mach->pc - br1 - 1; } static void dump_node_cond (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { indent (str, level); mu_stream_printf (str, "COND\n"); ++level; indent (str, level); mu_stream_printf (str, "EXPR:\n"); tree_dump (str, node->v.cond.expr, level + 1, mach); indent (str, level); mu_stream_printf (str, "IFTRUE:\n"); tree_dump (str, node->v.cond.iftrue, level + 1, mach); indent (str, level); mu_stream_printf (str, "IFFALSE:\n"); tree_dump (str, node->v.cond.iffalse, level + 1, mach); } /* mu_sieve_node_anyof & mu_sieve_node_allof */ static void free_node_x_of (struct mu_sieve_node *node) { tree_free (&node->v.node); } static void optimize_x_of (struct mu_sieve_node *node, enum mu_sieve_node_type solve) { struct mu_sieve_node *cur; tree_optimize (node->v.node); cur = node->v.node; while (cur) { struct mu_sieve_node *next = cur->next; switch (cur->type) { case mu_sieve_node_false: case mu_sieve_node_true: if (cur->type == solve) { tree_free (&node->v.node); node->type = solve; return; } else { if (cur->prev) cur->prev->next = next; else node->v.node = next; if (next) next->prev = cur->prev; node_free (cur); } break; default: break; } cur = next; } if (!node->v.node) node->type = solve == mu_sieve_node_false ? mu_sieve_node_true : mu_sieve_node_false; } static void code_node_x_of (struct mu_sieve_machine *mach, struct mu_sieve_node *node, sieve_op_t op) { struct mu_sieve_node *cur = node->v.node; size_t pc = 0; size_t end; while (cur) { node_code (mach, cur); if (cur->next) { mu_i_sv_code (mach, op); mu_i_sv_code (mach, (sieve_op_t) pc); pc = mach->pc - 1; } cur = cur->next; } /* Fix-up locations */ end = mach->pc; while (pc != 0) { size_t prev = mach->prog[pc].pc; mach->prog[pc].pc = end - pc - 1; pc = prev; } } static void dump_node_x_of (mu_stream_t str, struct mu_sieve_node *node, unsigned level, mu_sieve_machine_t mach) { indent (str, level); mu_stream_printf (str, "%s:\n", node->type == mu_sieve_node_allof ? "ALLOF" : "ANYOF"); ++level; node = node->v.node; while (node) { node_dump (str, node, level + 1, mach); node = node->next; if (node) { indent (str, level); mu_stream_printf (str, "%s:\n", node->type == mu_sieve_node_allof ? "AND" : "OR"); } } } /* mu_sieve_node_anyof */ static void optimize_node_anyof (struct mu_sieve_node *node) { optimize_x_of (node, mu_sieve_node_true); } static void code_node_anyof (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { code_node_x_of (mach, node, (sieve_op_t) _mu_i_sv_instr_brnz); } /* mu_sieve_node_allof */ static void optimize_node_allof (struct mu_sieve_node *node) { return optimize_x_of (node, mu_sieve_node_false); } static void code_node_allof (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { code_node_x_of (mach, node, (sieve_op_t) _mu_i_sv_instr_brz); } /* mu_sieve_node_not */ static void free_node_not (struct mu_sieve_node *node) { tree_free (&node->v.node); } static void optimize_node_not (struct mu_sieve_node *node) { tree_optimize (node->v.node); switch (node->v.node->type) { case mu_sieve_node_false: tree_free (&node->v.node); node->type = mu_sieve_node_true; break; case mu_sieve_node_true: tree_free (&node->v.node); node->type = mu_sieve_node_false; break; default: break; } } static void code_node_not (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { node_code (mach, node->v.node); mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_not); } static void dump_node_not (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { indent (str, level); mu_stream_printf (str, "NOT\n"); node_dump (str, node->v.node, level + 1, mach); } /* mu_sieve_node_end */ static void code_node_end (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { mu_i_sv_code (mach, (sieve_op_t) (sieve_instr_t) 0); } static void dump_node_end (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { indent (str, level); mu_stream_printf (str, "END\n"); } struct node_descr { void (*code_fn) (struct mu_sieve_machine *mach, struct mu_sieve_node *node); void (*optimize_fn) (struct mu_sieve_node *node); void (*free_fn) (struct mu_sieve_node *node); void (*dump_fn) (mu_stream_t str, struct mu_sieve_node *node, unsigned level, mu_sieve_machine_t); }; static struct node_descr node_descr[] = { [mu_sieve_node_noop] = { NULL, NULL, NULL, dump_node_noop }, [mu_sieve_node_false] = { NULL, NULL, NULL, dump_node_false }, [mu_sieve_node_true] = { NULL, NULL, NULL, dump_node_true }, [mu_sieve_node_test] = { code_node_test, NULL, free_node_command, dump_node_command }, [mu_sieve_node_action] = { code_node_action, NULL, free_node_command, dump_node_command }, [mu_sieve_node_cond] = { code_node_cond, optimize_node_cond, free_node_cond, dump_node_cond }, [mu_sieve_node_anyof] = { code_node_anyof, optimize_node_anyof, free_node_x_of, dump_node_x_of }, [mu_sieve_node_allof] = { code_node_allof, optimize_node_allof, free_node_x_of, dump_node_x_of }, [mu_sieve_node_not] = { code_node_not, optimize_node_not, free_node_not, dump_node_not }, [mu_sieve_node_end] = { code_node_end, NULL, NULL, dump_node_end } }; static void node_optimize (struct mu_sieve_node *node) { if ((int)node->type >= MU_ARRAY_SIZE (node_descr)) abort (); if (node_descr[node->type].optimize_fn) node_descr[node->type].optimize_fn (node); } static void node_free (struct mu_sieve_node *node) { if ((int)node->type >= MU_ARRAY_SIZE (node_descr)) abort (); if (node_descr[node->type].free_fn) node_descr[node->type].free_fn (node); free (node); } static void node_replace (struct mu_sieve_node *node, struct mu_sieve_node *repl) { struct mu_sieve_node copy; if ((int)node->type >= MU_ARRAY_SIZE (node_descr)) abort (); copy = *node; if (repl) { node->type = repl->type; node->v = repl->v; switch (copy.type) { case mu_sieve_node_cond: if (repl == copy.v.cond.expr) copy.v.cond.expr = NULL; else if (repl == copy.v.cond.iftrue) copy.v.cond.iftrue = NULL; else if (repl == copy.v.cond.iffalse) copy.v.cond.iffalse = NULL; break; case mu_sieve_node_not: if (repl == copy.v.node) copy.v.node = NULL; break; default: break; } } else node->type = mu_sieve_node_noop; if (node_descr[node->type].free_fn) node_descr[node->type].free_fn (©); } static void node_code (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { if ((int)node->type >= MU_ARRAY_SIZE (node_descr)) abort (); if (node_descr[node->type].code_fn) { mu_i_sv_locus (mach, &node->locus); node_descr[node->type].code_fn (mach, node); } } static void node_dump (mu_stream_t str, struct mu_sieve_node *node, unsigned level, struct mu_sieve_machine *mach) { if ((int)node->type >= MU_ARRAY_SIZE (node_descr) || !node_descr[node->type].dump_fn) abort (); node_descr[node->type].dump_fn (str, node, level, mach); } static void tree_free (struct mu_sieve_node **tree) { struct mu_sieve_node *cur = *tree; while (cur) { struct mu_sieve_node *next = cur->next; node_free (cur); cur = next; } } static void tree_optimize (struct mu_sieve_node *tree) { while (tree) { node_optimize (tree); tree = tree->next; } } static void tree_code (struct mu_sieve_machine *mach, struct mu_sieve_node *tree) { while (tree) { node_code (mach, tree); tree = tree->next; } } static void tree_dump (mu_stream_t str, struct mu_sieve_node *tree, unsigned level, struct mu_sieve_machine *mach) { while (tree) { node_dump (str, tree, level, mach); tree = tree->next; } } void mu_i_sv_error (mu_sieve_machine_t mach) { mach->state = mu_sieve_state_error; } int mu_sieve_machine_create (mu_sieve_machine_t *pmach) { int rc; mu_sieve_machine_t mach; mu_sieve_debug_init (); mach = malloc (sizeof (*mach)); if (!mach) return ENOMEM; memset (mach, 0, sizeof (*mach)); mach->memory_pool = NULL; rc = mu_opool_create (&mach->string_pool, MU_OPOOL_DEFAULT); if (rc) { mu_list_destroy (&mach->memory_pool); free (mach); return rc; } mach->data = NULL; mu_sieve_set_diag_stream (mach, mu_strerr); mu_sieve_set_dbg_stream (mach, mu_strerr); *pmach = mach; return 0; } void mu_i_sv_free_stringspace (mu_sieve_machine_t mach) { size_t i; for (i = 0; i < mach->stringcount; i++) { if (mach->stringspace[i].rx) { regex_t *rx = mach->stringspace[i].rx; regfree (rx); } /* There's no need to free mach->stringspace[i].exp, because it is allocated in mach's memory pool */ } } int mu_sieve_machine_reset (mu_sieve_machine_t mach) { switch (mach->state) { case mu_sieve_state_init: /* Nothing to do */ return 0; case mu_sieve_state_error: case mu_sieve_state_compiled: /* Do the right thing */ break; case mu_sieve_state_running: case mu_sieve_state_disass: /* Can't reset a running machine */ return MU_ERR_FAILURE; } mu_i_sv_free_stringspace (mach); mu_list_clear (mach->memory_pool); mu_list_clear (mach->destr_list); mu_opool_free (mach->string_pool, NULL); mu_i_sv_free_idspace (mach); mu_list_clear (mach->registry); mach->stringspace = NULL; mach->stringcount = 0; mach->stringmax = 0; mach->valspace = NULL; mach->valcount = 0; mach->valmax = 0; mach->progsize = 0; mach->prog = NULL; mu_assoc_destroy (&mach->vartab); mach->match_string = NULL; mach->match_buf = NULL; mach->match_count = 0; mach->match_max = 0; mach->state = mu_sieve_state_init; return 0; } static int regdup (void *item, void *data) { mu_sieve_registry_t *reg = item; mu_sieve_machine_t mach = data; mu_sieve_registry_require (mach, reg->name, reg->type); return 0; } static void copy_stream_state (mu_sieve_machine_t child, mu_sieve_machine_t parent) { child->state_flags = parent->state_flags; child->err_mode = parent->err_mode; mu_locus_range_copy (&child->err_locus, &parent->err_locus); child->dbg_mode = parent->dbg_mode; mu_locus_range_copy (&child->dbg_locus, &parent->dbg_locus); child->errstream = parent->errstream; mu_stream_ref (child->errstream); child->dbgstream = parent->dbgstream; mu_stream_ref (child->dbgstream); } int mu_sieve_machine_clone (mu_sieve_machine_t const parent, mu_sieve_machine_t *pmach) { size_t i; mu_sieve_machine_t child; int rc; if (!parent || parent->state == mu_sieve_state_error) return EINVAL; rc = mu_sieve_machine_create (&child); if (rc) return rc; rc = setjmp (child->errbuf); if (rc == 0) { child->state = mu_sieve_state_init; mu_i_sv_register_standard_actions (child); mu_i_sv_register_standard_tests (child); mu_i_sv_register_standard_comparators (child); /* Load necessary modules */ mu_list_foreach (parent->registry, regdup, child); /* Copy identifiers */ child->idspace = mu_sieve_calloc (child, parent->idcount, sizeof (child->idspace[0])); child->idcount = child->idmax = parent->idcount; for (i = 0; i < child->idcount; i++) child->idspace[i] = mu_sieve_strdup (parent, parent->idspace[i]); /* Copy string constants */ child->stringspace = mu_sieve_calloc (child, parent->stringcount, sizeof (child->stringspace[0])); child->stringcount = child->stringmax = parent->stringcount; for (i = 0; i < parent->stringcount; i++) { memset (&child->stringspace[i], 0, sizeof (child->stringspace[0])); child->stringspace[i].orig = mu_sieve_strdup (parent, parent->stringspace[i].orig); } /* Copy value space */ child->valspace = mu_sieve_calloc (child, parent->valcount, sizeof child->valspace[0]); child->valcount = child->valmax = parent->valcount; for (i = 0; i < child->valcount; i++) { child->valspace[i].type = parent->valspace[i].type; child->valspace[i].tag = mu_sieve_strdup (child, parent->valspace[i].tag); switch (child->valspace[i].type) { case SVT_TAG: child->valspace[i].v.string = mu_sieve_strdup (child, parent->valspace[i].v.string); break; default: child->valspace[i].v = parent->valspace[i].v; } } /* Copy progspace */ child->progsize = parent->progsize; child->prog = mu_sieve_calloc (child, parent->progsize, sizeof child->prog[0]); memcpy (child->prog, parent->prog, parent->progsize * sizeof (child->prog[0])); /* Copy variables */ if (mu_sieve_has_variables (parent)) { mu_i_sv_copy_variables (child, parent); child->match_string = NULL; child->match_buf = NULL; child->match_count = 0; child->match_max = 0; } /* Copy user-defined settings */ child->dry_run = parent->dry_run; copy_stream_state (child, parent); child->data = parent->data; child->logger = parent->logger; child->daemon_email = parent->daemon_email; *pmach = child; } else mu_sieve_machine_destroy (&child); return rc; } int mu_sieve_machine_dup (mu_sieve_machine_t const in, mu_sieve_machine_t *out) { int rc; mu_sieve_machine_t mach; if (!in || in->state == mu_sieve_state_error) return EINVAL; mach = malloc (sizeof (*mach)); if (!mach) return ENOMEM; memset (mach, 0, sizeof (*mach)); rc = mu_list_create (&mach->memory_pool); if (rc) { free (mach); return rc; } mach->destr_list = NULL; mach->registry = NULL; mach->progsize = in->progsize; mach->prog = in->prog; switch (in->state) { case mu_sieve_state_running: case mu_sieve_state_disass: mach->state = mu_sieve_state_compiled; break; default: mach->state = in->state; } rc = setjmp (mach->errbuf); if (rc == 0) { mach->pc = 0; mach->reg = 0; mach->dry_run = in->dry_run; mach->state_flags = in->state_flags; mach->err_mode = in->err_mode; mu_locus_range_copy (&mach->err_locus, &in->err_locus); mach->dbg_mode = in->dbg_mode; mu_locus_range_copy (&mach->dbg_locus, &in->dbg_locus); copy_stream_state (mach, in); mu_i_sv_copy_variables (mach, in); mach->data = in->data; mach->logger = in->logger; mach->daemon_email = in->daemon_email; *out = mach; } else mu_sieve_machine_destroy (&mach); return rc; } void mu_sieve_get_diag_stream (mu_sieve_machine_t mach, mu_stream_t *pstr) { *pstr = mach->errstream; mu_stream_ref (*pstr); } void mu_sieve_set_diag_stream (mu_sieve_machine_t mach, mu_stream_t str) { mu_stream_unref (mach->errstream); mach->errstream = str; mu_stream_ref (mach->errstream); } void mu_sieve_set_dbg_stream (mu_sieve_machine_t mach, mu_stream_t str) { mu_stream_unref (mach->dbgstream); mach->dbgstream = str; mu_stream_ref (mach->dbgstream); } void mu_sieve_get_dbg_stream (mu_sieve_machine_t mach, mu_stream_t *pstr) { *pstr = mach->dbgstream; mu_stream_ref (*pstr); } void mu_sieve_set_logger (mu_sieve_machine_t mach, mu_sieve_action_log_t logger) { mach->logger = logger; } mu_mailer_t mu_sieve_get_mailer (mu_sieve_machine_t mach) { if (!mach->mailer) { int rc; rc = mu_mailer_create (&mach->mailer, NULL); if (rc) { mu_sieve_error (mach, _("%lu: cannot create mailer: %s"), (unsigned long) mu_sieve_get_message_num (mach), mu_strerror (rc)); return NULL; } rc = mu_mailer_open (mach->mailer, 0); if (rc) { mu_url_t url = NULL; mu_mailer_get_url (mach->mailer, &url); mu_sieve_error (mach, _("%lu: cannot open mailer %s: %s"), (unsigned long) mu_sieve_get_message_num (mach), mu_url_to_string (url), mu_strerror (rc)); mu_mailer_destroy (&mach->mailer); return NULL; } } return mach->mailer; } void mu_sieve_set_mailer (mu_sieve_machine_t mach, mu_mailer_t mailer) { mu_mailer_destroy (&mach->mailer); mach->mailer = mailer; } #define MAILER_DAEMON_PFX "MAILER-DAEMON@" char * mu_sieve_get_daemon_email (mu_sieve_machine_t mach) { if (!mach->daemon_email) { const char *domain = NULL; mu_get_user_email_domain (&domain); mach->daemon_email = mu_sieve_malloc (mach, sizeof(MAILER_DAEMON_PFX) + strlen (domain)); sprintf (mach->daemon_email, "%s%s", MAILER_DAEMON_PFX, domain); } return mach->daemon_email; } void mu_sieve_set_daemon_email (mu_sieve_machine_t mach, const char *email) { mu_sieve_free (mach, (void *)mach->daemon_email); mach->daemon_email = mu_sieve_strdup (mach, email); } struct sieve_destr_record { mu_sieve_destructor_t destr; void *ptr; }; static void run_destructor (void *data) { struct sieve_destr_record *p = data; p->destr (p->ptr); free (data); } void mu_sieve_machine_add_destructor (mu_sieve_machine_t mach, mu_sieve_destructor_t destr, void *ptr) { int rc; struct sieve_destr_record *p; if (!mach->destr_list) { rc = mu_list_create (&mach->destr_list); if (rc) { mu_sieve_error (mach, "mu_list_create: %s", mu_strerror (rc)); destr (ptr); mu_sieve_abort (mach); } mu_list_set_destroy_item (mach->destr_list, run_destructor); } p = malloc (sizeof (*p)); if (!p) { mu_sieve_error (mach, "%s", mu_strerror (errno)); destr (ptr); mu_sieve_abort (mach); } p->destr = destr; p->ptr = ptr; rc = mu_list_prepend (mach->destr_list, p); if (rc) { mu_sieve_error (mach, "mu_list_prepend: %s", mu_strerror (rc)); destr (ptr); free (p); mu_sieve_abort (mach); } } void mu_sieve_machine_destroy (mu_sieve_machine_t *pmach) { mu_sieve_machine_t mach = *pmach; mu_i_sv_free_stringspace (mach); mu_sieve_free (mach, mach->stringspace); mu_stream_destroy (&mach->errstream); mu_stream_destroy (&mach->dbgstream); mu_mailer_destroy (&mach->mailer); mu_list_destroy (&mach->destr_list); mu_list_destroy (&mach->registry); mu_sieve_free (mach, mach->idspace); mu_opool_destroy (&mach->string_pool); mu_list_destroy (&mach->memory_pool); mu_assoc_destroy (&mach->vartab); mu_list_destroy (&mach->init_var); free (mach); *pmach = NULL; } int with_machine (mu_sieve_machine_t mach, int (*thunk) (void *), void *data) { int rc = 0; mu_stream_t save_errstr; rc = mu_sieve_machine_reset (mach); if (rc) return rc; save_errstr = mu_strerr; mu_stream_ref (save_errstr); mu_strerr = mach->errstream; mu_stream_ref (mu_strerr); mu_sieve_machine = mach; rc = setjmp (mach->errbuf); if (rc == 0) { mach->state = mu_sieve_state_init; mu_i_sv_register_standard_actions (mach); mu_i_sv_register_standard_tests (mach); mu_i_sv_register_standard_comparators (mach); mu_sieve_stream_save (mach); rc = thunk (data); mu_sieve_stream_restore (mach); mu_stream_unref (save_errstr); mu_strerr = save_errstr; mu_stream_unref (mu_strerr); } else mach->state = mu_sieve_state_error; return rc; } /* Rescan all registered strings to determine their properties */ static void string_rescan (mu_sieve_machine_t mach) { size_t i; int hasvar = mu_sieve_has_variables (mach); for (i = 0; i < mach->stringcount; i++) { mach->stringspace[i].changed = 0; if (hasvar) { mach->stringspace[i].constant = 0; mu_sieve_string_get (mach, &mach->stringspace[i]); mu_sieve_free (mach, mach->stringspace[i].exp); mach->stringspace[i].exp = NULL; mach->stringspace[i].constant = !mach->stringspace[i].changed; mach->stringspace[i].changed = 0; } else mach->stringspace[i].constant = 1; } } static int sieve_parse (void) { int rc; int old_mode, mode; sieve_tree = NULL; yydebug = mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE3); mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_GET_MODE, &old_mode); mode = old_mode | MU_LOGMODE_LOCUS; mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_SET_MODE, &mode); rc = yyparse (); mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_SET_MODE, &old_mode); mu_i_sv_lex_finish (); if (rc) mu_i_sv_error (mu_sieve_machine); if (mu_sieve_machine->state == mu_sieve_state_init) { if (mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE1)) { mu_error (_("Unoptimized parse tree")); tree_dump (mu_strerr, sieve_tree, 0, mu_sieve_machine); } tree_optimize (sieve_tree); if (mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE2)) { mu_error (_("Optimized parse tree")); tree_dump (mu_strerr, sieve_tree, 0, mu_sieve_machine); } mu_i_sv_code (mu_sieve_machine, (sieve_op_t) (sieve_instr_t) 0); /* Clear location, so that mu_i_sv_locus will do its job. */ /* FIXME: is it still needed? */ mu_locus_range_deinit (&mu_sieve_machine->locus); tree_code (mu_sieve_machine, sieve_tree); mu_i_sv_code (mu_sieve_machine, (sieve_op_t) (sieve_instr_t) 0); } if (rc == 0) { if (mu_sieve_machine->state == mu_sieve_state_error) rc = MU_ERR_PARSE; else { string_rescan (mu_sieve_machine); mu_sieve_machine->state = mu_sieve_state_compiled; } } tree_free (&sieve_tree); return rc; } static int sieve_compile_file (void *name) { if (mu_i_sv_lex_begin (name) == 0) return sieve_parse (); return MU_ERR_FAILURE; } int mu_sieve_compile (mu_sieve_machine_t mach, const char *name) { return with_machine (mach, sieve_compile_file, (void *) name); } struct strbuf { const char *ptr; size_t size; struct mu_locus_point const *pt; }; static int sieve_compile_strbuf (void *name) { struct strbuf *buf = name; if (mu_i_sv_lex_begin_string (buf->ptr, buf->size, buf->pt) == 0) return sieve_parse (); return MU_ERR_FAILURE; } int mu_sieve_compile_text (mu_sieve_machine_t mach, const char *str, size_t strsize, struct mu_locus_point const *loc) { struct strbuf buf; buf.ptr = str; buf.size = strsize; buf.pt = loc; return with_machine (mach, sieve_compile_strbuf, &buf); }