Commit 79951953 799519539556b38fc9f84d44927861cb9239d14c by Sergey Poznyakoff

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.
1 parent cab2fd5c
...@@ -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
......
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
...@@ -69,6 +69,7 @@ TESTSUITE_AT = \ ...@@ -69,6 +69,7 @@ TESTSUITE_AT = \
69 true.at\ 69 true.at\
70 testsuite.at\ 70 testsuite.at\
71 vacation.at\ 71 vacation.at\
72 variables.at\
72 version.at 73 version.at
73 74
74 TESTSUITE = $(srcdir)/testsuite 75 TESTSUITE = $(srcdir)/testsuite
......
...@@ -149,3 +149,4 @@ m4_include([addheader.at]) ...@@ -149,3 +149,4 @@ m4_include([addheader.at])
149 m4_include([delheader.at]) 149 m4_include([delheader.at])
150 m4_include([vacation.at]) 150 m4_include([vacation.at])
151 151
152 m4_include([variables.at])
......
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 ])