/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999-2002, 2004-2005, 2007-2012, 2014-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 . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include void mu_sieve_register_comparator (mu_sieve_machine_t mach, const char *name, int required, mu_sieve_comparator_t is, mu_sieve_comparator_t contains, mu_sieve_comparator_t matches, mu_sieve_comparator_t regex, mu_sieve_comparator_t eq) { mu_sieve_registry_t *reg = mu_sieve_registry_add (mach, name); reg->type = mu_sieve_record_comparator; reg->required = required; reg->name = name; reg->v.comp[MU_SIEVE_MATCH_IS] = is; reg->v.comp[MU_SIEVE_MATCH_CONTAINS] = contains; reg->v.comp[MU_SIEVE_MATCH_MATCHES] = matches; reg->v.comp[MU_SIEVE_MATCH_REGEX] = regex; reg->v.comp[MU_SIEVE_MATCH_EQ] = eq; } mu_sieve_comparator_t mu_sieve_comparator_lookup (mu_sieve_machine_t mach, const char *name, int matchtype) { mu_sieve_registry_t *reg = mu_sieve_registry_lookup (mach, name, mu_sieve_record_comparator); if (reg && reg->v.comp[matchtype]) return reg->v.comp[matchtype]; return NULL; } static int i_ascii_casemap_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text); mu_sieve_comparator_t mu_sieve_get_comparator (mu_sieve_machine_t mach) { if (!mach->comparator) return i_ascii_casemap_is; return mach->comparator; } /* Compile time support */ static void 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) { 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); char *errbuf = malloc (size + 1); if (errbuf) { regerror (rc, preg, errbuf, size); mu_sieve_error (mach, _("regex error: %s"), errbuf); free (errbuf); } else mu_sieve_error (mach, _("regex error")); mu_sieve_abort (mach); } pattern->rx = preg; } static void compile_wildcard (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) { 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")); mu_sieve_abort (mach); } pattern->rx = preg; } static int comp_false (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { return 0; } int mu_sieve_match_part_checker (mu_sieve_machine_t mach) { size_t i; mu_sieve_value_t *match = NULL; mu_sieve_comparator_t compfun = NULL; char *compname = NULL; int matchtype; if (mach->tagcount == 0) return 0; for (i = 0; i < mach->tagcount; i++) { mu_sieve_value_t *t = mu_sieve_get_tag_n (mach, i); if (strcmp (t->tag, "is") == 0 || strcmp (t->tag, "contains") == 0 || strcmp (t->tag, "matches") == 0 || strcmp (t->tag, "regex") == 0 || strcmp (t->tag, "count") == 0 || strcmp (t->tag, "value") == 0) { if (match) { mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _("match type specified twice in call to `%s'"), mach->identifier); mu_i_sv_error (mach); return 1; } else match = t; } else if (strcmp (t->tag, "comparator") == 0) { if (t->type != SVT_STRING) abort (); compname = mu_sieve_string (mach, &t->v.list, 0); } } if (!match || strcmp (match->tag, "is") == 0) matchtype = MU_SIEVE_MATCH_IS; else if (strcmp (match->tag, "contains") == 0) matchtype = MU_SIEVE_MATCH_CONTAINS; else if (strcmp (match->tag, "matches") == 0) matchtype = MU_SIEVE_MATCH_MATCHES; else if (strcmp (match->tag, "regex") == 0) matchtype = MU_SIEVE_MATCH_REGEX; else if (match->type == SVT_STRING) { char *str = mu_sieve_string (mach, &match->v.list, 0); if (strcmp (match->tag, "count") == 0) { mu_sieve_value_t *val; mu_sieve_string_t *argstr; if (compname && strcmp (compname, "i;ascii-numeric")) { mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, /* TRANSLATORS: Do not translate ':count'. It is the name of a Sieve tag */ _("comparator %s is incompatible with " ":count in call to `%s'"), compname, mach->identifier); mu_i_sv_error (mach); return 1; } matchtype = MU_SIEVE_MATCH_LAST; /* to not leave it undefined */ compname = "false"; compfun = comp_false; val = mu_sieve_get_arg_untyped (mach, 1); /* NOTE: Type of val is always SVT_STRING_LIST */ switch (val->type) { case SVT_STRING: break; case SVT_STRING_LIST: if (val->v.list.count == 1) break; /* fall through */ default: mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _(":count requires second argument to be a list of one element")); mu_i_sv_error (mach); return 1; } argstr = mu_sieve_string_raw (mach, &val->v.list, 0); if (argstr->constant) { 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 matchtype = MU_SIEVE_MATCH_EQ; if (mu_sieve_str_to_relcmp (str, NULL, NULL)) { mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _("invalid relational match `%s' in call to `%s'"), str, mach->identifier); mu_i_sv_error (mach); return 1; } } else abort ();//FIXME if (!compfun) { if (!compname) compname = "i;ascii-casemap"; compfun = mu_sieve_comparator_lookup (mach, compname, matchtype); if (!compfun) { mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _("comparator `%s' is incompatible with match type `%s' in call to `%s'"), compname, match ? match->tag : "is", mach->identifier); mu_i_sv_error (mach); return 1; } } mach->comparator = compfun; 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 */ static int i_octet_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { 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, mu_sieve_string_get (mach, pattern)) != NULL; } static int i_octet_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_wildcard (mach, pattern, 0); return regmatch (mach, pattern, text); } static int i_octet_regex (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_pattern (mach, pattern, 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, mu_sieve_string_get (mach, pattern)); } /* :comparator i;ascii-casemap */ static int i_ascii_casemap_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { 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, mu_sieve_string_get (mach, pattern)) != NULL; } static int 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 regmatch (mach, pattern, text); } static int i_ascii_casemap_regex (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { compile_pattern (mach, pattern, REG_ICASE); 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, mu_sieve_string_get (mach, pattern)); } /* :comparator i;ascii-numeric */ static int i_ascii_numeric_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { char *str = mu_sieve_string_get (mach, pattern); if (mu_isdigit (*str)) { if (mu_isdigit (*text)) //FIXME: Error checking return strtol (str, NULL, 10) == strtol (text, NULL, 10); else return 0; } else if (mu_isdigit (*text)) return 0; else return 1; } static int i_ascii_numeric_eq (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { char *str = mu_sieve_string_get (mach, pattern); if (mu_isdigit (*str)) { if (mu_isdigit (*text)) { size_t a = strtoul (str, NULL, 10); size_t b = strtoul (text, NULL, 10); if (b > a) return 1; else if (b < a) return -1; return 0; } else return 1; } else return 1; } void mu_i_sv_register_standard_comparators (mu_sieve_machine_t mach) { mu_sieve_register_comparator (mach, "i;octet", 1, i_octet_is, i_octet_contains, i_octet_matches, i_octet_regex, i_octet_eq); mu_sieve_register_comparator (mach, "i;ascii-casemap", 1, i_ascii_casemap_is, i_ascii_casemap_contains, i_ascii_casemap_matches, i_ascii_casemap_regex, i_ascii_casemap_eq); mu_sieve_register_comparator (mach, "i;ascii-numeric", 0, i_ascii_numeric_is, NULL, NULL, NULL, i_ascii_numeric_eq); }