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