%{
/* 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);
}