summaryrefslogtreecommitdiff
path: root/libmu_sieve
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-12-07 09:46:57 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2016-12-07 13:03:42 +0200
commit799519539556b38fc9f84d44927861cb9239d14c (patch)
tree304982f21416f837c8b5a678b85ba99edce62d40 /libmu_sieve
parentcab2fd5c0479d16a3dcd746c98de8be771a8963d (diff)
downloadmailutils-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.
Diffstat (limited to 'libmu_sieve')
-rw-r--r--libmu_sieve/Makefile.am3
-rw-r--r--libmu_sieve/comparator.c105
-rw-r--r--libmu_sieve/require.c7
-rw-r--r--libmu_sieve/runtime.c5
-rw-r--r--libmu_sieve/sieve-priv.h14
-rw-r--r--libmu_sieve/sieve.l2
-rw-r--r--libmu_sieve/sieve.y45
-rw-r--r--libmu_sieve/strexp.c12
-rw-r--r--libmu_sieve/string.c53
-rw-r--r--libmu_sieve/tests.c23
-rw-r--r--libmu_sieve/variables.c431
11 files changed, 651 insertions, 49 deletions
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 (char const *input, size_t len,
+ char **exp, void *data)
+{
+ mu_sieve_machine_t mach = data;
+ if (mu_isdigit (*input))
+ {
+ char *p;
+ size_t idx = 0;
+
+ while (len)
+ {
+ if (mu_isdigit (*input))
+ {
+ int d = *input - '0';
+ idx = idx * 10 + d;
+ input++;
+ len--;
+ }
+ else
+ return 1;
+ }
+
+ if (idx > mach->match_count)
+ {
+ *exp = NULL;
+ return 0;
+ }
+
+ len = mach->match_buf[idx].rm_eo - mach->match_buf[idx].rm_so;
+ p = malloc (len + 1);
+ if (!p)
+ {
+ mu_sieve_error (mach, "%s", mu_strerror (errno));
+ mu_sieve_abort (mach);
+ }
+ memcpy (p, mach->match_string + mach->match_buf[idx].rm_so, len);
+ p[len] = 0;
+ *exp = p;
+ }
+ else if (mu_isalpha (*input))
+ {
+ size_t i;
+ char *name;
+ struct sieve_variable *var;
+
+ for (i = 0; i < len; i++)
+ if (!(mu_isalnum (input[i]) || input[i] == '_'))
+ return 1;
+
+ name = malloc (len + 1);
+ if (!name)
+ {
+ mu_sieve_error (mach, "%s", mu_strerror (errno));
+ mu_sieve_abort (mach);
+ }
+ memcpy (name, input, len);
+ name[len] = 0;
+
+ var = mu_assoc_ref (mach->vartab, name);
+
+ free (name);
+
+ if (var)
+ {
+ *exp = strdup (var->value);
+ if (!*exp)
+ {
+ mu_sieve_error (mach, "%s", mu_strerror (errno));
+ mu_sieve_abort (mach);
+ }
+ }
+ else
+ *exp = NULL;
+ }
+ else
+ return 1;
+ return 0;
+}
+
+int
+mu_sieve_require_variables (mu_sieve_machine_t mach)
+{
+ int rc;
+
+ if (mach->vartab)
+ return 0;
+
+ rc = mu_assoc_create (&mach->vartab, sizeof (struct sieve_variable),
+ MU_ASSOC_ICASE);
+ if (rc)
+ mu_sieve_error (mach, "mu_assoc_create: %s", mu_strerror (rc));
+
+ if (rc == 0)
+ {
+ mu_sieve_register_action (mach, "set", sieve_action_set,
+ set_args, set_tag_groups, 1);
+ mu_sieve_register_test (mach, "string", sieve_test_string,
+ string_args, string_tag_groups, 1);
+ }
+ return rc;
+}
+
+int
+mu_sieve_has_variables (mu_sieve_machine_t mach)
+{
+ return mach->vartab != NULL;
+}
+
+void
+mu_i_sv_copy_variables (mu_sieve_machine_t child, mu_sieve_machine_t parent)
+{
+ mu_iterator_t itr;
+
+ mu_sieve_require_variables (child);
+
+ mu_assoc_get_iterator (parent->vartab, &itr);
+
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ const char *name;
+ struct sieve_variable const *val;
+ struct sieve_variable newval;
+
+ mu_iterator_current_kv (itr, (const void **)&name, (void**)&val);
+ newval.value = mu_sieve_strdup (child, val->value);
+ mu_assoc_install (child->vartab, name, &newval);
+ }
+
+ mu_iterator_destroy (&itr);
+}
+
+
+