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.
Showing
15 changed files
with
752 additions
and
39 deletions
... | @@ -35,9 +35,11 @@ typedef struct mu_sieve_machine *mu_sieve_machine_t; | ... | @@ -35,9 +35,11 @@ typedef struct mu_sieve_machine *mu_sieve_machine_t; |
35 | 35 | ||
36 | typedef struct mu_sieve_string | 36 | typedef struct mu_sieve_string |
37 | { | 37 | { |
38 | char *orig; | 38 | unsigned constant:1; /* String is constant */ |
39 | char *exp; | 39 | unsigned changed:1; /* String value has changed */ |
40 | void *rx; | 40 | char *orig; /* String original value */ |
41 | char *exp; /* Actual string value after expansion */ | ||
42 | void *rx; /* Pointer to the corresponding regular expr */ | ||
41 | } mu_sieve_string_t; | 43 | } mu_sieve_string_t; |
42 | 44 | ||
43 | typedef int (*mu_sieve_handler_t) (mu_sieve_machine_t mach); | 45 | typedef int (*mu_sieve_handler_t) (mu_sieve_machine_t mach); |
... | @@ -142,6 +144,8 @@ extern mu_list_t mu_sieve_include_path; | ... | @@ -142,6 +144,8 @@ extern mu_list_t mu_sieve_include_path; |
142 | extern mu_list_t mu_sieve_library_path; | 144 | extern mu_list_t mu_sieve_library_path; |
143 | extern mu_list_t mu_sieve_library_path_prefix; | 145 | extern mu_list_t mu_sieve_library_path_prefix; |
144 | 146 | ||
147 | extern mu_sieve_tag_def_t mu_sieve_match_part_tags[]; | ||
148 | |||
145 | /* Memory allocation functions */ | 149 | /* Memory allocation functions */ |
146 | typedef void (*mu_sieve_reclaim_t) (void *); | 150 | typedef void (*mu_sieve_reclaim_t) (void *); |
147 | void mu_sieve_register_memory (mu_sieve_machine_t mach, void *ptr, | 151 | 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, | ... | @@ -197,6 +201,11 @@ void mu_sieve_register_comparator (mu_sieve_machine_t mach, const char *name, |
197 | mu_sieve_comparator_t eq); | 201 | mu_sieve_comparator_t eq); |
198 | 202 | ||
199 | int mu_sieve_require_relational (mu_sieve_machine_t mach, const char *name); | 203 | int mu_sieve_require_relational (mu_sieve_machine_t mach, const char *name); |
204 | int mu_sieve_relational_count (mu_sieve_machine_t mach, size_t count, | ||
205 | int retval); | ||
206 | |||
207 | int mu_sieve_require_variables (mu_sieve_machine_t mach); | ||
208 | int mu_sieve_has_variables (mu_sieve_machine_t mach); | ||
200 | 209 | ||
201 | void *mu_sieve_load_ext (mu_sieve_machine_t mach, const char *name); | 210 | void *mu_sieve_load_ext (mu_sieve_machine_t mach, const char *name); |
202 | void mu_sieve_unload_ext (void *handle); | 211 | void mu_sieve_unload_ext (void *handle); |
... | @@ -234,9 +243,10 @@ void mu_sieve_get_arg (mu_sieve_machine_t mach, size_t index, | ... | @@ -234,9 +243,10 @@ void mu_sieve_get_arg (mu_sieve_machine_t mach, size_t index, |
234 | char *mu_sieve_string (mu_sieve_machine_t mach, | 243 | char *mu_sieve_string (mu_sieve_machine_t mach, |
235 | mu_sieve_slice_t slice, | 244 | mu_sieve_slice_t slice, |
236 | size_t i); | 245 | size_t i); |
237 | struct mu_sieve_string *mu_sieve_string_raw (mu_sieve_machine_t mach, | 246 | mu_sieve_string_t *mu_sieve_string_raw (mu_sieve_machine_t mach, |
238 | mu_sieve_slice_t slice, | 247 | mu_sieve_slice_t slice, |
239 | size_t i); | 248 | size_t i); |
249 | char *mu_sieve_string_get (mu_sieve_machine_t mach, mu_sieve_string_t *string); | ||
240 | 250 | ||
241 | /* Operations on value lists */ | 251 | /* Operations on value lists */ |
242 | int mu_sieve_vlist_do (mu_sieve_machine_t mach, | 252 | int mu_sieve_vlist_do (mu_sieve_machine_t mach, | ... | ... |
... | @@ -44,7 +44,8 @@ libmu_sieve_la_SOURCES = \ | ... | @@ -44,7 +44,8 @@ libmu_sieve_la_SOURCES = \ |
44 | strexp.c\ | 44 | strexp.c\ |
45 | string.c\ | 45 | string.c\ |
46 | tests.c\ | 46 | tests.c\ |
47 | util.c | 47 | util.c\ |
48 | variables.c | ||
48 | libmu_sieve_la_LIBADD = ${MU_LIB_MAILUTILS} @LTDL_LIB@ | 49 | libmu_sieve_la_LIBADD = ${MU_LIB_MAILUTILS} @LTDL_LIB@ |
49 | libmu_sieve_la_LDFLAGS = -version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ | 50 | libmu_sieve_la_LDFLAGS = -version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ |
50 | 51 | ... | ... |
... | @@ -79,11 +79,20 @@ compile_pattern (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, int flags) | ... | @@ -79,11 +79,20 @@ compile_pattern (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, int flags) |
79 | { | 79 | { |
80 | int rc; | 80 | int rc; |
81 | regex_t *preg; | 81 | regex_t *preg; |
82 | char *str; | ||
83 | |||
84 | str = mu_sieve_string_get (mach, pattern); | ||
82 | 85 | ||
83 | if (pattern->rx) | 86 | if (pattern->rx) |
87 | { | ||
88 | if (!pattern->changed) | ||
84 | return; | 89 | return; |
90 | preg = pattern->rx; | ||
91 | regfree (preg); | ||
92 | } | ||
93 | else | ||
85 | preg = mu_sieve_malloc (mach, sizeof (*preg)); | 94 | preg = mu_sieve_malloc (mach, sizeof (*preg)); |
86 | rc = regcomp (preg, pattern->orig, REG_EXTENDED | flags); | 95 | rc = regcomp (preg, str, REG_EXTENDED | flags); |
87 | if (rc) | 96 | if (rc) |
88 | { | 97 | { |
89 | size_t size = regerror (rc, preg, NULL, 0); | 98 | size_t size = regerror (rc, preg, NULL, 0); |
... | @@ -107,11 +116,23 @@ compile_wildcard (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, | ... | @@ -107,11 +116,23 @@ compile_wildcard (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, |
107 | { | 116 | { |
108 | int rc; | 117 | int rc; |
109 | regex_t *preg; | 118 | regex_t *preg; |
119 | char *str; | ||
120 | |||
121 | str = mu_sieve_string_get (mach, pattern); | ||
110 | 122 | ||
111 | if (pattern->rx) | 123 | if (pattern->rx) |
124 | { | ||
125 | if (!pattern->changed) | ||
112 | return; | 126 | return; |
127 | preg = pattern->rx; | ||
128 | regfree (preg); | ||
129 | } | ||
130 | else | ||
113 | preg = mu_sieve_malloc (mach, sizeof (*preg)); | 131 | preg = mu_sieve_malloc (mach, sizeof (*preg)); |
114 | rc = mu_glob_compile (preg, pattern->orig, flags); | 132 | |
133 | if (mu_sieve_has_variables (mach)) | ||
134 | flags |= MU_GLOBF_SUB; | ||
135 | rc = mu_glob_compile (preg, str, flags); | ||
115 | if (rc) | 136 | if (rc) |
116 | { | 137 | { |
117 | mu_sieve_error (mach, _("can't compile pattern")); | 138 | mu_sieve_error (mach, _("can't compile pattern")); |
... | @@ -184,7 +205,7 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) | ... | @@ -184,7 +205,7 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) |
184 | if (strcmp (match->tag, "count") == 0) | 205 | if (strcmp (match->tag, "count") == 0) |
185 | { | 206 | { |
186 | mu_sieve_value_t *val; | 207 | mu_sieve_value_t *val; |
187 | char *str; | 208 | mu_sieve_string_t *argstr; |
188 | 209 | ||
189 | if (compname && strcmp (compname, "i;ascii-numeric")) | 210 | if (compname && strcmp (compname, "i;ascii-numeric")) |
190 | { | 211 | { |
... | @@ -213,9 +234,11 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) | ... | @@ -213,9 +234,11 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) |
213 | mu_i_sv_error (mach); | 234 | mu_i_sv_error (mach); |
214 | return 1; | 235 | return 1; |
215 | } | 236 | } |
216 | str = mu_sieve_string_raw (mach, &val->v.list, 0)->orig; | 237 | argstr = mu_sieve_string_raw (mach, &val->v.list, 0); |
217 | str = mu_str_skip_class (str, MU_CTYPE_DIGIT); | 238 | if (argstr->constant) |
218 | if (*str) | 239 | { |
240 | char *p = mu_str_skip_class (argstr->orig, MU_CTYPE_DIGIT); | ||
241 | if (*p) | ||
219 | { | 242 | { |
220 | mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, | 243 | mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, |
221 | _("second argument cannot be converted to number")); | 244 | _("second argument cannot be converted to number")); |
... | @@ -223,6 +246,7 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) | ... | @@ -223,6 +246,7 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) |
223 | return 1; | 246 | return 1; |
224 | } | 247 | } |
225 | } | 248 | } |
249 | } | ||
226 | else | 250 | else |
227 | matchtype = MU_SIEVE_MATCH_EQ; | 251 | matchtype = MU_SIEVE_MATCH_EQ; |
228 | 252 | ||
... | @@ -259,6 +283,29 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) | ... | @@ -259,6 +283,29 @@ mu_sieve_match_part_checker (mu_sieve_machine_t mach) |
259 | return 0; | 283 | return 0; |
260 | } | 284 | } |
261 | 285 | ||
286 | static int | ||
287 | regmatch (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, char const *text) | ||
288 | { | ||
289 | regex_t *reg = pattern->rx; | ||
290 | regmatch_t *match_buf = NULL; | ||
291 | size_t match_count = 0; | ||
292 | |||
293 | if (mu_sieve_has_variables (mach)) | ||
294 | { | ||
295 | match_count = reg->re_nsub + 1; | ||
296 | while (mach->match_max < match_count) | ||
297 | mu_i_sv_2nrealloc (mach, (void **) &mach->match_buf, | ||
298 | &mach->match_max, | ||
299 | sizeof (mach->match_buf[0])); | ||
300 | mach->match_count = match_count; | ||
301 | mu_sieve_free (mach, mach->match_string); | ||
302 | mach->match_string = mu_sieve_strdup (mach, text); | ||
303 | |||
304 | match_buf = mach->match_buf; | ||
305 | } | ||
306 | |||
307 | return regexec (reg, text, match_count, match_buf, 0) == 0; | ||
308 | } | ||
262 | /* Particular comparators */ | 309 | /* Particular comparators */ |
263 | 310 | ||
264 | /* :comparator i;octet */ | 311 | /* :comparator i;octet */ |
... | @@ -267,14 +314,14 @@ static int | ... | @@ -267,14 +314,14 @@ static int |
267 | i_octet_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, | 314 | i_octet_is (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, |
268 | const char *text) | 315 | const char *text) |
269 | { | 316 | { |
270 | return strcmp (pattern->orig, text) == 0; | 317 | return strcmp (mu_sieve_string_get (mach, pattern), text) == 0; |
271 | } | 318 | } |
272 | 319 | ||
273 | static int | 320 | static int |
274 | i_octet_contains (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, | 321 | i_octet_contains (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, |
275 | const char *text) | 322 | const char *text) |
276 | { | 323 | { |
277 | return strstr (text, pattern->orig) != NULL; | 324 | return strstr (text, mu_sieve_string_get (mach, pattern)) != NULL; |
278 | } | 325 | } |
279 | 326 | ||
280 | static int | 327 | static int |
... | @@ -282,7 +329,7 @@ i_octet_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, | ... | @@ -282,7 +329,7 @@ i_octet_matches (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, |
282 | const char *text) | 329 | const char *text) |
283 | { | 330 | { |
284 | compile_wildcard (mach, pattern, 0); | 331 | compile_wildcard (mach, pattern, 0); |
285 | return regexec ((regex_t *)pattern->rx, text, 0, NULL, 0) == 0; | 332 | return regmatch (mach, pattern, text); |
286 | } | 333 | } |
287 | 334 | ||
288 | static int | 335 | static int |
... | @@ -290,14 +337,14 @@ i_octet_regex (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, | ... | @@ -290,14 +337,14 @@ i_octet_regex (mu_sieve_machine_t mach, mu_sieve_string_t *pattern, |
290 | const char *text) | 337 | const char *text) |
291 | { | 338 | { |
292 | compile_pattern (mach, pattern, 0); | 339 | compile_pattern (mach, pattern, 0); |
293 | return regexec ((regex_t *)pattern->rx, text, 0, NULL, 0) == 0; | 340 | return regmatch (mach, pattern, text); |
294 | } | 341 | } |
295 | 342 | ||
296 | static int | 343 | static int |
297 | i_octet_eq (mu_sieve_machine_t mach, | 344 | i_octet_eq (mu_sieve_machine_t mach, |
298 | mu_sieve_string_t *pattern, const char *text) | 345 | mu_sieve_string_t *pattern, const char *text) |
299 | { | 346 | { |
300 | return strcmp (text, pattern->orig); | 347 | return strcmp (text, mu_sieve_string_get (mach, pattern)); |
301 | } | 348 | } |
302 | 349 | ||
303 | /* :comparator i;ascii-casemap */ | 350 | /* :comparator i;ascii-casemap */ |
... | @@ -305,14 +352,14 @@ static int | ... | @@ -305,14 +352,14 @@ static int |
305 | i_ascii_casemap_is (mu_sieve_machine_t mach, | 352 | i_ascii_casemap_is (mu_sieve_machine_t mach, |
306 | mu_sieve_string_t *pattern, const char *text) | 353 | mu_sieve_string_t *pattern, const char *text) |
307 | { | 354 | { |
308 | return mu_c_strcasecmp (pattern->orig, text) == 0; | 355 | return mu_c_strcasecmp (mu_sieve_string_get (mach, pattern), text) == 0; |
309 | } | 356 | } |
310 | 357 | ||
311 | static int | 358 | static int |
312 | i_ascii_casemap_contains (mu_sieve_machine_t mach, | 359 | i_ascii_casemap_contains (mu_sieve_machine_t mach, |
313 | mu_sieve_string_t *pattern, const char *text) | 360 | mu_sieve_string_t *pattern, const char *text) |
314 | { | 361 | { |
315 | return mu_c_strcasestr (text, pattern->orig) != NULL; | 362 | return mu_c_strcasestr (text, mu_sieve_string_get (mach, pattern)) != NULL; |
316 | } | 363 | } |
317 | 364 | ||
318 | static int | 365 | static int |
... | @@ -320,7 +367,7 @@ i_ascii_casemap_matches (mu_sieve_machine_t mach, | ... | @@ -320,7 +367,7 @@ i_ascii_casemap_matches (mu_sieve_machine_t mach, |
320 | mu_sieve_string_t *pattern, const char *text) | 367 | mu_sieve_string_t *pattern, const char *text) |
321 | { | 368 | { |
322 | compile_wildcard (mach, pattern, MU_GLOBF_ICASE); | 369 | compile_wildcard (mach, pattern, MU_GLOBF_ICASE); |
323 | return regexec ((regex_t *)pattern->rx, text, 0, NULL, 0) == 0; | 370 | return regmatch (mach, pattern, text); |
324 | } | 371 | } |
325 | 372 | ||
326 | static int | 373 | static int |
... | @@ -328,14 +375,14 @@ i_ascii_casemap_regex (mu_sieve_machine_t mach, | ... | @@ -328,14 +375,14 @@ i_ascii_casemap_regex (mu_sieve_machine_t mach, |
328 | mu_sieve_string_t *pattern, const char *text) | 375 | mu_sieve_string_t *pattern, const char *text) |
329 | { | 376 | { |
330 | compile_pattern (mach, pattern, REG_ICASE); | 377 | compile_pattern (mach, pattern, REG_ICASE); |
331 | return regexec ((regex_t *) pattern->rx, text, 0, NULL, 0) == 0; | 378 | return regmatch (mach, pattern, text); |
332 | } | 379 | } |
333 | 380 | ||
334 | static int | 381 | static int |
335 | i_ascii_casemap_eq (mu_sieve_machine_t mach, | 382 | i_ascii_casemap_eq (mu_sieve_machine_t mach, |
336 | mu_sieve_string_t *pattern, const char *text) | 383 | mu_sieve_string_t *pattern, const char *text) |
337 | { | 384 | { |
338 | return mu_c_strcasecmp (text, pattern->orig); | 385 | return mu_c_strcasecmp (text, mu_sieve_string_get (mach, pattern)); |
339 | } | 386 | } |
340 | 387 | ||
341 | /* :comparator i;ascii-numeric */ | 388 | /* :comparator i;ascii-numeric */ |
... | @@ -343,11 +390,12 @@ static int | ... | @@ -343,11 +390,12 @@ static int |
343 | i_ascii_numeric_is (mu_sieve_machine_t mach, | 390 | i_ascii_numeric_is (mu_sieve_machine_t mach, |
344 | mu_sieve_string_t *pattern, const char *text) | 391 | mu_sieve_string_t *pattern, const char *text) |
345 | { | 392 | { |
346 | if (mu_isdigit (*pattern->orig)) | 393 | char *str = mu_sieve_string_get (mach, pattern); |
394 | if (mu_isdigit (*str)) | ||
347 | { | 395 | { |
348 | if (mu_isdigit (*text)) | 396 | if (mu_isdigit (*text)) |
349 | //FIXME: Error checking | 397 | //FIXME: Error checking |
350 | return strtol (pattern->orig, NULL, 10) == strtol (text, NULL, 10); | 398 | return strtol (str, NULL, 10) == strtol (text, NULL, 10); |
351 | else | 399 | else |
352 | return 0; | 400 | return 0; |
353 | } | 401 | } |
... | @@ -361,11 +409,12 @@ static int | ... | @@ -361,11 +409,12 @@ static int |
361 | i_ascii_numeric_eq (mu_sieve_machine_t mach, | 409 | i_ascii_numeric_eq (mu_sieve_machine_t mach, |
362 | mu_sieve_string_t *pattern, const char *text) | 410 | mu_sieve_string_t *pattern, const char *text) |
363 | { | 411 | { |
364 | if (mu_isdigit (*pattern->orig)) | 412 | char *str = mu_sieve_string_get (mach, pattern); |
413 | if (mu_isdigit (*str)) | ||
365 | { | 414 | { |
366 | if (mu_isdigit (*text)) | 415 | if (mu_isdigit (*text)) |
367 | { | 416 | { |
368 | size_t a = strtoul (pattern->orig, NULL, 10); | 417 | size_t a = strtoul (str, NULL, 10); |
369 | size_t b = strtoul (text, NULL, 10); | 418 | size_t b = strtoul (text, NULL, 10); |
370 | if (b > a) | 419 | if (b > a) |
371 | return 1; | 420 | return 1; | ... | ... |
... | @@ -37,7 +37,9 @@ mu_sieve_require (mu_sieve_machine_t mach, mu_sieve_slice_t list) | ... | @@ -37,7 +37,9 @@ mu_sieve_require (mu_sieve_machine_t mach, mu_sieve_slice_t list) |
37 | char *name = str->orig; | 37 | char *name = str->orig; |
38 | int rc; | 38 | int rc; |
39 | 39 | ||
40 | if (strcmp (name, "relational") == 0) /* RFC 3431 */ | 40 | if (strcmp (name, "variables") == 0) /* RFC 5229 */ |
41 | rc = mu_sieve_require_variables (mach); | ||
42 | else if (strcmp (name, "relational") == 0) /* RFC 3431 */ | ||
41 | rc = mu_sieve_require_relational (mach, name); | 43 | rc = mu_sieve_require_relational (mach, name); |
42 | else if (strcmp (name, "encoded-character") == 0) /* RFC 5228, 2.4.2.4 */ | 44 | else if (strcmp (name, "encoded-character") == 0) /* RFC 5228, 2.4.2.4 */ |
43 | rc = mu_sieve_require_encoded_character (mach, name); | 45 | 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) | ... | @@ -52,8 +54,7 @@ mu_sieve_require (mu_sieve_machine_t mach, mu_sieve_slice_t list) |
52 | 54 | ||
53 | if (rc) | 55 | if (rc) |
54 | { | 56 | { |
55 | mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, | 57 | mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, _("can't require %s"), |
56 | _("can't require %s is not available"), | ||
57 | name); | 58 | name); |
58 | mu_i_sv_error (mach); | 59 | mu_i_sv_error (mach); |
59 | } | 60 | } | ... | ... |
... | @@ -229,6 +229,9 @@ sieve_run (mu_sieve_machine_t mach) | ... | @@ -229,6 +229,9 @@ sieve_run (mu_sieve_machine_t mach) |
229 | { | 229 | { |
230 | mach->action_count = 0; | 230 | mach->action_count = 0; |
231 | 231 | ||
232 | if (mu_sieve_has_variables (mach)) | ||
233 | mu_assoc_clear (mach->vartab); | ||
234 | |||
232 | for (mach->pc = 1; mach->prog[mach->pc].handler; ) | 235 | for (mach->pc = 1; mach->prog[mach->pc].handler; ) |
233 | (*mach->prog[mach->pc++].instr) (mach); | 236 | (*mach->prog[mach->pc++].instr) (mach); |
234 | 237 | ... | ... |
... | @@ -17,6 +17,7 @@ | ... | @@ -17,6 +17,7 @@ |
17 | <http://www.gnu.org/licenses/>. */ | 17 | <http://www.gnu.org/licenses/>. */ |
18 | 18 | ||
19 | #include <mailutils/sieve.h> | 19 | #include <mailutils/sieve.h> |
20 | #include <mailutils/assoc.h> | ||
20 | #include <setjmp.h> | 21 | #include <setjmp.h> |
21 | #include <string.h> | 22 | #include <string.h> |
22 | #include <regex.h> | 23 | #include <regex.h> |
... | @@ -90,6 +91,13 @@ struct mu_sieve_machine | ... | @@ -90,6 +91,13 @@ struct mu_sieve_machine |
90 | size_t pc; /* Current program counter */ | 91 | size_t pc; /* Current program counter */ |
91 | long reg; /* Numeric register */ | 92 | long reg; /* Numeric register */ |
92 | 93 | ||
94 | /* Support for variables (RFC 5229) */ | ||
95 | mu_assoc_t vartab; /* Table of variables */ | ||
96 | char *match_string; /* The string used in the most recent match */ | ||
97 | regmatch_t *match_buf; /* Offsets of parenthesized groups */ | ||
98 | size_t match_count; /* Actual number of elements used in match_buf */ | ||
99 | size_t match_max; /* Total number of elements available in match_buf */ | ||
100 | |||
93 | /* Call environment */ | 101 | /* Call environment */ |
94 | const char *identifier; /* Name of action or test being executed */ | 102 | const char *identifier; /* Name of action or test being executed */ |
95 | size_t argstart; /* Index of the first argument in valspace */ | 103 | size_t argstart; /* Index of the first argument in valspace */ |
... | @@ -233,4 +241,8 @@ size_t mu_i_sv_id_num (mu_sieve_machine_t mach, char const *name); | ... | @@ -233,4 +241,8 @@ size_t mu_i_sv_id_num (mu_sieve_machine_t mach, char const *name); |
233 | char *mu_i_sv_id_str (mu_sieve_machine_t mach, size_t n); | 241 | char *mu_i_sv_id_str (mu_sieve_machine_t mach, size_t n); |
234 | void mu_i_sv_free_idspace (mu_sieve_machine_t mach); | 242 | void mu_i_sv_free_idspace (mu_sieve_machine_t mach); |
235 | 243 | ||
244 | void mu_i_sv_copy_variables (mu_sieve_machine_t child, | ||
245 | mu_sieve_machine_t parent); | ||
246 | int mu_i_sv_expand_variables (char const *input, size_t len, | ||
247 | char **exp, void *data); | ||
236 | 248 | ... | ... |
... | @@ -529,6 +529,8 @@ multiline_strip_tabs (char *text) | ... | @@ -529,6 +529,8 @@ multiline_strip_tabs (char *text) |
529 | static void | 529 | static void |
530 | line_add (char *text, size_t len) | 530 | line_add (char *text, size_t len) |
531 | { | 531 | { |
532 | if (len == 0) | ||
533 | len = strlen (text); | ||
532 | mu_opool_append (mu_sieve_machine->string_pool, text, len); | 534 | mu_opool_append (mu_sieve_machine->string_pool, text, len); |
533 | } | 535 | } |
534 | 536 | ... | ... |
... | @@ -1067,6 +1067,12 @@ mu_sieve_machine_reset (mu_sieve_machine_t mach) | ... | @@ -1067,6 +1067,12 @@ mu_sieve_machine_reset (mu_sieve_machine_t mach) |
1067 | mach->progsize = 0; | 1067 | mach->progsize = 0; |
1068 | mach->prog = NULL; | 1068 | mach->prog = NULL; |
1069 | 1069 | ||
1070 | mu_assoc_destroy (&mach->vartab); | ||
1071 | mach->match_string = NULL; | ||
1072 | mach->match_buf = NULL; | ||
1073 | mach->match_count = 0; | ||
1074 | mach->match_max = 0; | ||
1075 | |||
1070 | mach->state = mu_sieve_state_init; | 1076 | mach->state = mu_sieve_state_init; |
1071 | 1077 | ||
1072 | return 0; | 1078 | return 0; |
... | @@ -1175,6 +1181,16 @@ mu_sieve_machine_clone (mu_sieve_machine_t const parent, | ... | @@ -1175,6 +1181,16 @@ mu_sieve_machine_clone (mu_sieve_machine_t const parent, |
1175 | memcpy (child->prog, parent->prog, | 1181 | memcpy (child->prog, parent->prog, |
1176 | parent->progsize * sizeof (child->prog[0])); | 1182 | parent->progsize * sizeof (child->prog[0])); |
1177 | 1183 | ||
1184 | /* Copy variables */ | ||
1185 | if (mu_sieve_has_variables (parent)) | ||
1186 | { | ||
1187 | mu_i_sv_copy_variables (child, parent); | ||
1188 | child->match_string = NULL; | ||
1189 | child->match_buf = NULL; | ||
1190 | child->match_count = 0; | ||
1191 | child->match_max = 0; | ||
1192 | } | ||
1193 | |||
1178 | /* Copy user-defined settings */ | 1194 | /* Copy user-defined settings */ |
1179 | 1195 | ||
1180 | child->dry_run = parent->dry_run; | 1196 | child->dry_run = parent->dry_run; |
... | @@ -1468,6 +1484,30 @@ with_machine (mu_sieve_machine_t mach, char const *name, | ... | @@ -1468,6 +1484,30 @@ with_machine (mu_sieve_machine_t mach, char const *name, |
1468 | return rc; | 1484 | return rc; |
1469 | } | 1485 | } |
1470 | 1486 | ||
1487 | /* Rescan all registered strings to determine their properties */ | ||
1488 | static void | ||
1489 | string_rescan (mu_sieve_machine_t mach) | ||
1490 | { | ||
1491 | size_t i; | ||
1492 | int hasvar = mu_sieve_has_variables (mach); | ||
1493 | |||
1494 | for (i = 0; i < mach->stringcount; i++) | ||
1495 | { | ||
1496 | mach->stringspace[i].changed = 0; | ||
1497 | if (hasvar) | ||
1498 | { | ||
1499 | mach->stringspace[i].constant = 0; | ||
1500 | mu_sieve_string_get (mach, &mach->stringspace[i]); | ||
1501 | mu_sieve_free (mach, mach->stringspace[i].exp); | ||
1502 | mach->stringspace[i].exp = NULL; | ||
1503 | mach->stringspace[i].constant = !mach->stringspace[i].changed; | ||
1504 | mach->stringspace[i].changed = 0; | ||
1505 | } | ||
1506 | else | ||
1507 | mach->stringspace[i].constant = 1; | ||
1508 | } | ||
1509 | } | ||
1510 | |||
1471 | static int | 1511 | static int |
1472 | sieve_parse (void) | 1512 | sieve_parse (void) |
1473 | { | 1513 | { |
... | @@ -1509,8 +1549,11 @@ sieve_parse (void) | ... | @@ -1509,8 +1549,11 @@ sieve_parse (void) |
1509 | if (mu_sieve_machine->state == mu_sieve_state_error) | 1549 | if (mu_sieve_machine->state == mu_sieve_state_error) |
1510 | rc = MU_ERR_PARSE; | 1550 | rc = MU_ERR_PARSE; |
1511 | else | 1551 | else |
1552 | { | ||
1553 | string_rescan (mu_sieve_machine); | ||
1512 | mu_sieve_machine->state = mu_sieve_state_compiled; | 1554 | mu_sieve_machine->state = mu_sieve_state_compiled; |
1513 | } | 1555 | } |
1556 | } | ||
1514 | 1557 | ||
1515 | tree_free (&sieve_tree); | 1558 | tree_free (&sieve_tree); |
1516 | return rc; | 1559 | return rc; | ... | ... |
... | @@ -155,6 +155,7 @@ update_len (void *item, void *data) | ... | @@ -155,6 +155,7 @@ update_len (void *item, void *data) |
155 | break; | 155 | break; |
156 | 156 | ||
157 | case segm_repl: | 157 | case segm_repl: |
158 | if (segm->repl) | ||
158 | st->len += strlen (segm->repl); | 159 | st->len += strlen (segm->repl); |
159 | break; | 160 | break; |
160 | } | 161 | } |
... | @@ -176,9 +177,14 @@ append_segm (void *item, void *data) | ... | @@ -176,9 +177,14 @@ append_segm (void *item, void *data) |
176 | break; | 177 | break; |
177 | 178 | ||
178 | case segm_repl: | 179 | case segm_repl: |
180 | if (segm->repl) | ||
181 | { | ||
179 | len = strlen (segm->repl); | 182 | len = strlen (segm->repl); |
180 | memcpy (buf->endptr, segm->repl, len); | 183 | memcpy (buf->endptr, segm->repl, len); |
181 | } | 184 | } |
185 | else | ||
186 | len = 0; | ||
187 | } | ||
182 | 188 | ||
183 | buf->endptr += len; | 189 | buf->endptr += len; |
184 | return 0; | 190 | return 0; | ... | ... |
... | @@ -44,7 +44,7 @@ mu_i_sv_string_create (mu_sieve_machine_t mach, char *str) | ... | @@ -44,7 +44,7 @@ mu_i_sv_string_create (mu_sieve_machine_t mach, char *str) |
44 | return n; | 44 | return n; |
45 | } | 45 | } |
46 | 46 | ||
47 | struct mu_sieve_string * | 47 | mu_sieve_string_t * |
48 | mu_sieve_string_raw (mu_sieve_machine_t mach, mu_sieve_slice_t slice, | 48 | mu_sieve_string_raw (mu_sieve_machine_t mach, mu_sieve_slice_t slice, |
49 | size_t i) | 49 | size_t i) |
50 | { | 50 | { |
... | @@ -54,8 +54,57 @@ mu_sieve_string_raw (mu_sieve_machine_t mach, mu_sieve_slice_t slice, | ... | @@ -54,8 +54,57 @@ mu_sieve_string_raw (mu_sieve_machine_t mach, mu_sieve_slice_t slice, |
54 | } | 54 | } |
55 | 55 | ||
56 | char * | 56 | char * |
57 | mu_sieve_string_get (mu_sieve_machine_t mach, mu_sieve_string_t *string) | ||
58 | { | ||
59 | char *exp; | ||
60 | int rc; | ||
61 | |||
62 | if (string->constant) | ||
63 | return string->orig; | ||
64 | |||
65 | rc = mu_i_sv_string_expand (string->orig, mu_i_sv_expand_variables, mach, | ||
66 | &exp); | ||
67 | switch (rc) | ||
68 | { | ||
69 | case 0: | ||
70 | if (string->exp == NULL) | ||
71 | { | ||
72 | string->changed = strcmp (string->orig, exp) != 0; | ||
73 | string->exp = mu_sieve_strdup (mach, exp); | ||
74 | free (exp); | ||
75 | } | ||
76 | else if (strcmp (exp, string->exp) == 0) | ||
77 | { | ||
78 | string->changed = 0; | ||
79 | free (exp); | ||
80 | } | ||
81 | else | ||
82 | { | ||
83 | string->changed = 1; | ||
84 | mu_sieve_free (mach, string->exp); | ||
85 | string->exp = mu_sieve_strdup (mach, exp); | ||
86 | free (exp); | ||
87 | } | ||
88 | break; | ||
89 | |||
90 | case MU_ERR_CANCELED: | ||
91 | string->changed = 0; | ||
92 | return string->orig; | ||
93 | |||
94 | default: | ||
95 | mu_sieve_error (mach, "error expanding variables: %s", | ||
96 | mu_strerror (rc)); | ||
97 | mu_sieve_abort (mach); | ||
98 | } | ||
99 | |||
100 | return string->exp; | ||
101 | } | ||
102 | |||
103 | char * | ||
57 | mu_sieve_string (mu_sieve_machine_t mach, mu_sieve_slice_t slice, | 104 | mu_sieve_string (mu_sieve_machine_t mach, mu_sieve_slice_t slice, |
58 | size_t i) | 105 | size_t i) |
59 | { | 106 | { |
60 | return mu_sieve_string_raw (mach, slice, i)->orig; | 107 | return mu_sieve_string_get (mach, mu_sieve_string_raw (mach, slice, i)); |
61 | } | 108 | } |
109 | |||
110 | ... | ... |
... | @@ -55,21 +55,26 @@ struct address_closure | ... | @@ -55,21 +55,26 @@ struct address_closure |
55 | mu_address_t addr; /* Obtained address */ | 55 | mu_address_t addr; /* Obtained address */ |
56 | }; | 56 | }; |
57 | 57 | ||
58 | static int | 58 | int |
59 | do_count (mu_sieve_machine_t mach, size_t count, int retval) | 59 | mu_sieve_relational_count (mu_sieve_machine_t mach, size_t count, int retval) |
60 | { | 60 | { |
61 | char *relcmp; | 61 | char *relcmp; |
62 | 62 | ||
63 | if (mu_sieve_get_tag (mach, "count", SVT_STRING, &relcmp)) | 63 | if (mu_sieve_get_tag (mach, "count", SVT_STRING, &relcmp)) |
64 | { | 64 | { |
65 | size_t limit; | 65 | size_t limit; |
66 | char *str; | 66 | char *str, *p; |
67 | struct mu_sieve_slice slice; | 67 | struct mu_sieve_slice slice; |
68 | mu_sieve_relcmpn_t stest; | 68 | mu_sieve_relcmpn_t stest; |
69 | 69 | ||
70 | mu_sieve_get_arg (mach, 1, SVT_STRING_LIST, &slice); | 70 | mu_sieve_get_arg (mach, 1, SVT_STRING_LIST, &slice); |
71 | str = mu_sieve_string (mach, &slice, 0); | 71 | str = mu_sieve_string (mach, &slice, 0); |
72 | limit = strtoul (str, &str, 10); | 72 | limit = strtoul (str, &p, 10); |
73 | if (*p) | ||
74 | { | ||
75 | mu_sieve_error (mach, _("%s: not an integer"), str); | ||
76 | mu_sieve_abort (mach); | ||
77 | } | ||
73 | 78 | ||
74 | mu_sieve_str_to_relcmp (relcmp, NULL, &stest); | 79 | mu_sieve_str_to_relcmp (relcmp, NULL, &stest); |
75 | return stest (count, limit); | 80 | return stest (count, limit); |
... | @@ -122,7 +127,7 @@ sieve_test_address (mu_sieve_machine_t mach) | ... | @@ -122,7 +127,7 @@ sieve_test_address (mu_sieve_machine_t mach) |
122 | &count); | 127 | &count); |
123 | mu_address_destroy (&clos.addr); | 128 | mu_address_destroy (&clos.addr); |
124 | 129 | ||
125 | return do_count (mach, count, rc); | 130 | return mu_sieve_relational_count (mach, count, rc); |
126 | } | 131 | } |
127 | 132 | ||
128 | struct header_closure | 133 | struct header_closure |
... | @@ -196,7 +201,7 @@ sieve_test_header (mu_sieve_machine_t mach) | ... | @@ -196,7 +201,7 @@ sieve_test_header (mu_sieve_machine_t mach) |
196 | &count)) | 201 | &count)) |
197 | return 1; | 202 | return 1; |
198 | 203 | ||
199 | return do_count (mach, count + mcount, 0); | 204 | return mu_sieve_relational_count (mach, count + mcount, 0); |
200 | } | 205 | } |
201 | 206 | ||
202 | int | 207 | int |
... | @@ -246,7 +251,7 @@ sieve_test_envelope (mu_sieve_machine_t mach) | ... | @@ -246,7 +251,7 @@ sieve_test_envelope (mu_sieve_machine_t mach) |
246 | rc = mu_sieve_vlist_compare (mach, h, v, comp, test, retrieve_envelope, &clos, | 251 | rc = mu_sieve_vlist_compare (mach, h, v, comp, test, retrieve_envelope, &clos, |
247 | &count); | 252 | &count); |
248 | mu_address_destroy (&clos.addr); | 253 | mu_address_destroy (&clos.addr); |
249 | return do_count (mach, count, rc); | 254 | return mu_sieve_relational_count (mach, count, rc); |
250 | } | 255 | } |
251 | 256 | ||
252 | int | 257 | int |
... | @@ -301,7 +306,7 @@ static mu_sieve_tag_def_t address_part_tags[] = { | ... | @@ -301,7 +306,7 @@ static mu_sieve_tag_def_t address_part_tags[] = { |
301 | { NULL } | 306 | { NULL } |
302 | }; | 307 | }; |
303 | 308 | ||
304 | static mu_sieve_tag_def_t match_part_tags[] = { | 309 | mu_sieve_tag_def_t mu_sieve_match_part_tags[] = { |
305 | { "is", SVT_VOID }, | 310 | { "is", SVT_VOID }, |
306 | { "contains", SVT_VOID }, | 311 | { "contains", SVT_VOID }, |
307 | { "matches", SVT_VOID }, | 312 | { "matches", SVT_VOID }, |
... | @@ -327,7 +332,7 @@ static mu_sieve_tag_def_t mime_tags[] = { | ... | @@ -327,7 +332,7 @@ static mu_sieve_tag_def_t mime_tags[] = { |
327 | { address_part_tags, NULL } | 332 | { address_part_tags, NULL } |
328 | 333 | ||
329 | #define MATCH_PART_GROUP \ | 334 | #define MATCH_PART_GROUP \ |
330 | { match_part_tags, mu_sieve_match_part_checker } | 335 | { mu_sieve_match_part_tags, mu_sieve_match_part_checker } |
331 | 336 | ||
332 | #define SIZE_GROUP { size_tags, NULL } | 337 | #define SIZE_GROUP { size_tags, NULL } |
333 | 338 | ... | ... |
libmu_sieve/variables.c
0 → 100644
1 | /* Sieve variables extension (RFC 5229) | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 3 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General | ||
15 | Public License along with this library. If not, see | ||
16 | <http://www.gnu.org/licenses/>. */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | #include <sieve-priv.h> | ||
22 | #include <ctype.h> | ||
23 | |||
24 | struct sieve_variable | ||
25 | { | ||
26 | char *value; | ||
27 | }; | ||
28 | |||
29 | |||
30 | /* FIXME: UTF support */ | ||
31 | char * | ||
32 | mod_lower (mu_sieve_machine_t mach, char const *value) | ||
33 | { | ||
34 | char *newval = mu_sieve_malloc (mach, strlen (value) + 1); | ||
35 | char *p; | ||
36 | |||
37 | for (p = newval; *value; p++, value++) | ||
38 | *p = tolower (*value); | ||
39 | *p = 0; | ||
40 | return newval; | ||
41 | } | ||
42 | |||
43 | char * | ||
44 | mod_upper (mu_sieve_machine_t mach, char const *value) | ||
45 | { | ||
46 | char *newval = mu_sieve_malloc (mach, strlen (value) + 1); | ||
47 | char *p; | ||
48 | |||
49 | for (p = newval; *value; p++, value++) | ||
50 | *p = toupper (*value); | ||
51 | *p = 0; | ||
52 | return newval; | ||
53 | } | ||
54 | |||
55 | char * | ||
56 | mod_lowerfirst (mu_sieve_machine_t mach, char const *value) | ||
57 | { | ||
58 | char *newval = mu_sieve_strdup (mach, value); | ||
59 | *newval = tolower (*newval); | ||
60 | return newval; | ||
61 | } | ||
62 | |||
63 | char * | ||
64 | mod_upperfirst (mu_sieve_machine_t mach, char const *value) | ||
65 | { | ||
66 | char *newval = mu_sieve_strdup (mach, value); | ||
67 | *newval = toupper (*newval); | ||
68 | return newval; | ||
69 | } | ||
70 | |||
71 | char * | ||
72 | mod_quotewildcard (mu_sieve_machine_t mach, char const *value) | ||
73 | { | ||
74 | size_t len; | ||
75 | char *newval; | ||
76 | char const *p; | ||
77 | char *q; | ||
78 | |||
79 | len = 0; | ||
80 | for (p = value; *p; p++) | ||
81 | { | ||
82 | switch (*p) | ||
83 | { | ||
84 | case '*': | ||
85 | case '?': | ||
86 | case '\\': | ||
87 | len += 2; | ||
88 | break; | ||
89 | default: | ||
90 | len++; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | newval = mu_sieve_malloc (mach, len + 1); | ||
95 | for (p = value, q = newval; *p;) | ||
96 | { | ||
97 | switch (*p) | ||
98 | { | ||
99 | case '*': | ||
100 | case '?': | ||
101 | case '\\': | ||
102 | *q++ = '\\'; | ||
103 | } | ||
104 | *q++ = *p++; | ||
105 | } | ||
106 | *q = 0; | ||
107 | return newval; | ||
108 | } | ||
109 | |||
110 | char * | ||
111 | mod_length (mu_sieve_machine_t mach, char const *value) | ||
112 | { | ||
113 | char *newval, *p; | ||
114 | int rc = mu_asprintf (&newval, "%zu", strlen (value)); | ||
115 | if (rc) | ||
116 | { | ||
117 | mu_diag_funcall (MU_DIAG_ERROR, "mu_asprintf", NULL, rc); | ||
118 | mu_sieve_abort (mach); | ||
119 | } | ||
120 | p = mu_sieve_strdup (mach, newval); | ||
121 | free (newval); | ||
122 | return p; | ||
123 | } | ||
124 | |||
125 | static mu_sieve_tag_def_t set_tags[] = { | ||
126 | { "lower", SVT_VOID }, | ||
127 | { "upper", SVT_VOID }, | ||
128 | { "lowerfirst", SVT_VOID }, | ||
129 | { "upperfirst", SVT_VOID }, | ||
130 | { "quotewildcard", SVT_VOID }, | ||
131 | { "length", SVT_VOID }, | ||
132 | { NULL } | ||
133 | }; | ||
134 | |||
135 | struct modprec | ||
136 | { | ||
137 | char *name; | ||
138 | unsigned prec; | ||
139 | char *(*modify) (mu_sieve_machine_t mach, char const *value); | ||
140 | }; | ||
141 | |||
142 | static struct modprec modprec[] = { | ||
143 | { "lower", 40, mod_lower }, | ||
144 | { "upper", 40, mod_upper }, | ||
145 | { "lowerfirst", 30, mod_lowerfirst }, | ||
146 | { "upperfirst", 30, mod_upperfirst }, | ||
147 | { "quotewildcard", 20, mod_quotewildcard }, | ||
148 | { "length", 10, mod_length }, | ||
149 | }; | ||
150 | |||
151 | static struct modprec * | ||
152 | findprec (char const *name) | ||
153 | { | ||
154 | int i; | ||
155 | |||
156 | for (i = 0; i < MU_ARRAY_SIZE (modprec); i++) | ||
157 | if (strcmp (modprec[i].name, name) == 0) | ||
158 | return &modprec[i]; | ||
159 | mu_error ("%s:%d: INTERNAL ERROR", __FILE__, __LINE__); | ||
160 | abort (); | ||
161 | } | ||
162 | |||
163 | static int | ||
164 | sieve_action_set (mu_sieve_machine_t mach) | ||
165 | { | ||
166 | size_t i; | ||
167 | char *name; | ||
168 | char *value; | ||
169 | struct sieve_variable *vptr; | ||
170 | int rc; | ||
171 | |||
172 | mu_sieve_get_arg (mach, 0, SVT_STRING, &name); | ||
173 | mu_sieve_get_arg (mach, 1, SVT_STRING, &value); | ||
174 | |||
175 | value = mu_sieve_strdup (mach, value); | ||
176 | for (i = 0; i < mach->tagcount; i++) | ||
177 | { | ||
178 | mu_sieve_value_t *p = mu_sieve_get_tag_n (mach, i); | ||
179 | char *str = findprec (p->tag)->modify (mach, value); | ||
180 | mu_sieve_free (mach, value); | ||
181 | value = str; | ||
182 | } | ||
183 | |||
184 | rc = mu_assoc_ref_install (mach->vartab, name, (void **)&vptr); | ||
185 | switch (rc) | ||
186 | { | ||
187 | case 0: | ||
188 | break; | ||
189 | |||
190 | case MU_ERR_EXISTS: | ||
191 | mu_sieve_free (mach, vptr->value); | ||
192 | break; | ||
193 | |||
194 | default: | ||
195 | mu_sieve_error (mach, "mu_assoc_ref_install: %s", mu_strerror (rc)); | ||
196 | mu_sieve_abort (mach); | ||
197 | } | ||
198 | vptr->value = value; | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int | ||
203 | set_tag_checker (mu_sieve_machine_t mach) | ||
204 | { | ||
205 | int i, j; | ||
206 | |||
207 | /* Sort tags by decreasing priority value (RFC 5229, 4.1) */ | ||
208 | for (i = 1; i < mach->tagcount; i++) | ||
209 | { | ||
210 | mu_sieve_value_t tmp = *mu_sieve_get_tag_n (mach, i); | ||
211 | int tmp_prec = findprec (tmp.tag)->prec; | ||
212 | |||
213 | for (j = i - 1; j >= 0; j--) | ||
214 | { | ||
215 | mu_sieve_value_t *t = mu_sieve_get_tag_n (mach, j); | ||
216 | int prec = findprec (t->tag)->prec; | ||
217 | if (prec < tmp_prec) | ||
218 | *mu_sieve_get_tag_n (mach, j + 1) = *t; | ||
219 | else if (prec == tmp_prec) | ||
220 | { | ||
221 | mu_diag_at_locus (MU_LOG_ERROR, &mach->locus, | ||
222 | _("%s and %s can't be used together"), | ||
223 | tmp.tag, t->tag); | ||
224 | mu_i_sv_error (mach); | ||
225 | return 1; | ||
226 | } | ||
227 | else | ||
228 | break; | ||
229 | } | ||
230 | *mu_sieve_get_tag_n (mach, j + 1) = tmp; | ||
231 | } | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static mu_sieve_tag_group_t set_tag_groups[] = { | ||
236 | { set_tags, set_tag_checker }, | ||
237 | { NULL } | ||
238 | }; | ||
239 | |||
240 | static mu_sieve_data_type set_args[] = { | ||
241 | SVT_STRING, | ||
242 | SVT_STRING, | ||
243 | SVT_VOID | ||
244 | }; | ||
245 | |||
246 | /* RFC 5229, 5. Test string */ | ||
247 | int | ||
248 | sieve_test_string (mu_sieve_machine_t mach) | ||
249 | { | ||
250 | mu_sieve_value_t *source, *key_list; | ||
251 | mu_sieve_comparator_t comp = mu_sieve_get_comparator (mach); | ||
252 | mu_sieve_relcmp_t test = mu_sieve_get_relcmp (mach); | ||
253 | size_t count = 0; | ||
254 | int rc = 0; | ||
255 | size_t i; | ||
256 | |||
257 | source = mu_sieve_get_arg_untyped (mach, 0); | ||
258 | key_list = mu_sieve_get_arg_untyped (mach, 1); | ||
259 | |||
260 | for (i = 0; i < source->v.list.count; i++) | ||
261 | { | ||
262 | char *item = mu_sieve_string (mach, &source->v.list, i); | ||
263 | size_t k; | ||
264 | |||
265 | /* The "relational" extension [RELATIONAL] adds a match type called | ||
266 | ":count". The count of a single string is 0 if it is the empty | ||
267 | string, or 1 otherwise. The count of a string list is the sum of the | ||
268 | counts of the member strings. | ||
269 | */ | ||
270 | if (item[0]) | ||
271 | count++; | ||
272 | for (k = 0; k < key_list->v.list.count; k++) | ||
273 | { | ||
274 | mu_sieve_string_t *s = | ||
275 | mu_sieve_string_raw (mach, &key_list->v.list, k); | ||
276 | rc = test (comp (mach, s, item), 0); | ||
277 | if (rc) | ||
278 | return rc; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | return mu_sieve_relational_count (mach, count, 0); | ||
283 | } | ||
284 | |||
285 | mu_sieve_data_type string_args[] = { | ||
286 | SVT_STRING_LIST, | ||
287 | SVT_STRING_LIST, | ||
288 | SVT_VOID | ||
289 | }; | ||
290 | |||
291 | mu_sieve_tag_group_t string_tag_groups[] = { | ||
292 | { mu_sieve_match_part_tags, mu_sieve_match_part_checker }, | ||
293 | { NULL } | ||
294 | }; | ||
295 | |||
296 | int | ||
297 | mu_i_sv_expand_variables (char const *input, size_t len, | ||
298 | char **exp, void *data) | ||
299 | { | ||
300 | mu_sieve_machine_t mach = data; | ||
301 | if (mu_isdigit (*input)) | ||
302 | { | ||
303 | char *p; | ||
304 | size_t idx = 0; | ||
305 | |||
306 | while (len) | ||
307 | { | ||
308 | if (mu_isdigit (*input)) | ||
309 | { | ||
310 | int d = *input - '0'; | ||
311 | idx = idx * 10 + d; | ||
312 | input++; | ||
313 | len--; | ||
314 | } | ||
315 | else | ||
316 | return 1; | ||
317 | } | ||
318 | |||
319 | if (idx > mach->match_count) | ||
320 | { | ||
321 | *exp = NULL; | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | len = mach->match_buf[idx].rm_eo - mach->match_buf[idx].rm_so; | ||
326 | p = malloc (len + 1); | ||
327 | if (!p) | ||
328 | { | ||
329 | mu_sieve_error (mach, "%s", mu_strerror (errno)); | ||
330 | mu_sieve_abort (mach); | ||
331 | } | ||
332 | memcpy (p, mach->match_string + mach->match_buf[idx].rm_so, len); | ||
333 | p[len] = 0; | ||
334 | *exp = p; | ||
335 | } | ||
336 | else if (mu_isalpha (*input)) | ||
337 | { | ||
338 | size_t i; | ||
339 | char *name; | ||
340 | struct sieve_variable *var; | ||
341 | |||
342 | for (i = 0; i < len; i++) | ||
343 | if (!(mu_isalnum (input[i]) || input[i] == '_')) | ||
344 | return 1; | ||
345 | |||
346 | name = malloc (len + 1); | ||
347 | if (!name) | ||
348 | { | ||
349 | mu_sieve_error (mach, "%s", mu_strerror (errno)); | ||
350 | mu_sieve_abort (mach); | ||
351 | } | ||
352 | memcpy (name, input, len); | ||
353 | name[len] = 0; | ||
354 | |||
355 | var = mu_assoc_ref (mach->vartab, name); | ||
356 | |||
357 | free (name); | ||
358 | |||
359 | if (var) | ||
360 | { | ||
361 | *exp = strdup (var->value); | ||
362 | if (!*exp) | ||
363 | { | ||
364 | mu_sieve_error (mach, "%s", mu_strerror (errno)); | ||
365 | mu_sieve_abort (mach); | ||
366 | } | ||
367 | } | ||
368 | else | ||
369 | *exp = NULL; | ||
370 | } | ||
371 | else | ||
372 | return 1; | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | int | ||
377 | mu_sieve_require_variables (mu_sieve_machine_t mach) | ||
378 | { | ||
379 | int rc; | ||
380 | |||
381 | if (mach->vartab) | ||
382 | return 0; | ||
383 | |||
384 | rc = mu_assoc_create (&mach->vartab, sizeof (struct sieve_variable), | ||
385 | MU_ASSOC_ICASE); | ||
386 | if (rc) | ||
387 | mu_sieve_error (mach, "mu_assoc_create: %s", mu_strerror (rc)); | ||
388 | |||
389 | if (rc == 0) | ||
390 | { | ||
391 | mu_sieve_register_action (mach, "set", sieve_action_set, | ||
392 | set_args, set_tag_groups, 1); | ||
393 | mu_sieve_register_test (mach, "string", sieve_test_string, | ||
394 | string_args, string_tag_groups, 1); | ||
395 | } | ||
396 | return rc; | ||
397 | } | ||
398 | |||
399 | int | ||
400 | mu_sieve_has_variables (mu_sieve_machine_t mach) | ||
401 | { | ||
402 | return mach->vartab != NULL; | ||
403 | } | ||
404 | |||
405 | void | ||
406 | mu_i_sv_copy_variables (mu_sieve_machine_t child, mu_sieve_machine_t parent) | ||
407 | { | ||
408 | mu_iterator_t itr; | ||
409 | |||
410 | mu_sieve_require_variables (child); | ||
411 | |||
412 | mu_assoc_get_iterator (parent->vartab, &itr); | ||
413 | |||
414 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); | ||
415 | mu_iterator_next (itr)) | ||
416 | { | ||
417 | const char *name; | ||
418 | struct sieve_variable const *val; | ||
419 | struct sieve_variable newval; | ||
420 | |||
421 | mu_iterator_current_kv (itr, (const void **)&name, (void**)&val); | ||
422 | newval.value = mu_sieve_strdup (child, val->value); | ||
423 | mu_assoc_install (child->vartab, name, &newval); | ||
424 | } | ||
425 | |||
426 | mu_iterator_destroy (&itr); | ||
427 | } | ||
428 | |||
429 | |||
430 | |||
431 |
sieve/tests/variables.at
0 → 100644
1 | # This file is part of GNU Mailutils. -*- Autotest -*- | ||
2 | # Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | # | ||
4 | # GNU Mailutils is free software; you can redistribute it and/or | ||
5 | # modify it under the terms of the GNU General Public License as | ||
6 | # published by the Free Software Foundation; either version 3, or (at | ||
7 | # your option) any later version. | ||
8 | # | ||
9 | # GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | # General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License | ||
15 | # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | |||
17 | AT_BANNER([Variables extension]) | ||
18 | |||
19 | MUT_TESTCASE([match variable],[variables match match-variable], | ||
20 | [require [["variables", "fileinto"]]; | ||
21 | if address :matches [[ "To", "Cc" ] [ "*@**.com", "*@**.edu" ]] | ||
22 | { | ||
23 | fileinto "INBOX.${3}.${1}"; | ||
24 | }], | ||
25 | [],[0],[], | ||
26 | [FILEINTO on msg uid 1: delivering into INBOX.acme.example.roadrunner | ||
27 | FILEINTO on msg uid 2: delivering into INBOX.landru.example.rube | ||
28 | IMPLICIT KEEP on msg uid 3 | ||
29 | ]) | ||
30 | |||
31 | MUT_TESTCASE([set action],[variables action set], | ||
32 | [require [["variables", "fileinto"]]; | ||
33 | set "name" "value"; | ||
34 | fileinto "INBOX.${name}"; | ||
35 | ], | ||
36 | [],[0],[], | ||
37 | [FILEINTO on msg uid 1: delivering into INBOX.value | ||
38 | FILEINTO on msg uid 2: delivering into INBOX.value | ||
39 | FILEINTO on msg uid 3: delivering into INBOX.value | ||
40 | ]) | ||
41 | |||
42 | MUT_TESTCASE([variables with encoded characters],[variables encoded-character], | ||
43 | [require [["encoded-character", "variables", "fileinto"]]; | ||
44 | set "myvar" "INBOX"; | ||
45 | fileinto "${${hex: 6D 79 76}ar}"; | ||
46 | ], | ||
47 | [],[0],[], | ||
48 | [FILEINTO on msg uid 1: delivering into INBOX | ||
49 | FILEINTO on msg uid 2: delivering into INBOX | ||
50 | FILEINTO on msg uid 3: delivering into INBOX | ||
51 | ]) | ||
52 | |||
53 | MUT_TESTCASE([set modifiers],[variables action set], | ||
54 | [require [["variables", "fileinto"]]; | ||
55 | set "name" :upperfirst :lower "VALUE"; | ||
56 | fileinto "INBOX.${name}"; | ||
57 | ], | ||
58 | [],[0],[], | ||
59 | [FILEINTO on msg uid 1: delivering into INBOX.Value | ||
60 | FILEINTO on msg uid 2: delivering into INBOX.Value | ||
61 | FILEINTO on msg uid 3: delivering into INBOX.Value | ||
62 | ]) | ||
63 | |||
64 | MUT_TESTCASE([:quotewildcard modifier],[variables action set], | ||
65 | [require [["variables", "fileinto"]]; | ||
66 | set "name" :quotewildcard ".a*strange?name\\!"; | ||
67 | fileinto "INBOX${name}"; | ||
68 | ], | ||
69 | [],[0],[], | ||
70 | [FILEINTO on msg uid 1: delivering into INBOX.a\*strange\?name\\! | ||
71 | FILEINTO on msg uid 2: delivering into INBOX.a\*strange\?name\\! | ||
72 | FILEINTO on msg uid 3: delivering into INBOX.a\*strange\?name\\! | ||
73 | ]) | ||
74 | |||
75 | MUT_TESTCASE([:length modifier],[variables action set], | ||
76 | [require [["variables", "fileinto"]]; | ||
77 | set "name" :length "value"; | ||
78 | fileinto "INBOX.${name}"; | ||
79 | ], | ||
80 | [],[0],[], | ||
81 | [FILEINTO on msg uid 1: delivering into INBOX.5 | ||
82 | FILEINTO on msg uid 2: delivering into INBOX.5 | ||
83 | FILEINTO on msg uid 3: delivering into INBOX.5 | ||
84 | ]) | ||
85 | |||
86 | MUT_TESTCASE([string test],[variables test string], | ||
87 | [require "variables"; | ||
88 | if address "To" :matches "*@*.*.*" | ||
89 | { | ||
90 | if string "${2}" [[ "landru", "ACME" ]] | ||
91 | { | ||
92 | discard; | ||
93 | } | ||
94 | }], | ||
95 | [],[0],[], | ||
96 | [DISCARD on msg uid 1: marking as deleted | ||
97 | DISCARD on msg uid 2: marking as deleted | ||
98 | IMPLICIT KEEP on msg uid 3 | ||
99 | ]) |
-
Please register or sign in to post a comment