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