1 /* Sieve variables extension (RFC 5229)
2 Copyright (C) 2016-2021 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 *
mod_lower(mu_sieve_machine_t mach,char const * value)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 *
mod_upper(mu_sieve_machine_t mach,char const * value)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 *
mod_lowerfirst(mu_sieve_machine_t mach,char const * value)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 *
mod_upperfirst(mu_sieve_machine_t mach,char const * value)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 *
mod_quotewildcard(mu_sieve_machine_t mach,char const * value)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 *
mod_length(mu_sieve_machine_t mach,char const * value)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 *
findprec(char const * name)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, please report"), __FILE__, __LINE__);
160 abort ();
161 }
162
163 static void
variable_set(mu_sieve_machine_t mach,char const * name,char * value)164 variable_set (mu_sieve_machine_t mach, char const *name, char *value)
165 {
166 struct sieve_variable *var, **vptr;
167 int rc;
168
169 rc = mu_assoc_install_ref (mach->vartab, name, &vptr);
170 switch (rc)
171 {
172 case 0:
173 var = malloc (sizeof (*var));
174 if (!var)
175 {
176 mu_sieve_error (mach, "variable_set: %s", mu_strerror (rc));
177 mu_sieve_abort (mach);
178 }
179 *vptr = var;
180 break;
181
182 case MU_ERR_EXISTS:
183 var = *vptr;
184 mu_sieve_free (mach, var->value);
185 break;
186
187 default:
188 mu_sieve_error (mach, "variable_set: %s", mu_strerror (rc));
189 mu_sieve_abort (mach);
190 }
191 var->value = value;
192 }
193
194 static int
sieve_action_set(mu_sieve_machine_t mach)195 sieve_action_set (mu_sieve_machine_t mach)
196 {
197 size_t i;
198 char *name;
199 char *value;
200
201 mu_sieve_get_arg (mach, 0, SVT_STRING, &name);
202 mu_sieve_get_arg (mach, 1, SVT_STRING, &value);
203
204 value = mu_sieve_strdup (mach, value);
205 for (i = 0; i < mach->tagcount; i++)
206 {
207 mu_sieve_value_t *p = mu_sieve_get_tag_n (mach, i);
208 char *str = findprec (p->tag)->modify (mach, value);
209 mu_sieve_free (mach, value);
210 value = str;
211 }
212
213 variable_set (mach, name, value);
214 return 0;
215 }
216
217 static int
varini_append(mu_sieve_machine_t mach,struct mu_sieve_variable_initializer * vini)218 varini_append (mu_sieve_machine_t mach,
219 struct mu_sieve_variable_initializer *vini)
220 {
221 if (!mu_sieve_has_variables (mach))
222 return EINVAL;
223 if (!mach->init_var)
224 {
225 mu_list_create (&mach->init_var);
226 mu_list_set_destroy_item (mach->init_var, mu_list_free_item);
227 }
228 return mu_list_append (mach->init_var, vini);
229 }
230
231 static struct mu_sieve_variable_initializer *
varini_alloc(const char * name,const char * value)232 varini_alloc (const char *name, const char *value)
233 {
234 struct mu_sieve_variable_initializer *vini;
235 size_t namelen;
236
237 namelen = strlen (name);
238 vini = malloc (sizeof (*vini) + namelen + strlen (value) + 2);
239 if (vini)
240 {
241 char *p = (char*) (vini + 1);
242 vini->name = p;
243 vini->value = p + namelen + 1;
244 strcpy (vini->name, name);
245 strcpy (vini->value, value);
246 }
247 return vini;
248 }
249
250 int
mu_sieve_variable_initialize(mu_sieve_machine_t mach,char const * name,char const * value)251 mu_sieve_variable_initialize (mu_sieve_machine_t mach, char const *name,
252 char const *value)
253 {
254 struct mu_sieve_variable_initializer *vini;
255 int rc;
256
257 if (!name || !value)
258 return EINVAL;
259 if (!mu_sieve_has_variables (mach))
260 return EINVAL;
261 vini = varini_alloc (name, value);
262 if (!vini)
263 return ENOMEM;
264 rc = varini_append (mach, vini);
265 if (rc)
266 free (vini);
267 return rc;
268 }
269
270 static int
set_tag_checker(mu_sieve_machine_t mach)271 set_tag_checker (mu_sieve_machine_t mach)
272 {
273 int i, j;
274
275 /* Sort tags by decreasing priority value (RFC 5229, 4.1) */
276 for (i = 1; i < mach->tagcount; i++)
277 {
278 mu_sieve_value_t tmp = *mu_sieve_get_tag_n (mach, i);
279 int tmp_prec = findprec (tmp.tag)->prec;
280
281 for (j = i - 1; j >= 0; j--)
282 {
283 mu_sieve_value_t *t = mu_sieve_get_tag_n (mach, j);
284 int prec = findprec (t->tag)->prec;
285 if (prec < tmp_prec)
286 *mu_sieve_get_tag_n (mach, j + 1) = *t;
287 else if (prec == tmp_prec)
288 {
289 mu_diag_at_locus_range (MU_LOG_ERROR, &t->locus,
290 _("%s and %s can't be used together"),
291 tmp.tag, t->tag);
292 mu_diag_at_locus_range (MU_LOG_ERROR, &tmp.locus,
293 _("%s encountered here"),
294 tmp.tag);
295 mu_i_sv_error (mach);
296 return 1;
297 }
298 else
299 break;
300 }
301 *mu_sieve_get_tag_n (mach, j + 1) = tmp;
302 }
303 return 0;
304 }
305
306 static mu_sieve_tag_group_t set_tag_groups[] = {
307 { set_tags, set_tag_checker },
308 { NULL }
309 };
310
311 static mu_sieve_data_type set_args[] = {
312 SVT_STRING,
313 SVT_STRING,
314 SVT_VOID
315 };
316
317 static int
retrieve_string(void * item,void * data,size_t idx,char ** pval)318 retrieve_string (void *item, void *data, size_t idx, char **pval)
319 {
320 if (idx)
321 return MU_ERR_NOENT;
322 *pval = strdup ((char*)item);
323 if (!*pval)
324 return errno;
325 return 0;
326 }
327
328 int
fold_string(void * item,void * data,void * prev,void ** ret)329 fold_string (void *item, void *data, void *prev, void **ret)
330 {
331 char *str = item;
332 size_t count = *(size_t*) prev;
333
334 /* The "relational" extension [RELATIONAL] adds a match type called
335 ":count". The count of a single string is 0 if it is the empty
336 string, or 1 otherwise. The count of a string list is the sum of the
337 counts of the member strings.
338 */
339 if (*str)
340 ++count;
341 *(size_t*)ret = count;
342 return 0;
343 }
344
345 /* RFC 5229, 5. Test string */
346 int
sieve_test_string(mu_sieve_machine_t mach)347 sieve_test_string (mu_sieve_machine_t mach)
348 {
349 mu_sieve_value_t *source, *key_list;
350
351 source = mu_sieve_get_arg_untyped (mach, 0);
352 key_list = mu_sieve_get_arg_untyped (mach, 1);
353
354 return mu_sieve_vlist_compare (mach, source, key_list,
355 retrieve_string, fold_string, mach);
356 }
357
358 static mu_sieve_data_type string_args[] = {
359 SVT_STRING_LIST,
360 SVT_STRING_LIST,
361 SVT_VOID
362 };
363
364 static mu_sieve_tag_group_t string_tag_groups[] = {
365 { mu_sieve_match_part_tags, mu_sieve_match_part_checker },
366 { NULL }
367 };
368
369 int
mu_i_sv_expand_variables(char const * input,size_t len,char ** exp,void * data)370 mu_i_sv_expand_variables (char const *input, size_t len,
371 char **exp, void *data)
372 {
373 mu_sieve_machine_t mach = data;
374 if (mu_isdigit (*input))
375 {
376 char *p;
377 size_t idx = 0;
378
379 while (len)
380 {
381 if (mu_isdigit (*input))
382 {
383 int d = *input - '0';
384 idx = idx * 10 + d;
385 input++;
386 len--;
387 }
388 else
389 return 1;
390 }
391
392 if (idx > mach->match_count)
393 {
394 *exp = NULL;
395 return 0;
396 }
397
398 len = mach->match_buf[idx].rm_eo - mach->match_buf[idx].rm_so;
399 p = malloc (len + 1);
400 if (!p)
401 {
402 mu_sieve_error (mach, "%s", mu_strerror (errno));
403 mu_sieve_abort (mach);
404 }
405 memcpy (p, mach->match_string + mach->match_buf[idx].rm_so, len);
406 p[len] = 0;
407 *exp = p;
408 }
409 else if (mu_isalpha (*input))
410 {
411 size_t i;
412 char *name;
413 struct sieve_variable *var;
414
415 for (i = 0; i < len; i++)
416 if (!(mu_isalnum (input[i]) || input[i] == '_'))
417 return 1;
418
419 name = malloc (len + 1);
420 if (!name)
421 {
422 mu_sieve_error (mach, "%s", mu_strerror (errno));
423 mu_sieve_abort (mach);
424 }
425 memcpy (name, input, len);
426 name[len] = 0;
427
428 var = mu_assoc_get (mach->vartab, name);
429
430 free (name);
431
432 if (var)
433 {
434 *exp = strdup (var->value);
435 if (!*exp)
436 {
437 mu_sieve_error (mach, "%s", mu_strerror (errno));
438 mu_sieve_abort (mach);
439 }
440 }
441 else
442 *exp = NULL;
443 }
444 else
445 return 1;
446 return 0;
447 }
448
449 int
mu_sieve_require_variables(mu_sieve_machine_t mach)450 mu_sieve_require_variables (mu_sieve_machine_t mach)
451 {
452 int rc;
453
454 if (mach->vartab)
455 return 0;
456
457 rc = mu_assoc_create (&mach->vartab, MU_ASSOC_ICASE);
458 if (rc)
459 mu_sieve_error (mach, "mu_assoc_create: %s", mu_strerror (rc));
460 mu_assoc_set_destroy_item (mach->vartab, mu_list_free_item);
461 if (rc == 0)
462 {
463 mu_sieve_register_action (mach, "set", sieve_action_set,
464 set_args, set_tag_groups, 1);
465 mu_sieve_register_test (mach, "string", sieve_test_string,
466 string_args, string_tag_groups, 1);
467 }
468 return rc;
469 }
470
471 int
mu_sieve_has_variables(mu_sieve_machine_t mach)472 mu_sieve_has_variables (mu_sieve_machine_t mach)
473 {
474 return mach->vartab != NULL;
475 }
476
477 static int
copy_init_var(void * item,void * data)478 copy_init_var (void *item, void *data)
479 {
480 struct mu_sieve_variable_initializer *vini = item, *vini_new;
481 mu_sieve_machine_t mach = data;
482 vini_new = varini_alloc (vini->name, vini->value);
483 if (!vini_new)
484 return ENOMEM;
485 return varini_append (mach, vini_new);
486 }
487
488 void
mu_i_sv_copy_variables(mu_sieve_machine_t child,mu_sieve_machine_t parent)489 mu_i_sv_copy_variables (mu_sieve_machine_t child, mu_sieve_machine_t parent)
490 {
491 mu_iterator_t itr;
492 int rc;
493
494 mu_sieve_require_variables (child);
495
496 rc = mu_assoc_get_iterator (parent->vartab, &itr);
497 if (rc)
498 {
499 mu_sieve_error (child, "mu_assoc_get_iterator: %s", mu_strerror (rc));
500 mu_sieve_abort (child);
501 }
502
503 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
504 mu_iterator_next (itr))
505 {
506 const char *name;
507 struct sieve_variable const *val;
508 struct sieve_variable *newval;
509
510 mu_iterator_current_kv (itr, (const void **)&name, (void**)&val);
511 newval = malloc (sizeof (*newval));
512 if (!newval)
513 mu_sieve_abort (child);
514 newval->value = mu_sieve_strdup (child, val->value);
515 mu_assoc_install (child->vartab, name, newval);
516 }
517
518 mu_iterator_destroy (&itr);
519
520 rc = mu_list_foreach (parent->init_var, copy_init_var, child);
521 if (rc)
522 {
523 mu_sieve_error (child, "copy_init_var: %s", mu_strerror (rc));
524 mu_sieve_abort (child);
525 }
526 }
527
528 static int
sieve_setvar(void * item,void * data)529 sieve_setvar (void *item, void *data)
530 {
531 struct mu_sieve_variable_initializer *vini = item;
532 mu_sieve_machine_t mach = data;
533 variable_set (mach, vini->name, mu_sieve_strdup (mach, vini->value));
534 return 0;
535 }
536
537 void
mu_i_sv_init_variables(mu_sieve_machine_t mach)538 mu_i_sv_init_variables (mu_sieve_machine_t mach)
539 {
540 if (mu_sieve_has_variables (mach))
541 {
542 mu_assoc_clear (mach->vartab);
543 mu_list_foreach (mach->init_var, sieve_setvar, mach);
544 }
545 }
546
547
548
549
550