dbe64cffa10578e425769ec2ca7e0b2cfc7635bf
[exim.git] / src / src / sieve.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /*
6  * Copyright (c) The Exim Maintainers 2016 - 2023
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 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 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 {
1887   *m = MATCH_IS;
1888   return 1;
1889 }
1890 else if (parse_identifier(filter, CUS ":contains") == 1)
1891 {
1892   *m = MATCH_CONTAINS;
1893   return 1;
1894 }
1895 else if (parse_identifier(filter, CUS ":matches") == 1)
1896 {
1897   *m = MATCH_MATCHES;
1898   return 1;
1899 }
1900 else return 0;
1901 }
1902
1903
1904 /*************************************************
1905 *   Parse and interpret an optional test list    *
1906 *************************************************/
1907
1908 /*
1909 Grammar:
1910   test-list = "(" test *("," test) ")"
1911
1912 Arguments:
1913   filter      points to the Sieve filter including its state
1914   n           total number of tests
1915   num_true    number of passed tests
1916   exec        Execute parsed statements
1917
1918 Returns:      1                success
1919               0                no test list found
1920               -1               syntax or execution error
1921 */
1922
1923 static int
1924 parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1925 {
1926 if (parse_white(filter) == -1) return -1;
1927 if (*filter->pc == '(')
1928   {
1929   ++filter->pc;
1930   *n = 0;
1931    *num_true = 0;
1932   for (;;)
1933     {
1934     int cond;
1935
1936     switch (parse_test(filter, &cond, exec))
1937       {
1938       case -1: return -1;
1939       case 0: filter->errmsg = CUS "missing test"; return -1;
1940       default: ++*n; if (cond) ++*num_true; break;
1941       }
1942     if (parse_white(filter) == -1) return -1;
1943     if (*filter->pc == ',') ++filter->pc;
1944     else break;
1945     }
1946   if (*filter->pc == ')')
1947     {
1948     ++filter->pc;
1949     return 1;
1950     }
1951   else
1952     {
1953     filter->errmsg = CUS "missing closing paren";
1954     return -1;
1955     }
1956   }
1957 else return 0;
1958 }
1959
1960
1961 /*************************************************
1962 *     Parse and interpret an optional test       *
1963 *************************************************/
1964
1965 /*
1966 Arguments:
1967   filter      points to the Sieve filter including its state
1968   cond        returned condition status
1969   exec        Execute parsed statements
1970
1971 Returns:      1                success
1972               0                no test found
1973               -1               syntax or execution error
1974 */
1975
1976 static int
1977 parse_test(struct Sieve *filter, int *cond, int exec)
1978 {
1979 if (parse_white(filter) == -1) return -1;
1980 if (parse_identifier(filter, CUS "address"))
1981   {
1982   /*
1983   address-test = "address" { [address-part] [comparator] [match-type] }
1984                  <header-list: string-list> <key-list: string-list>
1985
1986   header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1987   */
1988
1989   enum AddressPart addressPart = ADDRPART_ALL;
1990   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
1991   enum MatchType matchType = MATCH_IS;
1992   gstring *hdr, *key;
1993   int m;
1994   int ap = 0, co = 0, mt = 0;
1995
1996   for (;;)
1997     {
1998     if (parse_white(filter) == -1) return -1;
1999     if ((m = parse_addresspart(filter, &addressPart)) != 0)
2000       {
2001       if (m == -1) return -1;
2002       if (ap)
2003         {
2004         filter->errmsg = CUS "address part already specified";
2005         return -1;
2006         }
2007       else ap = 1;
2008       }
2009     else if ((m = parse_comparator(filter, &comparator)) != 0)
2010       {
2011       if (m == -1) return -1;
2012       if (co)
2013         {
2014         filter->errmsg = CUS "comparator already specified";
2015         return -1;
2016         }
2017       else co = 1;
2018       }
2019     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2020       {
2021       if (m == -1) return -1;
2022       if (mt)
2023         {
2024         filter->errmsg = CUS "match type already specified";
2025         return -1;
2026         }
2027       else mt = 1;
2028       }
2029     else break;
2030     }
2031   if (parse_white(filter) == -1)
2032     return -1;
2033   if ((m = parse_stringlist(filter, &hdr)) != 1)
2034     {
2035     if (m == 0) filter->errmsg = CUS "header string list expected";
2036     return -1;
2037     }
2038   if (parse_white(filter) == -1)
2039     return -1;
2040   if ((m = parse_stringlist(filter, &key)) != 1)
2041     {
2042     if (m == 0) filter->errmsg = CUS "key string list expected";
2043     return -1;
2044     }
2045   *cond = 0;
2046   for (gstring * h = hdr; h->ptr != -1 && !*cond; ++h)
2047     {
2048     uschar * header_value = NULL, * extracted_addr, * end_addr;
2049
2050     if (  !eq_asciicase(h, &str_from, FALSE)
2051        && !eq_asciicase(h, &str_to, FALSE)
2052        && !eq_asciicase(h, &str_cc, FALSE)
2053        && !eq_asciicase(h, &str_bcc, FALSE)
2054        && !eq_asciicase(h, &str_sender, FALSE)
2055        && !eq_asciicase(h, &str_resent_from, FALSE)
2056        && !eq_asciicase(h, &str_resent_to, FALSE)
2057        )
2058       {
2059       filter->errmsg = CUS "invalid header field";
2060       return -1;
2061       }
2062     if (exec)
2063       {
2064       /* We are only interested in addresses below, so no MIME decoding */
2065       if (!(header_value = expand_string(string_sprintf("$rheader_%s", quote(h)))))
2066         {
2067         filter->errmsg = CUS "header string expansion failed";
2068         return -1;
2069         }
2070       f.parse_allow_group = TRUE;
2071       while (*header_value && !*cond)
2072         {
2073         uschar *error;
2074         int start, end, domain;
2075         int saveend;
2076         uschar *part = NULL;
2077
2078         end_addr = parse_find_address_end(header_value, FALSE);
2079         saveend = *end_addr;
2080         *end_addr = 0;
2081         extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2082
2083         if (extracted_addr) switch (addressPart)
2084           {
2085           case ADDRPART_ALL: part = extracted_addr; break;
2086 #ifdef SUBADDRESS
2087           case ADDRPART_USER:
2088 #endif
2089           case ADDRPART_LOCALPART: part = extracted_addr; part[domain-1] = '\0'; break;
2090           case ADDRPART_DOMAIN: part = extracted_addr+domain; break;
2091 #ifdef SUBADDRESS
2092           case ADDRPART_DETAIL: part = NULL; break;
2093 #endif
2094           }
2095
2096         *end_addr = saveend;
2097         if (part && extracted_addr)
2098           {
2099           gstring partStr = {.s = part, .ptr = Ustrlen(part), .size = Ustrlen(part)+1};
2100           for (gstring * k = key; k->ptr != - 1; ++k)
2101             {
2102             *cond = compare(filter, k, &partStr, comparator, matchType);
2103             if (*cond == -1) return -1;
2104             if (*cond) break;
2105             }
2106           }
2107
2108         if (saveend == 0) break;
2109         header_value = end_addr + 1;
2110         }
2111       f.parse_allow_group = FALSE;
2112       f.parse_found_group = FALSE;
2113       }
2114     }
2115   return 1;
2116   }
2117 else if (parse_identifier(filter, CUS "allof"))
2118   {
2119   /*
2120   allof-test   = "allof" <tests: test-list>
2121   */
2122
2123   int n, num_true;
2124
2125   switch (parse_testlist(filter, &n, &num_true, exec))
2126     {
2127     case -1: return -1;
2128     case 0: filter->errmsg = CUS "missing test list"; return -1;
2129     default: *cond = (n == num_true); return 1;
2130     }
2131   }
2132 else if (parse_identifier(filter, CUS "anyof"))
2133   {
2134   /*
2135   anyof-test   = "anyof" <tests: test-list>
2136   */
2137
2138   int n, num_true;
2139
2140   switch (parse_testlist(filter, &n, &num_true, exec))
2141     {
2142     case -1: return -1;
2143     case 0: filter->errmsg = CUS "missing test list"; return -1;
2144     default: *cond = (num_true>0); return 1;
2145     }
2146   }
2147 else if (parse_identifier(filter, CUS "exists"))
2148   {
2149   /*
2150   exists-test = "exists" <header-names: string-list>
2151   */
2152
2153   gstring *hdr;
2154   int m;
2155
2156   if (parse_white(filter) == -1)
2157     return -1;
2158   if ((m = parse_stringlist(filter, &hdr)) != 1)
2159     {
2160     if (m == 0) filter->errmsg = CUS "header string list expected";
2161     return -1;
2162     }
2163   if (exec)
2164     {
2165     *cond = 1;
2166     for (gstring * h = hdr; h->ptr != -1 && *cond; ++h)
2167       {
2168       uschar *header_def;
2169
2170       header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}", quote(h)));
2171       if (!header_def)
2172         {
2173         filter->errmsg = CUS "header string expansion failed";
2174         return -1;
2175         }
2176       if (Ustrcmp(header_def,"false") == 0) *cond = 0;
2177       }
2178     }
2179   return 1;
2180   }
2181 else if (parse_identifier(filter, CUS "false"))
2182   {
2183   /*
2184   false-test = "false"
2185   */
2186
2187   *cond = 0;
2188   return 1;
2189   }
2190 else if (parse_identifier(filter, CUS "header"))
2191   {
2192   /*
2193   header-test = "header" { [comparator] [match-type] }
2194                 <header-names: string-list> <key-list: string-list>
2195   */
2196
2197   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
2198   enum MatchType matchType = MATCH_IS;
2199   gstring *hdr, *key;
2200   int m;
2201   int co = 0, mt = 0;
2202
2203   for (;;)
2204     {
2205     if (parse_white(filter) == -1)
2206       return -1;
2207     if ((m = parse_comparator(filter, &comparator)) != 0)
2208       {
2209       if (m == -1) return -1;
2210       if (co)
2211         {
2212         filter->errmsg = CUS "comparator already specified";
2213         return -1;
2214         }
2215       else co = 1;
2216       }
2217     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2218       {
2219       if (m == -1) return -1;
2220       if (mt)
2221         {
2222         filter->errmsg = CUS "match type already specified";
2223         return -1;
2224         }
2225       else mt = 1;
2226       }
2227     else break;
2228     }
2229   if (parse_white(filter) == -1)
2230     return -1;
2231   if ((m = parse_stringlist(filter, &hdr)) != 1)
2232     {
2233     if (m == 0) filter->errmsg = CUS "header string list expected";
2234     return -1;
2235     }
2236   if (parse_white(filter) == -1)
2237     return -1;
2238   if ((m = parse_stringlist(filter, &key)) != 1)
2239     {
2240     if (m == 0) filter->errmsg = CUS "key string list expected";
2241     return -1;
2242     }
2243   *cond = 0;
2244   for (gstring * h = hdr; h->ptr != -1 && !*cond; ++h)
2245     {
2246     if (!is_header(h))
2247       {
2248       filter->errmsg = CUS "invalid header field";
2249       return -1;
2250       }
2251     if (exec)
2252       {
2253       gstring header_value;
2254       uschar *header_def;
2255
2256       expand_header(&header_value, h);
2257       header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}", quote(h)));
2258       if (!header_value.s || !header_def)
2259         {
2260         filter->errmsg = CUS "header string expansion failed";
2261         return -1;
2262         }
2263       for (gstring * k = key; k->ptr != -1; ++k)
2264         if (Ustrcmp(header_def,"true") == 0)
2265           {
2266           *cond = compare(filter, k, &header_value, comparator, matchType);
2267           if (*cond == -1) return -1;
2268           if (*cond) break;
2269           }
2270       }
2271     }
2272   return 1;
2273   }
2274 else if (parse_identifier(filter, CUS "not"))
2275   {
2276   if (parse_white(filter) == -1) return -1;
2277   switch (parse_test(filter, cond, exec))
2278     {
2279     case -1: return -1;
2280     case 0: filter->errmsg = CUS "missing test"; return -1;
2281     default: *cond = !*cond; return 1;
2282     }
2283   }
2284 else if (parse_identifier(filter, CUS "size"))
2285   {
2286   /*
2287   relop = ":over" / ":under"
2288   size-test = "size" relop <limit: number>
2289   */
2290
2291   unsigned long limit;
2292   int overNotUnder;
2293
2294   if (parse_white(filter) == -1) return -1;
2295   if (parse_identifier(filter, CUS ":over")) overNotUnder = 1;
2296   else if (parse_identifier(filter, CUS ":under")) overNotUnder = 0;
2297   else
2298     {
2299     filter->errmsg = CUS "missing :over or :under";
2300     return -1;
2301     }
2302   if (parse_white(filter) == -1) return -1;
2303   if (parse_number(filter, &limit) == -1) return -1;
2304   *cond = (overNotUnder ? (message_size>limit) : (message_size<limit));
2305   return 1;
2306   }
2307 else if (parse_identifier(filter, CUS "true"))
2308   {
2309   *cond = 1;
2310   return 1;
2311   }
2312 else if (parse_identifier(filter, CUS "envelope"))
2313   {
2314   /*
2315   envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2316                   <envelope-part: string-list> <key-list: string-list>
2317
2318   envelope-part is case insensitive "from" or "to"
2319 #ifdef ENVELOPE_AUTH
2320   envelope-part = / "auth"
2321 #endif
2322   */
2323
2324   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
2325   enum AddressPart addressPart = ADDRPART_ALL;
2326   enum MatchType matchType = MATCH_IS;
2327   gstring *env, *key;
2328   int m;
2329   int co = 0, ap = 0, mt = 0;
2330
2331   if (!filter->require_envelope)
2332     {
2333     filter->errmsg = CUS "missing previous require \"envelope\";";
2334     return -1;
2335     }
2336   for (;;)
2337     {
2338     if (parse_white(filter) == -1) return -1;
2339     if ((m = parse_comparator(filter, &comparator)) != 0)
2340       {
2341       if (m == -1) return -1;
2342       if (co)
2343         {
2344         filter->errmsg = CUS "comparator already specified";
2345         return -1;
2346         }
2347       else co = 1;
2348       }
2349     else if ((m = parse_addresspart(filter, &addressPart)) != 0)
2350       {
2351       if (m == -1) return -1;
2352       if (ap)
2353         {
2354         filter->errmsg = CUS "address part already specified";
2355         return -1;
2356         }
2357       else ap = 1;
2358       }
2359     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2360       {
2361       if (m == -1) return -1;
2362       if (mt)
2363         {
2364         filter->errmsg = CUS "match type already specified";
2365         return -1;
2366         }
2367       else mt = 1;
2368       }
2369     else break;
2370     }
2371   if (parse_white(filter) == -1)
2372     return -1;
2373   if ((m = parse_stringlist(filter, &env)) != 1)
2374     {
2375     if (m == 0) filter->errmsg = CUS "envelope string list expected";
2376     return -1;
2377     }
2378   if (parse_white(filter) == -1)
2379     return -1;
2380   if ((m = parse_stringlist(filter, &key)) != 1)
2381     {
2382     if (m == 0) filter->errmsg = CUS "key string list expected";
2383     return -1;
2384     }
2385   *cond = 0;
2386   for (gstring * e = env; e->ptr != -1 && !*cond; ++e)
2387     {
2388     const uschar *envelopeExpr = CUS 0;
2389     uschar *envelope = US 0;
2390
2391     if (eq_asciicase(e, &str_from, FALSE))
2392       {
2393       switch (addressPart)
2394         {
2395         case ADDRPART_ALL: envelopeExpr = CUS "$sender_address"; break;
2396 #ifdef SUBADDRESS
2397         case ADDRPART_USER:
2398 #endif
2399         case ADDRPART_LOCALPART: envelopeExpr = CUS "${local_part:$sender_address}"; break;
2400         case ADDRPART_DOMAIN: envelopeExpr = CUS "${domain:$sender_address}"; break;
2401 #ifdef SUBADDRESS
2402         case ADDRPART_DETAIL: envelopeExpr = CUS 0; break;
2403 #endif
2404         }
2405       }
2406     else if (eq_asciicase(e, &str_to, FALSE))
2407       {
2408       switch (addressPart)
2409         {
2410         case ADDRPART_ALL: envelopeExpr = CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2411 #ifdef SUBADDRESS
2412         case ADDRPART_USER: envelopeExpr = filter->useraddress; break;
2413         case ADDRPART_DETAIL: envelopeExpr = filter->subaddress; break;
2414 #endif
2415         case ADDRPART_LOCALPART: envelopeExpr = CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2416         case ADDRPART_DOMAIN: envelopeExpr = CUS "$domain"; break;
2417         }
2418       }
2419 #ifdef ENVELOPE_AUTH
2420     else if (eq_asciicase(e, &str_auth, FALSE))
2421       {
2422       switch (addressPart)
2423         {
2424         case ADDRPART_ALL: envelopeExpr = CUS "$authenticated_sender"; break;
2425 #ifdef SUBADDRESS
2426         case ADDRPART_USER:
2427 #endif
2428         case ADDRPART_LOCALPART: envelopeExpr = CUS "${local_part:$authenticated_sender}"; break;
2429         case ADDRPART_DOMAIN: envelopeExpr = CUS "${domain:$authenticated_sender}"; break;
2430 #ifdef SUBADDRESS
2431         case ADDRPART_DETAIL: envelopeExpr = CUS 0; break;
2432 #endif
2433         }
2434       }
2435 #endif
2436     else
2437       {
2438       filter->errmsg = CUS "invalid envelope string";
2439       return -1;
2440       }
2441     if (exec && envelopeExpr)
2442       {
2443       if (!(envelope = expand_string(US envelopeExpr)))
2444         {
2445         filter->errmsg = CUS "header string expansion failed";
2446         return -1;
2447         }
2448       for (gstring * k = key; k->ptr != -1; ++k)
2449         {
2450         gstring envelopeStr = {.s = envelope, .ptr = Ustrlen(envelope), .size = Ustrlen(envelope)+1};
2451
2452         *cond = compare(filter, k, &envelopeStr, comparator, matchType);
2453         if (*cond == -1) return -1;
2454         if (*cond) break;
2455         }
2456       }
2457     }
2458   return 1;
2459   }
2460 #ifdef ENOTIFY
2461 else if (parse_identifier(filter, CUS "valid_notify_method"))
2462   {
2463   /*
2464   valid_notify_method = "valid_notify_method"
2465                         <notification-uris: string-list>
2466   */
2467
2468   gstring *uris;
2469   int m;
2470
2471   if (!filter->require_enotify)
2472     {
2473     filter->errmsg = CUS "missing previous require \"enotify\";";
2474     return -1;
2475     }
2476   if (parse_white(filter) == -1)
2477     return -1;
2478   if ((m = parse_stringlist(filter, &uris)) != 1)
2479     {
2480     if (m == 0) filter->errmsg = CUS "URI string list expected";
2481     return -1;
2482     }
2483   if (exec)
2484     {
2485     *cond = 1;
2486     for (gstring * u = uris; u->ptr != -1 && *cond; ++u)
2487       {
2488         string_item * recipient = NULL;
2489         gstring header =  { .s = NULL, .ptr = -1 };
2490         gstring subject = { .s = NULL, .ptr = -1 };
2491         gstring body =    { .s = NULL, .ptr = -1 };
2492
2493         if (parse_mailto_uri(filter, u->s, &recipient, &header, &subject, &body) != 1)
2494           *cond = 0;
2495       }
2496     }
2497   return 1;
2498   }
2499 else if (parse_identifier(filter, CUS "notify_method_capability"))
2500   {
2501   /*
2502   notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2503                              <notification-uri: string>
2504                              <notification-capability: string>
2505                              <key-list: string-list>
2506   */
2507
2508   int m;
2509   int co = 0, mt = 0;
2510
2511   enum Comparator comparator = COMP_EN_ASCII_CASEMAP;
2512   enum MatchType matchType = MATCH_IS;
2513   gstring uri, capa, *keys;
2514
2515   if (!filter->require_enotify)
2516     {
2517     filter->errmsg = CUS "missing previous require \"enotify\";";
2518     return -1;
2519     }
2520   for (;;)
2521     {
2522     if (parse_white(filter) == -1) return -1;
2523     if ((m = parse_comparator(filter, &comparator)) != 0)
2524       {
2525       if (m == -1) return -1;
2526       if (co)
2527         {
2528         filter->errmsg = CUS "comparator already specified";
2529         return -1;
2530         }
2531       else co = 1;
2532       }
2533     else if ((m = parse_matchtype(filter, &matchType)) != 0)
2534       {
2535       if (m == -1) return -1;
2536       if (mt)
2537         {
2538         filter->errmsg = CUS "match type already specified";
2539         return -1;
2540         }
2541       else mt = 1;
2542       }
2543     else break;
2544     }
2545     if ((m = parse_string(filter, &uri)) != 1)
2546       {
2547       if (m == 0) filter->errmsg = CUS "missing notification URI string";
2548       return -1;
2549       }
2550     if (parse_white(filter) == -1)
2551       return -1;
2552     if ((m = parse_string(filter, &capa)) != 1)
2553       {
2554       if (m == 0) filter->errmsg = CUS "missing notification capability string";
2555       return -1;
2556       }
2557     if (parse_white(filter) == -1)
2558       return -1;
2559     if ((m = parse_stringlist(filter, &keys)) != 1)
2560       {
2561       if (m == 0) filter->errmsg = CUS "missing key string list";
2562       return -1;
2563       }
2564     if (exec)
2565       {
2566       string_item * recipient = NULL;
2567       gstring header =  { .s = NULL, .ptr = -1 };
2568       gstring subject = { .s = NULL, .ptr = -1 };
2569       gstring body =    { .s = NULL, .ptr = -1 };
2570
2571       *cond = 0;
2572       if (parse_mailto_uri(filter, uri.s, &recipient, &header, &subject, &body) == 1)
2573         if (eq_asciicase(&capa, &str_online,  FALSE) == 1)
2574           for (gstring * k = keys; k->ptr != -1; ++k)
2575             {
2576             *cond = compare(filter, k, &str_maybe, comparator, matchType);
2577             if (*cond == -1) return -1;
2578             if (*cond) break;
2579             }
2580       }
2581     return 1;
2582   }
2583 #endif
2584 else return 0;
2585 }
2586
2587
2588 /*************************************************
2589 *     Parse and interpret an optional block      *
2590 *************************************************/
2591
2592 /*
2593 Arguments:
2594   filter      points to the Sieve filter including its state
2595   exec        Execute parsed statements
2596   generated   where to hang newly-generated addresses
2597
2598 Returns:      2                success by stop
2599               1                other success
2600               0                no block command found
2601               -1               syntax or execution error
2602 */
2603
2604 static int
2605 parse_block(struct Sieve * filter, int exec, address_item ** generated)
2606 {
2607 int r;
2608
2609 if (parse_white(filter) == -1)
2610   return -1;
2611 if (*filter->pc == '{')
2612   {
2613   ++filter->pc;
2614   if ((r = parse_commands(filter, exec, generated)) == -1 || r == 2) return r;
2615   if (*filter->pc == '}')
2616     {
2617     ++filter->pc;
2618     return 1;
2619     }
2620   filter->errmsg = CUS "expecting command or closing brace";
2621   return -1;
2622   }
2623 return 0;
2624 }
2625
2626
2627 /*************************************************
2628 *           Match a semicolon                    *
2629 *************************************************/
2630
2631 /*
2632 Arguments:
2633   filter      points to the Sieve filter including its state
2634
2635 Returns:      1                success
2636               -1               syntax error
2637 */
2638
2639 static int
2640 parse_semicolon(struct Sieve *filter)
2641 {
2642 if (parse_white(filter) == -1)
2643   return -1;
2644 if (*filter->pc == ';')
2645   {
2646   ++filter->pc;
2647   return 1;
2648   }
2649 filter->errmsg = CUS "missing semicolon";
2650 return -1;
2651 }
2652
2653
2654 /*************************************************
2655 *     Parse and interpret a Sieve command        *
2656 *************************************************/
2657
2658 /*
2659 Arguments:
2660   filter      points to the Sieve filter including its state
2661   exec        Execute parsed statements
2662   generated   where to hang newly-generated addresses
2663
2664 Returns:      2                success by stop
2665               1                other success
2666               -1               syntax or execution error
2667 */
2668 static int
2669 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2670 {
2671 while (*filter->pc)
2672   {
2673   if (parse_white(filter) == -1)
2674     return -1;
2675   if (parse_identifier(filter, CUS "if"))
2676     {
2677     /*
2678     if-command = "if" test block *( "elsif" test block ) [ else block ]
2679     */
2680
2681     int cond, m, unsuccessful;
2682
2683     /* test block */
2684     if (parse_white(filter) == -1)
2685       return -1;
2686     if ((m = parse_test(filter, &cond, exec)) == -1)
2687       return -1;
2688     if (m == 0)
2689       {
2690       filter->errmsg = CUS "missing test";
2691       return -1;
2692       }
2693     if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2694         (debug_selector & D_filter) != 0)
2695       {
2696       if (exec) debug_printf_indent("if %s\n", cond?"true":"false");
2697       }
2698     m = parse_block(filter, exec ? cond : 0, generated);
2699     if (m == -1 || m == 2)
2700       return m;
2701     if (m == 0)
2702       {
2703       filter->errmsg = CUS "missing block";
2704       return -1;
2705       }
2706     unsuccessful = !cond;
2707     for (;;) /* elsif test block */
2708       {
2709       if (parse_white(filter) == -1)
2710         return -1;
2711       if (parse_identifier(filter, CUS "elsif"))
2712         {
2713         if (parse_white(filter) == -1)
2714           return -1;
2715         m = parse_test(filter, &cond, exec && unsuccessful);
2716         if (m == -1 || m == 2)
2717           return m;
2718         if (m == 0)
2719           {
2720           filter->errmsg = CUS "missing test";
2721           return -1;
2722           }
2723         if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2724             (debug_selector & D_filter) != 0)
2725           {
2726           if (exec) debug_printf_indent("elsif %s\n", cond?"true":"false");
2727           }
2728         m = parse_block(filter, exec && unsuccessful ? cond : 0, generated);
2729         if (m == -1 || m == 2)
2730           return m;
2731         if (m == 0)
2732           {
2733           filter->errmsg = CUS "missing block";
2734           return -1;
2735           }
2736         if (exec && unsuccessful && cond)
2737           unsuccessful = 0;
2738         }
2739       else break;
2740       }
2741     /* else block */
2742     if (parse_white(filter) == -1)
2743       return -1;
2744     if (parse_identifier(filter, CUS "else"))
2745       {
2746       m = parse_block(filter, exec && unsuccessful, generated);
2747       if (m == -1 || m == 2)
2748         return m;
2749       if (m == 0)
2750         {
2751         filter->errmsg = CUS "missing block";
2752         return -1;
2753         }
2754       }
2755     }
2756   else if (parse_identifier(filter, CUS "stop"))
2757     {
2758     /*
2759     stop-command     =  "stop" { stop-options } ";"
2760     stop-options     =
2761     */
2762
2763     if (parse_semicolon(filter) == -1)
2764       return -1;
2765     if (exec)
2766       {
2767       filter->pc += Ustrlen(filter->pc);
2768       return 2;
2769       }
2770     }
2771   else if (parse_identifier(filter, CUS "keep"))
2772     {
2773     /*
2774     keep-command     =  "keep" { keep-options } ";"
2775     keep-options     =
2776     */
2777
2778     if (parse_semicolon(filter) == -1)
2779       return -1;
2780     if (exec)
2781       {
2782       add_addr(generated, filter->inbox, 1, 0, 0, 0);
2783       filter->keep = 0;
2784       }
2785     }
2786   else if (parse_identifier(filter, CUS "discard"))
2787     {
2788     /*
2789     discard-command  =  "discard" { discard-options } ";"
2790     discard-options  =
2791     */
2792
2793     if (parse_semicolon(filter) == -1)
2794       return -1;
2795     if (exec) filter->keep = 0;
2796     }
2797   else if (parse_identifier(filter, CUS "redirect"))
2798     {
2799     /*
2800     redirect-command =  "redirect" redirect-options "string" ";"
2801     redirect-options =
2802     redirect-options = ) ":copy"
2803     */
2804
2805     gstring recipient;
2806     int m;
2807     int copy = 0;
2808
2809     for (;;)
2810       {
2811       if (parse_white(filter) == -1)
2812         return -1;
2813       if (parse_identifier(filter, CUS ":copy") == 1)
2814         {
2815         if (!filter->require_copy)
2816           {
2817           filter->errmsg = CUS "missing previous require \"copy\";";
2818           return -1;
2819           }
2820         copy = 1;
2821         }
2822       else break;
2823       }
2824     if (parse_white(filter) == -1)
2825       return -1;
2826     if ((m = parse_string(filter, &recipient)) != 1)
2827       {
2828       if (m == 0)
2829         filter->errmsg = CUS "missing redirect recipient string";
2830       return -1;
2831       }
2832     if (strchr(CCS recipient.s, '@') == NULL)
2833       {
2834       filter->errmsg = CUS "unqualified recipient address";
2835       return -1;
2836       }
2837     if (exec)
2838       {
2839       add_addr(generated, recipient.s, 0, 0, 0, 0);
2840       if (!copy) filter->keep = 0;
2841       }
2842     if (parse_semicolon(filter) == -1) return -1;
2843     }
2844   else if (parse_identifier(filter, CUS "fileinto"))
2845     {
2846     /*
2847     fileinto-command =  "fileinto" { fileinto-options } string ";"
2848     fileinto-options =
2849     fileinto-options = ) [ ":copy" ]
2850     */
2851
2852     gstring folder;
2853     uschar *s;
2854     int m;
2855     unsigned long maxage, maxmessages, maxstorage;
2856     int copy = 0;
2857
2858     maxage = maxmessages = maxstorage = 0;
2859     if (!filter->require_fileinto)
2860       {
2861       filter->errmsg = CUS "missing previous require \"fileinto\";";
2862       return -1;
2863       }
2864     for (;;)
2865       {
2866       if (parse_white(filter) == -1)
2867         return -1;
2868       if (parse_identifier(filter, CUS ":copy") == 1)
2869         {
2870         if (!filter->require_copy)
2871           {
2872           filter->errmsg = CUS "missing previous require \"copy\";";
2873           return -1;
2874           }
2875           copy = 1;
2876         }
2877       else break;
2878       }
2879     if (parse_white(filter) == -1)
2880       return -1;
2881     if ((m = parse_string(filter, &folder)) != 1)
2882       {
2883       if (m == 0) filter->errmsg = CUS "missing fileinto folder string";
2884       return -1;
2885       }
2886     m = 0; s = folder.s;
2887     if (folder.ptr == 0)
2888       m = 1;
2889     if (Ustrcmp(s,"..") == 0 || Ustrncmp(s,"../", 3) == 0)
2890       m = 1;
2891     else while (*s)
2892       {
2893       if (Ustrcmp(s,"/..") == 0 || Ustrncmp(s,"/../", 4) == 0) { m = 1; break; }
2894       ++s;
2895       }
2896     if (m)
2897       {
2898       filter->errmsg = CUS "invalid folder";
2899       return -1;
2900       }
2901     if (exec)
2902       {
2903       add_addr(generated, folder.s, 1, maxage, maxmessages, maxstorage);
2904       if (!copy) filter->keep = 0;
2905       }
2906     if (parse_semicolon(filter) == -1)
2907       return -1;
2908     }
2909 #ifdef ENOTIFY
2910   else if (parse_identifier(filter, CUS "notify"))
2911     {
2912     /*
2913     notify-command =  "notify" { notify-options } <method: string> ";"
2914     notify-options =  [":from" string]
2915                       [":importance" <"1" / "2" / "3">]
2916                       [":options" 1*(string-list / number)]
2917                       [":message" string]
2918     */
2919
2920     int m;
2921     gstring from =       { .s = NULL, .ptr = -1 };
2922     gstring importance = { .s = NULL, .ptr = -1 };
2923     gstring message =    { .s = NULL, .ptr = -1 };
2924     gstring method;
2925     struct Notification *already;
2926     string_item * recipient = NULL;
2927     gstring header =     { .s = NULL, .ptr = -1 };
2928     gstring subject =    { .s = NULL, .ptr = -1 };
2929     gstring body =       { .s = NULL, .ptr = -1 };
2930     uschar *envelope_from;
2931     gstring auto_submitted_value;
2932     uschar *auto_submitted_def;
2933
2934     if (!filter->require_enotify)
2935       {
2936       filter->errmsg = CUS "missing previous require \"enotify\";";
2937       return -1;
2938       }
2939     envelope_from = sender_address && sender_address[0]
2940      ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2941     if (!envelope_from)
2942       {
2943       filter->errmsg = CUS "expansion failure for envelope from";
2944       return -1;
2945       }
2946     for (;;)
2947       {
2948       if (parse_white(filter) == -1)
2949         return -1;
2950       if (parse_identifier(filter, CUS ":from") == 1)
2951         {
2952         if (parse_white(filter) == -1)
2953           return -1;
2954         if ((m = parse_string(filter, &from)) != 1)
2955           {
2956           if (m == 0) filter->errmsg = CUS "from string expected";
2957           return -1;
2958           }
2959         }
2960       else if (parse_identifier(filter, CUS ":importance") == 1)
2961         {
2962         if (parse_white(filter) == -1)
2963           return -1;
2964         if ((m = parse_string(filter, &importance)) != 1)
2965           {
2966           if (m == 0)
2967             filter->errmsg = CUS "importance string expected";
2968           return -1;
2969           }
2970         if (importance.ptr != 1 || importance.s[0] < '1' || importance.s[0] > '3')
2971           {
2972           filter->errmsg = CUS "invalid importance";
2973           return -1;
2974           }
2975         }
2976       else if (parse_identifier(filter, CUS ":options") == 1)
2977         {
2978         if (parse_white(filter) == -1)
2979           return -1;
2980         }
2981       else if (parse_identifier(filter, CUS ":message") == 1)
2982         {
2983         if (parse_white(filter) == -1)
2984           return -1;
2985         if ((m = parse_string(filter, &message)) != 1)
2986           {
2987           if (m == 0)
2988             filter->errmsg = CUS "message string expected";
2989           return -1;
2990           }
2991         }
2992       else break;
2993       }
2994     if (parse_white(filter) == -1)
2995       return -1;
2996     if ((m = parse_string(filter, &method)) != 1)
2997       {
2998       if (m == 0)
2999         filter->errmsg = CUS "missing method string";
3000       return -1;
3001       }
3002     if (parse_semicolon(filter) == -1)
3003       return -1;
3004     if (parse_mailto_uri(filter, method.s, &recipient, &header, &subject, &body) != 1)
3005       return -1;
3006     if (exec)
3007       {
3008       if (message.ptr == -1)
3009         message = subject;
3010       if (message.ptr == -1)
3011         expand_header(&message, &str_subject);
3012       expand_header(&auto_submitted_value, &str_auto_submitted);
3013       auto_submitted_def = expand_string(US"${if def:header_auto-submitted {true}{false}}");
3014       if (!auto_submitted_value.s || !auto_submitted_def)
3015         {
3016         filter->errmsg = CUS "header string expansion failed";
3017         return -1;
3018         }
3019         if (Ustrcmp(auto_submitted_def,"true") != 0 || Ustrcmp(auto_submitted_value.s,"no") == 0)
3020         {
3021         for (already = filter->notified; already; already = already->next)
3022           {
3023           if (   already->method.ptr == method.ptr
3024               && (method.ptr == -1 || Ustrcmp(already->method.s, method.s) == 0)
3025               && already->importance.ptr == importance.ptr
3026               && (importance.ptr == -1 || Ustrcmp(already->importance.s, importance.s) == 0)
3027               && already->message.ptr == message.ptr
3028               && (message.ptr == -1 || Ustrcmp(already->message.s, message.s) == 0))
3029             break;
3030           }
3031         if (!already)
3032           /* New notification, process it */
3033           {
3034           struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED);
3035           sent->method = method;
3036           sent->importance = importance;
3037           sent->message = message;
3038           sent->next = filter->notified;
3039           filter->notified = sent;
3040   #ifndef COMPILE_SYNTAX_CHECKER
3041           if (filter_test == FTEST_NONE)
3042             {
3043             int pid, fd;
3044
3045             if ((pid = child_open_exim2(&fd, envelope_from, envelope_from,
3046                         US"sieve-notify")) >= 1)
3047               {
3048               FILE * f = fdopen(fd, "wb");
3049
3050               fprintf(f,"From: %s\n", from.ptr == -1
3051                 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
3052                 : from.s);
3053               for (string_item * p = recipient; p; p = p->next)
3054                 fprintf(f, "To: %s\n", p->text);
3055               fprintf(f, "Auto-Submitted: auto-notified; %s\n", filter->enotify_mailto_owner);
3056               if (header.ptr > 0) fprintf(f, "%s", header.s);
3057               if (message.ptr == -1)
3058                 {
3059                 message.s = US"Notification";
3060                 message.ptr = Ustrlen(message.s);
3061                 }
3062               if (message.ptr != -1)
3063                 fprintf(f, "Subject: %s\n", parse_quote_2047(message.s,
3064                   message.ptr, US"utf-8", TRUE));
3065               fprintf(f,"\n");
3066               if (body.ptr > 0) fprintf(f, "%s\n", body.s);
3067               fflush(f);
3068               (void)fclose(f);
3069               (void)child_close(pid, 0);
3070               }
3071             }
3072           if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3073             debug_printf_indent("Notification to `%s': '%s'.\n", method.s, message.ptr != -1 ? message.s : CUS "");
3074 #endif
3075           }
3076         else
3077           if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3078             debug_printf_indent("Repeated notification to `%s' ignored.\n", method.s);
3079         }
3080       else
3081         if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3082           debug_printf_indent("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3083       }
3084     }
3085 #endif
3086 #ifdef VACATION
3087   else if (parse_identifier(filter, CUS "vacation"))
3088     {
3089     /*
3090     vacation-command =  "vacation" { vacation-options } <reason: string> ";"
3091     vacation-options =  [":days" number]
3092                         [":subject" string]
3093                         [":from" string]
3094                         [":addresses" string-list]
3095                         [":mime"]
3096                         [":handle" string]
3097     */
3098
3099     int m;
3100     unsigned long days;
3101     gstring subject;
3102     gstring from;
3103     gstring *addresses;
3104     int reason_is_mime;
3105     string_item *aliases;
3106     gstring handle;
3107     gstring reason;
3108
3109     if (!filter->require_vacation)
3110       {
3111       filter->errmsg = CUS "missing previous require \"vacation\";";
3112       return -1;
3113       }
3114     if (exec)
3115       {
3116       if (filter->vacation_ran)
3117         {
3118         filter->errmsg = CUS "trying to execute vacation more than once";
3119         return -1;
3120         }
3121       filter->vacation_ran = TRUE;
3122       }
3123     days = VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3124     subject.s = (uschar*)0;
3125     subject.ptr = -1;
3126     from.s = (uschar*)0;
3127     from.ptr = -1;
3128     addresses = (gstring*)0;
3129     aliases = NULL;
3130     reason_is_mime = 0;
3131     handle.s = (uschar*)0;
3132     handle.ptr = -1;
3133     for (;;)
3134       {
3135       if (parse_white(filter) == -1)
3136         return -1;
3137       if (parse_identifier(filter, CUS ":days") == 1)
3138         {
3139         if (parse_white(filter) == -1)
3140           return -1;
3141         if (parse_number(filter, &days) == -1)
3142           return -1;
3143         if (days<VACATION_MIN_DAYS)
3144           days = VACATION_MIN_DAYS;
3145         else if (days>VACATION_MAX_DAYS)
3146           days = VACATION_MAX_DAYS;
3147         }
3148       else if (parse_identifier(filter, CUS ":subject") == 1)
3149         {
3150         if (parse_white(filter) == -1)
3151           return -1;
3152         if ((m = parse_string(filter, &subject)) != 1)
3153           {
3154           if (m == 0)
3155             filter->errmsg = CUS "subject string expected";
3156           return -1;
3157           }
3158         }
3159       else if (parse_identifier(filter, CUS ":from") == 1)
3160         {
3161         if (parse_white(filter) == -1)
3162           return -1;
3163         if ((m = parse_string(filter, &from)) != 1)
3164           {
3165           if (m == 0)
3166             filter->errmsg = CUS "from string expected";
3167           return -1;
3168           }
3169         if (check_mail_address(filter, &from) != 1)
3170           return -1;
3171         }
3172       else if (parse_identifier(filter, CUS ":addresses") == 1)
3173         {
3174         if (parse_white(filter) == -1)
3175           return -1;
3176         if ((m = parse_stringlist(filter, &addresses)) != 1)
3177           {
3178           if (m == 0)
3179             filter->errmsg = CUS "addresses string list expected";
3180           return -1;
3181           }
3182         for (gstring * a = addresses; a->ptr != -1; ++a)
3183           {
3184           string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
3185
3186           new->text = store_get(a->ptr+1, a->s);
3187           if (a->ptr) memcpy(new->text, a->s, a->ptr);
3188           new->text[a->ptr] = '\0';
3189           new->next = aliases;
3190           aliases = new;
3191           }
3192         }
3193       else if (parse_identifier(filter, CUS ":mime") == 1)
3194         reason_is_mime = 1;
3195       else if (parse_identifier(filter, CUS ":handle") == 1)
3196         {
3197         if (parse_white(filter) == -1)
3198           return -1;
3199         if ((m = parse_string(filter, &from)) != 1)
3200           {
3201           if (m == 0)
3202             filter->errmsg = CUS "handle string expected";
3203           return -1;
3204           }
3205         }
3206       else break;
3207       }
3208     if (parse_white(filter) == -1)
3209       return -1;
3210     if ((m = parse_string(filter, &reason)) != 1)
3211       {
3212       if (m == 0)
3213         filter->errmsg = CUS "missing reason string";
3214       return -1;
3215       }
3216     if (reason_is_mime)
3217       {
3218       uschar *s, *end;
3219
3220       for (s = reason.s, end = reason.s + reason.ptr;
3221           s<end && (*s&0x80) == 0; ) s++;
3222       if (s<end)
3223         {
3224         filter->errmsg = CUS "MIME reason string contains 8bit text";
3225         return -1;
3226         }
3227       }
3228     if (parse_semicolon(filter) == -1) return -1;
3229
3230     if (exec)
3231       {
3232       address_item *addr;
3233       md5 base;
3234       uschar digest[16];
3235       uschar hexdigest[33];
3236       gstring * once;
3237
3238       if (filter_personal(aliases, TRUE))
3239         {
3240         if (filter_test == FTEST_NONE)
3241           {
3242           /* ensure oncelog directory exists; failure will be detected later */
3243
3244           (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3245           }
3246         /* build oncelog filename */
3247
3248         md5_start(&base);
3249
3250         if (handle.ptr == -1)
3251           {
3252           gstring * key = NULL;
3253           if (subject.ptr != -1)
3254             key = string_catn(key, subject.s, subject.ptr);
3255           if (from.ptr != -1)
3256             key = string_catn(key, from.s, from.ptr);
3257           key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3258           key = string_catn(key, reason.s, reason.ptr);
3259           md5_end(&base, key->s, key->ptr, digest);
3260           }
3261         else
3262           md5_end(&base, handle.s, handle.ptr, digest);
3263
3264         for (int i = 0; i < 16; i++)
3265           sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3266
3267         if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3268           debug_printf_indent("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3269
3270         if (filter_test == FTEST_NONE)
3271           {
3272           once = string_cat (NULL, filter->vacation_directory);
3273           once = string_catn(once, US"/", 1);
3274           once = string_catn(once, hexdigest, 33);
3275
3276           /* process subject */
3277
3278           if (subject.ptr == -1)
3279             {
3280             uschar * subject_def;
3281
3282             subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3283             if (subject_def && Ustrcmp(subject_def,"true") == 0)
3284               {
3285               gstring * g = string_catn(NULL, US"Auto: ", 6);
3286
3287               expand_header(&subject, &str_subject);
3288               g = string_catn(g, subject.s, subject.ptr);
3289               subject.ptr = len_string_from_gstring(g, &subject.s);
3290               }
3291             else
3292               {
3293               subject.s = US"Automated reply";
3294               subject.ptr = Ustrlen(subject.s);
3295               }
3296             }
3297
3298           /* add address to list of generated addresses */
3299
3300           addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3301           setflag(addr, af_pfr);
3302           addr->prop.ignore_error = TRUE;
3303           addr->next = *generated;
3304           *generated = addr;
3305           addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
3306           memset(addr->reply, 0, sizeof(reply_item)); /* XXX */
3307           addr->reply->to = string_copy(sender_address);
3308           if (from.ptr == -1)
3309             addr->reply->from = expand_string(US"$local_part@$domain");
3310           else
3311             addr->reply->from = from.s;
3312           /* deconst cast safe as we pass in a non-const item */
3313           addr->reply->subject = US parse_quote_2047(subject.s, subject.ptr, US"utf-8", TRUE);
3314           addr->reply->oncelog = string_from_gstring(once);
3315           addr->reply->once_repeat = days*86400;
3316
3317           /* build body and MIME headers */
3318
3319           if (reason_is_mime)
3320             {
3321             uschar *mime_body, *reason_end;
3322             static const uschar nlnl[] = "\r\n\r\n";
3323
3324             for
3325               (
3326               mime_body = reason.s, reason_end = reason.s + reason.ptr;
3327               mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3328               ) mime_body++;
3329
3330             addr->reply->headers = string_copyn(reason.s, mime_body-reason.s);
3331
3332             if (mime_body+(sizeof(nlnl)-1)<reason_end)
3333               mime_body += (sizeof(nlnl)-1);
3334             else mime_body = reason_end-1;
3335             addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3336             }
3337           else
3338             {
3339             addr->reply->headers = US"MIME-Version: 1.0\n"
3340                                    "Content-Type: text/plain;\n"
3341                                    "\tcharset=\"utf-8\"\n"
3342                                    "Content-Transfer-Encoding: quoted-printable";
3343             addr->reply->text = quoted_printable_encode(&reason)->s;
3344             }
3345           }
3346         }
3347         else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3348           debug_printf_indent("Sieve: mail was not personal, vacation would ignore it\n");
3349       }
3350     }
3351     else break;
3352 #endif
3353   }
3354 return 1;
3355 }
3356
3357
3358 /*************************************************
3359 *       Parse and interpret a sieve filter       *
3360 *************************************************/
3361
3362 /*
3363 Arguments:
3364   filter      points to the Sieve filter including its state
3365   exec        Execute parsed statements
3366   generated   where to hang newly-generated addresses
3367
3368 Returns:      1                success
3369               -1               syntax or execution error
3370 */
3371
3372 static int
3373 parse_start(struct Sieve *filter, int exec, address_item **generated)
3374 {
3375 filter->pc = filter->filter;
3376 filter->line = 1;
3377 filter->keep = 1;
3378 filter->require_envelope = 0;
3379 filter->require_fileinto = 0;
3380 #ifdef ENCODED_CHARACTER
3381 filter->require_encoded_character = FALSE;
3382 #endif
3383 #ifdef ENVELOPE_AUTH
3384 filter->require_envelope_auth = 0;
3385 #endif
3386 #ifdef ENOTIFY
3387 filter->require_enotify = 0;
3388 filter->notified = (struct Notification*)0;
3389 #endif
3390 #ifdef SUBADDRESS
3391 filter->require_subaddress = FALSE;
3392 #endif
3393 #ifdef VACATION
3394 filter->require_vacation = FALSE;
3395 filter->vacation_ran = 0;               /*XXX missing init? */
3396 #endif
3397 filter->require_copy = FALSE;
3398 filter->require_iascii_numeric = FALSE;
3399
3400 if (parse_white(filter) == -1) return -1;
3401
3402 if (exec && filter->vacation_directory && filter_test == FTEST_NONE)
3403   {
3404   DIR *oncelogdir;
3405   struct dirent *oncelog;
3406   struct stat properties;
3407   time_t now;
3408
3409   /* clean up old vacation log databases */
3410
3411   if (  !(oncelogdir = exim_opendir(filter->vacation_directory))
3412      && errno != ENOENT)
3413     {
3414     filter->errmsg = CUS "unable to open vacation directory";
3415     return -1;
3416     }
3417
3418   if (oncelogdir)
3419     {
3420     time(&now);
3421
3422     while ((oncelog = readdir(oncelogdir)))
3423       if (strlen(oncelog->d_name) == 32)
3424         {
3425         uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name);
3426         if (Ustat(s, &properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now)
3427           Uunlink(s);
3428         }
3429     closedir(oncelogdir);
3430     }
3431   }
3432
3433 while (parse_identifier(filter, CUS "require"))
3434   {
3435   /*
3436   require-command = "require" <capabilities: string-list>
3437   */
3438
3439   gstring *cap;
3440   int m;
3441
3442   if (parse_white(filter) == -1) return -1;
3443   if ((m = parse_stringlist(filter, &cap)) != 1)
3444     {
3445     if (m == 0) filter->errmsg = CUS "capability string list expected";
3446     return -1;
3447     }
3448   for (gstring * check = cap; check->s; ++check)
3449     {
3450     if (eq_octet(check, &str_envelope, FALSE)) filter->require_envelope = 1;
3451     else if (eq_octet(check, &str_fileinto, FALSE)) filter->require_fileinto = 1;
3452 #ifdef ENCODED_CHARACTER
3453     else if (eq_octet(check, &str_encoded_character, FALSE)) filter->require_encoded_character = TRUE;
3454 #endif
3455 #ifdef ENVELOPE_AUTH
3456     else if (eq_octet(check, &str_envelope_auth, FALSE)) filter->require_envelope_auth = 1;
3457 #endif
3458 #ifdef ENOTIFY
3459     else if (eq_octet(check, &str_enotify, FALSE))
3460       {
3461       if (!filter->enotify_mailto_owner)
3462         {
3463         filter->errmsg = CUS "enotify disabled";
3464         return -1;
3465         }
3466         filter->require_enotify = 1;
3467       }
3468 #endif
3469 #ifdef SUBADDRESS
3470     else if (eq_octet(check, &str_subaddress, FALSE)) filter->require_subaddress = TRUE;
3471 #endif
3472 #ifdef VACATION
3473     else if (eq_octet(check, &str_vacation, FALSE))
3474       {
3475       if (filter_test == FTEST_NONE && !filter->vacation_directory)
3476         {
3477         filter->errmsg = CUS "vacation disabled";
3478         return -1;
3479         }
3480       filter->require_vacation = TRUE;
3481       }
3482 #endif
3483     else if (eq_octet(check, &str_copy, FALSE)) filter->require_copy = TRUE;
3484     else if (eq_octet(check, &str_comparator_ioctet, FALSE)) ;
3485     else if (eq_octet(check, &str_comparator_iascii_casemap, FALSE)) ;
3486     else if (eq_octet(check, &str_comparator_enascii_casemap, FALSE)) ;
3487     else if (eq_octet(check, &str_comparator_iascii_numeric, FALSE)) filter->require_iascii_numeric = TRUE;
3488     else
3489       {
3490       filter->errmsg = CUS "unknown capability";
3491       return -1;
3492       }
3493     }
3494     if (parse_semicolon(filter) == -1) return -1;
3495   }
3496   if (parse_commands(filter, exec, generated) == -1) return -1;
3497   if (*filter->pc)
3498     {
3499     filter->errmsg = CUS "syntax error";
3500     return -1;
3501     }
3502   return 1;
3503 }
3504
3505
3506 /*************************************************
3507 *            Interpret a sieve filter file       *
3508 *************************************************/
3509
3510 /*
3511 Arguments:
3512   filter      points to the entire file, read into store as a single string
3513   options     controls whether various special things are allowed, and requests
3514               special actions (not currently used)
3515   sieve
3516     vacation_directory          where to store vacation "once" files
3517     enotify_mailto_owner        owner of mailto notifications
3518     useraddress                 string expression for :user part of address
3519     subaddress                  string expression for :subaddress part of address
3520     inbox                       string expression for "keep"
3521   generated   where to hang newly-generated addresses
3522   error       where to pass back an error text
3523
3524 Returns:      FF_DELIVERED     success, a significant action was taken
3525               FF_NOTDELIVERED  success, no significant action
3526               FF_DEFER         defer requested
3527               FF_FAIL          fail requested
3528               FF_FREEZE        freeze requested
3529               FF_ERROR         there was a problem
3530 */
3531
3532 int
3533 sieve_interpret(const uschar * filter, int options, const sieve_block * sb,
3534   address_item ** generated, uschar ** error)
3535 {
3536 struct Sieve sieve;
3537 int r;
3538 uschar * msg;
3539
3540 DEBUG(D_route) debug_printf_indent("Sieve: start of processing\n");
3541 expand_level++;
3542 sieve.filter = filter;
3543
3544 GET_OPTION("sieve_vacation_directory");
3545 if (!sb || !sb->vacation_dir)
3546   sieve.vacation_directory = NULL;
3547 else if (!(sieve.vacation_directory = expand_cstring(sb->vacation_dir)))
3548   {
3549   *error = string_sprintf("failed to expand \"%s\" "
3550     "(sieve_vacation_directory): %s", sb->vacation_dir, expand_string_message);
3551   return FF_ERROR;
3552   }
3553
3554 GET_OPTION("sieve_vacation_directory");
3555 if (!sb || !sb->inbox)
3556   sieve.inbox = US"inbox";
3557 else if (!(sieve.inbox = expand_cstring(sb->inbox)))
3558   {
3559   *error = string_sprintf("failed to expand \"%s\" "
3560     "(sieve_inbox): %s", sb->inbox, expand_string_message);
3561   return FF_ERROR;
3562   }
3563
3564 GET_OPTION("sieve_enotify_mailto_owner");
3565 if (!sb || !sb->enotify_mailto_owner)
3566   sieve.enotify_mailto_owner = NULL;
3567 else if (!(sieve.enotify_mailto_owner = expand_cstring(sb->enotify_mailto_owner)))
3568   {
3569   *error = string_sprintf("failed to expand \"%s\" "
3570     "(sieve_enotify_mailto_owner): %s", sb->enotify_mailto_owner,
3571     expand_string_message);
3572   return FF_ERROR;
3573   }
3574
3575 GET_OPTION("sieve_useraddress");
3576 sieve.useraddress = sb && sb->useraddress
3577   ? sb->useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3578 GET_OPTION("sieve_subaddress");
3579 sieve.subaddress = sb ? sb->subaddress : NULL;
3580
3581 #ifdef COMPILE_SYNTAX_CHECKER
3582 if (parse_start(&sieve, 0, generated) == 1)
3583 #else
3584 if (parse_start(&sieve, 1, generated) == 1)
3585 #endif
3586   if (sieve.keep)
3587     {
3588     add_addr(generated, sieve.inbox, 1, 0, 0, 0);
3589     msg = US"Implicit keep";
3590     r = FF_DELIVERED;
3591     }
3592   else
3593     {
3594     msg = US"No implicit keep";
3595     r = FF_DELIVERED;
3596     }
3597 else
3598   {
3599   msg = string_sprintf("Sieve error: %s in line %d", sieve.errmsg, sieve.line);
3600 #ifdef COMPILE_SYNTAX_CHECKER
3601   r = FF_ERROR;
3602   *error = msg;
3603 #else
3604   add_addr(generated, sieve.inbox, 1, 0, 0, 0);
3605   r = FF_DELIVERED;
3606 #endif
3607   }
3608
3609 #ifndef COMPILE_SYNTAX_CHECKER
3610 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3611   else debug_printf_indent("%s\n", msg);
3612 #endif
3613
3614 expand_level--;
3615 DEBUG(D_route) debug_printf_indent("Sieve: end of processing\n");
3616 return r;
3617 }