diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-12-07 09:46:57 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-12-07 13:03:42 +0200 |
commit | 799519539556b38fc9f84d44927861cb9239d14c (patch) | |
tree | 304982f21416f837c8b5a678b85ba99edce62d40 | |
parent | cab2fd5c0479d16a3dcd746c98de8be771a8963d (diff) | |
download | mailutils-799519539556b38fc9f84d44927861cb9239d14c.tar.gz mailutils-799519539556b38fc9f84d44927861cb9239d14c.tar.bz2 |
Implement the "variables" Sieve extension (RFC 5229)
* include/mailutils/sieve.h (mu_sieve_string): New fields
"constant" and "changed".
(mu_sieve_match_part_tags): New extern.
(mu_sieve_relational_count)
(mu_sieve_require_variables)
(mu_sieve_has_variables)
(mu_sieve_string_get): New functions.
* libmu_sieve/variables.c: New file.
* libmu_sieve/Makefile.am: Add variables.c
* libmu_sieve/comparator.c: Use mu_sieve_string_get to obtain
the actual value of the string.
* libmu_sieve/require.c: Support the "variables" extension.
* libmu_sieve/sieve-priv.h (mu_sieve_machine): New fields
vartab, match_string, match_buf, match_count, match_max.
(mu_i_sv_copy_variables)
(mu_i_sv_expand_variables): New protos.
* libmu_sieve/sieve.l (line_add): zero length means add entire
asciiz string.
* libmu_sieve/sieve.y (mu_sieve_machine_reset): Reset the new
fields.
(mu_sieve_machine_clone): Copy variables and initialize new
fields.
(string_rescan): New function.
(sieve_parse): Rescan string to determine their properties.
* libmu_sieve/strexp.c (update_len): Allow for NULL replacement
values.
* libmu_sieve/string.c (mu_sieve_string_get): New function.
(mu_sieve_string): Use it.
* libmu_sieve/tests.c (do_count): Rename to mu_sieve_relational_count,
make global. All uses changed.
(match_part_tags): Rename to mu_sieve_match_part_tags, make global.
All uses changed.
* sieve/tests/variables.at: New file.
* sieve/tests/Makefile.am: Add new testcases.
* sieve/tests/testsuite.at: Likewise.
-rw-r--r-- | include/mailutils/sieve.h | 24 | ||||
-rw-r--r-- | libmu_sieve/Makefile.am | 3 | ||||
-rw-r--r-- | libmu_sieve/comparator.c | 105 | ||||
-rw-r--r-- | libmu_sieve/require.c | 7 | ||||
-rw-r--r-- | libmu_sieve/runtime.c | 5 | ||||
-rw-r--r-- | libmu_sieve/sieve-priv.h | 14 | ||||
-rw-r--r-- | libmu_sieve/sieve.l | 2 | ||||
-rw-r--r-- | libmu_sieve/sieve.y | 45 | ||||
-rw-r--r-- | libmu_sieve/strexp.c | 12 | ||||
-rw-r--r-- | libmu_sieve/string.c | 53 | ||||
-rw-r--r-- | libmu_sieve/tests.c | 23 | ||||
-rw-r--r-- | libmu_sieve/variables.c | 431 | ||||
-rw-r--r-- | sieve/tests/Makefile.am | 1 | ||||
-rw-r--r-- | sieve/tests/testsuite.at | 1 | ||||
-rw-r--r-- | sieve/tests/variables.at | 99 |
15 files changed, 769 insertions, 56 deletions
diff --git a/include/mailutils/sieve.h b/include/mailutils/sieve.h index 6316332b8..8dfa3814f 100644 --- a/include/mailutils/sieve.h +++ b/include/mailutils/sieve.h @@ -35,9 +35,11 @@ typedef struct mu_sieve_machine *mu_sieve_machine_t; typedef struct mu_sieve_string { - char *orig; - char *exp; - void *rx; + unsigned constant:1; /* String is constant */ + unsigned changed:1; /* String value has changed */ + char *orig; /* String original value */ + char *exp; /* Actual string value after expansion */ + void *rx; /* Pointer to the corresponding regular expr */ } mu_sieve_string_t; typedef int (*mu_sieve_handler_t) (mu_sieve_machine_t mach); @@ -141,7 +143,9 @@ extern mu_debug_handle_t mu_sieve_debug_handle; extern mu_list_t mu_sieve_include_path; extern mu_list_t mu_sieve_library_path; extern mu_list_t mu_sieve_library_path_prefix; - + +extern mu_sieve_tag_def_t mu_sieve_match_part_tags[]; + /* Memory allocation functions */ typedef void (*mu_sieve_reclaim_t) (void *); void mu_sieve_register_memory (mu_sieve_machine_t mach, void *ptr, @@ -197,6 +201,11 @@ void mu_sieve_register_comparator (mu_sieve_machine_t mach, const char *name, mu_sieve_comparator_t eq); int mu_sieve_require_relational (mu_sieve_machine_t mach, const char *name); +int mu_sieve_relational_count (mu_sieve_machine_t mach, size_t count, + int retval); + +int mu_sieve_require_variables (mu_sieve_machine_t mach); +int mu_sieve_has_variables (mu_sieve_machine_t mach); void *mu_sieve_load_ext (mu_sieve_machine_t mach, const char *name); void mu_sieve_unload_ext (void *handle); @@ -234,9 +243,10 @@ void mu_sieve_get_arg (mu_sieve_machine_t mach, size_t index, char *mu_sieve_string (mu_sieve_machine_t mach, mu_sieve_slice_t slice, size_t i); -struct mu_sieve_string *mu_sieve_string_raw (mu_sieve_machine_t mach, - mu_sieve_slice_t slice, - size_t i); +mu_sieve_string_t *mu_sieve_string_raw (mu_sieve_machine_t mach, + mu_sieve_slice_t slice, + size_t i); +char *mu_sieve_string_get (mu_sieve_machine_t mach, mu_sieve_string_t *string); /* Operations on value lists */ int mu_sieve_vlist_do (mu_sieve_machine_t mach, diff --git a/libmu_sieve/Makefile.am b/libmu_sieve/Makefile.am index d0a77a967..a0ce50b65 100644 --- a/libmu_sieve/Makefile.am +++ b/libmu_sieve/Makefile.am @@ -44,7 +44,8 @@ libmu_sieve_la_SOURCES = \ strexp.c\ string.c\ tests.c\ - util.c + util.c\ + variables.c libmu_sieve_la_LIBADD = ${MU_LIB_MAILUTILS} @LTDL_LIB@ libmu_sieve_la_LDFLAGS = -version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ diff --git a/libmu_sieve/comparator.c b/libmu_sieve/comparator.c index 5e10c83bd..e0e12b35a 100644 --- a/libmu_sieve/comparator.c +++ b/libmu_sieve/comparator.c @@ -79,11 +79,20 @@ compile_pattern (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, int flags) { int rc; regex_t *preg; + char *str; + str = mu_sieve_string_get (mach, pattern); + if (pattern->rx) - return; - preg = mu_sieve_malloc (mach, sizeof (*preg)); - rc = regcomp (preg, pattern->orig, REG_EXTENDED | flags); + { + if (!pattern->changed) + return; + preg = pattern->rx; + regfree (preg); + } + else + preg = mu_sieve_malloc (mach, sizeof (*preg)); + rc = regcomp (preg, str, REG_EXTENDED | flags); if (rc) { size_t size = regerror (rc, preg, NULL, 0); @@ -107,11 +116,23 @@ compile_wildcard (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, { int rc; regex_t *preg; + char *str; + str = mu_sieve_string_get (mach, pattern); + if (pattern->rx) - return; - preg = mu_sieve_malloc (mach, sizeof (*preg)); - rc = mu_glob_compile (preg, pattern->orig, flags); + { + if (!pattern->changed) + return; + preg = pattern->rx; + regfree (preg); + } + else + preg = mu_sieve_malloc (mach, sizeof (*preg)); + + if (mu_sieve_has_variables (mach)) + flags |= MU_GLOBF_SUB; + rc = mu_glob_compile (preg, str, flags); if (rc) { mu_sieve_error (mach, _("can't compile pattern")); @@ -184,7 +205,7 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) if (strcmp (match->tag, "count") == 0) { mu_sieve_value_t *val; - char *str; + mu_sieve_string_t *argstr; if (compname && strcmp (compname, "i;ascii-numeric")) { @@ -213,14 +234,17 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) mu_i_sv_error (mach); return 1; } - str = mu_sieve_string_raw (mach, &val->v.list, 0)->orig; - str = mu_str_skip_class (str, MU_CTYPE_DIGIT); - if (*str) + argstr = mu_sieve_string_raw (mach, &val->v.list, 0); + if (argstr->constant) { - mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, - _("second argument cannot be converted to number")); - mu_i_sv_error (mach); - return 1; + char *p = mu_str_skip_class (argstr->orig, MU_CTYPE_DIGIT); + if (*p) + { + mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, + _("second argument cannot be converted to number")); + mu_i_sv_error (mach); + return 1; + } } } else @@ -258,7 +282,30 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) return 0; } + +static int +regmatch (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, char const *text) +{ + regex_t *reg = pattern->rx; + regmatch_t *match_buf = NULL; + size_t match_count = 0; + if (mu_sieve_has_variables (mach)) + { + match_count = reg->re_nsub + 1; + while (mach->match_max < match_count) + mu_i_sv_2nrealloc (mach, (void **) &mach->match_buf, + &mach->match_max, + sizeof (mach->match_buf[0])); + mach->match_count = match_count; + mu_sieve_free (mach, mach->match_string); + mach->match_string = mu_sieve_strdup (mach, text); + + match_buf = mach->match_buf; + } + + return regexec (reg, text, match_count, match_buf, 0) == 0; +} /* Particular comparators */ /* :comparator i;octet */ @@ -267,14 +314,14 @@ static int i_octet_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - return strcmp (pattern->orig, text) == 0; + return strcmp (mu_sieve_string_get (mach, pattern), text) == 0; } static int i_octet_contains (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - return strstr (text, pattern->orig) != NULL; + return strstr (text, mu_sieve_string_get (mach, pattern)) != NULL; } static int @@ -282,7 +329,7 @@ i_octet_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_wildcard (mach, pattern, 0); - return regexec ((regex_t *)pattern->rx, text, 0, NULL, 0) == 0; + return regmatch (mach, pattern, text); } static int @@ -290,14 +337,14 @@ i_octet_regex (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_pattern (mach, pattern, 0); - return regexec ((regex_t *)pattern->rx, text, 0, NULL, 0) == 0; + return regmatch (mach, pattern, text); } static int i_octet_eq (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - return strcmp (text, pattern->orig); + return strcmp (text, mu_sieve_string_get (mach, pattern)); } /* :comparator i;ascii-casemap */ @@ -305,14 +352,14 @@ static int i_ascii_casemap_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - return mu_c_strcasecmp (pattern->orig, text) == 0; + return mu_c_strcasecmp (mu_sieve_string_get (mach, pattern), text) == 0; } static int i_ascii_casemap_contains (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - return mu_c_strcasestr (text, pattern->orig) != NULL; + return mu_c_strcasestr (text, mu_sieve_string_get (mach, pattern)) != NULL; } static int @@ -320,7 +367,7 @@ i_ascii_casemap_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_wildcard (mach, pattern, MU_GLOBF_ICASE); - return regexec ((regex_t *)pattern->rx, text, 0, NULL, 0) == 0; + return regmatch (mach, pattern, text); } static int @@ -328,14 +375,14 @@ i_ascii_casemap_regex (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_pattern (mach, pattern, REG_ICASE); - return regexec ((regex_t *) pattern->rx, text, 0, NULL, 0) == 0; + return regmatch (mach, pattern, text); } static int i_ascii_casemap_eq (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - return mu_c_strcasecmp (text, pattern->orig); + return mu_c_strcasecmp (text, mu_sieve_string_get (mach, pattern)); } /* :comparator i;ascii-numeric */ @@ -343,11 +390,12 @@ static int i_ascii_numeric_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - if (mu_isdigit (*pattern->orig)) + char *str = mu_sieve_string_get (mach, pattern); + if (mu_isdigit (*str)) { if (mu_isdigit (*text)) //FIXME: Error checking - return strtol (pattern->orig, NULL, 10) == strtol (text, NULL, 10); + return strtol (str, NULL, 10) == strtol (text, NULL, 10); else return 0; } @@ -361,11 +409,12 @@ static int i_ascii_numeric_eq (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { - if (mu_isdigit (*pattern->orig)) + char *str = mu_sieve_string_get (mach, pattern); + if (mu_isdigit (*str)) { if (mu_isdigit (*text)) { - size_t a = strtoul (pattern->orig, NULL, 10); + size_t a = strtoul (str, NULL, 10); size_t b = strtoul (text, NULL, 10); if (b > a) return 1; diff --git a/libmu_sieve/require.c b/libmu_sieve/require.c index 372bd070c..f0ff27c77 100644 --- a/libmu_sieve/require.c +++ b/libmu_sieve/require.c @@ -37,7 +37,9 @@ mu_sieve_require (mu_sieve_machine_t mach, mu_sieve_slice_t list) char *name = str->orig; int rc; - if (strcmp (name, "relational") == 0) /* RFC 3431 */ + if (strcmp (name, "variables") == 0) /* RFC 5229 */ + rc = mu_sieve_require_variables (mach); + else if (strcmp (name, "relational") == 0) /* RFC 3431 */ rc = mu_sieve_require_relational (mach, name); else if (strcmp (name, "encoded-character") == 0) /* RFC 5228, 2.4.2.4 */ rc = mu_sieve_require_encoded_character (mach, name); @@ -52,8 +54,7 @@ mu_sieve_require (mu_sieve_machine_t mach, mu_sieve_slice_t list) if (rc) { - mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, - _("can't require %s is not available"), + mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _("can't require %s"), name); mu_i_sv_error (mach); } diff --git a/libmu_sieve/runtime.c b/libmu_sieve/runtime.c index 8317f8165..264fa5fc3 100644 --- a/libmu_sieve/runtime.c +++ b/libmu_sieve/runtime.c @@ -228,7 +228,10 @@ sieve_run (mu_sieve_machine_t mach) if (rc == 0) { mach->action_count = 0; - + + if (mu_sieve_has_variables (mach)) + mu_assoc_clear (mach->vartab); + for (mach->pc = 1; mach->prog[mach->pc].handler; ) (*mach->prog[mach->pc++].instr) (mach); diff --git a/libmu_sieve/sieve-priv.h b/libmu_sieve/sieve-priv.h index a960b8959..f5fb38a45 100644 --- a/libmu_sieve/sieve-priv.h +++ b/libmu_sieve/sieve-priv.h @@ -17,6 +17,7 @@ <http://www.gnu.org/licenses/>. */ #include <mailutils/sieve.h> +#include <mailutils/assoc.h> #include <setjmp.h> #include <string.h> #include <regex.h> @@ -90,13 +91,20 @@ struct mu_sieve_machine size_t pc; /* Current program counter */ long reg; /* Numeric register */ + /* Support for variables (RFC 5229) */ + mu_assoc_t vartab; /* Table of variables */ + char *match_string; /* The string used in the most recent match */ + regmatch_t *match_buf; /* Offsets of parenthesized groups */ + size_t match_count; /* Actual number of elements used in match_buf */ + size_t match_max; /* Total number of elements available in match_buf */ + /* Call environment */ const char *identifier; /* Name of action or test being executed */ size_t argstart; /* Index of the first argument in valspace */ size_t argcount; /* Number of positional arguments */ size_t tagcount; /* Number of tagged arguments */ mu_sieve_comparator_t comparator; /* Comparator (for tests) */ - + int dry_run; /* Dry-run mode */ jmp_buf errbuf; /* Target location for non-local exits */ @@ -233,4 +241,8 @@ size_t mu_i_sv_id_num (mu_sieve_machine_t mach, char const *name); char *mu_i_sv_id_str (mu_sieve_machine_t mach, size_t n); void mu_i_sv_free_idspace (mu_sieve_machine_t mach); +void mu_i_sv_copy_variables (mu_sieve_machine_t child, + mu_sieve_machine_t parent); +int mu_i_sv_expand_variables (char const *input, size_t len, + char **exp, void *data); diff --git a/libmu_sieve/sieve.l b/libmu_sieve/sieve.l index 8f51b4cc0..b18cd4ae6 100644 --- a/libmu_sieve/sieve.l +++ b/libmu_sieve/sieve.l @@ -529,6 +529,8 @@ multiline_strip_tabs (char *text) static void line_add (char *text, size_t len) { + if (len == 0) + len = strlen (text); mu_opool_append (mu_sieve_machine->string_pool, text, len); } diff --git a/libmu_sieve/sieve.y b/libmu_sieve/sieve.y index 46a8fba32..0a612f1ba 100644 --- a/libmu_sieve/sieve.y +++ b/libmu_sieve/sieve.y @@ -1067,6 +1067,12 @@ mu_sieve_machine_reset (mu_sieve_machine_t mach) 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; @@ -1174,6 +1180,16 @@ mu_sieve_machine_clone (mu_sieve_machine_t const parent, 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 */ @@ -1468,6 +1484,30 @@ with_machine (mu_sieve_machine_t mach, char const *name, 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) { @@ -1509,7 +1549,10 @@ sieve_parse (void) if (mu_sieve_machine->state == mu_sieve_state_error) rc = MU_ERR_PARSE; else - mu_sieve_machine->state = mu_sieve_state_compiled; + { + string_rescan (mu_sieve_machine); + mu_sieve_machine->state = mu_sieve_state_compiled; + } } tree_free (&sieve_tree); diff --git a/libmu_sieve/strexp.c b/libmu_sieve/strexp.c index b4f31e3be..d629e4aa5 100644 --- a/libmu_sieve/strexp.c +++ b/libmu_sieve/strexp.c @@ -155,7 +155,8 @@ update_len (void *item, void *data) break; case segm_repl: - st->len += strlen (segm->repl); + if (segm->repl) + st->len += strlen (segm->repl); break; } return 0; @@ -176,8 +177,13 @@ append_segm (void *item, void *data) break; case segm_repl: - len = strlen (segm->repl); - memcpy (buf->endptr, segm->repl, len); + if (segm->repl) + { + len = strlen (segm->repl); + memcpy (buf->endptr, segm->repl, len); + } + else + len = 0; } buf->endptr += len; diff --git a/libmu_sieve/string.c b/libmu_sieve/string.c index 254f3ad92..6712bb6c4 100644 --- a/libmu_sieve/string.c +++ b/libmu_sieve/string.c @@ -44,7 +44,7 @@ mu_i_sv_string_create (mu_sieve_machine_t mach, char *str) return n; } -struct mu_sieve_string * +mu_sieve_string_t * mu_sieve_string_raw (mu_sieve_machine_t mach, mu_sieve_slice_t slice, size_t i) { @@ -54,8 +54,57 @@ mu_sieve_string_raw (mu_sieve_machine_t mach, mu_sieve_slice_t slice, } char * +mu_sieve_string_get (mu_sieve_machine_t mach, mu_sieve_string_t *string) +{ + char *exp; + int rc; + + if (string->constant) + return string->orig; + + rc = mu_i_sv_string_expand (string->orig, mu_i_sv_expand_variables, mach, + &exp); + switch (rc) + { + case 0: + if (string->exp == NULL) + { + string->changed = strcmp (string->orig, exp) != 0; + string->exp = mu_sieve_strdup (mach, exp); + free (exp); + } + else if (strcmp (exp, string->exp) == 0) + { + string->changed = 0; + free (exp); + } + else + { + string->changed = 1; + mu_sieve_free (mach, string->exp); + string->exp = mu_sieve_strdup (mach, exp); + free (exp); + } + break; + + case MU_ERR_CANCELED: + string->changed = 0; + return string->orig; + + default: + mu_sieve_error (mach, "error expanding variables: %s", + mu_strerror (rc)); + mu_sieve_abort (mach); + } + + return string->exp; +} + +char * mu_sieve_string (mu_sieve_machine_t mach, mu_sieve_slice_t slice, size_t i) { - return mu_sieve_string_raw (mach, slice, i)->orig; + return mu_sieve_string_get (mach, mu_sieve_string_raw (mach, slice, i)); } + + diff --git a/libmu_sieve/tests.c b/libmu_sieve/tests.c index 647484bdf..e7b6406ed 100644 --- a/libmu_sieve/tests.c +++ b/libmu_sieve/tests.c @@ -55,21 +55,26 @@ struct address_closure mu_address_t addr; /* Obtained address */ }; -static int -do_count (mu_sieve_machine_t mach, size_t count, int retval) +int +mu_sieve_relational_count (mu_sieve_machine_t mach, size_t count, int retval) { char *relcmp; if (mu_sieve_get_tag (mach, "count", SVT_STRING, &relcmp)) { size_t limit; - char *str; + char *str, *p; struct mu_sieve_slice slice; mu_sieve_relcmpn_t stest; mu_sieve_get_arg (mach, 1, SVT_STRING_LIST, &slice); str = mu_sieve_string (mach, &slice, 0); - limit = strtoul (str, &str, 10); + limit = strtoul (str, &p, 10); + if (*p) + { + mu_sieve_error (mach, _("%s: not an integer"), str); + mu_sieve_abort (mach); + } mu_sieve_str_to_relcmp (relcmp, NULL, &stest); return stest (count, limit); @@ -122,7 +127,7 @@ sieve_test_address (mu_sieve_machine_t mach) &count); mu_address_destroy (&clos.addr); - return do_count (mach, count, rc); + return mu_sieve_relational_count (mach, count, rc); } struct header_closure @@ -196,7 +201,7 @@ sieve_test_header (mu_sieve_machine_t mach) &count)) return 1; - return do_count (mach, count + mcount, 0); + return mu_sieve_relational_count (mach, count + mcount, 0); } int @@ -246,7 +251,7 @@ sieve_test_envelope (mu_sieve_machine_t mach) rc = mu_sieve_vlist_compare (mach, h, v, comp, test, retrieve_envelope, &clos, &count); mu_address_destroy (&clos.addr); - return do_count (mach, count, rc); + return mu_sieve_relational_count (mach, count, rc); } int @@ -301,7 +306,7 @@ static mu_sieve_tag_def_t address_part_tags[] = { { NULL } }; -static mu_sieve_tag_def_t match_part_tags[] = { +mu_sieve_tag_def_t mu_sieve_match_part_tags[] = { { "is", SVT_VOID }, { "contains", SVT_VOID }, { "matches", SVT_VOID }, @@ -327,7 +332,7 @@ static mu_sieve_tag_def_t mime_tags[] = { { address_part_tags, NULL } #define MATCH_PART_GROUP \ - { match_part_tags, mu_sieve_match_part_checker } + { mu_sieve_match_part_tags, mu_sieve_match_part_checker } #define SIZE_GROUP { size_tags, NULL } diff --git a/libmu_sieve/variables.c b/libmu_sieve/variables.c new file mode 100644 index 000000000..5c6ddac8a --- /dev/null +++ b/libmu_sieve/variables.c @@ -0,0 +1,431 @@ +/* Sieve variables extension (RFC 5229) + Copyright (C) 2016 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 + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sieve-priv.h> +#include <ctype.h> + +struct sieve_variable +{ + char *value; +}; + + +/* FIXME: UTF support */ +char * +mod_lower (mu_sieve_machine_t mach, char const *value) +{ + char *newval = mu_sieve_malloc (mach, strlen (value) + 1); + char *p; + + for (p = newval; *value; p++, value++) + *p = tolower (*value); + *p = 0; + return newval; +} + +char * +mod_upper (mu_sieve_machine_t mach, char const *value) +{ + char *newval = mu_sieve_malloc (mach, strlen (value) + 1); + char *p; + + for (p = newval; *value; p++, value++) + *p = toupper (*value); + *p = 0; + return newval; +} + +char * +mod_lowerfirst (mu_sieve_machine_t mach, char const *value) +{ + char *newval = mu_sieve_strdup (mach, value); + *newval = tolower (*newval); + return newval; +} + +char * +mod_upperfirst (mu_sieve_machine_t mach, char const *value) +{ + char *newval = mu_sieve_strdup (mach, value); + *newval = toupper (*newval); + return newval; +} + +char * +mod_quotewildcard (mu_sieve_machine_t mach, char const *value) +{ + size_t len; + char *newval; + char const *p; + char *q; + + len = 0; + for (p = value; *p; p++) + { + switch (*p) + { + case '*': + case '?': + case '\\': + len += 2; + break; + default: + len++; + } + } + + newval = mu_sieve_malloc (mach, len + 1); + for (p = value, q = newval; *p;) + { + switch (*p) + { + case '*': + case '?': + case '\\': + *q++ = '\\'; + } + *q++ = *p++; + } + *q = 0; + return newval; +} + +char * +mod_length (mu_sieve_machine_t mach, char const *value) +{ + char *newval, *p; + int rc = mu_asprintf (&newval, "%zu", strlen (value)); + if (rc) + { + mu_diag_funcall (MU_DIAG_ERROR, "mu_asprintf", NULL, rc); + mu_sieve_abort (mach); + } + p = mu_sieve_strdup (mach, newval); + free (newval); + return p; +} + +static mu_sieve_tag_def_t set_tags[] = { + { "lower", SVT_VOID }, + { "upper", SVT_VOID }, + { "lowerfirst", SVT_VOID }, + { "upperfirst", SVT_VOID }, + { "quotewildcard", SVT_VOID }, + { "length", SVT_VOID }, + { NULL } +}; + +struct modprec +{ + char *name; + unsigned prec; + char *(*modify) (mu_sieve_machine_t mach, char const *value); +}; + +static struct modprec modprec[] = { + { "lower", 40, mod_lower }, + { "upper", 40, mod_upper }, + { "lowerfirst", 30, mod_lowerfirst }, + { "upperfirst", 30, mod_upperfirst }, + { "quotewildcard", 20, mod_quotewildcard }, + { "length", 10, mod_length }, +}; + +static struct modprec * +findprec (char const *name) +{ + int i; + + for (i = 0; i < MU_ARRAY_SIZE (modprec); i++) + if (strcmp (modprec[i].name, name) == 0) + return &modprec[i]; + mu_error ("%s:%d: INTERNAL ERROR", __FILE__, __LINE__); + abort (); +} + +static int +sieve_action_set (mu_sieve_machine_t mach) +{ + size_t i; + char *name; + char *value; + struct sieve_variable *vptr; + int rc; + + mu_sieve_get_arg (mach, 0, SVT_STRING, &name); + mu_sieve_get_arg (mach, 1, SVT_STRING, &value); + + value = mu_sieve_strdup (mach, value); + for (i = 0; i < mach->tagcount; i++) + { + mu_sieve_value_t *p = mu_sieve_get_tag_n (mach, i); + char *str = findprec (p->tag)->modify (mach, value); + mu_sieve_free (mach, value); + value = str; + } + + rc = mu_assoc_ref_install (mach->vartab, name, (void **)&vptr); + switch (rc) + { + case 0: + break; + + case MU_ERR_EXISTS: + mu_sieve_free (mach, vptr->value); + break; + + default: + mu_sieve_error (mach, "mu_assoc_ref_install: %s", mu_strerror (rc)); + mu_sieve_abort (mach); + } + vptr->value = value; + return 0; +} + +static int +set_tag_checker (mu_sieve_machine_t mach) +{ + int i, j; + + /* Sort tags by decreasing priority value (RFC 5229, 4.1) */ + for (i = 1; i < mach->tagcount; i++) + { + mu_sieve_value_t tmp = *mu_sieve_get_tag_n (mach, i); + int tmp_prec = findprec (tmp.tag)->prec; + + for (j = i - 1; j >= 0; j--) + { + mu_sieve_value_t *t = mu_sieve_get_tag_n (mach, j); + int prec = findprec (t->tag)->prec; + if (prec < tmp_prec) + *mu_sieve_get_tag_n (mach, j + 1) = *t; + else if (prec == tmp_prec) + { + mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, + _("%s and %s can't be used together"), + tmp.tag, t->tag); + mu_i_sv_error (mach); + return 1; + } + else + break; + } + *mu_sieve_get_tag_n (mach, j + 1) = tmp; + } + return 0; +} + +static mu_sieve_tag_group_t set_tag_groups[] = { + { set_tags, set_tag_checker }, + { NULL } +}; + +static mu_sieve_data_type set_args[] = { + SVT_STRING, + SVT_STRING, + SVT_VOID +}; + +/* RFC 5229, 5. Test string */ +int +sieve_test_string (mu_sieve_machine_t mach) +{ + mu_sieve_value_t *source, *key_list; + mu_sieve_comparator_t comp = mu_sieve_get_comparator (mach); + mu_sieve_relcmp_t test = mu_sieve_get_relcmp (mach); + size_t count = 0; + int rc = 0; + size_t i; + + source = mu_sieve_get_arg_untyped (mach, 0); + key_list = mu_sieve_get_arg_untyped (mach, 1); + + for (i = 0; i < source->v.list.count; i++) + { + char *item = mu_sieve_string (mach, &source->v.list, i); + size_t k; + + /* The "relational" extension [RELATIONAL] adds a match type called + ":count". The count of a single string is 0 if it is the empty + string, or 1 otherwise. The count of a string list is the sum of the + counts of the member strings. + */ + if (item[0]) + count++; + for (k = 0; k < key_list->v.list.count; k++) + { + mu_sieve_string_t *s = + mu_sieve_string_raw (mach, &key_list->v.list, k); + rc = test (comp (mach, s, item), 0); + if (rc) + return rc; + } + } + + return mu_sieve_relational_count (mach, count, 0); +} + +mu_sieve_data_type string_args[] = { + SVT_STRING_LIST, + SVT_STRING_LIST, + SVT_VOID +}; + +mu_sieve_tag_group_t string_tag_groups[] = { + { mu_sieve_match_part_tags, mu_sieve_match_part_checker }, + { NULL } +}; + +int +mu_i_sv_expand_variables (cha |