Taint: reject or log more tainted list metadata elements
[exim.git] / src / src / miscmods / sieve_filter.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /*
6 Copyright (c) The Exim Maintainers 2016 - 2024
7 Copyright (c) Michael Haardt 2003 - 2015
8 See the file NOTICE for conditions of use and distribution.
9 SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 /* This code was contributed by Michael Haardt. */
13
14
15 /* Sieve mail filter. */
16
17 #include <ctype.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "../exim.h"
24
25 #if HAVE_ICONV
26 # include <iconv.h>
27 #endif
28
29 /* Define this for RFC compliant \r\n end-of-line terminators.      */
30 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 #undef RFC_EOL
32
33 /* Define this for development of the Sieve extension "encoded-character". */
34 #define ENCODED_CHARACTER
35
36 /* Define this for development of the Sieve extension "envelope-auth". */
37 #undef ENVELOPE_AUTH
38
39 /* Define this for development of the Sieve extension "enotify".    */
40 #define ENOTIFY
41
42 /* Define this for the Sieve extension "subaddress".                */
43 #define SUBADDRESS
44
45 /* Define this for the Sieve extension "vacation".                  */
46 #define VACATION
47
48 /* Must be >= 1                                                     */
49 #define VACATION_MIN_DAYS 1
50 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30        */
51 #define VACATION_MAX_DAYS 31
52
53 /* Keep this at 75 to accept only RFC compliant MIME words.         */
54 /* Increase it if you want to match headers from buggy MUAs.        */
55 #define MIMEWORD_LENGTH 75
56
57 struct Sieve {
58   const uschar *filter;
59   const uschar *pc;
60   int   line;
61   const uschar *errmsg;
62   int   keep;
63   int   require_envelope;
64   int   require_fileinto;
65 #ifdef ENCODED_CHARACTER
66   BOOL  require_encoded_character;
67 #endif
68 #ifdef ENVELOPE_AUTH
69   int   require_envelope_auth;
70 #endif
71 #ifdef ENOTIFY
72   int   require_enotify;
73   struct Notification *notified;
74 #endif
75   const uschar *enotify_mailto_owner;
76 #ifdef SUBADDRESS
77   int   require_subaddress;
78 #endif
79 #ifdef VACATION
80   BOOL  require_vacation;
81   BOOL  vacation_ran;
82 #endif
83   const uschar *inbox;
84   const uschar *vacation_directory;
85   const uschar *subaddress;
86   const uschar *useraddress;
87   BOOL  require_copy;
88   BOOL  require_iascii_numeric;
89 };
90
91 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
92 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
93 #ifdef SUBADDRESS
94 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
95 #else
96 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
97 #endif
98 enum RelOp { LT, LE, EQ, GE, GT, NE };
99
100 struct Notification {
101   gstring method;
102   gstring importance;
103   gstring message;
104   struct Notification *next;
105 };
106
107 /* This should be a complete list of supported extensions, so that an external
108 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
109 list of extensions and provide correct information to a client.
110
111 We'll emit the list in the order given here; keep it alphabetically sorted, so
112 that callers don't get surprised.
113
114 List *MUST* end with a NULL.  Which at least makes ifdef-vs-comma easier. */
115
116 static const uschar *exim_sieve_extension_list[] = {
117   CUS"comparator-i;ascii-numeric",
118   CUS"copy",
119 #ifdef ENCODED_CHARACTER
120   CUS"encoded-character",
121 #endif
122 #ifdef ENOTIFY
123   CUS"enotify",
124 #endif
125   CUS"envelope",
126 #ifdef ENVELOPE_AUTH
127   CUS"envelope-auth",
128 #endif
129   CUS"fileinto",
130 #ifdef SUBADDRESS
131   CUS"subaddress",
132 #endif
133 #ifdef VACATION
134   CUS"vacation",
135 #endif
136   NULL
137 };
138
139 static int eq_asciicase(const gstring * needle, const gstring * haystack, BOOL match_prefix);
140 static int parse_test(struct Sieve *filter, int *cond, int exec);
141 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
142
143 static uschar str_from_c[] = "From";
144 static const gstring str_from = { .s = str_from_c, .ptr = 4, .size = 5 };
145 static uschar str_to_c[] = "To";
146 static const gstring str_to = { .s = str_to_c, .ptr = 2, .size = 3 };
147 static uschar str_cc_c[] = "Cc";
148 static const gstring str_cc = { .s = str_cc_c, .ptr = 2, .size = 3 };
149 static uschar str_bcc_c[] = "Bcc";
150 static const gstring str_bcc = { .s = str_bcc_c, .ptr = 3, .size = 4 };
151 #ifdef ENVELOPE_AUTH
152 static uschar str_auth_c[] = "auth";
153 static const gstring str_auth = { .s = str_auth_c, .ptr = 4, .size = 5 };
154 #endif
155 static uschar str_sender_c[] = "Sender";
156 static const gstring str_sender = { .s = str_sender_c, .ptr = 6, .size = 7 };
157 static uschar str_resent_from_c[] = "Resent-From";
158 static const gstring str_resent_from = { .s = str_resent_from_c, .ptr = 11, .size = 12 };
159 static uschar str_resent_to_c[] = "Resent-To";
160 static const gstring str_resent_to = { .s = str_resent_to_c, .ptr = 9, .size = 10 };
161 static uschar str_fileinto_c[] = "fileinto";
162 static const gstring str_fileinto = { .s = str_fileinto_c, .ptr = 8, .size = 9 };
163 static uschar str_envelope_c[] = "envelope";
164 static const gstring str_envelope = { .s = str_envelope_c, .ptr = 8, .size = 9 };
165 #ifdef ENCODED_CHARACTER
166 static uschar str_encoded_character_c[] = "encoded-character";
167 static const gstring str_encoded_character = { .s = str_encoded_character_c, .ptr = 17, .size = 18 };
168 #endif
169 #ifdef ENVELOPE_AUTH
170 static uschar str_envelope_auth_c[] = "envelope-auth";
171 static const gstring str_envelope_auth = { .s = str_envelope_auth_c, .ptr = 13, .size = 14 };
172 #endif
173 #ifdef ENOTIFY
174 static uschar str_enotify_c[] = "enotify";
175 static const gstring str_enotify = { .s = str_enotify_c, .ptr = 7, .size = 8 };
176 static uschar str_online_c[] = "online";
177 static const gstring str_online = { .s = str_online_c, .ptr = 6, .size = 7 };
178 static uschar str_maybe_c[] = "maybe";
179 static const gstring str_maybe = { .s = str_maybe_c, .ptr = 5, .size = 6 };
180 static uschar str_auto_submitted_c[] = "Auto-Submitted";
181 static const gstring str_auto_submitted = { .s = str_auto_submitted_c, .ptr = 14, .size = 15 };
182 #endif
183 #ifdef SUBADDRESS
184 static uschar str_subaddress_c[] = "subaddress";
185 static const gstring str_subaddress = { .s = str_subaddress_c, .ptr = 10, .size = 11 };
186 #endif
187 #ifdef VACATION
188 static uschar str_vacation_c[] = "vacation";
189 static const gstring str_vacation = { .s = str_vacation_c, .ptr = 8, .size = 9 };
190 static uschar str_subject_c[] = "Subject";
191 static const gstring str_subject = { .s = str_subject_c, .ptr = 7, .size = 8 };
192 #endif
193 static uschar str_copy_c[] = "copy";
194 static const gstring str_copy = { .s = str_copy_c, .ptr = 4, .size = 5 };
195 static uschar str_iascii_casemap_c[] = "i;ascii-casemap";
196 static const gstring str_iascii_casemap = { .s = str_iascii_casemap_c, .ptr = 15, .size = 16 };
197 static uschar str_enascii_casemap_c[] = "en;ascii-casemap";
198 static const gstring str_enascii_casemap = { .s = str_enascii_casemap_c, .ptr = 16, .size = 17 };
199 static uschar str_ioctet_c[] = "i;octet";
200 static const gstring str_ioctet = { .s = str_ioctet_c, .ptr = 7, .size = 8 };
201 static uschar str_iascii_numeric_c[] = "i;ascii-numeric";
202 static const gstring str_iascii_numeric = { .s = str_iascii_numeric_c, .ptr = 15, .size = 16 };
203 static uschar str_comparator_iascii_casemap_c[] = "comparator-i;ascii-casemap";
204 static const gstring str_comparator_iascii_casemap = { .s = str_comparator_iascii_casemap_c, .ptr = 26, .size = 27 };
205 static uschar str_comparator_enascii_casemap_c[] = "comparator-en;ascii-casemap";
206 static const gstring str_comparator_enascii_casemap = { .s = str_comparator_enascii_casemap_c, .ptr = 27, .size = 28 };
207 static uschar str_comparator_ioctet_c[] = "comparator-i;octet";
208 static const gstring str_comparator_ioctet = { .s = str_comparator_ioctet_c, .ptr = 18, .size = 19 };
209 static uschar str_comparator_iascii_numeric_c[] = "comparator-i;ascii-numeric";
210 static const gstring str_comparator_iascii_numeric = { .s = str_comparator_iascii_numeric_c, .ptr = 26, .size = 27 };
211
212
213 /*************************************************
214 *          Encode to quoted-printable            *
215 *************************************************/
216
217 /*
218 Arguments:
219   src               UTF-8 string
220
221 Returns
222   dst, allocated, a US-ASCII string
223 */
224
225 static gstring *
226 quoted_printable_encode(const gstring * src)
227 {
228 gstring * dst = NULL;
229 uschar ch;
230 size_t line = 0;
231
232 for (const uschar * start = src->s, * end = start + src->ptr;
233      start < end; ++start)
234   {
235   ch = *start;
236   if (line >= 73)       /* line length limit */
237     {
238     dst = string_catn(dst, US"=\n", 2); /* line split */
239     line = 0;
240     }
241   if (  (ch >= '!' && ch <= '<')
242      || (ch >= '>' && ch <= '~')
243      || (  (ch == '\t' || ch == ' ')
244         && start+2 < end && (start[1] != '\r' || start[2] != '\n')      /* CRLF */
245         )
246      )
247     {
248     dst = string_catn(dst, start, 1);           /* copy char */
249     ++line;
250     }
251   else if (ch == '\r' && start+1 < end && start[1] == '\n')             /* CRLF */
252     {
253     dst = string_catn(dst, US"\n", 1);          /* NL */
254     line = 0;
255     ++start;    /* consume extra input char */
256     }
257   else
258     {
259     dst = string_fmt_append(dst, "=%02X", ch);
260     line += 3;
261     }
262   }
263
264 (void) string_from_gstring(dst);
265 gstring_release_unused(dst);
266 return dst;
267 }
268
269
270 /*************************************************
271 *     Check mail address for correct syntax      *
272 *************************************************/
273
274 /*
275 Check mail address for being syntactically correct.
276
277 Arguments:
278   filter      points to the Sieve filter including its state
279   address     String containing one address
280
281 Returns
282   1           Mail address is syntactically OK
283  -1           syntax error
284 */
285
286 static int
287 check_mail_address(struct Sieve * filter, const gstring * address)
288 {
289 int start, end, domain;
290 uschar * error, * ss;
291
292 if (address->ptr > 0)
293   {
294   ss = parse_extract_address(address->s, &error, &start, &end, &domain,
295     FALSE);
296   if (!ss)
297     {
298     filter->errmsg = string_sprintf("malformed address \"%s\" (%s)",
299       address->s, error);
300     return -1;
301     }
302   else
303     return 1;
304   }
305 else
306   {
307   filter->errmsg = CUS "empty address";
308   return -1;
309   }
310 }
311
312
313 /*************************************************
314 *          Decode URI encoded string             *
315 *************************************************/
316
317 /*
318 Arguments:
319   str               URI encoded string
320
321 Returns
322   str is modified in place
323   TRUE              Decoding successful
324   FALSE             Encoding error
325 */
326
327 #ifdef ENOTIFY
328 static BOOL
329 uri_decode(gstring * str)
330 {
331 uschar *s, *t, *e;
332
333 if (str->ptr == 0) return TRUE;
334 for (t = s = str->s, e = s + str->ptr; s < e; )
335   if (*s == '%')
336     {
337     if (s+2 < e && isxdigit(s[1]) && isxdigit(s[2]))
338       {
339       *t++ = ((isdigit(s[1]) ? s[1]-'0' : tolower(s[1])-'a'+10)<<4)
340             | (isdigit(s[2]) ? s[2]-'0' : tolower(s[2])-'a'+10);
341       s += 3;
342       }
343     else return FALSE;
344     }
345   else
346     *t++ = *s++;
347
348 *t = '\0';
349 str->ptr = t - str->s;
350 return TRUE;
351 }
352
353
354 /*************************************************
355 *               Parse mailto URI                 *
356 *************************************************/
357
358 /*
359 Parse mailto-URI.
360
361        mailtoURI   = "mailto:" [ to ] [ headers ]
362        to          = [ addr-spec *("%2C" addr-spec ) ]
363        headers     = "?" header *( "&" header )
364        header      = hname " = " hvalue
365        hname       = *urlc
366        hvalue      = *urlc
367
368 Arguments:
369   filter      points to the Sieve filter including its state
370   uri         URI, excluding scheme
371   recipient   list of recipients; prepnded to
372   body
373
374 Returns
375   1           URI is syntactically OK
376   0           Unknown URI scheme
377  -1           syntax error
378 */
379
380 static int
381 parse_mailto_uri(struct Sieve * filter, const uschar * uri,
382   string_item ** recipient, gstring * header, gstring * subject,
383   gstring * body)
384 {
385 const uschar * start;
386
387 if (Ustrncmp(uri, "mailto:", 7))
388   {
389   filter->errmsg = US "Unknown URI scheme";
390   return 0;
391   }
392
393 uri += 7;
394 if (*uri && *uri != '?')
395   for (;;)
396     {
397     /* match to */
398     for (start = uri; *uri && *uri != '?' && (*uri != '%' || uri[1] != '2' || tolower(uri[2]) != 'c'); ++uri);
399     if (uri > start)
400       {
401       gstring * to = string_catn(NULL, start, uri - start);
402       string_item * new;
403
404       if (!uri_decode(to))
405         {
406         filter->errmsg = US"Invalid URI encoding";
407         return -1;
408         }
409       new = store_get(sizeof(string_item), GET_UNTAINTED);
410       new->text = string_from_gstring(to);
411       new->next = *recipient;
412       *recipient = new;
413       }
414     else
415       {
416       filter->errmsg = US"Missing addr-spec in URI";
417       return -1;
418       }
419     if (*uri == '%') uri += 3;
420     else break;
421     }
422 if (*uri == '?')
423   for (uri++; ;)
424     {
425     gstring * hname = string_get(0), * hvalue = NULL;
426
427     /* match hname */
428     for (start = uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(), %", *uri)); ++uri) ;
429     if (uri > start)
430       {
431       hname = string_catn(hname, start, uri-start);
432
433       if (!uri_decode(hname))
434         {
435         filter->errmsg = US"Invalid URI encoding";
436         return -1;
437         }
438       }
439     /* match = */
440     if (*uri++ != '=')
441       {
442       filter->errmsg = US"Missing equal after hname";
443       return -1;
444       }
445
446     /* match hvalue */
447     for (start = uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(), %", *uri)); ++uri) ;
448     if (uri > start)
449       {
450       hvalue = string_catn(NULL, start, uri-start);     /*XXX this used to say "hname =" */
451
452       if (!uri_decode(hvalue))
453         {
454         filter->errmsg = US"Invalid URI encoding";
455         return -1;
456         }
457       }
458     if (hname->ptr == 2 && strcmpic(hname->s, US"to") == 0)
459       {
460       string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
461       new->text = string_from_gstring(hvalue);
462       new->next = *recipient;
463       *recipient = new;
464       }
465     else if (hname->ptr == 4 && strcmpic(hname->s, US"body") == 0)
466       *body = *hvalue;
467     else if (hname->ptr == 7 && strcmpic(hname->s, US"subject") == 0)
468       *subject = *hvalue;
469     else
470       {
471       static gstring ignore[] =
472         {
473         {.s = US"date", .ptr = 4, .size = 5},
474         {.s = US"from", .ptr = 4, .size = 5},
475         {.s = US"message-id", .ptr = 10, .size = 11},
476         {.s = US"received", .ptr = 8, .size = 9},
477         {.s = US"auto-submitted", .ptr = 14, .size = 15}
478         };
479       static gstring * end = ignore + nelem(ignore);
480       gstring * i;
481
482       for (i = ignore; i < end && !eq_asciicase(hname, i,  FALSE); ++i);
483       if (i == end)
484         {
485         hname = string_fmt_append(NULL, "%Y%Y: %Y\n", header, hname, hvalue);
486         (void) string_from_gstring(hname);
487         /*XXX we seem to do nothing with this new hname? */
488         }
489       }
490     if (*uri == '&') ++uri;
491     else break;
492     }
493 if (*uri)
494   {
495   filter->errmsg = US"Syntactically invalid URI";
496   return -1;
497   }
498 return 1;
499 }
500 #endif
501
502
503 /*************************************************
504 *          Octet-wise string comparison          *
505 *************************************************/
506
507 /*
508 Arguments:
509   needle            UTF-8 string to search ...
510   haystack          ... inside the haystack
511   match_prefix      TRUE to compare if needle is a prefix of haystack
512
513 Returns:      0               needle not found in haystack
514               1               needle found
515 */
516
517 static int
518 eq_octet(const gstring *needle, const gstring *haystack, BOOL match_prefix)
519 {
520 size_t nl, hl;
521 const uschar *n, *h;
522
523 nl = needle->ptr;
524 n = needle->s;
525 hl = haystack->ptr;
526 h = haystack->s;
527 while (nl>0 && hl>0)
528   {
529 #if !HAVE_ICONV
530   if (*n & 0x80) return 0;
531   if (*h & 0x80) return 0;
532 #endif
533   if (*n != *h) return 0;
534   ++n;
535   ++h;
536   --nl;
537   --hl;
538   }
539 return (match_prefix ? nl == 0 : nl == 0 && hl == 0);
540 }
541
542
543 /*************************************************
544 *    ASCII case-insensitive string comparison    *
545 *************************************************/
546
547 /*
548 Arguments:
549   needle            UTF-8 string to search ...
550   haystack          ... inside the haystack
551   match_prefix      TRUE to compare if needle is a prefix of haystack
552
553 Returns:      0               needle not found in haystack
554               1               needle found
555 */
556
557 static int
558 eq_asciicase(const gstring *needle, const gstring *haystack, BOOL match_prefix)
559 {
560 size_t nl, hl;
561 const uschar *n, *h;
562 uschar nc, hc;
563
564 nl = needle->ptr;
565 n = needle->s;
566 hl = haystack->ptr;
567 h = haystack->s;
568 while (nl > 0 && hl > 0)
569   {
570   nc = *n;
571   hc = *h;
572 #if !HAVE_ICONV
573   if (nc & 0x80) return 0;
574   if (hc & 0x80) return 0;
575 #endif
576   /* tolower depends on the locale and only ASCII case must be insensitive */
577   if ((nc >= 'A' && nc <= 'Z' ? nc | 0x20 : nc) != (hc >= 'A' && hc <= 'Z' ? hc | 0x20 : hc)) return 0;
578   ++n;
579   ++h;
580   --nl;
581   --hl;
582   }
583 return (match_prefix ? nl == 0 : nl == 0 && hl == 0);
584 }
585
586
587 /*************************************************
588 *              Glob pattern search               *
589 *************************************************/
590
591 /*
592 Arguments:
593   needle          pattern to search ...
594   haystack        ... inside the haystack
595   ascii_caseless  ignore ASCII case
596   match_octet     match octets, not UTF-8 multi-octet characters
597
598 Returns:      0               needle not found in haystack
599               1               needle found
600               -1              pattern error
601 */
602
603 static int
604 eq_glob(const gstring *needle,
605   const gstring *haystack, BOOL ascii_caseless, BOOL match_octet)
606 {
607 const uschar *n, *h, *nend, *hend;
608 int may_advance = 0;
609
610 n = needle->s;
611 h = haystack->s;
612 nend = n+needle->ptr;
613 hend = h+haystack->ptr;
614 while (n < nend)
615   if (*n == '*')
616     {
617     ++n;
618     may_advance = 1;
619     }
620   else
621     {
622     const uschar *npart, *hpart;
623
624     /* Try to match a non-star part of the needle at the current */
625     /* position in the haystack.                                 */
626     match_part:
627     npart = n;
628     hpart = h;
629     while (npart<nend && *npart != '*') switch (*npart)
630       {
631       case '?':
632         {
633         if (hpart == hend) return 0;
634         if (match_octet)
635           ++hpart;
636         else
637           {
638           /* Match one UTF8 encoded character */
639           if ((*hpart&0xc0) == 0xc0)
640             {
641             ++hpart;
642             while (hpart<hend && ((*hpart&0xc0) == 0x80)) ++hpart;
643             }
644           else
645             ++hpart;
646           }
647         ++npart;
648         break;
649         }
650       case '\\':
651         {
652         ++npart;
653         if (npart == nend) return -1;
654         /* FALLTHROUGH */
655         }
656       default:
657         {
658         if (hpart == hend) return 0;
659         /* tolower depends on the locale, but we need ASCII */
660         if
661           (
662 #if !HAVE_ICONV
663           (*hpart&0x80) || (*npart&0x80) ||
664 #endif
665           ascii_caseless
666           ? ((*npart>= 'A' && *npart<= 'Z' ? *npart|0x20 : *npart) != (*hpart>= 'A' && *hpart<= 'Z' ? *hpart|0x20 : *hpart))
667           : *hpart != *npart
668           )
669           {
670           if (may_advance)
671             /* string match after a star failed, advance and try again */
672             {
673             ++h;
674             goto match_part;
675             }
676           else return 0;
677           }
678         else
679           {
680           ++npart;
681           ++hpart;
682           };
683         }
684       }
685     /* at this point, a part was matched successfully */
686     if (may_advance && npart == nend && hpart<hend)
687       /* needle ends, but haystack does not: if there was a star before, advance and try again */
688       {
689       ++h;
690       goto match_part;
691       }
692     h = hpart;
693     n = npart;
694     may_advance = 0;
695     }
696 return (h == hend ? 1 : may_advance);
697 }
698
699
700 /*************************************************
701 *    ASCII numeric comparison                    *
702 *************************************************/
703
704 /*
705 Arguments:
706   a                 first numeric string
707   b                 second numeric string
708   relop             relational operator
709
710 Returns:      0               not (a relop b)
711               1               a relop b
712 */
713
714 static int
715 eq_asciinumeric(const gstring *a, const gstring *b, enum RelOp relop)
716 {
717 size_t al, bl;
718 const uschar *as, *aend, *bs, *bend;
719 int cmp;
720
721 as = a->s;
722 aend = a->s+a->ptr;
723 bs = b->s;
724 bend = b->s+b->ptr;
725
726 while (*as>= '0' && *as<= '9' && as<aend) ++as;
727 al = as-a->s;
728 while (*bs>= '0' && *bs<= '9' && bs<bend) ++bs;
729 bl = bs-b->s;
730
731 if (al && bl == 0) cmp = -1;
732 else if (al == 0 && bl == 0) cmp = 0;
733 else if (al == 0 && bl) cmp = 1;
734 else
735   {
736   cmp = al-bl;
737   if (cmp == 0) cmp = memcmp(a->s, b->s, al);
738   }
739 switch (relop)
740   {
741   case LT: return cmp < 0;
742   case LE: return cmp <= 0;
743   case EQ: return cmp == 0;
744   case GE: return cmp >= 0;
745   case GT: return cmp > 0;
746   case NE: return cmp != 0;
747   }
748   /*NOTREACHED*/
749   return -1;
750 }
751
752
753 /*************************************************
754 *             Compare strings                    *
755 *************************************************/
756
757 /*
758 Arguments:
759   filter      points to the Sieve filter including its state
760   needle      UTF-8 pattern or string to search ...
761   haystack    ... inside the haystack
762   co          comparator to use
763   mt          match type to use
764
765 Returns:      0               needle not found in haystack
766               1               needle found
767               -1              comparator does not offer matchtype
768 */
769
770 static int
771 compare(struct Sieve * filter, const gstring * needle, const gstring * haystack,
772   enum Comparator co, enum MatchType mt)
773 {
774 int r = 0;
775
776 if (   (filter_test != FTEST_NONE && debug_selector != 0)
777    || (debug_selector & D_filter) != 0)
778   {
779   debug_printf_indent("String comparison (match ");
780   switch (mt)
781     {
782     case MATCH_IS: debug_printf_indent(":is"); break;
783     case MATCH_CONTAINS: debug_printf_indent(":contains"); break;
784     case MATCH_MATCHES: debug_printf_indent(":matches"); break;
785     }
786   debug_printf_indent(", comparison \"");
787   switch (co)
788     {
789     case COMP_OCTET: debug_printf_indent("i;octet"); break;
790     case COMP_EN_ASCII_CASEMAP: debug_printf_indent("en;ascii-casemap"); break;
791     case COMP_ASCII_NUMERIC: debug_printf_indent("i;ascii-numeric"); break;
792     }
793   debug_printf_indent("\"):\n");
794   debug_printf_indent("  Search = %s (%d chars)\n", needle->s, needle->ptr);
795   debug_printf_indent("  Inside = %s (%d chars)\n", haystack->s, haystack->ptr);
796   }
797 switch (mt)
798   {
799   case MATCH_IS:
800     switch (co)
801       {
802       case COMP_OCTET:
803         if (eq_octet(needle, haystack, FALSE)) r = 1;
804         break;
805       case COMP_EN_ASCII_CASEMAP:
806         if (eq_asciicase(needle, haystack, FALSE)) r = 1;
807         break;
808       case COMP_ASCII_NUMERIC:
809         if (!filter->require_iascii_numeric)
810           {
811           filter->errmsg = CUS "missing previous require \"comparator-i;ascii-numeric\";";
812           return -1;
813           }
814         if (eq_asciinumeric(needle, haystack, EQ)) r = 1;
815         break;
816       }
817     break;
818
819   case MATCH_CONTAINS:
820     {
821     gstring h;
822
823     switch (co)
824       {
825       case COMP_OCTET:
826         for (h = *haystack; h.ptr; ++h.s, --h.ptr)
827          if (eq_octet(needle, &h, TRUE)) { r = 1; break; }
828         break;
829       case COMP_EN_ASCII_CASEMAP:
830         for (h = *haystack; h.ptr; ++h.s, --h.ptr)
831           if (eq_asciicase(needle, &h, TRUE)) { r = 1; break; }
832         break;
833       default:
834         filter->errmsg = CUS "comparator does not offer specified matchtype";
835         return -1;
836       }
837     break;
838     }
839
840   case MATCH_MATCHES:
841     switch (co)
842       {
843       case COMP_OCTET:
844         if ((r = eq_glob(needle, haystack, FALSE, TRUE)) == -1)
845           {
846           filter->errmsg = CUS "syntactically invalid pattern";
847           return -1;
848           }
849         break;
850       case COMP_EN_ASCII_CASEMAP:
851         if ((r = eq_glob(needle, haystack, TRUE, TRUE)) == -1)
852           {
853           filter->errmsg = CUS "syntactically invalid pattern";
854           return -1;
855           }
856         break;
857       default:
858         filter->errmsg = CUS "comparator does not offer specified matchtype";
859         return -1;
860       }
861     break;
862   }
863 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
864   (debug_selector & D_filter) != 0)
865   debug_printf_indent("  Result %s\n", r?"true":"false");
866 return r;
867 }
868
869
870 /*************************************************
871 *         Check header field syntax              *
872 *************************************************/
873
874 /*
875 RFC 2822, section 3.6.8 says:
876
877   field-name      =       1*ftext
878
879   ftext           =       %d33-57 /               ; Any character except
880                           %d59-126                ;  controls, SP, and
881                                                   ;  ":".
882
883 That forbids 8-bit header fields.  This implementation accepts them, since
884 all of Exim is 8-bit clean, so it adds %d128-%d255.
885
886 Arguments:
887   header      header field to quote for suitable use in Exim expansions
888
889 Returns:      0               string is not a valid header field
890               1               string is a value header field
891 */
892
893 static int
894 is_header(const gstring *header)
895 {
896 size_t l;
897 const uschar *h;
898
899 l = header->ptr;
900 h = header->s;
901 if (l == 0) return 0;
902 while (l)
903   {
904   if (*h < 33 || *h == ':' || *h == 127)
905     return 0;
906   ++h;
907   --l;
908   }
909 return 1;
910 }
911
912
913 /*************************************************
914 *       Quote special characters string          *
915 *************************************************/
916
917 /*
918 Arguments:
919   header      header field to quote for suitable use in Exim expansions
920               or as debug output
921
922 Returns:      quoted string
923 */
924
925 static const uschar *
926 quote(const gstring * header)
927 {
928 gstring * quoted = NULL;
929 size_t l;
930 const uschar * h;
931
932 for (l = header->ptr, h = header->s; l; ++h, --l)
933   switch (*h)
934     {
935     case '\0':
936       quoted = string_catn(quoted, CUS "\\0", 2);
937       break;
938     case '$':
939     case '{':
940     case '}':
941       quoted = string_catn(quoted, CUS "\\", 1);
942     default:
943       quoted = string_catn(quoted, h, 1);
944     }
945
946 return string_from_gstring(quoted);
947 }
948
949
950 /*************************************************
951 *   Add address to list of generated addresses   *
952 *************************************************/
953
954 /*
955 According to RFC 5228, duplicate delivery to the same address must
956 not happen, so the list is first searched for the address.
957
958 Arguments:
959   generated   list of generated addresses
960   addr        new address to add
961   file        address denotes a file
962
963 Returns:      nothing
964 */
965
966 static void
967 add_addr(address_item ** generated, const uschar * addr, int file, int maxage,
968   int maxmessages, int maxstorage)
969 {
970 address_item * new_addr;
971
972 for (new_addr = *generated; new_addr; new_addr = new_addr->next)
973   if (  Ustrcmp(new_addr->address, addr) == 0
974      && (  !file
975         || testflag(new_addr, af_pfr)
976         || testflag(new_addr, af_file)
977         )
978      )
979     {
980     if (  filter_test != FTEST_NONE && debug_selector != 0
981        || (debug_selector & D_filter) != 0)
982       debug_printf_indent("Repeated %s `%s' ignored.\n",
983                           file ? "fileinto" : "redirect", addr);
984
985     return;
986     }
987
988 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
989   debug_printf_indent("%s `%s'\n", file ? "fileinto" : "redirect", addr);
990
991 new_addr = deliver_make_addr(addr, TRUE);
992 if (file)
993   {
994   setflag(new_addr, af_pfr);
995   setflag(new_addr, af_file);
996   new_addr->mode = 0;
997   }
998 new_addr->prop.errors_address = NULL;
999 new_addr->next = *generated;
1000 *generated = new_addr;
1001 }
1002
1003
1004 /*************************************************
1005 *         Return decoded header field            *
1006 *************************************************/
1007
1008 /*
1009 Unfold the header field as described in RFC 2822 and remove all
1010 leading and trailing white space, then perform MIME decoding and
1011 translate the header field to UTF-8.
1012
1013 Arguments:
1014   value       returned value of the field
1015   header      name of the header field
1016
1017 Returns:      nothing          The expanded string is empty
1018                                in case there is no such header
1019 */
1020
1021 static void
1022 expand_header(gstring * value, const gstring * header)
1023 {
1024 uschar *s, *r, *t;
1025 uschar *errmsg;
1026
1027 value->ptr = 0;
1028 value->s = (uschar*)0;
1029
1030 t = r = s = expand_string(string_sprintf("$rheader_%s", quote(header)));
1031 if (!t) return;
1032 while (*r == ' ' || *r == '\t') ++r;
1033 while (*r)
1034   if (*r == '\n')
1035     ++r;
1036   else
1037     *t++ = *r++;
1038
1039 while (t>s && (*(t-1) == ' ' || *(t-1) == '\t')) --t;
1040 *t = '\0';
1041 value->s = rfc2047_decode(s, check_rfc2047_length, US"utf-8", '\0', &value->ptr, &errmsg);
1042 }
1043
1044
1045 /*************************************************
1046 *        Parse remaining hash comment            *
1047 *************************************************/
1048
1049 /*
1050 Token definition:
1051   Comment up to terminating CRLF
1052
1053 Arguments:
1054   filter      points to the Sieve filter including its state
1055
1056 Returns:      1                success
1057               -1               syntax error
1058 */
1059
1060 static int
1061 parse_hashcomment(struct Sieve * filter)
1062 {
1063 ++filter->pc;
1064 while (*filter->pc)
1065   {
1066 #ifdef RFC_EOL
1067   if (*filter->pc == '\r' && (filter->pc)[1] == '\n')
1068 #else
1069   if (*filter->pc == '\n')
1070 #endif
1071     {
1072 #ifdef RFC_EOL
1073     filter->pc += 2;
1074 #else
1075     ++filter->pc;
1076 #endif
1077     ++filter->line;
1078     return 1;
1079     }
1080   else ++filter->pc;
1081   }
1082 filter->errmsg = CUS "missing end of comment";
1083 return -1;
1084 }
1085
1086
1087 /*************************************************
1088 *       Parse remaining C-style comment          *
1089 *************************************************/
1090
1091 /*
1092 Token definition:
1093   Everything up to star slash
1094
1095 Arguments:
1096   filter      points to the Sieve filter including its state
1097
1098 Returns:      1                success
1099               -1               syntax error
1100 */
1101
1102 static int
1103 parse_comment(struct Sieve *filter)
1104 {
1105 filter->pc += 2;
1106 while (*filter->pc)
1107   if (*filter->pc == '*' && (filter->pc)[1] == '/')
1108     {
1109     filter->pc +=  2;
1110     return 1;
1111     }
1112   else
1113     ++filter->pc;
1114
1115 filter->errmsg = CUS "missing end of comment";
1116 return -1;
1117 }
1118
1119
1120 /*************************************************
1121 *         Parse optional white space             *
1122 *************************************************/
1123
1124 /*
1125 Token definition:
1126   Spaces, tabs, CRLFs, hash comments or C-style comments
1127
1128 Arguments:
1129   filter      points to the Sieve filter including its state
1130
1131 Returns:      1                success
1132               -1               syntax error
1133 */
1134
1135 static int
1136 parse_white(struct Sieve *filter)
1137 {
1138 while (*filter->pc)
1139   {
1140   if (*filter->pc == ' ' || *filter->pc == '\t') ++filter->pc;
1141 #ifdef RFC_EOL
1142   else if (*filter->pc == '\r' && (filter->pc)[1] == '\n')
1143 #else
1144   else if (*filter->pc == '\n')
1145 #endif
1146     {
1147 #ifdef RFC_EOL
1148     filter->pc +=  2;
1149 #else
1150     ++filter->pc;
1151 #endif
1152     ++filter->line;
1153     }
1154   else if (*filter->pc == '#')
1155     {
1156     if (parse_hashcomment(filter) == -1) return -1;
1157     }
1158   else if (*filter->pc == '/' && (filter->pc)[1] == '*')
1159     {
1160     if (parse_comment(filter) == -1) return -1;
1161     }
1162   else break;
1163   }
1164 return 1;
1165 }
1166
1167
1168 #ifdef ENCODED_CHARACTER
1169 /*************************************************
1170 *      Decode hex-encoded-character string       *
1171 *************************************************/
1172
1173 /*
1174 Encoding definition:
1175    blank                = SP / TAB / CRLF
1176    hex-pair-seq         = *blank hex-pair *(1*blank hex-pair) *blank
1177    hex-pair             = 1*2HEXDIG
1178
1179 Arguments:
1180   src         points to a hex-pair-seq
1181   end         points to its end
1182   dst         points to the destination of the decoded octets,
1183               optionally to (uschar*)0 for checking only
1184
1185 Returns:      >= 0              number of decoded octets
1186               -1               syntax error
1187 */
1188
1189 static int
1190 hex_decode(uschar *src, uschar *end, uschar *dst)
1191 {
1192 int decoded = 0;
1193
1194 while (*src == ' ' || *src == '\t' || *src == '\n') ++src;
1195 do
1196   {
1197   int x, d, n;
1198
1199   for (x = 0, d = 0;
1200       d<2 && src<end && isxdigit(n = tolower(*src));
1201       x = (x<<4)|(n>= '0' && n<= '9' ? n-'0' : 10+(n-'a')) , ++d, ++src) ;
1202   if (d == 0) return -1;
1203   if (dst) *dst++ = x;
1204   ++decoded;
1205   if (src == end) return decoded;
1206   if (*src == ' ' || *src == '\t' || *src == '\n')
1207     while (*src == ' ' || *src == '\t' || *src == '\n') ++src;
1208   else
1209     return -1;
1210   }
1211 while (src < end);
1212 return decoded;
1213 }
1214
1215
1216 /*************************************************
1217 *    Decode unicode-encoded-character string     *
1218 *************************************************/
1219
1220 /*
1221 Encoding definition:
1222    blank                = SP / TAB / CRLF
1223    unicode-hex-seq      = *blank unicode-hex *(blank unicode-hex) *blank
1224    unicode-hex          = 1*HEXDIG
1225
1226    It is an error for a script to use a hexadecimal value that isn't in
1227    either the range 0 to D7FF or the range E000 to 10FFFF.
1228
1229    At this time, strings are already scanned, thus the CRLF is converted
1230    to the internally used \n (should RFC_EOL have been used).
1231
1232 Arguments:
1233   src         points to a unicode-hex-seq
1234   end         points to its end
1235   dst         points to the destination of the decoded octets,
1236               optionally to (uschar*)0 for checking only
1237
1238 Returns:      >= 0              number of decoded octets
1239               -1               syntax error
1240               -2               semantic error (character range violation)
1241 */
1242
1243 static int
1244 unicode_decode(uschar *src, uschar *end, uschar *dst)
1245 {
1246 int decoded = 0;
1247
1248 while (*src == ' ' || *src == '\t' || *src == '\n') ++src;
1249 do
1250   {
1251   uschar *hex_seq;
1252   int c, d, n;
1253
1254   unicode_hex:
1255   for (hex_seq = src; src < end && *src == '0'; ) src++;
1256   for (c = 0, d = 0;
1257        d < 7 && src < end && isxdigit(n = tolower(*src));
1258        c = (c<<4)|(n>= '0' && n<= '9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1259   if (src == hex_seq) return -1;
1260   if (d == 7 || (!((c >= 0 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0x10ffff)))) return -2;
1261   if (c<128)
1262     {
1263     if (dst) *dst++ = c;
1264     ++decoded;
1265     }
1266   else if (c>= 0x80 && c<= 0x7ff)
1267     {
1268       if (dst)
1269         {
1270         *dst++ = 192+(c>>6);
1271         *dst++ = 128+(c&0x3f);
1272         }
1273       decoded += 2;
1274     }
1275   else if (c>= 0x800 && c<= 0xffff)
1276     {
1277       if (dst)
1278         {
1279         *dst++ = 224+(c>>12);
1280         *dst++ = 128+((c>>6)&0x3f);
1281         *dst++ = 128+(c&0x3f);
1282         }
1283       decoded += 3;
1284     }
1285   else if (c>= 0x10000 && c<= 0x1fffff)
1286     {
1287       if (dst)
1288         {
1289         *dst++ = 240+(c>>18);
1290         *dst++ = 128+((c>>10)&0x3f);
1291         *dst++ = 128+((c>>6)&0x3f);
1292         *dst++ = 128+(c&0x3f);
1293         }
1294       decoded += 4;
1295     }
1296   if (*src == ' ' || *src == '\t' || *src == '\n')
1297     {
1298     while (*src == ' ' || *src == '\t' || *src == '\n') ++src;
1299     if (src == end) return decoded;
1300     goto unicode_hex;
1301     }
1302   }
1303 while (src < end);
1304 return decoded;
1305 }
1306
1307
1308 /*************************************************
1309 *       Decode encoded-character string          *
1310 *************************************************/
1311
1312 /*
1313 Encoding definition:
1314    encoded-arb-octets   = "${hex:" hex-pair-seq "}"
1315    encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1316
1317 Arguments:
1318   encoded     points to an encoded string, returns decoded string
1319   filter      points to the Sieve filter including its state
1320
1321 Returns:      1                success
1322               -1               syntax error
1323 */
1324
1325 static int
1326 string_decode(struct Sieve *filter, gstring *data)
1327 {
1328 uschar *src, *dst, *end;
1329
1330 src = data->s;
1331 dst = src;
1332 end = data->s+data->ptr;
1333 while (src < end)
1334   {
1335   uschar *brace;
1336
1337   if (
1338       strncmpic(src, US "${hex:", 6) == 0
1339       && (brace = Ustrchr(src+6, '}')) != (uschar*)0
1340       && (hex_decode(src+6, brace, (uschar*)0))>= 0
1341      )
1342     {
1343     dst += hex_decode(src+6, brace, dst);
1344     src = brace+1;
1345     }
1346   else if (
1347            strncmpic(src, US "${unicode:", 10) == 0
1348            && (brace = Ustrchr(src+10, '}')) != (uschar*)0
1349           )
1350     {
1351     switch (unicode_decode(src+10, brace, (uschar*)0))
1352       {
1353       case -2:
1354         {
1355         filter->errmsg = CUS "unicode character out of range";
1356         return -1;
1357         }
1358       case -1:
1359         {
1360         *dst++ = *src++;
1361         break;
1362         }
1363       default:
1364         {
1365         dst += unicode_decode(src+10, brace, dst);
1366         src = brace+1;
1367         }
1368       }
1369     }
1370   else *dst++ = *src++;
1371   }
1372   data->ptr = dst-data->s;
1373   *dst = '\0';
1374 return 1;
1375 }
1376 #endif
1377
1378
1379 /*************************************************
1380 *          Parse an optional string              *
1381 *************************************************/
1382
1383 /*
1384 Token definition:
1385    quoted-string = DQUOTE *CHAR DQUOTE
1386            ;; in general, \ CHAR inside a string maps to CHAR
1387            ;; so \" maps to " and \\ maps to \
1388            ;; note that newlines and other characters are all allowed
1389            ;; in strings
1390
1391    multi-line          = "text:" *(SP / HTAB) (hash-comment / CRLF)
1392                          *(multi-line-literal / multi-line-dotstuff)
1393                          "." CRLF
1394    multi-line-literal  = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1395    multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1396            ;; A line containing only "." ends the multi-line.
1397            ;; Remove a leading '.' if followed by another '.'.
1398   string           = quoted-string / multi-line
1399
1400 Arguments:
1401   filter      points to the Sieve filter including its state
1402   id          specifies identifier to match
1403
1404 Returns:      1                success
1405               -1               syntax error
1406               0                identifier not matched
1407 */
1408
1409 static int
1410 parse_string(struct Sieve *filter, gstring *data)
1411 {
1412 gstring * g = NULL;
1413
1414 data->ptr = 0;
1415 data->s = NULL;
1416
1417 if (*filter->pc == '"') /* quoted string */
1418   {
1419   ++filter->pc;
1420   while (*filter->pc)
1421     {
1422     if (*filter->pc == '"') /* end of string */
1423       {
1424       ++filter->pc;
1425
1426       if (g)
1427         data->ptr = len_string_from_gstring(g, &data->s);
1428       else
1429         data->s = US"\0";
1430       /* that way, there will be at least one character allocated */
1431
1432 #ifdef ENCODED_CHARACTER
1433       if (   filter->require_encoded_character
1434           && string_decode(filter, data) == -1)
1435         return -1;
1436 #endif
1437       return 1;
1438       }
1439     else if (*filter->pc == '\\' && (filter->pc)[1]) /* quoted character */
1440       {
1441       g = string_catn(g, filter->pc+1, 1);
1442       filter->pc +=  2;
1443       }
1444     else /* regular character */
1445       {
1446 #ifdef RFC_EOL
1447       if (*filter->pc == '\r' && (filter->pc)[1] == '\n') ++filter->line;
1448 #else
1449       if (*filter->pc == '\n')
1450         {
1451         g = string_catn(g, US"\r", 1);
1452         ++filter->line;
1453         }
1454 #endif
1455       g = string_catn(g, filter->pc, 1);
1456       filter->pc++;
1457       }
1458     }
1459   filter->errmsg = CUS "missing end of string";
1460   return -1;
1461   }
1462 else if (Ustrncmp(filter->pc, CUS "text:", 5) == 0) /* multiline string */
1463   {
1464   filter->pc +=  5;
1465   /* skip optional white space followed by hashed comment or CRLF */
1466   while (*filter->pc == ' ' || *filter->pc == '\t') ++filter->pc;
1467   if (*filter->pc == '#')
1468     {
1469     if (parse_hashcomment(filter) == -1) return -1;
1470     }
1471 #ifdef RFC_EOL
1472   else if (*filter->pc == '\r' && (filter->pc)[1] == '\n')
1473 #else
1474   else if (*filter->pc == '\n')
1475 #endif
1476     {
1477 #ifdef RFC_EOL
1478     filter->pc +=  2;
1479 #else
1480     ++filter->pc;
1481 #endif
1482     ++filter->line;
1483     }
1484   else
1485     {
1486     filter->errmsg = CUS "syntax error";
1487     return -1;
1488     }
1489   while (*filter->pc)
1490     {
1491 #ifdef RFC_EOL
1492     if (*filter->pc == '\r' && (filter->pc)[1] == '\n') /* end of line */
1493 #else
1494     if (*filter->pc == '\n') /* end of line */
1495 #endif
1496       {
1497       g = string_catn(g, CUS "\r\n", 2);
1498 #ifdef RFC_EOL
1499       filter->pc +=  2;
1500 #else
1501       ++filter->pc;
1502 #endif
1503       ++filter->line;
1504 #ifdef RFC_EOL
1505       if (*filter->pc == '.' && (filter->pc)[1] == '\r' && (filter->pc)[2] == '\n') /* end of string */
1506 #else
1507       if (*filter->pc == '.' && (filter->pc)[1] == '\n') /* end of string */
1508 #endif
1509         {
1510         if (g)
1511           data->ptr = len_string_from_gstring(g, &data->s);
1512         else
1513           data->s = US"\0";
1514         /* that way, there will be at least one character allocated */
1515
1516 #ifdef RFC_EOL
1517         filter->pc +=  3;
1518 #else
1519         filter->pc +=  2;
1520 #endif
1521         ++filter->line;
1522 #ifdef ENCODED_CHARACTER
1523         if (   filter->require_encoded_character
1524             && string_decode(filter, data) == -1)
1525           return -1;
1526 #endif
1527         return 1;
1528         }
1529       else if (*filter->pc == '.' && (filter->pc)[1] == '.') /* remove dot stuffing */
1530         {
1531         g = string_catn(g, CUS ".", 1);
1532         filter->pc +=  2;
1533         }
1534       }
1535     else /* regular character */
1536       {
1537       g = string_catn(g, filter->pc, 1);
1538       filter->pc++;
1539       }
1540     }
1541   filter->errmsg = CUS "missing end of multi line string";
1542   return -1;
1543   }
1544 else return 0;
1545 }
1546
1547
1548 /*************************************************
1549 *          Parse a specific identifier           *
1550 *************************************************/
1551
1552 /*
1553 Token definition:
1554   identifier       = (ALPHA / "_") *(ALPHA DIGIT "_")
1555
1556 Arguments:
1557   filter      points to the Sieve filter including its state
1558   id          specifies identifier to match
1559
1560 Returns:      1                success
1561               0                identifier not matched
1562 */
1563
1564 static int
1565 parse_identifier(struct Sieve *filter, const uschar *id)
1566 {
1567 size_t idlen = Ustrlen(id);
1568
1569 if (strncmpic(US filter->pc, US id, idlen) == 0)
1570   {
1571   uschar next = filter->pc[idlen];
1572
1573   if ((next>= 'A' && next<= 'Z') || (next>= 'a' && next<= 'z') || next == '_' || (next>= '0' && next<= '9')) return 0;
1574   filter->pc += idlen;
1575   return 1;
1576   }
1577 else return 0;
1578 }
1579
1580
1581 /*************************************************
1582 *                 Parse a number                 *
1583 *************************************************/
1584
1585 /*
1586 Token definition:
1587   number           = 1*DIGIT [QUANTIFIER]
1588   QUANTIFIER       = "K" / "M" / "G"
1589
1590 Arguments:
1591   filter      points to the Sieve filter including its state
1592   data        returns value
1593
1594 Returns:      1                success
1595               -1               no string list found
1596 */
1597
1598 static int
1599 parse_number(struct Sieve *filter, unsigned long *data)
1600 {
1601 unsigned long d, u;
1602
1603 if (*filter->pc>= '0' && *filter->pc<= '9')
1604   {
1605   uschar *e;
1606
1607   errno = 0;
1608   d = Ustrtoul(filter->pc, &e, 10);
1609   if (errno == ERANGE)
1610     {
1611     filter->errmsg = CUstrerror(ERANGE);
1612     return -1;
1613     }
1614   filter->pc = e;
1615   u = 1;
1616   if (*filter->pc == 'K') { u = 1024; ++filter->pc; }
1617   else if (*filter->pc == 'M') { u = 1024*1024; ++filter->pc; }
1618   else if (*filter->pc == 'G') { u = 1024*1024*1024; ++filter->pc; }
1619   if (d>(ULONG_MAX/u))
1620     {
1621     filter->errmsg = CUstrerror(ERANGE);
1622     return -1;
1623     }
1624   d *= u;
1625   *data = d;
1626   return 1;
1627   }
1628 else
1629   {
1630   filter->errmsg = CUS "missing number";
1631   return -1;
1632   }
1633 }
1634
1635
1636 /*************************************************
1637 *              Parse a string list               *
1638 *************************************************/
1639
1640 /*
1641 Grammar:
1642   string-list      = "[" string *(", " string) "]" / string
1643
1644 Arguments:
1645   filter      points to the Sieve filter including its state
1646   data        returns string list
1647
1648 Returns:      1                success
1649               -1               no string list found
1650 */
1651
1652 static int
1653 parse_stringlist(struct Sieve *filter, gstring **data)
1654 {
1655 const uschar *orig = filter->pc;
1656 int dataCapacity = 0;
1657 int dataLength = 0;
1658 gstring *d = NULL;
1659 int m;
1660
1661 if (*filter->pc == '[') /* string list */
1662   {
1663   ++filter->pc;
1664   for (;;)
1665     {
1666     if (parse_white(filter) == -1) goto error;
1667     if (dataLength+1 >= dataCapacity) /* increase buffer */
1668       {
1669       gstring *new;
1670
1671       dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1672       new = store_get(sizeof(gstring) * dataCapacity, GET_UNTAINTED);
1673
1674       if (d) memcpy(new, d, sizeof(gstring)*dataLength);
1675       d = new;
1676       }
1677
1678     m = parse_string(filter, &d[dataLength]);
1679     if (m == 0)
1680       {
1681       if (dataLength == 0) break;
1682       else
1683         {
1684         filter->errmsg = CUS "missing string";
1685         goto error;
1686         }
1687       }
1688     else if (m == -1) goto error;
1689     else ++dataLength;
1690     if (parse_white(filter) == -1) goto error;
1691     if (*filter->pc == ',') ++filter->pc;
1692     else break;
1693     }
1694   if (*filter->pc == ']')
1695     {
1696     d[dataLength].s = (uschar*)0;
1697     d[dataLength].ptr = -1;
1698     ++filter->pc;
1699     *data = d;
1700     return 1;
1701     }
1702   else
1703     {
1704     filter->errmsg = CUS "missing closing bracket";
1705     goto error;
1706     }
1707   }
1708 else /* single string */
1709   {
1710   if (!(d = store_get(sizeof(gstring)*2, GET_UNTAINTED)))
1711     return -1;
1712
1713   m = parse_string(filter, &d[0]);
1714   if (m == -1)
1715     return -1;
1716
1717   else if (m == 0)
1718     {
1719     filter->pc = orig;
1720     return 0;
1721     }
1722   else
1723     {
1724     d[1].s = (uschar*)0;
1725     d[1].ptr = -1;
1726     *data = d;
1727     return 1;
1728     }
1729   }
1730 error:
1731 filter->errmsg = CUS "missing string list";
1732 return -1;
1733 }
1734
1735
1736 /*************************************************
1737 *    Parse an optional address part specifier    *
1738 *************************************************/
1739
1740 /*
1741 Grammar:
1742   address-part     =  ":localpart" / ":domain" / ":all"
1743   address-part     = / ":user" / ":detail"
1744
1745 Arguments:
1746   filter      points to the Sieve filter including its state
1747   a           returns address part specified
1748
1749 Returns:      1                success
1750               0                no comparator found
1751               -1               syntax error
1752 */
1753
1754 static int
1755 parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1756 {
1757 #ifdef SUBADDRESS
1758 if (parse_identifier(filter, CUS ":user") == 1)
1759   {
1760   if (!filter->require_subaddress)
1761     {
1762     filter->errmsg = CUS "missing previous require \"subaddress\";";
1763     return -1;
1764     }
1765   *a = ADDRPART_USER;
1766   return 1;
1767   }
1768 else if (parse_identifier(filter, CUS ":detail") == 1)
1769   {
1770   if (!filter->require_subaddress)
1771     {
1772     filter->errmsg = CUS "missing previous require \"subaddress\";";
1773     return -1;
1774     }
1775   *a = ADDRPART_DETAIL;
1776   return 1;
1777   }
1778 else
1779 #endif
1780 if (parse_identifier(filter, CUS ":localpart") == 1)
1781   {
1782   *a = ADDRPART_LOCALPART;
1783   return 1;
1784   }
1785 else if (parse_identifier(filter, CUS ":domain") == 1)
1786   {
1787   *a = ADDRPART_DOMAIN;
1788   return 1;
1789   }
1790 else if (parse_identifier(filter, CUS ":all") == 1)
1791   {
1792   *a = ADDRPART_ALL;
1793   return 1;
1794   }
1795 else return 0;
1796 }
1797
1798
1799 /*************************************************
1800 *         Parse an optional comparator           *
1801 *************************************************/
1802
1803 /*
1804 Grammar:
1805   comparator = ":comparator" <comparator-name: string>
1806
1807 Arguments:
1808   filter      points to the Sieve filter including its state
1809   c           returns comparator
1810
1811 Returns:      1                success
1812               0                no comparator found
1813               -1               incomplete comparator found
1814 */
1815
1816 static int
1817 parse_comparator(struct Sieve *filter, enum Comparator *c)
1818 {
1819 gstring comparator_name;
1820
1821 if (parse_identifier(filter, CUS ":comparator") == 0) return 0;
1822 if (parse_white(filter) == -1) return -1;
1823 switch (parse_string(filter, &comparator_name))
1824   {
1825   case -1: return -1;
1826   case 0:
1827     {
1828     filter->errmsg = CUS "missing comparator";
1829     return -1;
1830     }
1831   default:
1832     {
1833     int match;
1834
1835     if (eq_asciicase(&comparator_name, &str_ioctet, FALSE))
1836       {
1837       *c = COMP_OCTET;
1838       match = 1;
1839       }
1840     else if (eq_asciicase(&comparator_name, &str_iascii_casemap, FALSE))
1841       {
1842       *c = COMP_EN_ASCII_CASEMAP;
1843       match = 1;
1844       }
1845     else if (eq_asciicase(&comparator_name, &str_enascii_casemap, FALSE))
1846       {
1847       *c = COMP_EN_ASCII_CASEMAP;
1848       match = 1;
1849       }
1850     else if (eq_asciicase(&comparator_name, &str_iascii_numeric, FALSE))
1851       {
1852       *c = COMP_ASCII_NUMERIC;
1853       match = 1;
1854       }
1855     else
1856       {
1857       filter->errmsg = CUS "invalid comparator";
1858       match = -1;
1859       }
1860     return match;
1861     }
1862   }
1863 }
1864
1865
1866 /*************************************************
1867 *          Parse an optional match type          *
1868 *************************************************/
1869
1870 /*
1871 Grammar:
1872   match-type = ":is" / ":contains" / ":matches"
1873
1874 Arguments:
1875   filter      points to the Sieve filter including its state
1876   m           returns match type
1877
1878 Returns:      1                success
1879               0                no match type found
1880 */
1881
1882 static int
1883 parse_matchtype(struct Sieve *filter, enum MatchType *m)
1884 {
1885 if (parse_identifier(filter, CUS ":is") == 1)
1886   { *m = MATCH_IS; return 1; }
1887 else if (parse_identifier(filter, CUS ":contains") == 1)
1888   { *m = MATCH_CONTAINS; return 1; }
1889 else if (parse_identifier(filter, CUS ":matches") == 1)
1890   { *m = MATCH_MATCHES; return 1; }
1891 else return 0;
1892 }
1893
1894
1895 /*************************************************
1896 *   Parse and interpret an optional test list    *
1897 *************************************************/
1898
1899 /*
1900 Grammar:
1901   test-list = "(" test *("," test) ")"
1902
1903 Arguments:
1904   filter      points to the Sieve filter including its state
1905   n           total number of tests
1906   num_true    number of passed tests
1907   exec        Execute parsed statements
1908
1909 Returns:      1                success
1910               0                no test list found
1911               -1               syntax or execution error
1912 */
1913
1914 static int
1915 parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1916 {
1917 if (parse_white(filter) == -1) return -1;
1918 if (*filter->pc == '(')
1919   {
1920   ++filter->pc;
1921   *n = 0;
1922    *num_true = 0;
1923   for (;;)
1924     {
1925     int cond;
1926
1927     switch (parse_test(filter, &cond, exec))
1928       {
1929       case -1: return -1;
1930       case 0: filter->errmsg = CUS "missing test"; return -1;
1931       default: ++*n; if (cond) ++*num_true; break;
1932       }
1933     if (parse_white(filter) == -1) return -1;
1934     if (*filter->pc == ',') ++filter->pc;
1935     else break;
1936     }
1937   if (*filter->pc == ')')
1938     {
1939     ++filter->pc;
1940     return 1;
1941     }
1942   else
1943     {
1944     filter->errmsg = CUS "missing closing paren";
1945     return -1;
1946     }
1947   }
1948 else return 0;
1949 }
1950
1951
1952 /*************************************************
1953 *     Parse and interpret an optional test       *
1954 *************************************************/
1955
1956 /*
1957 Arguments:
1958   filter      points to the Sieve filter including its state
1959   cond        returned condition status
1960   exec        Execute parsed statements
1961
1962 Returns:      1                success
1963               0                no test found
1964               -1               syntax or execution error
1965 */
1966
1967 static int
1968 parse_test(struct Sieve *filter, int *cond, int exec)
1969 {
1970 if (parse_white(filter) == -1) return -1;
1971 if (parse_identifier(filter, CUS "address"))
1972   {
1973   /*
1974   address-test = "address" { [address-part] [comparator] [match-type] }
1975                  <header-list: string-list> <key-list: string-list>
1976
1977   header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1978   */
1979
1980   enum AddressPart addressPart = ADDRPART_ALL;
1981   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
1982   enum MatchType matchType = MATCH_IS;
1983   gstring *hdr, *key;
1984   int m;
1985   int ap = 0, co = 0, mt = 0;
1986
1987   for (;;)
1988     {
1989     if (parse_white(filter) == -1) return -1;
1990     if ((m = parse_addresspart(filter, &addressPart)) != 0)
1991       {
1992       if (m == -1) return -1;
1993       if (ap)
1994         {
1995         filter->errmsg = CUS "address part already specified";
1996         return -1;
1997         }
1998       else ap = 1;
1999       }
2000     else if ((m = parse_comparator(filter, &comparator)) != 0)
2001       {
2002       if (m == -1) return -1;
2003       if (co)
2004         {
2005         filter->errmsg = CUS "comparator already specified";
2006         return -1;
2007         }
2008       else co = 1;
2009       }
2010     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2011       {
2012       if (m == -1) return -1;
2013       if (mt)
2014         {
2015         filter->errmsg = CUS "match type already specified";
2016         return -1;
2017         }
2018       else mt = 1;
2019       }
2020     else break;
2021     }
2022   if (parse_white(filter) == -1)
2023     return -1;
2024   if ((m = parse_stringlist(filter, &hdr)) != 1)
2025     {
2026     if (m == 0) filter->errmsg = CUS "header string list expected";
2027     return -1;
2028     }
2029   if (parse_white(filter) == -1)
2030     return -1;
2031   if ((m = parse_stringlist(filter, &key)) != 1)
2032     {
2033     if (m == 0) filter->errmsg = CUS "key string list expected";
2034     return -1;
2035     }
2036   *cond = 0;
2037   for (gstring * h = hdr; h->ptr != -1 && !*cond; ++h)
2038     {
2039     uschar * header_value = NULL, * extracted_addr, * end_addr;
2040
2041     if (  !eq_asciicase(h, &str_from, FALSE)
2042        && !eq_asciicase(h, &str_to, FALSE)
2043        && !eq_asciicase(h, &str_cc, FALSE)
2044        && !eq_asciicase(h, &str_bcc, FALSE)
2045        && !eq_asciicase(h, &str_sender, FALSE)
2046        && !eq_asciicase(h, &str_resent_from, FALSE)
2047        && !eq_asciicase(h, &str_resent_to, FALSE)
2048        )
2049       {
2050       filter->errmsg = CUS "invalid header field";
2051       return -1;
2052       }
2053     if (exec)
2054       {
2055       /* We are only interested in addresses below, so no MIME decoding */
2056       if (!(header_value = expand_string(string_sprintf("$rheader_%s", quote(h)))))
2057         {
2058         filter->errmsg = CUS "header string expansion failed";
2059         return -1;
2060         }
2061       f.parse_allow_group = TRUE;
2062       while (*header_value && !*cond)
2063         {
2064         uschar *error;
2065         int start, end, domain;
2066         int saveend;
2067         uschar *part = NULL;
2068
2069         end_addr = parse_find_address_end(header_value, FALSE);
2070         saveend = *end_addr;
2071         *end_addr = 0;
2072         extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2073
2074         if (extracted_addr) switch (addressPart)
2075           {
2076           case ADDRPART_ALL: part = extracted_addr; break;
2077 #ifdef SUBADDRESS
2078           case ADDRPART_USER:
2079 #endif
2080           case ADDRPART_LOCALPART: part = extracted_addr; part[domain-1] = '\0'; break;
2081           case ADDRPART_DOMAIN: part = extracted_addr+domain; break;
2082 #ifdef SUBADDRESS
2083           case ADDRPART_DETAIL: part = NULL; break;
2084 #endif
2085           }
2086
2087         *end_addr = saveend;
2088         if (part && extracted_addr)
2089           {
2090           gstring partStr = {.s = part, .ptr = Ustrlen(part), .size = Ustrlen(part)+1};
2091           for (gstring * k = key; k->ptr != - 1; ++k)
2092             {
2093             *cond = compare(filter, k, &partStr, comparator, matchType);
2094             if (*cond == -1) return -1;
2095             if (*cond) break;
2096             }
2097           }
2098
2099         if (saveend == 0) break;
2100         header_value = end_addr + 1;
2101         }
2102       f.parse_allow_group = FALSE;
2103       f.parse_found_group = FALSE;
2104       }
2105     }
2106   return 1;
2107   }
2108 else if (parse_identifier(filter, CUS "allof"))
2109   {
2110   /*
2111   allof-test   = "allof" <tests: test-list>
2112   */
2113
2114   int n, num_true;
2115
2116   switch (parse_testlist(filter, &n, &num_true, exec))
2117     {
2118     case -1: return -1;
2119     case 0: filter->errmsg = CUS "missing test list"; return -1;
2120     default: *cond = (n == num_true); return 1;
2121     }
2122   }
2123 else if (parse_identifier(filter, CUS "anyof"))
2124   {
2125   /*
2126   anyof-test   = "anyof" <tests: test-list>
2127   */
2128
2129   int n, num_true;
2130
2131   switch (parse_testlist(filter, &n, &num_true, exec))
2132     {
2133     case -1: return -1;
2134     case 0: filter->errmsg = CUS "missing test list"; return -1;
2135     default: *cond = (num_true>0); return 1;
2136     }
2137   }
2138 else if (parse_identifier(filter, CUS "exists"))
2139   {
2140   /*
2141   exists-test = "exists" <header-names: string-list>
2142   */
2143
2144   gstring *hdr;
2145   int m;
2146
2147   if (parse_white(filter) == -1)
2148     return -1;
2149   if ((m = parse_stringlist(filter, &hdr)) != 1)
2150     {
2151     if (m == 0) filter->errmsg = CUS "header string list expected";
2152     return -1;
2153     }
2154   if (exec)
2155     {
2156     *cond = 1;
2157     for (gstring * h = hdr; h->ptr != -1 && *cond; ++h)
2158       {
2159       uschar *header_def;
2160
2161       header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}", quote(h)));
2162       if (!header_def)
2163         {
2164         filter->errmsg = CUS "header string expansion failed";
2165         return -1;
2166         }
2167       if (Ustrcmp(header_def,"false") == 0) *cond = 0;
2168       }
2169     }
2170   return 1;
2171   }
2172 else if (parse_identifier(filter, CUS "false"))
2173   {
2174   /*
2175   false-test = "false"
2176   */
2177
2178   *cond = 0;
2179   return 1;
2180   }
2181 else if (parse_identifier(filter, CUS "header"))
2182   {
2183   /*
2184   header-test = "header" { [comparator] [match-type] }
2185                 <header-names: string-list> <key-list: string-list>
2186   */
2187
2188   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
2189   enum MatchType matchType = MATCH_IS;
2190   gstring *hdr, *key;
2191   int m;
2192   int co = 0, mt = 0;
2193
2194   for (;;)
2195     {
2196     if (parse_white(filter) == -1)
2197       return -1;
2198     if ((m = parse_comparator(filter, &comparator)) != 0)
2199       {
2200       if (m == -1) return -1;
2201       if (co)
2202         {
2203         filter->errmsg = CUS "comparator already specified";
2204         return -1;
2205         }
2206       else co = 1;
2207       }
2208     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2209       {
2210       if (m == -1) return -1;
2211       if (mt)
2212         {
2213         filter->errmsg = CUS "match type already specified";
2214         return -1;
2215         }
2216       else mt = 1;
2217       }
2218     else break;
2219     }
2220   if (parse_white(filter) == -1)
2221     return -1;
2222   if ((m = parse_stringlist(filter, &hdr)) != 1)
2223     {
2224     if (m == 0) filter->errmsg = CUS "header string list expected";
2225     return -1;
2226     }
2227   if (parse_white(filter) == -1)
2228     return -1;
2229   if ((m = parse_stringlist(filter, &key)) != 1)
2230     {
2231     if (m == 0) filter->errmsg = CUS "key string list expected";
2232     return -1;
2233     }
2234   *cond = 0;
2235   for (gstring * h = hdr; h->ptr != -1 && !*cond; ++h)
2236     {
2237     if (!is_header(h))
2238       {
2239       filter->errmsg = CUS "invalid header field";
2240       return -1;
2241       }
2242     if (exec)
2243       {
2244       gstring header_value;
2245       uschar *header_def;
2246
2247       expand_header(&header_value, h);
2248       header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}", quote(h)));
2249       if (!header_value.s || !header_def)
2250         {
2251         filter->errmsg = CUS "header string expansion failed";
2252         return -1;
2253         }
2254       for (gstring * k = key; k->ptr != -1; ++k)
2255         if (Ustrcmp(header_def,"true") == 0)
2256           {
2257           *cond = compare(filter, k, &header_value, comparator, matchType);
2258           if (*cond == -1) return -1;
2259           if (*cond) break;
2260           }
2261       }
2262     }
2263   return 1;
2264   }
2265 else if (parse_identifier(filter, CUS "not"))
2266   {
2267   if (parse_white(filter) == -1) return -1;
2268   switch (parse_test(filter, cond, exec))
2269     {
2270     case -1: return -1;
2271     case 0: filter->errmsg = CUS "missing test"; return -1;
2272     default: *cond = !*cond; return 1;
2273     }
2274   }
2275 else if (parse_identifier(filter, CUS "size"))
2276   {
2277   /*
2278   relop = ":over" / ":under"
2279   size-test = "size" relop <limit: number>
2280   */
2281
2282   unsigned long limit;
2283   int overNotUnder;
2284
2285   if (parse_white(filter) == -1) return -1;
2286   if (parse_identifier(filter, CUS ":over")) overNotUnder = 1;
2287   else if (parse_identifier(filter, CUS ":under")) overNotUnder = 0;
2288   else
2289     {
2290     filter->errmsg = CUS "missing :over or :under";
2291     return -1;
2292     }
2293   if (parse_white(filter) == -1) return -1;
2294   if (parse_number(filter, &limit) == -1) return -1;
2295   *cond = (overNotUnder ? (message_size>limit) : (message_size<limit));
2296   return 1;
2297   }
2298 else if (parse_identifier(filter, CUS "true"))
2299   {
2300   *cond = 1;
2301   return 1;
2302   }
2303 else if (parse_identifier(filter, CUS "envelope"))
2304   {
2305   /*
2306   envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2307                   <envelope-part: string-list> <key-list: string-list>
2308
2309   envelope-part is case insensitive "from" or "to"
2310 #ifdef ENVELOPE_AUTH
2311   envelope-part = / "auth"
2312 #endif
2313   */
2314
2315   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
2316   enum AddressPart addressPart = ADDRPART_ALL;
2317   enum MatchType matchType = MATCH_IS;
2318   gstring *env, *key;
2319   int m;
2320   int co = 0, ap = 0, mt = 0;
2321
2322   if (!filter->require_envelope)
2323     {
2324     filter->errmsg = CUS "missing previous require \"envelope\";";
2325     return -1;
2326     }
2327   for (;;)
2328     {
2329     if (parse_white(filter) == -1) return -1;
2330     if ((m = parse_comparator(filter, &comparator)) != 0)
2331       {
2332       if (m == -1) return -1;
2333       if (co)
2334         {
2335         filter->errmsg = CUS "comparator already specified";
2336         return -1;
2337         }
2338       else co = 1;
2339       }
2340     else if ((m = parse_addresspart(filter, &addressPart)) != 0)
2341       {
2342       if (m == -1) return -1;
2343       if (ap)
2344         {
2345         filter->errmsg = CUS "address part already specified";
2346         return -1;
2347         }
2348       else ap = 1;
2349       }
2350     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2351       {
2352       if (m == -1) return -1;
2353       if (mt)
2354         {
2355         filter->errmsg = CUS "match type already specified";
2356         return -1;
2357         }
2358       else mt = 1;
2359       }
2360     else break;
2361     }
2362   if (parse_white(filter) == -1)
2363     return -1;
2364   if ((m = parse_stringlist(filter, &env)) != 1)
2365     {
2366     if (m == 0) filter->errmsg = CUS "envelope string list expected";
2367     return -1;
2368     }
2369   if (parse_white(filter) == -1)
2370     return -1;
2371   if ((m = parse_stringlist(filter, &key)) != 1)
2372     {
2373     if (m == 0) filter->errmsg = CUS "key string list expected";
2374     return -1;
2375     }
2376   *cond = 0;
2377   for (gstring * e = env; e->ptr != -1 && !*cond; ++e)
2378     {
2379     const uschar *envelopeExpr = CUS 0;
2380     uschar *envelope = US 0;
2381
2382     if (eq_asciicase(e, &str_from, FALSE))
2383       {
2384       switch (addressPart)
2385         {
2386         case ADDRPART_ALL: envelopeExpr = CUS "$sender_address"; break;
2387 #ifdef SUBADDRESS
2388         case ADDRPART_USER:
2389 #endif
2390         case ADDRPART_LOCALPART: envelopeExpr = CUS "${local_part:$sender_address}"; break;
2391         case ADDRPART_DOMAIN: envelopeExpr = CUS "${domain:$sender_address}"; break;
2392 #ifdef SUBADDRESS
2393         case ADDRPART_DETAIL: envelopeExpr = CUS 0; break;
2394 #endif
2395         }
2396       }
2397     else if (eq_asciicase(e, &str_to, FALSE))
2398       {
2399       switch (addressPart)
2400         {
2401         case ADDRPART_ALL: envelopeExpr = CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2402 #ifdef SUBADDRESS
2403         case ADDRPART_USER: envelopeExpr = filter->useraddress; break;
2404         case ADDRPART_DETAIL: envelopeExpr = filter->subaddress; break;
2405 #endif
2406         case ADDRPART_LOCALPART: envelopeExpr = CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2407         case ADDRPART_DOMAIN: envelopeExpr = CUS "$domain"; break;
2408         }
2409       }
2410 #ifdef ENVELOPE_AUTH
2411     else if (eq_asciicase(e, &str_auth, FALSE))
2412       {
2413       switch (addressPart)
2414         {
2415         case ADDRPART_ALL: envelopeExpr = CUS "$authenticated_sender"; break;
2416 #ifdef SUBADDRESS
2417         case ADDRPART_USER:
2418 #endif
2419         case ADDRPART_LOCALPART: envelopeExpr = CUS "${local_part:$authenticated_sender}"; break;
2420         case ADDRPART_DOMAIN: envelopeExpr = CUS "${domain:$authenticated_sender}"; break;
2421 #ifdef SUBADDRESS
2422         case ADDRPART_DETAIL: envelopeExpr = CUS 0; break;
2423 #endif
2424         }
2425       }
2426 #endif
2427     else
2428       {
2429       filter->errmsg = CUS "invalid envelope string";
2430       return -1;
2431       }
2432     if (exec && envelopeExpr)
2433       {
2434       if (!(envelope = expand_string(US envelopeExpr)))
2435         {
2436         filter->errmsg = CUS "header string expansion failed";
2437         return -1;
2438         }
2439       for (gstring * k = key; k->ptr != -1; ++k)
2440         {
2441         gstring envelopeStr = {.s = envelope, .ptr = Ustrlen(envelope), .size = Ustrlen(envelope)+1};
2442
2443         *cond = compare(filter, k, &envelopeStr, comparator, matchType);
2444         if (*cond == -1) return -1;
2445         if (*cond) break;
2446         }
2447       }
2448     }
2449   return 1;
2450   }
2451 #ifdef ENOTIFY
2452 else if (parse_identifier(filter, CUS "valid_notify_method"))
2453   {
2454   /*
2455   valid_notify_method = "valid_notify_method"
2456                         <notification-uris: string-list>
2457   */
2458
2459   gstring *uris;
2460   int m;
2461
2462   if (!filter->require_enotify)
2463     {
2464     filter->errmsg = CUS "missing previous require \"enotify\";";
2465     return -1;
2466     }
2467   if (parse_white(filter) == -1)
2468     return -1;
2469   if ((m = parse_stringlist(filter, &uris)) != 1)
2470     {
2471     if (m == 0) filter->errmsg = CUS "URI string list expected";
2472     return -1;
2473     }
2474   if (exec)
2475     {
2476     *cond = 1;
2477     for (gstring * u = uris; u->ptr != -1 && *cond; ++u)
2478       {
2479         string_item * recipient = NULL;
2480         gstring header =  { .s = NULL, .ptr = -1 };
2481         gstring subject = { .s = NULL, .ptr = -1 };
2482         gstring body =    { .s = NULL, .ptr = -1 };
2483
2484         if (parse_mailto_uri(filter, u->s, &recipient, &header, &subject, &body) != 1)
2485           *cond = 0;
2486       }
2487     }
2488   return 1;
2489   }
2490 else if (parse_identifier(filter, CUS "notify_method_capability"))
2491   {
2492   /*
2493   notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2494                              <notification-uri: string>
2495                              <notification-capability: string>
2496                              <key-list: string-list>
2497   */
2498
2499   int m;
2500   int co = 0, mt = 0;
2501
2502   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
2503   enum MatchType matchType = MATCH_IS;
2504   gstring uri, capa, *keys;
2505
2506   if (!filter->require_enotify)
2507     {
2508     filter->errmsg = CUS "missing previous require \"enotify\";";
2509     return -1;
2510     }
2511   for (;;)
2512     {
2513     if (parse_white(filter) == -1) return -1;
2514     if ((m = parse_comparator(filter, &comparator)) != 0)
2515       {
2516       if (m == -1) return -1;
2517       if (co)
2518         {
2519         filter->errmsg = CUS "comparator already specified";
2520         return -1;
2521         }
2522       else co = 1;
2523       }
2524     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2525       {
2526       if (m == -1) return -1;
2527       if (mt)
2528         {
2529         filter->errmsg = CUS "match type already specified";
2530         return -1;
2531         }
2532       else mt = 1;
2533       }
2534     else break;
2535     }
2536     if ((m = parse_string(filter, &uri)) != 1)
2537       {
2538       if (m == 0) filter->errmsg = CUS "missing notification URI string";
2539       return -1;
2540       }
2541     if (parse_white(filter) == -1)
2542       return -1;
2543     if ((m = parse_string(filter, &capa)) != 1)
2544       {
2545       if (m == 0) filter->errmsg = CUS "missing notification capability string";
2546       return -1;
2547       }
2548     if (parse_white(filter) == -1)
2549       return -1;
2550     if ((m = parse_stringlist(filter, &keys)) != 1)
2551       {
2552       if (m == 0) filter->errmsg = CUS "missing key string list";
2553       return -1;
2554       }
2555     if (exec)
2556       {
2557       string_item * recipient = NULL;
2558       gstring header =  { .s = NULL, .ptr = -1 };
2559       gstring subject = { .s = NULL, .ptr = -1 };
2560       gstring body =    { .s = NULL, .ptr = -1 };
2561
2562       *cond = 0;
2563       if (parse_mailto_uri(filter, uri.s, &recipient, &header, &subject, &body) == 1)
2564         if (eq_asciicase(&capa, &str_online,  FALSE) == 1)
2565           for (gstring * k = keys; k->ptr != -1; ++k)
2566             {
2567             *cond = compare(filter, k, &str_maybe, comparator, matchType);
2568             if (*cond == -1) return -1;
2569             if (*cond) break;
2570             }
2571       }
2572     return 1;
2573   }
2574 #endif
2575 else return 0;
2576 }
2577
2578
2579 /*************************************************
2580 *     Parse and interpret an optional block      *
2581 *************************************************/
2582
2583 /*
2584 Arguments:
2585   filter      points to the Sieve filter including its state
2586   exec        Execute parsed statements
2587   generated   where to hang newly-generated addresses
2588
2589 Returns:      2                success by stop
2590               1                other success
2591               0                no block command found
2592               -1               syntax or execution error
2593 */
2594
2595 static int
2596 parse_block(struct Sieve * filter, int exec, address_item ** generated)
2597 {
2598 int r;
2599
2600 if (parse_white(filter) == -1)
2601   return -1;
2602 if (*filter->pc == '{')
2603   {
2604   ++filter->pc;
2605   if ((r = parse_commands(filter, exec, generated)) == -1 || r == 2) return r;
2606   if (*filter->pc == '}')
2607     {
2608     ++filter->pc;
2609     return 1;
2610     }
2611   filter->errmsg = CUS "expecting command or closing brace";
2612   return -1;
2613   }
2614 return 0;
2615 }
2616
2617
2618 /*************************************************
2619 *           Match a semicolon                    *
2620 *************************************************/
2621
2622 /*
2623 Arguments:
2624   filter      points to the Sieve filter including its state
2625
2626 Returns:      1                success
2627               -1               syntax error
2628 */
2629
2630 static int
2631 parse_semicolon(struct Sieve *filter)
2632 {
2633 if (parse_white(filter) == -1)
2634   return -1;
2635 if (*filter->pc == ';')
2636   {
2637   ++filter->pc;
2638   return 1;
2639   }
2640 filter->errmsg = CUS "missing semicolon";
2641 return -1;
2642 }
2643
2644
2645 /*************************************************
2646 *     Parse and interpret a Sieve command        *
2647 *************************************************/
2648
2649 /*
2650 Arguments:
2651   filter      points to the Sieve filter including its state
2652   exec        Execute parsed statements
2653   generated   where to hang newly-generated addresses
2654
2655 Returns:      2                success by stop
2656               1                other success
2657               -1               syntax or execution error
2658 */
2659 static int
2660 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2661 {
2662 while (*filter->pc)
2663   {
2664   if (parse_white(filter) == -1)
2665     return -1;
2666   if (parse_identifier(filter, CUS "if"))
2667     {
2668     /*
2669     if-command = "if" test block *( "elsif" test block ) [ else block ]
2670     */
2671
2672     int cond, m, unsuccessful;
2673
2674     /* test block */
2675     if (parse_white(filter) == -1)
2676       return -1;
2677     if ((m = parse_test(filter, &cond, exec)) == -1)
2678       return -1;
2679     if (m == 0)
2680       {
2681       filter->errmsg = CUS "missing test";
2682       return -1;
2683       }
2684     if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2685         (debug_selector & D_filter) != 0)
2686       {
2687       if (exec) debug_printf_indent("if %s\n", cond?"true":"false");
2688       }
2689     m = parse_block(filter, exec ? cond : 0, generated);
2690     if (m == -1 || m == 2)
2691       return m;
2692     if (m == 0)
2693       {
2694       filter->errmsg = CUS "missing block";
2695       return -1;
2696       }
2697     unsuccessful = !cond;
2698     for (;;) /* elsif test block */
2699       {
2700       if (parse_white(filter) == -1)
2701         return -1;
2702       if (parse_identifier(filter, CUS "elsif"))
2703         {
2704         if (parse_white(filter) == -1)
2705           return -1;
2706         m = parse_test(filter, &cond, exec && unsuccessful);
2707         if (m == -1 || m == 2)
2708           return m;
2709         if (m == 0)
2710           {
2711           filter->errmsg = CUS "missing test";
2712           return -1;
2713           }
2714         if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2715             (debug_selector & D_filter) != 0)
2716           {
2717           if (exec) debug_printf_indent("elsif %s\n", cond?"true":"false");
2718           }
2719         m = parse_block(filter, exec && unsuccessful ? cond : 0, generated);
2720         if (m == -1 || m == 2)
2721           return m;
2722         if (m == 0)
2723           {
2724           filter->errmsg = CUS "missing block";
2725           return -1;
2726           }
2727         if (exec && unsuccessful && cond)
2728           unsuccessful = 0;
2729         }
2730       else break;
2731       }
2732     /* else block */
2733     if (parse_white(filter) == -1)
2734       return -1;
2735     if (parse_identifier(filter, CUS "else"))
2736       {
2737       m = parse_block(filter, exec && unsuccessful, generated);
2738       if (m == -1 || m == 2)
2739         return m;
2740       if (m == 0)
2741         {
2742         filter->errmsg = CUS "missing block";
2743         return -1;
2744         }
2745       }
2746     }
2747   else if (parse_identifier(filter, CUS "stop"))
2748     {
2749     /*
2750     stop-command     =  "stop" { stop-options } ";"
2751     stop-options     =
2752     */
2753
2754     if (parse_semicolon(filter) == -1)
2755       return -1;
2756     if (exec)
2757       {
2758       filter->pc += Ustrlen(filter->pc);
2759       return 2;
2760       }
2761     }
2762   else if (parse_identifier(filter, CUS "keep"))
2763     {
2764     /*
2765     keep-command     =  "keep" { keep-options } ";"
2766     keep-options     =
2767     */
2768
2769     if (parse_semicolon(filter) == -1)
2770       return -1;
2771     if (exec)
2772       {
2773       add_addr(generated, filter->inbox, 1, 0, 0, 0);
2774       filter->keep = 0;
2775       }
2776     }
2777   else if (parse_identifier(filter, CUS "discard"))
2778     {
2779     /*
2780     discard-command  =  "discard" { discard-options } ";"
2781     discard-options  =
2782     */
2783
2784     if (parse_semicolon(filter) == -1)
2785       return -1;
2786     if (exec) filter->keep = 0;
2787     }
2788   else if (parse_identifier(filter, CUS "redirect"))
2789     {
2790     /*
2791     redirect-command =  "redirect" redirect-options "string" ";"
2792     redirect-options =
2793     redirect-options = ) ":copy"
2794     */
2795
2796     gstring recipient;
2797     int m;
2798     int copy = 0;
2799
2800     for (;;)
2801       {
2802       if (parse_white(filter) == -1)
2803         return -1;
2804       if (parse_identifier(filter, CUS ":copy") == 1)
2805         {
2806         if (!filter->require_copy)
2807           {
2808           filter->errmsg = CUS "missing previous require \"copy\";";
2809           return -1;
2810           }
2811         copy = 1;
2812         }
2813       else break;
2814       }
2815     if (parse_white(filter) == -1)
2816       return -1;
2817     if ((m = parse_string(filter, &recipient)) != 1)
2818       {
2819       if (m == 0)
2820         filter->errmsg = CUS "missing redirect recipient string";
2821       return -1;
2822       }
2823     if (strchr(CCS recipient.s, '@') == NULL)
2824       {
2825       filter->errmsg = CUS "unqualified recipient address";
2826       return -1;
2827       }
2828     if (exec)
2829       {
2830       add_addr(generated, recipient.s, 0, 0, 0, 0);
2831       if (!copy) filter->keep = 0;
2832       }
2833     if (parse_semicolon(filter) == -1) return -1;
2834     }
2835   else if (parse_identifier(filter, CUS "fileinto"))
2836     {
2837     /*
2838     fileinto-command =  "fileinto" { fileinto-options } string ";"
2839     fileinto-options =
2840     fileinto-options = ) [ ":copy" ]
2841     */
2842
2843     gstring folder;
2844     uschar *s;
2845     int m;
2846     unsigned long maxage, maxmessages, maxstorage;
2847     int copy = 0;
2848
2849     maxage = maxmessages = maxstorage = 0;
2850     if (!filter->require_fileinto)
2851       {
2852       filter->errmsg = CUS "missing previous require \"fileinto\";";
2853       return -1;
2854       }
2855     for (;;)
2856       {
2857       if (parse_white(filter) == -1)
2858         return -1;
2859       if (parse_identifier(filter, CUS ":copy") == 1)
2860         {
2861         if (!filter->require_copy)
2862           {
2863           filter->errmsg = CUS "missing previous require \"copy\";";
2864           return -1;
2865           }
2866           copy = 1;
2867         }
2868       else break;
2869       }
2870     if (parse_white(filter) == -1)
2871       return -1;
2872     if ((m = parse_string(filter, &folder)) != 1)
2873       {
2874       if (m == 0) filter->errmsg = CUS "missing fileinto folder string";
2875       return -1;
2876       }
2877     m = 0; s = folder.s;
2878     if (folder.ptr == 0)
2879       m = 1;
2880     if (Ustrcmp(s,"..") == 0 || Ustrncmp(s,"../", 3) == 0)
2881       m = 1;
2882     else while (*s)
2883       {
2884       if (Ustrcmp(s,"/..") == 0 || Ustrncmp(s,"/../", 4) == 0) { m = 1; break; }
2885       ++s;
2886       }
2887     if (m)
2888       {
2889       filter->errmsg = CUS "invalid folder";
2890       return -1;
2891       }
2892     if (exec)
2893       {
2894       add_addr(generated, folder.s, 1, maxage, maxmessages, maxstorage);
2895       if (!copy) filter->keep = 0;
2896       }
2897     if (parse_semicolon(filter) == -1)
2898       return -1;
2899     }
2900 #ifdef ENOTIFY
2901   else if (parse_identifier(filter, CUS "notify"))
2902     {
2903     /*
2904     notify-command =  "notify" { notify-options } <method: string> ";"
2905     notify-options =  [":from" string]
2906                       [":importance" <"1" / "2" / "3">]
2907                       [":options" 1*(string-list / number)]
2908                       [":message" string]
2909     */
2910
2911     int m;
2912     gstring from =       { .s = NULL, .ptr = -1 };
2913     gstring importance = { .s = NULL, .ptr = -1 };
2914     gstring message =    { .s = NULL, .ptr = -1 };
2915     gstring method;
2916     struct Notification *already;
2917     string_item * recipient = NULL;
2918     gstring header =     { .s = NULL, .ptr = -1 };
2919     gstring subject =    { .s = NULL, .ptr = -1 };
2920     gstring body =       { .s = NULL, .ptr = -1 };
2921     uschar *envelope_from;
2922     gstring auto_submitted_value;
2923     uschar *auto_submitted_def;
2924
2925     if (!filter->require_enotify)
2926       {
2927       filter->errmsg = CUS "missing previous require \"enotify\";";
2928       return -1;
2929       }
2930     envelope_from = sender_address && sender_address[0]
2931      ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2932     if (!envelope_from)
2933       {
2934       filter->errmsg = CUS "expansion failure for envelope from";
2935       return -1;
2936       }
2937     for (;;)
2938       {
2939       if (parse_white(filter) == -1)
2940         return -1;
2941       if (parse_identifier(filter, CUS ":from") == 1)
2942         {
2943         if (parse_white(filter) == -1)
2944           return -1;
2945         if ((m = parse_string(filter, &from)) != 1)
2946           {
2947           if (m == 0) filter->errmsg = CUS "from string expected";
2948           return -1;
2949           }
2950         }
2951       else if (parse_identifier(filter, CUS ":importance") == 1)
2952         {
2953         if (parse_white(filter) == -1)
2954           return -1;
2955         if ((m = parse_string(filter, &importance)) != 1)
2956           {
2957           if (m == 0)
2958             filter->errmsg = CUS "importance string expected";
2959           return -1;
2960           }
2961         if (importance.ptr != 1 || importance.s[0] < '1' || importance.s[0] > '3')
2962           {
2963           filter->errmsg = CUS "invalid importance";
2964           return -1;
2965           }
2966         }
2967       else if (parse_identifier(filter, CUS ":options") == 1)
2968         {
2969         if (parse_white(filter) == -1)
2970           return -1;
2971         }
2972       else if (parse_identifier(filter, CUS ":message") == 1)
2973         {
2974         if (parse_white(filter) == -1)
2975           return -1;
2976         if ((m = parse_string(filter, &message)) != 1)
2977           {
2978           if (m == 0)
2979             filter->errmsg = CUS "message string expected";
2980           return -1;
2981           }
2982         }
2983       else break;
2984       }
2985     if (parse_white(filter) == -1)
2986       return -1;
2987     if ((m = parse_string(filter, &method)) != 1)
2988       {
2989       if (m == 0)
2990         filter->errmsg = CUS "missing method string";
2991       return -1;
2992       }
2993     if (parse_semicolon(filter) == -1)
2994       return -1;
2995     if (parse_mailto_uri(filter, method.s, &recipient, &header, &subject, &body) != 1)
2996       return -1;
2997     if (exec)
2998       {
2999       if (message.ptr == -1)
3000         message = subject;
3001       if (message.ptr == -1)
3002         expand_header(&message, &str_subject);
3003       expand_header(&auto_submitted_value, &str_auto_submitted);
3004       auto_submitted_def = expand_string(US"${if def:header_auto-submitted {true}{false}}");
3005       if (!auto_submitted_value.s || !auto_submitted_def)
3006         {
3007         filter->errmsg = CUS "header string expansion failed";
3008         return -1;
3009         }
3010         if (Ustrcmp(auto_submitted_def,"true") != 0 || Ustrcmp(auto_submitted_value.s,"no") == 0)
3011         {
3012         for (already = filter->notified; already; already = already->next)
3013           {
3014           if (   already->method.ptr == method.ptr
3015               && (method.ptr == -1 || Ustrcmp(already->method.s, method.s) == 0)
3016               && already->importance.ptr == importance.ptr
3017               && (importance.ptr == -1 || Ustrcmp(already->importance.s, importance.s) == 0)
3018               && already->message.ptr == message.ptr
3019               && (message.ptr == -1 || Ustrcmp(already->message.s, message.s) == 0))
3020             break;
3021           }
3022         if (!already)
3023           /* New notification, process it */
3024           {
3025           struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED);
3026           sent->method = method;
3027           sent->importance = importance;
3028           sent->message = message;
3029           sent->next = filter->notified;
3030           filter->notified = sent;
3031   #ifndef COMPILE_SYNTAX_CHECKER
3032           if (filter_test == FTEST_NONE)
3033             {
3034             int pid, fd;
3035
3036             if ((pid = child_open_exim2(&fd, envelope_from, envelope_from,
3037                         US"sieve-notify")) >= 1)
3038               {
3039               FILE * f = fdopen(fd, "wb");
3040
3041               fprintf(f,"From: %s\n", from.ptr == -1
3042                 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
3043                 : from.s);
3044               for (string_item * p = recipient; p; p = p->next)
3045                 fprintf(f, "To: %s\n", p->text);
3046               fprintf(f, "Auto-Submitted: auto-notified; %s\n", filter->enotify_mailto_owner);
3047               if (header.ptr > 0) fprintf(f, "%s", header.s);
3048               if (message.ptr == -1)
3049                 {
3050                 message.s = US"Notification";
3051                 message.ptr = Ustrlen(message.s);
3052                 }
3053               if (message.ptr != -1)
3054                 fprintf(f, "Subject: %s\n", parse_quote_2047(message.s,
3055                   message.ptr, US"utf-8", TRUE));
3056               fprintf(f,"\n");
3057               if (body.ptr > 0) fprintf(f, "%s\n", body.s);
3058               fflush(f);
3059               (void)fclose(f);
3060               (void)child_close(pid, 0);
3061               }
3062             }
3063           if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3064             debug_printf_indent("Notification to `%s': '%s'.\n", method.s, message.ptr != -1 ? message.s : CUS "");
3065 #endif
3066           }
3067         else
3068           if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3069             debug_printf_indent("Repeated notification to `%s' ignored.\n", method.s);
3070         }
3071       else
3072         if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3073           debug_printf_indent("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3074       }
3075     }
3076 #endif
3077 #ifdef VACATION
3078   else if (parse_identifier(filter, CUS "vacation"))
3079     {
3080     /*
3081     vacation-command =  "vacation" { vacation-options } <reason: string> ";"
3082     vacation-options =  [":days" number]
3083                         [":subject" string]
3084                         [":from" string]
3085                         [":addresses" string-list]
3086                         [":mime"]
3087                         [":handle" string]
3088     */
3089
3090     int m;
3091     unsigned long days;
3092     gstring subject;
3093     gstring from;
3094     gstring *addresses;
3095     int reason_is_mime;
3096     string_item *aliases;
3097     gstring handle;
3098     gstring reason;
3099
3100     if (!filter->require_vacation)
3101       {
3102       filter->errmsg = CUS "missing previous require \"vacation\";";
3103       return -1;
3104       }
3105     if (exec)
3106       {
3107       if (filter->vacation_ran)
3108         {
3109         filter->errmsg = CUS "trying to execute vacation more than once";
3110         return -1;
3111         }
3112       filter->vacation_ran = TRUE;
3113       }
3114     days = VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3115     subject.s = (uschar*)0;
3116     subject.ptr = -1;
3117     from.s = (uschar*)0;
3118     from.ptr = -1;
3119     addresses = (gstring*)0;
3120     aliases = NULL;
3121     reason_is_mime = 0;
3122     handle.s = (uschar*)0;
3123     handle.ptr = -1;
3124     for (;;)
3125       {
3126       if (parse_white(filter) == -1)
3127         return -1;
3128       if (parse_identifier(filter, CUS ":days") == 1)
3129         {
3130         if (parse_white(filter) == -1)
3131           return -1;
3132         if (parse_number(filter, &days) == -1)
3133           return -1;
3134         if (days<VACATION_MIN_DAYS)
3135           days = VACATION_MIN_DAYS;
3136         else if (days>VACATION_MAX_DAYS)
3137           days = VACATION_MAX_DAYS;
3138         }
3139       else if (parse_identifier(filter, CUS ":subject") == 1)
3140         {
3141         if (parse_white(filter) == -1)
3142           return -1;
3143         if ((m = parse_string(filter, &subject)) != 1)
3144           {
3145           if (m == 0)
3146             filter->errmsg = CUS "subject string expected";
3147           return -1;
3148           }
3149         }
3150       else if (parse_identifier(filter, CUS ":from") == 1)
3151         {
3152         if (parse_white(filter) == -1)
3153           return -1;
3154         if ((m = parse_string(filter, &from)) != 1)
3155           {
3156           if (m == 0)
3157             filter->errmsg = CUS "from string expected";
3158           return -1;
3159           }
3160         if (check_mail_address(filter, &from) != 1)
3161           return -1;
3162         }
3163       else if (parse_identifier(filter, CUS ":addresses") == 1)
3164         {
3165         if (parse_white(filter) == -1)
3166           return -1;
3167         if ((m = parse_stringlist(filter, &addresses)) != 1)
3168           {
3169           if (m == 0)
3170             filter->errmsg = CUS "addresses string list expected";
3171           return -1;
3172           }
3173         for (gstring * a = addresses; a->ptr != -1; ++a)
3174           {
3175           string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
3176
3177           new->text = store_get(a->ptr+1, a->s);
3178           if (a->ptr) memcpy(new->text, a->s, a->ptr);
3179           new->text[a->ptr] = '\0';
3180           new->next = aliases;
3181           aliases = new;
3182           }
3183         }
3184       else if (parse_identifier(filter, CUS ":mime") == 1)
3185         reason_is_mime = 1;
3186       else if (parse_identifier(filter, CUS ":handle") == 1)
3187         {
3188         if (parse_white(filter) == -1)
3189           return -1;
3190         if ((m = parse_string(filter, &from)) != 1)
3191           {
3192           if (m == 0)
3193             filter->errmsg = CUS "handle string expected";
3194           return -1;
3195           }
3196         }
3197       else break;
3198       }
3199     if (parse_white(filter) == -1)
3200       return -1;
3201     if ((m = parse_string(filter, &reason)) != 1)
3202       {
3203       if (m == 0)
3204         filter->errmsg = CUS "missing reason string";
3205       return -1;
3206       }
3207     if (reason_is_mime)
3208       {
3209       uschar *s, *end;
3210
3211       for (s = reason.s, end = reason.s + reason.ptr;
3212           s<end && (*s&0x80) == 0; ) s++;
3213       if (s<end)
3214         {
3215         filter->errmsg = CUS "MIME reason string contains 8bit text";
3216         return -1;
3217         }
3218       }
3219     if (parse_semicolon(filter) == -1) return -1;
3220
3221     if (exec)
3222       {
3223       address_item * addr;
3224       md5 base;
3225       uschar digest[16], hexdigest[33];
3226       gstring * once;
3227       misc_module_info * mi;
3228       typedef BOOL (*fn_t)(string_item *, BOOL);
3229
3230       if (!(mi = misc_mod_find(US"exim_filter", NULL)))
3231         {
3232         filter->errmsg = CUS "test for 'personal': module not available";
3233         return -1;
3234         }
3235       if ((((fn_t *) mi->functions)[EXIM_FILTER_PERSONAL])(aliases, TRUE))
3236         {
3237         if (filter_test == FTEST_NONE)
3238           {
3239           /* ensure oncelog directory exists; failure will be detected later */
3240
3241           (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3242           }
3243         /* build oncelog filename */
3244
3245         md5_start(&base);
3246
3247         if (handle.ptr == -1)
3248           {
3249           gstring * key = NULL;
3250           if (subject.ptr != -1)
3251             key = string_catn(key, subject.s, subject.ptr);
3252           if (from.ptr != -1)
3253             key = string_catn(key, from.s, from.ptr);
3254           key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3255           key = string_catn(key, reason.s, reason.ptr);
3256           md5_end(&base, key->s, key->ptr, digest);
3257           }
3258         else
3259           md5_end(&base, handle.s, handle.ptr, digest);
3260
3261         for (int i = 0; i < 16; i++)
3262           sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3263
3264         if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3265           debug_printf_indent("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3266
3267         if (filter_test == FTEST_NONE)
3268           {
3269           once = string_cat (NULL, filter->vacation_directory);
3270           once = string_catn(once, US"/", 1);
3271           once = string_catn(once, hexdigest, 33);
3272
3273           /* process subject */
3274
3275           if (subject.ptr == -1)
3276             {
3277             uschar * subject_def;
3278
3279             subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3280             if (subject_def && Ustrcmp(subject_def,"true") == 0)
3281               {
3282               gstring * g = string_catn(NULL, US"Auto: ", 6);
3283
3284               expand_header(&subject, &str_subject);
3285               g = string_catn(g, subject.s, subject.ptr);
3286               subject.ptr = len_string_from_gstring(g, &subject.s);
3287               }
3288             else
3289               {
3290               subject.s = US"Automated reply";
3291               subject.ptr = Ustrlen(subject.s);
3292               }
3293             }
3294
3295           /* add address to list of generated addresses */
3296
3297           addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3298           setflag(addr, af_pfr);
3299           addr->prop.ignore_error = TRUE;
3300           addr->next = *generated;
3301           *generated = addr;
3302           addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
3303           memset(addr->reply, 0, sizeof(reply_item)); /* XXX */
3304           addr->reply->to = string_copy(sender_address);
3305           if (from.ptr == -1)
3306             addr->reply->from = expand_string(US"$local_part@$domain");
3307           else
3308             addr->reply->from = from.s;
3309           /* deconst cast safe as we pass in a non-const item */
3310           addr->reply->subject = US parse_quote_2047(subject.s, subject.ptr, US"utf-8", TRUE);
3311           addr->reply->oncelog = string_from_gstring(once);
3312           addr->reply->once_repeat = days*86400;
3313
3314           /* build body and MIME headers */
3315
3316           if (reason_is_mime)
3317             {
3318             uschar *mime_body, *reason_end;
3319             static const uschar nlnl[] = "\r\n\r\n";
3320
3321             for
3322               (
3323               mime_body = reason.s, reason_end = reason.s + reason.ptr;
3324               mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3325               ) mime_body++;
3326
3327             addr->reply->headers = string_copyn(reason.s, mime_body-reason.s);
3328
3329             if (mime_body+(sizeof(nlnl)-1)<reason_end)
3330               mime_body += (sizeof(nlnl)-1);
3331             else mime_body = reason_end-1;
3332             addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3333             }
3334           else
3335             {
3336             addr->reply->headers = US"MIME-Version: 1.0\n"
3337                                    "Content-Type: text/plain;\n"
3338                                    "\tcharset=\"utf-8\"\n"
3339                                    "Content-Transfer-Encoding: quoted-printable";
3340             addr->reply->text = quoted_printable_encode(&reason)->s;
3341             }
3342           }
3343         }
3344         else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3345           debug_printf_indent("Sieve: mail was not personal, vacation would ignore it\n");
3346       }
3347     }
3348     else break;
3349 #endif
3350   }
3351 return 1;
3352 }
3353
3354
3355 /*************************************************
3356 *       Parse and interpret a sieve filter       *
3357 *************************************************/
3358
3359 /*
3360 Arguments:
3361   filter      points to the Sieve filter including its state
3362   exec        Execute parsed statements
3363   generated   where to hang newly-generated addresses
3364
3365 Returns:      1                success
3366               -1               syntax or execution error
3367 */
3368
3369 static int
3370 parse_start(struct Sieve *filter, int exec, address_item **generated)
3371 {
3372 filter->pc = filter->filter;
3373 filter->line = 1;
3374 filter->keep = 1;
3375 filter->require_envelope = 0;
3376 filter->require_fileinto = 0;
3377 #ifdef ENCODED_CHARACTER
3378 filter->require_encoded_character = FALSE;
3379 #endif
3380 #ifdef ENVELOPE_AUTH
3381 filter->require_envelope_auth = 0;
3382 #endif
3383 #ifdef ENOTIFY
3384 filter->require_enotify = 0;
3385 filter->notified = (struct Notification*)0;
3386 #endif
3387 #ifdef SUBADDRESS
3388 filter->require_subaddress = FALSE;
3389 #endif
3390 #ifdef VACATION
3391 filter->require_vacation = FALSE;
3392 filter->vacation_ran = 0;               /*XXX missing init? */
3393 #endif
3394 filter->require_copy = FALSE;
3395 filter->require_iascii_numeric = FALSE;
3396
3397 if (parse_white(filter) == -1) return -1;
3398
3399 if (exec && filter->vacation_directory && filter_test == FTEST_NONE)
3400   {
3401   DIR *oncelogdir;
3402   struct dirent *oncelog;
3403   struct stat properties;
3404   time_t now;
3405
3406   /* clean up old vacation log databases */
3407
3408   if (  !(oncelogdir = exim_opendir(filter->vacation_directory))
3409      && errno != ENOENT)
3410     {
3411     filter->errmsg = CUS "unable to open vacation directory";
3412     return -1;
3413     }
3414
3415   if (oncelogdir)
3416     {
3417     time(&now);
3418
3419     while ((oncelog = readdir(oncelogdir)))
3420       if (strlen(oncelog->d_name) == 32)
3421         {
3422         uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name);
3423         if (Ustat(s, &properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now)
3424           Uunlink(s);
3425         }
3426     closedir(oncelogdir);
3427     }
3428   }
3429
3430 while (parse_identifier(filter, CUS "require"))
3431   {
3432   /*
3433   require-command = "require" <capabilities: string-list>
3434   */
3435
3436   gstring *cap;
3437   int m;
3438
3439   if (parse_white(filter) == -1) return -1;
3440   if ((m = parse_stringlist(filter, &cap)) != 1)
3441     {
3442     if (m == 0) filter->errmsg = CUS "capability string list expected";
3443     return -1;
3444     }
3445   for (gstring * check = cap; check->s; ++check)
3446     {
3447     if (eq_octet(check, &str_envelope, FALSE)) filter->require_envelope = 1;
3448     else if (eq_octet(check, &str_fileinto, FALSE)) filter->require_fileinto = 1;
3449 #ifdef ENCODED_CHARACTER
3450     else if (eq_octet(check, &str_encoded_character, FALSE)) filter->require_encoded_character = TRUE;
3451 #endif
3452 #ifdef ENVELOPE_AUTH
3453     else if (eq_octet(check, &str_envelope_auth, FALSE)) filter->require_envelope_auth = 1;
3454 #endif
3455 #ifdef ENOTIFY
3456     else if (eq_octet(check, &str_enotify, FALSE))
3457       {
3458       if (!filter->enotify_mailto_owner)
3459         {
3460         filter->errmsg = CUS "enotify disabled";
3461         return -1;
3462         }
3463         filter->require_enotify = 1;
3464       }
3465 #endif
3466 #ifdef SUBADDRESS
3467     else if (eq_octet(check, &str_subaddress, FALSE)) filter->require_subaddress = TRUE;
3468 #endif
3469 #ifdef VACATION
3470     else if (eq_octet(check, &str_vacation, FALSE))
3471       {
3472       if (filter_test == FTEST_NONE && !filter->vacation_directory)
3473         {
3474         filter->errmsg = CUS "vacation disabled";
3475         return -1;
3476         }
3477       filter->require_vacation = TRUE;
3478       }
3479 #endif
3480     else if (eq_octet(check, &str_copy, FALSE)) filter->require_copy = TRUE;
3481     else if (eq_octet(check, &str_comparator_ioctet, FALSE)) ;
3482     else if (eq_octet(check, &str_comparator_iascii_casemap, FALSE)) ;
3483     else if (eq_octet(check, &str_comparator_enascii_casemap, FALSE)) ;
3484     else if (eq_octet(check, &str_comparator_iascii_numeric, FALSE)) filter->require_iascii_numeric = TRUE;
3485     else
3486       {
3487       filter->errmsg = CUS "unknown capability";
3488       return -1;
3489       }
3490     }
3491     if (parse_semicolon(filter) == -1) return -1;
3492   }
3493   if (parse_commands(filter, exec, generated) == -1) return -1;
3494   if (*filter->pc)
3495     {
3496     filter->errmsg = CUS "syntax error";
3497     return -1;
3498     }
3499   return 1;
3500 }
3501
3502
3503 /*************************************************
3504 *            Interpret a sieve filter file       *
3505 *************************************************/
3506
3507 /*
3508 Arguments:
3509   filter      points to the entire file, read into store as a single string
3510   options     controls whether various special things are allowed, and requests
3511               special actions (not currently used)
3512   sieve
3513     vacation_directory          where to store vacation "once" files
3514     enotify_mailto_owner        owner of mailto notifications
3515     useraddress                 string expression for :user part of address
3516     subaddress                  string expression for :subaddress part of address
3517     inbox                       string expression for "keep"
3518   generated   where to hang newly-generated addresses
3519   error       where to pass back an error text
3520
3521 Returns:      FF_DELIVERED     success, a significant action was taken
3522               FF_NOTDELIVERED  success, no significant action
3523               FF_DEFER         defer requested
3524               FF_FAIL          fail requested
3525               FF_FREEZE        freeze requested
3526               FF_ERROR         there was a problem
3527 */
3528
3529 int
3530 sieve_interpret(const uschar * filter, int options, const sieve_block * sb,
3531   address_item ** generated, uschar ** error)
3532 {
3533 struct Sieve sieve;
3534 int r;
3535 uschar * msg;
3536
3537 DEBUG(D_route) debug_printf_indent("Sieve: start of processing\n");
3538 expand_level++;
3539 sieve.filter = filter;
3540
3541 GET_OPTION("sieve_vacation_directory");
3542 if (!sb || !sb->vacation_dir)
3543   sieve.vacation_directory = NULL;
3544 else if (!(sieve.vacation_directory = expand_cstring(sb->vacation_dir)))
3545   {
3546   *error = string_sprintf("failed to expand \"%s\" "
3547     "(sieve_vacation_directory): %s", sb->vacation_dir, expand_string_message);
3548   return FF_ERROR;
3549   }
3550
3551 GET_OPTION("sieve_vacation_directory");
3552 if (!sb || !sb->inbox)
3553   sieve.inbox = US"inbox";
3554 else if (!(sieve.inbox = expand_cstring(sb->inbox)))
3555   {
3556   *error = string_sprintf("failed to expand \"%s\" "
3557     "(sieve_inbox): %s", sb->inbox, expand_string_message);
3558   return FF_ERROR;
3559   }
3560
3561 GET_OPTION("sieve_enotify_mailto_owner");
3562 if (!sb || !sb->enotify_mailto_owner)
3563   sieve.enotify_mailto_owner = NULL;
3564 else if (!(sieve.enotify_mailto_owner = expand_cstring(sb->enotify_mailto_owner)))
3565   {
3566   *error = string_sprintf("failed to expand \"%s\" "
3567     "(sieve_enotify_mailto_owner): %s", sb->enotify_mailto_owner,
3568     expand_string_message);
3569   return FF_ERROR;
3570   }
3571
3572 GET_OPTION("sieve_useraddress");
3573 sieve.useraddress = sb && sb->useraddress
3574   ? sb->useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3575 GET_OPTION("sieve_subaddress");
3576 sieve.subaddress = sb ? sb->subaddress : NULL;
3577
3578 #ifdef COMPILE_SYNTAX_CHECKER
3579 if (parse_start(&sieve, 0, generated) == 1)
3580 #else
3581 if (parse_start(&sieve, 1, generated) == 1)
3582 #endif
3583   if (sieve.keep)
3584     {
3585     add_addr(generated, sieve.inbox, 1, 0, 0, 0);
3586     msg = US"Implicit keep";
3587     r = FF_DELIVERED;
3588     }
3589   else
3590     {
3591     msg = US"No implicit keep";
3592     r = FF_DELIVERED;
3593     }
3594 else
3595   {
3596   msg = string_sprintf("Sieve error: %s in line %d", sieve.errmsg, sieve.line);
3597 #ifdef COMPILE_SYNTAX_CHECKER
3598   r = FF_ERROR;
3599   *error = msg;
3600 #else
3601   add_addr(generated, sieve.inbox, 1, 0, 0, 0);
3602   r = FF_DELIVERED;
3603 #endif
3604   }
3605
3606 #ifndef COMPILE_SYNTAX_CHECKER
3607 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3608   else debug_printf_indent("%s\n", msg);
3609 #endif
3610
3611 expand_level--;
3612 DEBUG(D_route) debug_printf_indent("Sieve: end of processing\n");
3613 return r;
3614 }
3615
3616
3617 /* Module API: print list of supported sieve extensions to given stream */
3618 static void
3619 sieve_extensions(FILE * fp)
3620 {
3621 for (const uschar ** pp = exim_sieve_extension_list; *pp; ++pp)
3622   fprintf(fp, "%s\n", *pp);
3623 }
3624
3625
3626 /******************************************************************************/
3627 /* Module API */
3628
3629 static void * sieve_functions[] = {
3630   [SIEVE_INTERPRET] =   (void *) sieve_interpret,
3631   [SIEVE_EXTENSIONS] =  (void *) sieve_extensions,
3632 };
3633
3634 misc_module_info sieve_filter_module_info =
3635 {
3636   .name =               US"sieve_filter",
3637 # ifdef DYNLOOKUP
3638   .dyn_magic =          MISC_MODULE_MAGIC,
3639 # endif
3640
3641   .functions =          sieve_functions,
3642   .functions_count =    nelem(sieve_functions),
3643 };
3644
3645 /* End of sieve_filter.c */