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