/* 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 #include typedef struct { const char *name; int required; mu_sieve_comparator_t comp[MU_SIEVE_MATCH_LAST]; } sieve_comparator_record_t; int 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) { sieve_comparator_record_t *rp; if (!mach->comp_list) { int rc = mu_list_create (&mach->comp_list); if (rc) return rc; } rp = mu_sieve_malloc (mach, sizeof (*rp)); rp->required = required; rp->name = name; rp->comp[MU_SIEVE_MATCH_IS] = is; rp->comp[MU_SIEVE_MATCH_CONTAINS] = contains; rp->comp[MU_SIEVE_MATCH_MATCHES] = matches; rp->comp[MU_SIEVE_MATCH_REGEX] = regex; rp->comp[MU_SIEVE_MATCH_EQ] = eq; return mu_list_append (mach->comp_list, rp); } sieve_comparator_record_t * _lookup (mu_list_t list, const char *name) { mu_iterator_t itr; sieve_comparator_record_t *reg; if (!list || mu_list_get_iterator (list, &itr)) return NULL; for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { mu_iterator_current (itr, (void **)®); if (strcmp (reg->name, name) == 0) break; else reg = NULL; } mu_iterator_destroy (&itr); return reg; } int mu_sieve_require_comparator (mu_sieve_machine_t mach, const char *name) { sieve_comparator_record_t *reg = _lookup (mach->comp_list, name); if (!reg) { if (!(mu_sieve_load_ext (mach, name) == 0 && (reg = _lookup (mach->comp_list, name)) != NULL)) return 1; } reg->required = 1; return 0; } mu_sieve_comparator_t mu_sieve_comparator_lookup (mu_sieve_machine_t mach, const char *name, int matchtype) { sieve_comparator_record_t *reg = _lookup (mach->comp_list, name); if (reg && reg->comp[matchtype]) return reg->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; if (pattern->rx) return; preg = mu_sieve_malloc (mach, sizeof (*preg)); rc = regcomp (preg, pattern->orig, 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 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; char *str; size_t count; 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 */ if (val->type != SVT_STRING_LIST) abort (); if (val->v.list.count > 1) { mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _("second argument must be a list of one element")); mu_i_sv_error (mach); return 1; } str = mu_sieve_string_raw (mach, &val->v.list, 0)->orig; count = strtoul (str, &str, 10); if (*str) { 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; } /* 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 (pattern->orig, 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; } static int i_octet_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { return fnmatch (pattern->orig, text, 0) == 0; } static int 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; } static int i_octet_eq (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { return strcmp (text, pattern->orig); } /* :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 (pattern->orig, 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; } static int i_ascii_casemap_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { return fnmatch (pattern->orig, text, FNM_CASEFOLD) == 0; } 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 regexec ((regex_t *) pattern->rx, text, 0, NULL, 0) == 0; } 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); } /* :comparator i;ascii-numeric */ static int i_ascii_numeric_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, const char *text) { if (mu_isdigit (*pattern->orig)) { if (mu_isdigit (*text)) //FIXME: Error checking return strtol (pattern->orig, 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) { if (mu_isdigit (*pattern->orig)) { if (mu_isdigit (*text)) { size_t a = strtoul (pattern->orig, 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); }