Apply the latest Sieve patches.
[exim.git] / src / src / sieve.c
1 /* $Cambridge: exim/src/src/sieve.c,v 1.14 2005/11/14 11:41:23 ph10 Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) Michael Haardt 2003-2005 */
8 /* See the file NOTICE for conditions of use and distribution. */
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 "notify".     */
32 #undef NOTIFY
33
34 /* Define this for the Sieve extension "subaddress".                */
35 #define SUBADDRESS
36
37 /* Define this for the Sieve extension "vacation".                  */
38 #define VACATION
39
40 /* Must be >= 1                                                     */
41 #define VACATION_MIN_DAYS 1
42 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30        */
43 #define VACATION_MAX_DAYS 31
44
45 /* Keep this at 75 to accept only RFC compliant MIME words.         */
46 /* Increase it if you want to match headers from buggy MUAs.        */
47 #define MIMEWORD_LENGTH 75
48
49 struct Sieve
50   {
51   uschar *filter;
52   const uschar *pc;
53   int line;
54   const uschar *errmsg;
55   int keep;
56   int require_envelope;
57   int require_fileinto;
58 #ifdef NOTIFY
59   int require_notify;
60 #endif
61 #ifdef SUBADDRESS
62   int require_subaddress;
63 #endif
64 #ifdef VACATION
65   int require_vacation;
66   int vacation_ran;
67 #endif
68   uschar *vacation_directory;
69   const uschar *subaddress;
70   const uschar *useraddress;
71   int require_copy;
72   int require_iascii_numeric;
73   };
74
75 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
76 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
77 #ifdef SUBADDRESS
78 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
79 #else
80 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
81 #endif
82 enum RelOp { LT, LE, EQ, GE, GT, NE };
83
84 struct String
85   {
86   uschar *character;
87   int length;
88   };
89
90 static int parse_test(struct Sieve *filter, int *cond, int exec);
91 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
92
93 static uschar str_from_c[]="From";
94 static const struct String str_from={ str_from_c, 4 };
95 static uschar str_to_c[]="To";
96 static const struct String str_to={ str_to_c, 2 };
97 static uschar str_cc_c[]="Cc";
98 static const struct String str_cc={ str_cc_c, 2 };
99 static uschar str_bcc_c[]="Bcc";
100 static const struct String str_bcc={ str_bcc_c, 3 };
101 static uschar str_sender_c[]="Sender";
102 static const struct String str_sender={ str_sender_c, 6 };
103 static uschar str_resent_from_c[]="Resent-From";
104 static const struct String str_resent_from={ str_resent_from_c, 11 };
105 static uschar str_resent_to_c[]="Resent-To";
106 static const struct String str_resent_to={ str_resent_to_c, 9 };
107 static uschar str_fileinto_c[]="fileinto";
108 static const struct String str_fileinto={ str_fileinto_c, 8 };
109 static uschar str_envelope_c[]="envelope";
110 static const struct String str_envelope={ str_envelope_c, 8 };
111 #ifdef NOTIFY
112 static uschar str_notify_c[]="notify";
113 static const struct String str_notify={ str_notify_c, 6 };
114 #endif
115 #ifdef SUBADDRESS
116 static uschar str_subaddress_c[]="subaddress";
117 static const struct String str_subaddress={ str_subaddress_c, 10 };
118 #endif
119 #ifdef VACATION
120 static uschar str_vacation_c[]="vacation";
121 static const struct String str_vacation={ str_vacation_c, 8 };
122 static uschar str_subject_c[]="Subject";
123 static const struct String str_subject={ str_subject_c, 7 };
124 #endif
125 static uschar str_copy_c[]="copy";
126 static const struct String str_copy={ str_copy_c, 4 };
127 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
128 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
129 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
130 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
131 static uschar str_ioctet_c[]="i;octet";
132 static const struct String str_ioctet={ str_ioctet_c, 7 };
133 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
134 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
135 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
136 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
137 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
138 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
139 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
140 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
141 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
142 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
143
144
145 /*************************************************
146 *          Encode to quoted-printable            *
147 *************************************************/
148
149 /*
150 Arguments:
151   src               UTF-8 string
152 */
153
154 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
155 {
156 int pass;
157 const uschar *start,*end;
158 uschar *new = NULL;
159 uschar ch;
160 size_t line;
161
162 for (pass=0; pass<=1; ++pass)
163   {
164   line=0;
165   if (pass==0)
166     dst->length=0;
167   else
168     {
169     dst->character=store_get(dst->length+1); /* plus one for \0 */
170     new=dst->character;
171     }
172   for (start=src->character,end=start+src->length; start<end; ++start)
173     {
174     ch=*start;
175     if (line>=73)
176       {
177       if (pass==0)
178         dst->length+=2;
179       else
180         {
181         *new++='=';
182         *new++='\n';
183         }
184       line=0;
185       }
186     if
187       (
188       (ch>=33 && ch<=60)
189       || (ch>=62 && ch<=126)
190       ||
191         (
192         (ch==9 || ch==32)
193 #ifdef RFC_EOL
194         && start+2<end
195         && (*(start+1)!='\r' || *(start+2)!='\n')
196 #else
197         && start+1<end
198         && *(start+1)!='\n'
199 #endif
200         )
201       )
202       {
203       if (pass==0)
204         ++dst->length;
205       else
206         *new++=*start;
207       ++line;
208       }
209 #ifdef RFC_EOL
210     else if (ch=='\r' && start+1<end && *(start+1)=='\n')
211       {
212       if (pass==0)
213         {
214         ++dst->length;
215         line=0;
216         ++start;
217         }
218       else
219         *new++='\n';
220         line=0;
221       }
222 #else
223     else if (ch=='\n')
224       {
225       if (pass==0)
226         ++dst->length;
227       else
228         *new++=*start;
229       ++line;
230       }
231 #endif
232     else
233       {
234       if (pass==0)
235         dst->length+=3;
236       else
237         {
238         sprintf(CS new,"=%02X",ch);
239         new+=3;
240         }
241       line+=3;
242       }
243     }
244   }
245   *new='\0'; /* not included in length, but nice */
246   return dst;
247 }
248
249
250 /*************************************************
251 *          Octet-wise string comparison          *
252 *************************************************/
253
254 /*
255 Arguments:
256   needle            UTF-8 string to search ...
257   haystack          ... inside the haystack
258   match_prefix      1 to compare if needle is a prefix of haystack
259
260 Returns:      0               needle not found in haystack
261               1               needle found
262 */
263
264 static int eq_octet(const struct String *needle,
265   const struct String *haystack, int match_prefix)
266 {
267 size_t nl,hl;
268 const uschar *n,*h;
269
270 nl=needle->length;
271 n=needle->character;
272 hl=haystack->length;
273 h=haystack->character;
274 while (nl>0 && hl>0)
275   {
276 #if !HAVE_ICONV
277   if (*n&0x80) return 0;
278   if (*h&0x80) return 0;
279 #endif
280   if (*n!=*h) return 0;
281   ++n;
282   ++h;
283   --nl;
284   --hl;
285   }
286 return (match_prefix ? nl==0 : nl==0 && hl==0);
287 }
288
289
290 /*************************************************
291 *    ASCII case-insensitive string comparison    *
292 *************************************************/
293
294 /*
295 Arguments:
296   needle            UTF-8 string to search ...
297   haystack          ... inside the haystack
298   match_prefix      1 to compare if needle is a prefix of haystack
299
300 Returns:      0               needle not found in haystack
301               1               needle found
302 */
303
304 static int eq_asciicase(const struct String *needle,
305   const struct String *haystack, int match_prefix)
306 {
307 size_t nl,hl;
308 const uschar *n,*h;
309 uschar nc,hc;
310
311 nl=needle->length;
312 n=needle->character;
313 hl=haystack->length;
314 h=haystack->character;
315 while (nl>0 && hl>0)
316   {
317   nc=*n;
318   hc=*h;
319 #if !HAVE_ICONV
320   if (nc&0x80) return 0;
321   if (hc&0x80) return 0;
322 #endif
323   /* tolower depends on the locale and only ASCII case must be insensitive */
324   if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
325   ++n;
326   ++h;
327   --nl;
328   --hl;
329   }
330 return (match_prefix ? nl==0 : nl==0 && hl==0);
331 }
332
333
334 /*************************************************
335 *              Glob pattern search               *
336 *************************************************/
337
338 /*
339 Arguments:
340   needle      pattern to search ...
341   haystack    ... inside the haystack
342
343 Returns:      0               needle not found in haystack
344               1               needle found
345               -1              pattern error
346 */
347
348 static int eq_glob(const struct String *needle,
349   const struct String *haystack, int ascii_caseless)
350 {
351 const uschar *n,*h,*nend,*hend;
352 int may_advance=0;
353
354 n=needle->character;
355 h=haystack->character;
356 nend=n+needle->length;
357 hend=h+haystack->length;
358 while (n<nend)
359   {
360   if (*n=='*')
361     {
362     ++n;
363     may_advance=1;
364     }
365   else
366     {
367     const uschar *npart,*hpart;
368
369     /* Try to match a non-star part of the needle at the current */
370     /* position in the haystack.                                 */
371     match_part:
372     npart=n;
373     hpart=h;
374     while (npart<nend && *npart!='*') switch (*npart)
375       {
376       case '?':
377         {
378         if (hpart==hend) return 0;
379         /* watch out: Do not match one character, but one UTF8 encoded character */
380         if ((*hpart&0xc0)==0xc0)
381           {
382           ++hpart;
383           while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
384           }
385         else
386          ++hpart;
387         ++npart;
388         break;
389         }
390       case '\\':
391         {
392         ++npart;
393         if (npart==nend) return -1;
394         /* FALLTHROUGH */
395         }
396       default:
397         {
398         if (hpart==hend) return 0;
399         /* tolower depends on the locale, but we need ASCII */
400         if
401           (
402 #if !HAVE_ICONV
403           (*hpart&0x80) || (*npart&0x80) ||
404 #endif
405           ascii_caseless
406           ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
407           : *hpart!=*npart
408           )
409           {
410           if (may_advance)
411             /* string match after a star failed, advance and try again */
412             {
413             ++h;
414             goto match_part;
415             }
416           else return 0;
417           }
418         else
419           {
420           ++npart;
421           ++hpart;
422           };
423         }
424       }
425     /* at this point, a part was matched successfully */
426     if (may_advance && npart==nend && hpart<hend)
427       /* needle ends, but haystack does not: if there was a star before, advance and try again */
428       {
429       ++h;
430       goto match_part;
431       }
432     h=hpart;
433     n=npart;
434     may_advance=0;
435     }
436   }
437 return (h==hend ? 1 : may_advance);
438 }
439
440
441 /*************************************************
442 *    ASCII numeric comparison                    *
443 *************************************************/
444
445 /*
446 Arguments:
447   a                 first numeric string
448   b                 second numeric string
449   relop             relational operator
450
451 Returns:      0               not (a relop b)
452               1               a relop b
453 */
454
455 static int eq_asciinumeric(const struct String *a,
456   const struct String *b, enum RelOp relop)
457 {
458 size_t al,bl;
459 const uschar *as,*aend,*bs,*bend;
460 int cmp;
461
462 as=a->character;
463 aend=a->character+a->length;
464 bs=b->character;
465 bend=b->character+b->length;
466
467 while (*as>='0' && *as<='9' && as<aend) ++as;
468 al=as-a->character;
469 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
470 bl=bs-b->character;
471
472 if (al && bl==0) cmp=-1;
473 else if (al==0 && bl==0) cmp=0;
474 else if (al==0 && bl) cmp=1;
475 else
476   {
477   cmp=al-bl;
478   if (cmp==0) cmp=memcmp(a->character,b->character,al);
479   }
480 switch (relop)
481   {
482   case LT: return cmp<0;
483   case LE: return cmp<=0;
484   case EQ: return cmp==0;
485   case GE: return cmp>=0;
486   case GT: return cmp>0;
487   case NE: return cmp!=0;
488   }
489   /*NOTREACHED*/
490   return -1;
491 }
492
493
494 /*************************************************
495 *             Compare strings                    *
496 *************************************************/
497
498 /*
499 Arguments:
500   needle      UTF-8 pattern or string to search ...
501   haystack    ... inside the haystack
502   co          comparator to use
503   mt          match type to use
504
505 Returns:      0               needle not found in haystack
506               1               needle found
507               -1              comparator does not offer matchtype
508 */
509
510 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
511   enum Comparator co, enum MatchType mt)
512 {
513 int r=0;
514
515 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
516   (debug_selector & D_filter) != 0)
517   {
518   debug_printf("String comparison (match ");
519   switch (mt)
520     {
521     case MATCH_IS: debug_printf(":is"); break;
522     case MATCH_CONTAINS: debug_printf(":contains"); break;
523     case MATCH_MATCHES: debug_printf(":matches"); break;
524     }
525   debug_printf(", comparison \"");
526   switch (co)
527     {
528     case COMP_OCTET: debug_printf("i;octet"); break;
529     case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
530     case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
531     }
532   debug_printf("\"):\n");
533   debug_printf("  Search = %s (%d chars)\n", needle->character,needle->length);
534   debug_printf("  Inside = %s (%d chars)\n", haystack->character,haystack->length);
535   }
536 switch (mt)
537   {
538   case MATCH_IS:
539     {
540     switch (co)
541       {
542       case COMP_OCTET:
543         {
544         if (eq_octet(needle,haystack,0)) r=1;
545         break;
546         }
547       case COMP_EN_ASCII_CASEMAP:
548         {
549         if (eq_asciicase(needle,haystack,0)) r=1;
550         break;
551         }
552       case COMP_ASCII_NUMERIC:
553         {
554         if (!filter->require_iascii_numeric)
555           {
556           filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
557           return -1;
558           }
559         if (eq_asciinumeric(needle,haystack,EQ)) r=1;
560         break;
561         }
562       }
563     break;
564     }
565   case MATCH_CONTAINS:
566     {
567     struct String h;
568
569     switch (co)
570       {
571       case COMP_OCTET:
572         {
573         for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
574         break;
575         }
576       case COMP_EN_ASCII_CASEMAP:
577         {
578         for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
579         break;
580         }
581       default:
582         {
583         filter->errmsg=CUS "comparator does not offer specified matchtype";
584         return -1;
585         }
586       }
587     break;
588     }
589   case MATCH_MATCHES:
590     {
591     switch (co)
592       {
593       case COMP_OCTET:
594         {
595         if ((r=eq_glob(needle,haystack,0))==-1)
596           {
597           filter->errmsg=CUS "syntactically invalid pattern";
598           return -1;
599           }
600         break;
601         }
602       case COMP_EN_ASCII_CASEMAP:
603         {
604         if ((r=eq_glob(needle,haystack,1))==-1)
605           {
606           filter->errmsg=CUS "syntactically invalid pattern";
607           return -1;
608           }
609         break;
610         }
611       default:
612         {
613         filter->errmsg=CUS "comparator does not offer specified matchtype";
614         return -1;
615         }
616       }
617     break;
618     }
619   }
620 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
621   (debug_selector & D_filter) != 0)
622   debug_printf("  Result %s\n",r?"true":"false");
623 return r;
624 }
625
626
627 /*************************************************
628 *         Check header field syntax              *
629 *************************************************/
630
631 /*
632 RFC 2822, section 3.6.8 says:
633
634   field-name      =       1*ftext
635
636   ftext           =       %d33-57 /               ; Any character except
637                           %d59-126                ;  controls, SP, and
638                                                   ;  ":".
639
640 That forbids 8-bit header fields.  This implementation accepts them, since
641 all of Exim is 8-bit clean, so it adds %d128-%d255.
642
643 Arguments:
644   header      header field to quote for suitable use in Exim expansions
645
646 Returns:      0               string is not a valid header field
647               1               string is a value header field
648 */
649
650 static int is_header(const struct String *header)
651 {
652 size_t l;
653 const uschar *h;
654
655 l=header->length;
656 h=header->character;
657 if (l==0) return 0;
658 while (l)
659   {
660   if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
661   else
662     {
663     ++h;
664     --l;
665     }
666   }
667 return 1;
668 }
669
670
671 /*************************************************
672 *       Quote special characters string          *
673 *************************************************/
674
675 /*
676 Arguments:
677   header      header field to quote for suitable use in Exim expansions
678               or as debug output
679
680 Returns:      quoted string
681 */
682
683 static const uschar *quote(const struct String *header)
684 {
685 uschar *quoted=NULL;
686 int size=0,ptr=0;
687 size_t l;
688 const uschar *h;
689
690 l=header->length;
691 h=header->character;
692 while (l)
693   {
694   switch (*h)
695     {
696     case '\0':
697       {
698       quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
699       break;
700       }
701     case '$':
702     case '{':
703     case '}':
704       {
705       quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
706       }
707     default:
708       {
709       quoted=string_cat(quoted,&size,&ptr,h,1);
710       }
711     }
712   ++h;
713   --l;
714   }
715 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
716 return quoted;
717 }
718
719
720 /*************************************************
721 *   Add address to list of generated addresses   *
722 *************************************************/
723
724 /*
725 According to RFC 3028, duplicate delivery to the same address must
726 not happen, so the list is first searched for the address.
727
728 Arguments:
729   generated   list of generated addresses
730   addr        new address to add
731   file        address denotes a file
732
733 Returns:      nothing
734 */
735
736 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
737 {
738 address_item *new_addr;
739
740 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
741   {
742   if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
743     {
744     if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
745       {
746       debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
747       }
748     return;
749     }
750   }
751
752 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
753   {
754   debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
755   }
756 new_addr=deliver_make_addr(addr,TRUE);
757 if (file)
758   {
759   setflag(new_addr, af_pfr|af_file);
760   new_addr->mode = 0;
761   }
762 new_addr->p.errors_address = NULL;
763 new_addr->next = *generated;
764 *generated = new_addr;
765 }
766
767
768 /*************************************************
769 *         Return decoded header field            *
770 *************************************************/
771
772 /*
773 Arguments:
774   value       returned value of the field
775   header      name of the header field
776
777 Returns:      nothing          The expanded string is empty
778                                in case there is no such header
779 */
780
781 static void expand_header(struct String *value, const struct String *header)
782 {
783 uschar *s,*r,*t;
784 uschar *errmsg;
785
786 value->length=0;
787 value->character=(uschar*)0;
788
789 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
790 while (*r==' ') ++r;
791 while (*r)
792   {
793   if (*r=='\n')
794     {
795     ++r;
796     while (*r==' ' || *r=='\t') ++r;
797     if (*r) *t++=' ';
798     }
799   else
800     *t++=*r++;
801   }
802 *t++='\0';
803 value->character=rfc2047_decode(s,TRUE,US"utf-8",'\0',&value->length,&errmsg);
804 }
805
806
807 /*************************************************
808 *        Parse remaining hash comment            *
809 *************************************************/
810
811 /*
812 Token definition:
813   Comment up to terminating CRLF
814
815 Arguments:
816   filter      points to the Sieve filter including its state
817
818 Returns:      1                success
819               -1               syntax error
820 */
821
822 static int parse_hashcomment(struct Sieve *filter)
823 {
824 ++filter->pc;
825 while (*filter->pc)
826   {
827 #ifdef RFC_EOL
828   if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
829 #else
830   if (*filter->pc=='\n')
831 #endif
832     {
833 #ifdef RFC_EOL
834     filter->pc+=2;
835 #else
836     ++filter->pc;
837 #endif
838     ++filter->line;
839     return 1;
840     }
841   else ++filter->pc;
842   }
843 filter->errmsg=CUS "missing end of comment";
844 return -1;
845 }
846
847
848 /*************************************************
849 *       Parse remaining C-style comment          *
850 *************************************************/
851
852 /*
853 Token definition:
854   Everything up to star slash
855
856 Arguments:
857   filter      points to the Sieve filter including its state
858
859 Returns:      1                success
860               -1               syntax error
861 */
862
863 static int parse_comment(struct Sieve *filter)
864 {
865   filter->pc+=2;
866   while (*filter->pc)
867   {
868     if (*filter->pc=='*' && *(filter->pc+1)=='/')
869     {
870       filter->pc+=2;
871       return 1;
872     }
873     else ++filter->pc;
874   }
875   filter->errmsg=CUS "missing end of comment";
876   return -1;
877 }
878
879
880 /*************************************************
881 *         Parse optional white space             *
882 *************************************************/
883
884 /*
885 Token definition:
886   Spaces, tabs, CRLFs, hash comments or C-style comments
887
888 Arguments:
889   filter      points to the Sieve filter including its state
890
891 Returns:      1                success
892               -1               syntax error
893 */
894
895 static int parse_white(struct Sieve *filter)
896 {
897 while (*filter->pc)
898   {
899   if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
900 #ifdef RFC_EOL
901   else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
902 #else
903   else if (*filter->pc=='\n')
904 #endif
905     {
906 #ifdef RFC_EOL
907     filter->pc+=2;
908 #else
909     ++filter->pc;
910 #endif
911     ++filter->line;
912     }
913   else if (*filter->pc=='#')
914     {
915     if (parse_hashcomment(filter)==-1) return -1;
916     }
917   else if (*filter->pc=='/' && *(filter->pc+1)=='*')
918     {
919     if (parse_comment(filter)==-1) return -1;
920     }
921   else break;
922   }
923 return 1;
924 }
925
926
927 /*************************************************
928 *          Parse a optional string               *
929 *************************************************/
930
931 /*
932 Token definition:
933    quoted-string = DQUOTE *CHAR DQUOTE
934            ;; in general, \ CHAR inside a string maps to CHAR
935            ;; so \" maps to " and \\ maps to \
936            ;; note that newlines and other characters are all allowed
937            ;; in strings
938
939    multi-line          = "text:" *(SP / HTAB) (hash-comment / CRLF)
940                          *(multi-line-literal / multi-line-dotstuff)
941                          "." CRLF
942    multi-line-literal  = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
943    multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
944            ;; A line containing only "." ends the multi-line.
945            ;; Remove a leading '.' if followed by another '.'.
946   string           = quoted-string / multi-line
947
948 Arguments:
949   filter      points to the Sieve filter including its state
950   id          specifies identifier to match
951
952 Returns:      1                success
953               -1               syntax error
954               0                identifier not matched
955 */
956
957 static int parse_string(struct Sieve *filter, struct String *data)
958 {
959 int dataCapacity=0;
960
961 data->length=0;
962 data->character=(uschar*)0;
963 if (*filter->pc=='"') /* quoted string */
964   {
965   ++filter->pc;
966   while (*filter->pc)
967     {
968     if (*filter->pc=='"') /* end of string */
969       {
970       int foo=data->length;
971
972       ++filter->pc;
973       data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
974       return 1;
975       }
976     else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
977       {
978       if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
979       else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
980       filter->pc+=2;
981       }
982     else /* regular character */
983       {
984       data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
985       filter->pc++;
986       }
987     }
988   filter->errmsg=CUS "missing end of string";
989   return -1;
990   }
991 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
992   {
993   filter->pc+=5;
994   /* skip optional white space followed by hashed comment or CRLF */
995   while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
996   if (*filter->pc=='#')
997     {
998     if (parse_hashcomment(filter)==-1) return -1;
999     }
1000 #ifdef RFC_EOL
1001   else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1002 #else
1003   else if (*filter->pc=='\n')
1004 #endif
1005     {
1006 #ifdef RFC_EOL
1007     filter->pc+=2;
1008 #else
1009     ++filter->pc;
1010 #endif
1011     ++filter->line;
1012     }
1013   else
1014     {
1015     filter->errmsg=CUS "syntax error";
1016     return -1;
1017     }
1018   while (*filter->pc)
1019     {
1020 #ifdef RFC_EOL
1021     if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1022 #else
1023     if (*filter->pc=='\n') /* end of line */
1024 #endif
1025       {
1026       data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1027 #ifdef RFC_EOL
1028       filter->pc+=2;
1029 #else
1030       ++filter->pc;
1031 #endif
1032       ++filter->line;
1033 #ifdef RFC_EOL
1034       if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1035 #else
1036       if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1037 #endif
1038         {
1039         data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1040 #ifdef RFC_EOL
1041         filter->pc+=3;
1042 #else
1043         filter->pc+=2;
1044 #endif
1045         ++filter->line;
1046         return 1;
1047         }
1048       else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1049         {
1050         data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1051         filter->pc+=2;
1052         }
1053       }
1054     else /* regular character */
1055       {
1056       data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1057       filter->pc++;
1058       }
1059     }
1060   filter->errmsg=CUS "missing end of multi line string";
1061   return -1;
1062   }
1063 else return 0;
1064 }
1065
1066
1067 /*************************************************
1068 *          Parse a specific identifier           *
1069 *************************************************/
1070
1071 /*
1072 Token definition:
1073   identifier       = (ALPHA / "_") *(ALPHA DIGIT "_")
1074
1075 Arguments:
1076   filter      points to the Sieve filter including its state
1077   id          specifies identifier to match
1078
1079 Returns:      1                success
1080               0                identifier not matched
1081 */
1082
1083 static int parse_identifier(struct Sieve *filter, const uschar *id)
1084 {
1085   size_t idlen=Ustrlen(id);
1086
1087   if (Ustrncmp(filter->pc,id,idlen)==0)
1088   {
1089     uschar next=filter->pc[idlen];
1090
1091     if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1092     filter->pc+=idlen;
1093     return 1;
1094   }
1095   else return 0;
1096 }
1097
1098
1099 /*************************************************
1100 *                 Parse a number                 *
1101 *************************************************/
1102
1103 /*
1104 Token definition:
1105   number           = 1*DIGIT [QUANTIFIER]
1106   QUANTIFIER       = "K" / "M" / "G"
1107
1108 Arguments:
1109   filter      points to the Sieve filter including its state
1110   data        returns value
1111
1112 Returns:      1                success
1113               -1               no string list found
1114 */
1115
1116 static int parse_number(struct Sieve *filter, unsigned long *data)
1117 {
1118 unsigned long d,u;
1119
1120 if (*filter->pc>='0' && *filter->pc<='9')
1121   {
1122   uschar *e;
1123
1124   errno=0;
1125   d=Ustrtoul(filter->pc,&e,10);
1126   if (errno==ERANGE)
1127     {
1128     filter->errmsg=CUstrerror(ERANGE);
1129     return -1;
1130     }
1131   filter->pc=e;
1132   u=1;
1133   if (*filter->pc=='K') { u=1024; ++filter->pc; }
1134   else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1135   else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1136   if (d>(ULONG_MAX/u))
1137     {
1138     filter->errmsg=CUstrerror(ERANGE);
1139     return -1;
1140     }
1141   d*=u;
1142   *data=d;
1143   return 1;
1144   }
1145 else
1146   {
1147   filter->errmsg=CUS "missing number";
1148   return -1;
1149   }
1150 }
1151
1152
1153 /*************************************************
1154 *              Parse a string list               *
1155 *************************************************/
1156
1157 /*
1158 Grammar:
1159   string-list      = "[" string *("," string) "]" / string
1160
1161 Arguments:
1162   filter      points to the Sieve filter including its state
1163   data        returns string list
1164
1165 Returns:      1                success
1166               -1               no string list found
1167 */
1168
1169 static int parse_stringlist(struct Sieve *filter, struct String **data)
1170 {
1171 const uschar *orig=filter->pc;
1172 int dataCapacity=0;
1173 int dataLength=0;
1174 struct String *d=(struct String*)0;
1175 int m;
1176
1177 if (*filter->pc=='[') /* string list */
1178   {
1179   ++filter->pc;
1180   for (;;)
1181     {
1182     if (parse_white(filter)==-1) goto error;
1183     if ((dataLength+1)>=dataCapacity) /* increase buffer */
1184       {
1185       struct String *new;
1186       int newCapacity;          /* Don't amalgamate with next line; some compilers grumble */
1187       newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1188       if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1189         {
1190         filter->errmsg=CUstrerror(errno);
1191         goto error;
1192         }
1193       if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1194       d=new;
1195       dataCapacity=newCapacity;
1196       }
1197     m=parse_string(filter,&d[dataLength]);
1198     if (m==0)
1199       {
1200       if (dataLength==0) break;
1201       else
1202         {
1203         filter->errmsg=CUS "missing string";
1204         goto error;
1205         }
1206       }
1207     else if (m==-1) goto error;
1208     else ++dataLength;
1209     if (parse_white(filter)==-1) goto error;
1210     if (*filter->pc==',') ++filter->pc;
1211     else break;
1212     }
1213   if (*filter->pc==']')
1214     {
1215     d[dataLength].character=(uschar*)0;
1216     d[dataLength].length=-1;
1217     ++filter->pc;
1218     *data=d;
1219     return 1;
1220     }
1221   else
1222     {
1223     filter->errmsg=CUS "missing closing bracket";
1224     goto error;
1225     }
1226   }
1227 else /* single string */
1228   {
1229   if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1230     {
1231     return -1;
1232     }
1233   m=parse_string(filter,&d[0]);
1234   if (m==-1)
1235     {
1236     return -1;
1237     }
1238   else if (m==0)
1239     {
1240     filter->pc=orig;
1241     return 0;
1242     }
1243   else
1244     {
1245     d[1].character=(uschar*)0;
1246     d[1].length=-1;
1247     *data=d;
1248     return 1;
1249     }
1250   }
1251 error:
1252 filter->errmsg=CUS "missing string list";
1253 return -1;
1254 }
1255
1256
1257 /*************************************************
1258 *    Parse an optional address part specifier    *
1259 *************************************************/
1260
1261 /*
1262 Grammar:
1263   address-part     =  ":localpart" / ":domain" / ":all"
1264   address-part     =/ ":user" / ":detail"
1265
1266 Arguments:
1267   filter      points to the Sieve filter including its state
1268   a           returns address part specified
1269
1270 Returns:      1                success
1271               0                no comparator found
1272               -1               syntax error
1273 */
1274
1275 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1276 {
1277 #ifdef SUBADDRESS
1278 if (parse_identifier(filter,CUS ":user")==1)
1279   {
1280   if (!filter->require_subaddress)
1281     {
1282     filter->errmsg=CUS "missing previous require \"subaddress\";";
1283     return -1;
1284     }
1285   *a=ADDRPART_USER;
1286   return 1;
1287   }
1288 else if (parse_identifier(filter,CUS ":detail")==1)
1289   {
1290   if (!filter->require_subaddress)
1291     {
1292     filter->errmsg=CUS "missing previous require \"subaddress\";";
1293     return -1;
1294     }
1295   *a=ADDRPART_DETAIL;
1296   return 1;
1297   }
1298 else
1299 #endif
1300 if (parse_identifier(filter,CUS ":localpart")==1)
1301   {
1302   *a=ADDRPART_LOCALPART;
1303   return 1;
1304   }
1305 else if (parse_identifier(filter,CUS ":domain")==1)
1306   {
1307   *a=ADDRPART_DOMAIN;
1308   return 1;
1309   }
1310 else if (parse_identifier(filter,CUS ":all")==1)
1311   {
1312   *a=ADDRPART_ALL;
1313   return 1;
1314   }
1315 else return 0;
1316 }
1317
1318
1319 /*************************************************
1320 *         Parse an optional comparator           *
1321 *************************************************/
1322
1323 /*
1324 Grammar:
1325   comparator = ":comparator" <comparator-name: string>
1326
1327 Arguments:
1328   filter      points to the Sieve filter including its state
1329   c           returns comparator
1330
1331 Returns:      1                success
1332               0                no comparator found
1333               -1               incomplete comparator found
1334 */
1335
1336 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1337 {
1338 struct String comparator_name;
1339
1340 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1341 if (parse_white(filter)==-1) return -1;
1342 switch (parse_string(filter,&comparator_name))
1343   {
1344   case -1: return -1;
1345   case 0:
1346     {
1347     filter->errmsg=CUS "missing comparator";
1348     return -1;
1349     }
1350   default:
1351     {
1352     int match;
1353
1354     if (eq_asciicase(&comparator_name,&str_ioctet,0))
1355       {
1356       *c=COMP_OCTET;
1357       match=1;
1358       }
1359     else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1360       {
1361       *c=COMP_EN_ASCII_CASEMAP;
1362       match=1;
1363       }
1364     else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1365       {
1366       *c=COMP_EN_ASCII_CASEMAP;
1367       match=1;
1368       }
1369     else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1370       {
1371       *c=COMP_ASCII_NUMERIC;
1372       match=1;
1373       }
1374     else
1375       {
1376       filter->errmsg=CUS "invalid comparator";
1377       match=-1;
1378       }
1379     return match;
1380     }
1381   }
1382 }
1383
1384
1385 /*************************************************
1386 *          Parse an optional match type          *
1387 *************************************************/
1388
1389 /*
1390 Grammar:
1391   match-type = ":is" / ":contains" / ":matches"
1392
1393 Arguments:
1394   filter      points to the Sieve filter including its state
1395   m           returns match type
1396
1397 Returns:      1                success
1398               0                no match type found
1399 */
1400
1401 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1402 {
1403   if (parse_identifier(filter,CUS ":is")==1)
1404   {
1405     *m=MATCH_IS;
1406     return 1;
1407   }
1408   else if (parse_identifier(filter,CUS ":contains")==1)
1409   {
1410     *m=MATCH_CONTAINS;
1411     return 1;
1412   }
1413   else if (parse_identifier(filter,CUS ":matches")==1)
1414   {
1415     *m=MATCH_MATCHES;
1416     return 1;
1417   }
1418   else return 0;
1419 }
1420
1421
1422 /*************************************************
1423 *   Parse and interpret an optional test list    *
1424 *************************************************/
1425
1426 /*
1427 Grammar:
1428   test-list = "(" test *("," test) ")"
1429
1430 Arguments:
1431   filter      points to the Sieve filter including its state
1432   n           total number of tests
1433   true        number of passed tests
1434   exec        Execute parsed statements
1435
1436 Returns:      1                success
1437               0                no test list found
1438               -1               syntax or execution error
1439 */
1440
1441 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1442 {
1443 if (parse_white(filter)==-1) return -1;
1444 if (*filter->pc=='(')
1445   {
1446   ++filter->pc;
1447   *n=0;
1448    *true=0;
1449   for (;;)
1450     {
1451     int cond;
1452
1453     switch (parse_test(filter,&cond,exec))
1454       {
1455       case -1: return -1;
1456       case 0: filter->errmsg=CUS "missing test"; return -1;
1457       default: ++*n; if (cond) ++*true; break;
1458       }
1459     if (parse_white(filter)==-1) return -1;
1460     if (*filter->pc==',') ++filter->pc;
1461     else break;
1462     }
1463   if (*filter->pc==')')
1464     {
1465     ++filter->pc;
1466     return 1;
1467     }
1468   else
1469     {
1470     filter->errmsg=CUS "missing closing paren";
1471     return -1;
1472     }
1473   }
1474 else return 0;
1475 }
1476
1477
1478 /*************************************************
1479 *     Parse and interpret an optional test       *
1480 *************************************************/
1481
1482 /*
1483 Arguments:
1484   filter      points to the Sieve filter including its state
1485   cond        returned condition status
1486   exec        Execute parsed statements
1487
1488 Returns:      1                success
1489               0                no test found
1490               -1               syntax or execution error
1491 */
1492
1493 static int parse_test(struct Sieve *filter, int *cond, int exec)
1494 {
1495 if (parse_white(filter)==-1) return -1;
1496 if (parse_identifier(filter,CUS "address"))
1497   {
1498   /*
1499   address-test = "address" { [address-part] [comparator] [match-type] }
1500                  <header-list: string-list> <key-list: string-list>
1501
1502   header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1503   */
1504
1505   enum AddressPart addressPart=ADDRPART_ALL;
1506   enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1507   enum MatchType matchType=MATCH_IS;
1508   struct String *hdr,*h,*key,*k;
1509   int m;
1510   int ap=0,co=0,mt=0;
1511
1512   for (;;)
1513     {
1514     if (parse_white(filter)==-1) return -1;
1515     if ((m=parse_addresspart(filter,&addressPart))!=0)
1516       {
1517       if (m==-1) return -1;
1518       if (ap)
1519         {
1520         filter->errmsg=CUS "address part already specified";
1521         return -1;
1522         }
1523       else ap=1;
1524       }
1525     else if ((m=parse_comparator(filter,&comparator))!=0)
1526       {
1527       if (m==-1) return -1;
1528       if (co)
1529         {
1530         filter->errmsg=CUS "comparator already specified";
1531         return -1;
1532         }
1533       else co=1;
1534       }
1535     else if ((m=parse_matchtype(filter,&matchType))!=0)
1536       {
1537       if (m==-1) return -1;
1538       if (mt)
1539         {
1540         filter->errmsg=CUS "match type already specified";
1541         return -1;
1542         }
1543       else mt=1;
1544       }
1545     else break;
1546     }
1547   if (parse_white(filter)==-1) return -1;
1548   if ((m=parse_stringlist(filter,&hdr))!=1)
1549     {
1550     if (m==0) filter->errmsg=CUS "header string list expected";
1551     return -1;
1552     }
1553   if (parse_white(filter)==-1) return -1;
1554   if ((m=parse_stringlist(filter,&key))!=1)
1555     {
1556     if (m==0) filter->errmsg=CUS "key string list expected";
1557     return -1;
1558     }
1559   *cond=0;
1560   for (h=hdr; h->length!=-1 && !*cond; ++h)
1561     {
1562     uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1563
1564     if
1565       (
1566       !eq_asciicase(h,&str_from,0)
1567       && !eq_asciicase(h,&str_to,0)
1568       && !eq_asciicase(h,&str_cc,0)
1569       && !eq_asciicase(h,&str_bcc,0)
1570       && !eq_asciicase(h,&str_sender,0)
1571       && !eq_asciicase(h,&str_resent_from,0)
1572       && !eq_asciicase(h,&str_resent_to,0)
1573       )
1574       {
1575       filter->errmsg=CUS "invalid header field";
1576       return -1;
1577       }
1578     if (exec)
1579       {
1580       /* We are only interested in addresses below, so no MIME decoding */
1581       header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1582       if (header_value == NULL)
1583         {
1584         filter->errmsg=CUS "header string expansion failed";
1585         return -1;
1586         }
1587       parse_allow_group = TRUE;
1588       while (*header_value && !*cond)
1589         {
1590         uschar *error;
1591         int start, end, domain;
1592         int saveend;
1593         uschar *part=NULL;
1594
1595         end_addr = parse_find_address_end(header_value, FALSE);
1596         saveend = *end_addr;
1597         *end_addr = 0;
1598         extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1599
1600         if (extracted_addr) switch (addressPart)
1601           {
1602           case ADDRPART_ALL: part=extracted_addr; break;
1603 #ifdef SUBADDRESS
1604           case ADDRPART_USER:
1605 #endif
1606           case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1607           case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1608 #ifdef SUBADDRESS
1609           case ADDRPART_DETAIL: part=NULL; break;
1610 #endif
1611           }
1612
1613         *end_addr = saveend;
1614         if (part)
1615           {
1616           for (k=key; k->length!=-1; ++k)
1617             {
1618             struct String partStr;
1619
1620             partStr.character=part;
1621             partStr.length=Ustrlen(part);
1622             if (extracted_addr)
1623               {
1624               *cond=compare(filter,k,&partStr,comparator,matchType);
1625               if (*cond==-1) return -1;
1626               if (*cond) break;
1627               }
1628             }
1629           }
1630         if (saveend == 0) break;
1631         header_value = end_addr + 1;
1632         }
1633       }
1634     }
1635   return 1;
1636   }
1637 else if (parse_identifier(filter,CUS "allof"))
1638   {
1639   /*
1640   allof-test   = "allof" <tests: test-list>
1641   */
1642
1643   int n,true;
1644
1645   switch (parse_testlist(filter,&n,&true,exec))
1646     {
1647     case -1: return -1;
1648     case 0: filter->errmsg=CUS "missing test list"; return -1;
1649     default: *cond=(n==true); return 1;
1650     }
1651   }
1652 else if (parse_identifier(filter,CUS "anyof"))
1653   {
1654   /*
1655   anyof-test   = "anyof" <tests: test-list>
1656   */
1657
1658   int n,true;
1659
1660   switch (parse_testlist(filter,&n,&true,exec))
1661     {
1662     case -1: return -1;
1663     case 0: filter->errmsg=CUS "missing test list"; return -1;
1664     default: *cond=(true>0); return 1;
1665     }
1666   }
1667 else if (parse_identifier(filter,CUS "exists"))
1668   {
1669   /*
1670   exists-test = "exists" <header-names: string-list>
1671   */
1672
1673   struct String *hdr,*h;
1674   int m;
1675
1676   if (parse_white(filter)==-1) return -1;
1677   if ((m=parse_stringlist(filter,&hdr))!=1)
1678     {
1679     if (m==0) filter->errmsg=CUS "header string list expected";
1680     return -1;
1681     }
1682   if (exec)
1683     {
1684     *cond=1;
1685     for (h=hdr; h->length!=-1 && *cond; ++h)
1686       {
1687       uschar *header_def;
1688
1689       header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1690       if (header_def == NULL)
1691         {
1692         filter->errmsg=CUS "header string expansion failed";
1693         return -1;
1694         }
1695       if (Ustrcmp(header_def,"false")==0) *cond=0;
1696       }
1697     }
1698   return 1;
1699   }
1700 else if (parse_identifier(filter,CUS "false"))
1701   {
1702   /*
1703   false-test = "false"
1704   */
1705
1706   *cond=0;
1707   return 1;
1708   }
1709 else if (parse_identifier(filter,CUS "header"))
1710   {
1711   /*
1712   header-test = "header" { [comparator] [match-type] }
1713                 <header-names: string-list> <key-list: string-list>
1714   */
1715
1716   enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1717   enum MatchType matchType=MATCH_IS;
1718   struct String *hdr,*h,*key,*k;
1719   int m;
1720   int co=0,mt=0;
1721
1722   for (;;)
1723     {
1724     if (parse_white(filter)==-1) return -1;
1725     if ((m=parse_comparator(filter,&comparator))!=0)
1726       {
1727       if (m==-1) return -1;
1728       if (co)
1729         {
1730         filter->errmsg=CUS "comparator already specified";
1731         return -1;
1732         }
1733       else co=1;
1734       }
1735     else if ((m=parse_matchtype(filter,&matchType))!=0)
1736       {
1737       if (m==-1) return -1;
1738       if (mt)
1739         {
1740         filter->errmsg=CUS "match type already specified";
1741         return -1;
1742         }
1743       else mt=1;
1744       }
1745     else break;
1746     }
1747   if (parse_white(filter)==-1) return -1;
1748   if ((m=parse_stringlist(filter,&hdr))!=1)
1749     {
1750     if (m==0) filter->errmsg=CUS "header string list expected";
1751     return -1;
1752     }
1753   if (parse_white(filter)==-1) return -1;
1754   if ((m=parse_stringlist(filter,&key))!=1)
1755     {
1756     if (m==0) filter->errmsg=CUS "key string list expected";
1757     return -1;
1758     }
1759   *cond=0;
1760   for (h=hdr; h->length!=-1 && !*cond; ++h)
1761     {
1762     if (!is_header(h))
1763       {
1764       filter->errmsg=CUS "invalid header field";
1765       return -1;
1766       }
1767     if (exec)
1768       {
1769       struct String header_value;
1770       uschar *header_def;
1771
1772       expand_header(&header_value,h);
1773       header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1774       if (header_value.character == NULL || header_def == NULL)
1775         {
1776         filter->errmsg=CUS "header string expansion failed";
1777         return -1;
1778         }
1779       for (k=key; k->length!=-1; ++k)
1780         {
1781         if (Ustrcmp(header_def,"true")==0)
1782           {
1783           *cond=compare(filter,k,&header_value,comparator,matchType);
1784           if (*cond==-1) return -1;
1785           if (*cond) break;
1786           }
1787         }
1788       }
1789     }
1790   return 1;
1791   }
1792 else if (parse_identifier(filter,CUS "not"))
1793   {
1794   if (parse_white(filter)==-1) return -1;
1795   switch (parse_test(filter,cond,exec))
1796     {
1797     case -1: return -1;
1798     case 0: filter->errmsg=CUS "missing test"; return -1;
1799     default: *cond=!*cond; return 1;
1800     }
1801   }
1802 else if (parse_identifier(filter,CUS "size"))
1803   {
1804   /*
1805   relop = ":over" / ":under"
1806   size-test = "size" relop <limit: number>
1807   */
1808
1809   unsigned long limit;
1810   int overNotUnder;
1811
1812   if (parse_white(filter)==-1) return -1;
1813   if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
1814   else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
1815   else
1816     {
1817     filter->errmsg=CUS "missing :over or :under";
1818     return -1;
1819     }
1820   if (parse_white(filter)==-1) return -1;
1821   if (parse_number(filter,&limit)==-1) return -1;
1822   *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
1823   return 1;
1824   }
1825 else if (parse_identifier(filter,CUS "true"))
1826   {
1827   *cond=1;
1828   return 1;
1829   }
1830 else if (parse_identifier(filter,CUS "envelope"))
1831   {
1832   /*
1833   envelope-test = "envelope" { [comparator] [address-part] [match-type] }
1834                   <envelope-part: string-list> <key-list: string-list>
1835
1836   envelope-part is case insensitive "from" or "to"
1837   */
1838
1839   enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1840   enum AddressPart addressPart=ADDRPART_ALL;
1841   enum MatchType matchType=MATCH_IS;
1842   struct String *env,*e,*key,*k;
1843   int m;
1844   int co=0,ap=0,mt=0;
1845
1846   if (!filter->require_envelope)
1847     {
1848     filter->errmsg=CUS "missing previous require \"envelope\";";
1849     return -1;
1850     }
1851   for (;;)
1852     {
1853     if (parse_white(filter)==-1) return -1;
1854     if ((m=parse_comparator(filter,&comparator))!=0)
1855       {
1856       if (m==-1) return -1;
1857       if (co)
1858         {
1859         filter->errmsg=CUS "comparator already specified";
1860         return -1;
1861         }
1862       else co=1;
1863       }
1864     else if ((m=parse_addresspart(filter,&addressPart))!=0)
1865       {
1866       if (m==-1) return -1;
1867       if (ap)
1868         {
1869         filter->errmsg=CUS "address part already specified";
1870         return -1;
1871         }
1872       else ap=1;
1873       }
1874     else if ((m=parse_matchtype(filter,&matchType))!=0)
1875       {
1876       if (m==-1) return -1;
1877       if (mt)
1878         {
1879         filter->errmsg=CUS "match type already specified";
1880         return -1;
1881         }
1882       else mt=1;
1883       }
1884     else break;
1885     }
1886   if (parse_white(filter)==-1) return -1;
1887   if ((m=parse_stringlist(filter,&env))!=1)
1888     {
1889     if (m==0) filter->errmsg=CUS "envelope string list expected";
1890     return -1;
1891     }
1892   if (parse_white(filter)==-1) return -1;
1893   if ((m=parse_stringlist(filter,&key))!=1)
1894     {
1895     if (m==0) filter->errmsg=CUS "key string list expected";
1896     return -1;
1897     }
1898   *cond=0;
1899   for (e=env; e->length!=-1 && !*cond; ++e)
1900     {
1901     const uschar *envelopeExpr=CUS 0;
1902     uschar *envelope=US 0;
1903
1904     if (eq_asciicase(e,&str_from,0))
1905       {
1906       switch (addressPart)
1907         {
1908         case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
1909 #ifdef SUBADDRESS
1910         case ADDRPART_USER:
1911 #endif
1912         case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
1913         case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
1914 #ifdef SUBADDRESS
1915         case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
1916 #endif
1917         }
1918       }
1919     else if (eq_asciicase(e,&str_to,0))
1920       {
1921       switch (addressPart)
1922         {
1923         case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
1924 #ifdef SUBADDRESS
1925         case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
1926         case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
1927 #endif
1928         case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
1929         case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
1930         }
1931       }
1932     else
1933       {
1934       filter->errmsg=CUS "invalid envelope string";
1935       return -1;
1936       }
1937     if (exec && envelopeExpr)
1938       {
1939       if ((envelope=expand_string(US envelopeExpr)) == NULL)
1940         {
1941         filter->errmsg=CUS "header string expansion failed";
1942         return -1;
1943         }
1944       for (k=key; k->length!=-1; ++k)
1945         {
1946         struct String envelopeStr;
1947
1948         envelopeStr.character=envelope;
1949         envelopeStr.length=Ustrlen(envelope);
1950         *cond=compare(filter,k,&envelopeStr,comparator,matchType);
1951         if (*cond==-1) return -1;
1952         if (*cond) break;
1953         }
1954       }
1955     }
1956   return 1;
1957   }
1958 else return 0;
1959 }
1960
1961
1962 /*************************************************
1963 *     Parse and interpret an optional block      *
1964 *************************************************/
1965
1966 /*
1967 Arguments:
1968   filter      points to the Sieve filter including its state
1969   exec        Execute parsed statements
1970   generated   where to hang newly-generated addresses
1971
1972 Returns:      2                success by stop
1973               1                other success
1974               0                no block command found
1975               -1               syntax or execution error
1976 */
1977
1978 static int parse_block(struct Sieve *filter, int exec,
1979   address_item **generated)
1980 {
1981 int r;
1982
1983 if (parse_white(filter)==-1) return -1;
1984 if (*filter->pc=='{')
1985   {
1986   ++filter->pc;
1987   if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
1988   if (*filter->pc=='}')
1989     {
1990     ++filter->pc;
1991     return 1;
1992     }
1993   else
1994     {
1995     filter->errmsg=CUS "expecting command or closing brace";
1996     return -1;
1997     }
1998   }
1999 else return 0;
2000 }
2001
2002
2003 /*************************************************
2004 *           Match a semicolon                    *
2005 *************************************************/
2006
2007 /*
2008 Arguments:
2009   filter      points to the Sieve filter including its state
2010
2011 Returns:      1                success
2012               -1               syntax error
2013 */
2014
2015 static int parse_semicolon(struct Sieve *filter)
2016 {
2017   if (parse_white(filter)==-1) return -1;
2018   if (*filter->pc==';')
2019   {
2020     ++filter->pc;
2021     return 1;
2022   }
2023   else
2024   {
2025     filter->errmsg=CUS "missing semicolon";
2026     return -1;
2027   }
2028 }
2029
2030
2031 /*************************************************
2032 *     Parse and interpret a Sieve command        *
2033 *************************************************/
2034
2035 /*
2036 Arguments:
2037   filter      points to the Sieve filter including its state
2038   exec        Execute parsed statements
2039   generated   where to hang newly-generated addresses
2040
2041 Returns:      2                success by stop
2042               1                other success
2043               -1               syntax or execution error
2044 */
2045 static int parse_commands(struct Sieve *filter, int exec,
2046   address_item **generated)
2047 {
2048 while (*filter->pc)
2049   {
2050   if (parse_white(filter)==-1) return -1;
2051   if (parse_identifier(filter,CUS "if"))
2052     {
2053     /*
2054     if-command = "if" test block *( "elsif" test block ) [ else block ]
2055     */
2056
2057     int cond,m,unsuccessful;
2058
2059     /* test block */
2060     if (parse_white(filter)==-1) return -1;
2061     if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2062     if (m==0)
2063       {
2064       filter->errmsg=CUS "missing test";
2065       return -1;
2066       }
2067     if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2068         (debug_selector & D_filter) != 0)
2069       {
2070       if (exec) debug_printf("if %s\n",cond?"true":"false");
2071       }
2072     m=parse_block(filter,exec ? cond : 0, generated);
2073     if (m==-1 || m==2) return m;
2074     if (m==0)
2075       {
2076       filter->errmsg=CUS "missing block";
2077       return -1;
2078       }
2079     unsuccessful = !cond;
2080     for (;;) /* elsif test block */
2081       {
2082       if (parse_white(filter)==-1) return -1;
2083       if (parse_identifier(filter,CUS "elsif"))
2084         {
2085         if (parse_white(filter)==-1) return -1;
2086         m=parse_test(filter,&cond,exec && unsuccessful);
2087         if (m==-1 || m==2) return m;
2088         if (m==0)
2089           {
2090           filter->errmsg=CUS "missing test";
2091           return -1;
2092           }
2093         if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2094             (debug_selector & D_filter) != 0)
2095           {
2096           if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2097           }
2098         m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2099         if (m==-1 || m==2) return m;
2100         if (m==0)
2101           {
2102           filter->errmsg=CUS "missing block";
2103           return -1;
2104           }
2105         if (exec && unsuccessful && cond) unsuccessful = 0;
2106         }
2107       else break;
2108       }
2109     /* else block */
2110     if (parse_white(filter)==-1) return -1;
2111     if (parse_identifier(filter,CUS "else"))
2112       {
2113       m=parse_block(filter,exec && unsuccessful, generated);
2114       if (m==-1 || m==2) return m;
2115       if (m==0)
2116         {
2117         filter->errmsg=CUS "missing block";
2118         return -1;
2119         }
2120       }
2121     }
2122   else if (parse_identifier(filter,CUS "stop"))
2123     {
2124     /*
2125     stop-command     =  "stop" { stop-options } ";"
2126     stop-options     =
2127     */
2128
2129     if (parse_semicolon(filter)==-1) return -1;
2130     if (exec)
2131       {
2132       filter->pc+=Ustrlen(filter->pc);
2133       return 2;
2134       }
2135     }
2136   else if (parse_identifier(filter,CUS "keep"))
2137     {
2138     /*
2139     keep-command     =  "keep" { keep-options } ";"
2140     keep-options     =
2141     */
2142
2143     if (parse_semicolon(filter)==-1) return -1;
2144     if (exec)
2145       {
2146       add_addr(generated,US"inbox",1,0,0,0);
2147       filter->keep = 0;
2148       }
2149     }
2150   else if (parse_identifier(filter,CUS "discard"))
2151     {
2152     /*
2153     discard-command  =  "discard" { discard-options } ";"
2154     discard-options  =
2155     */
2156
2157     if (parse_semicolon(filter)==-1) return -1;
2158     if (exec) filter->keep=0;
2159     }
2160   else if (parse_identifier(filter,CUS "redirect"))
2161     {
2162     /*
2163     redirect-command =  "redirect" redirect-options "string" ";"
2164     redirect-options =
2165     redirect-options =) ":copy"
2166     */
2167
2168     struct String recipient;
2169     int m;
2170     int copy=0;
2171
2172     for (;;)
2173       {
2174       if (parse_white(filter)==-1) return -1;
2175       if (parse_identifier(filter,CUS ":copy")==1)
2176         {
2177         if (!filter->require_copy)
2178           {
2179           filter->errmsg=CUS "missing previous require \"copy\";";
2180           return -1;
2181           }
2182           copy=1;
2183         }
2184       else break;
2185       }
2186     if (parse_white(filter)==-1) return -1;
2187     if ((m=parse_string(filter,&recipient))!=1)
2188       {
2189       if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2190       return -1;
2191       }
2192     if (strchr(CCS recipient.character,'@')==(char*)0)
2193       {
2194       filter->errmsg=CUS "unqualified recipient address";
2195       return -1;
2196       }
2197     if (exec)
2198       {
2199       add_addr(generated,recipient.character,0,0,0,0);
2200       if (!copy) filter->keep = 0;
2201       }
2202     if (parse_semicolon(filter)==-1) return -1;
2203     }
2204   else if (parse_identifier(filter,CUS "fileinto"))
2205     {
2206     /*
2207     fileinto-command =  "fileinto" { fileinto-options } string ";"
2208     fileinto-options =
2209     fileinto-options =) [ ":copy" ]
2210     */
2211
2212     struct String folder;
2213     uschar *s;
2214     int m;
2215     unsigned long maxage, maxmessages, maxstorage;
2216     int copy=0;
2217
2218     maxage = maxmessages = maxstorage = 0;
2219     if (!filter->require_fileinto)
2220       {
2221       filter->errmsg=CUS "missing previous require \"fileinto\";";
2222       return -1;
2223       }
2224     for (;;)
2225       {
2226       if (parse_white(filter)==-1) return -1;
2227       if (parse_identifier(filter,CUS ":copy")==1)
2228         {
2229         if (!filter->require_copy)
2230           {
2231           filter->errmsg=CUS "missing previous require \"copy\";";
2232           return -1;
2233           }
2234           copy=1;
2235         }
2236       else break;
2237       }
2238     if (parse_white(filter)==-1) return -1;
2239     if ((m=parse_string(filter,&folder))!=1)
2240       {
2241       if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2242       return -1;
2243       }
2244     m=0; s=folder.character;
2245     if (folder.length==0) m=1;
2246     if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2247     else while (*s)
2248       {
2249       if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2250       ++s;
2251       }
2252     if (m)
2253       {
2254       filter->errmsg=CUS "invalid folder";
2255       return -1;
2256       }
2257     if (exec)
2258       {
2259       add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2260       if (!copy) filter->keep = 0;
2261       }
2262     if (parse_semicolon(filter)==-1) return -1;
2263     }
2264 #ifdef NOTIFY
2265   else if (parse_identifier(filter,CUS "notify"))
2266     {
2267     /*
2268     notify-command =  "notify" { notify-options } ";"
2269     notify-options =  [":method" string]
2270                       [":message" string]
2271     */
2272
2273     int m;
2274     struct String method;
2275     struct String message;
2276
2277     if (!filter->require_notify)
2278       {
2279       filter->errmsg=CUS "missing previous require \"notify\";";
2280       return -1;
2281       }
2282     method.character=(uschar*)0;
2283     method.length=-1;
2284     message.character=(uschar*)0;
2285     message.length=-1;
2286     for (;;)
2287       {
2288       if (parse_white(filter)==-1) return -1;
2289       if (parse_identifier(filter,CUS ":method")==1)
2290         {
2291         if (parse_white(filter)==-1) return -1;
2292         if ((m=parse_string(filter,&method))!=1)
2293           {
2294           if (m==0) filter->errmsg=CUS "method string expected";
2295           return -1;
2296           }
2297         }
2298       else if (parse_identifier(filter,CUS ":message")==1)
2299         {
2300         if (parse_white(filter)==-1) return -1;
2301         if ((m=parse_string(filter,&message))!=1)
2302           {
2303           if (m==0) filter->errmsg=CUS "message string expected";
2304           return -1;
2305           }
2306         }
2307       else break;
2308       }
2309     if (parse_semicolon(filter)==-1) return -1;
2310     }
2311 #endif
2312 #ifdef VACATION
2313   else if (parse_identifier(filter,CUS "vacation"))
2314     {
2315     /*
2316     vacation-command =  "vacation" { vacation-options } <reason: string> ";"
2317     vacation-options =  [":days" number]
2318                         [":subject" string]
2319                         [":from" string]
2320                         [":addresses" string-list]
2321                         [":mime"]
2322                         [":handle" string]
2323     */
2324
2325     int m;
2326     unsigned long days;
2327     struct String subject;
2328     struct String from;
2329     struct String *addresses;
2330     int reason_is_mime;
2331     string_item *aliases;
2332     struct String handle;
2333     struct String reason;
2334
2335     if (!filter->require_vacation)
2336       {
2337       filter->errmsg=CUS "missing previous require \"vacation\";";
2338       return -1;
2339       }
2340     if (exec)
2341       {
2342       if (filter->vacation_ran)
2343         {
2344         filter->errmsg=CUS "trying to execute vacation more than once";
2345         return -1;
2346         }
2347       filter->vacation_ran=1;
2348       }
2349     days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2350     subject.character=(uschar*)0;
2351     subject.length=-1;
2352     from.character=(uschar*)0;
2353     from.length=-1;
2354     addresses=(struct String*)0;
2355     aliases=NULL;
2356     reason_is_mime=0;
2357     handle.character=(uschar*)0;
2358     handle.length=-1;
2359     for (;;)
2360       {
2361       if (parse_white(filter)==-1) return -1;
2362       if (parse_identifier(filter,CUS ":days")==1)
2363         {
2364         if (parse_white(filter)==-1) return -1;
2365         if (parse_number(filter,&days)==-1) return -1;
2366         if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2367         else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2368         }
2369       else if (parse_identifier(filter,CUS ":subject")==1)
2370         {
2371         if (parse_white(filter)==-1) return -1;
2372         if ((m=parse_string(filter,&subject))!=1)
2373           {
2374           if (m==0) filter->errmsg=CUS "subject string expected";
2375           return -1;
2376           }
2377         }
2378       else if (parse_identifier(filter,CUS ":from")==1)
2379         {
2380         int start, end, domain;
2381         uschar *error,*ss;
2382
2383         if (parse_white(filter)==-1) return -1;
2384         if ((m=parse_string(filter,&from))!=1)
2385           {
2386           if (m==0) filter->errmsg=CUS "from string expected";
2387           return -1;
2388           }
2389         if (from.length>0)
2390           {
2391           ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2392             FALSE);
2393           if (ss == NULL)
2394             {
2395             filter->errmsg=string_sprintf("malformed address \"%s\" in "
2396               "Sieve filter: %s", from.character, error);
2397             return -1;
2398             }
2399           }
2400         else
2401           {
2402           filter->errmsg=CUS "empty :from address in Sieve filter";
2403           return -1;
2404           }
2405         }
2406       else if (parse_identifier(filter,CUS ":addresses")==1)
2407         {
2408         struct String *a;
2409
2410         if (parse_white(filter)==-1) return -1;
2411         if ((m=parse_stringlist(filter,&addresses))!=1)
2412           {
2413           if (m==0) filter->errmsg=CUS "addresses string list expected";
2414           return -1;
2415           }
2416         for (a=addresses; a->length!=-1; ++a)
2417           {
2418           string_item *new;
2419
2420           new=store_get(sizeof(string_item));
2421           new->text=store_get(a->length+1);
2422           if (a->length) memcpy(new->text,a->character,a->length);
2423           new->text[a->length]='\0';
2424           new->next=aliases;
2425           aliases=new;
2426           }
2427         }
2428       else if (parse_identifier(filter,CUS ":mime")==1)
2429         reason_is_mime=1;
2430       else if (parse_identifier(filter,CUS ":handle")==1)
2431         {
2432         if (parse_white(filter)==-1) return -1;
2433         if ((m=parse_string(filter,&from))!=1)
2434           {
2435           if (m==0) filter->errmsg=CUS "handle string expected";
2436           return -1;
2437           }
2438         }
2439       else break;
2440       }
2441     if (parse_white(filter)==-1) return -1;
2442     if ((m=parse_string(filter,&reason))!=1)
2443       {
2444       if (m==0) filter->errmsg=CUS "missing reason string";
2445       return -1;
2446       }
2447     if (reason_is_mime)
2448       {
2449       uschar *s,*end;
2450
2451       for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2452       if (s<end)
2453         {
2454         filter->errmsg=CUS "MIME reason string contains 8bit text";
2455         return -1;
2456         }
2457       }
2458     if (parse_semicolon(filter)==-1) return -1;
2459
2460     if (exec)
2461       {
2462       address_item *addr;
2463       int capacity,start;
2464       uschar *buffer;
2465       int buffer_capacity;
2466       struct String key;
2467       md5 base;
2468       uschar digest[16];
2469       uschar hexdigest[33];
2470       int i;
2471       uschar *once;
2472
2473       if (filter_personal(aliases,TRUE))
2474         {
2475         if (filter_test == FTEST_NONE)
2476           {
2477           /* ensure oncelog directory exists; failure will be detected later */
2478
2479           (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2480           }
2481         /* build oncelog filename */
2482
2483         key.character=(uschar*)0;
2484         key.length=0;
2485         capacity=0;
2486         if (handle.length==-1)
2487           {
2488           if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2489           if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2490           key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2491           key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2492           }
2493         else
2494           key=handle;
2495         md5_start(&base);
2496         md5_end(&base, key.character, key.length, digest);
2497         for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2498         if (filter_test != FTEST_NONE)
2499           {
2500           debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2501           }
2502         else
2503           {
2504           capacity=Ustrlen(filter->vacation_directory);
2505           start=capacity;
2506           once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2507           once=string_cat(once,&capacity,&start,hexdigest,33);
2508           once[start] = '\0';
2509
2510           /* process subject */
2511
2512           if (subject.length==-1)
2513             {
2514             expand_header(&subject,&str_subject);
2515             capacity=6;
2516             start=6;
2517             subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2518             subject.length=start;
2519             }
2520
2521           /* add address to list of generated addresses */
2522
2523           addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2524           setflag(addr, af_pfr);
2525           setflag(addr, af_ignore_error);
2526           addr->next = *generated;
2527           *generated = addr;
2528           addr->reply = store_get(sizeof(reply_item));
2529           memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2530           addr->reply->to = string_copy(sender_address);
2531           if (from.length==-1)
2532             addr->reply->from = expand_string(US"$local_part@$domain");
2533           else
2534             addr->reply->from = from.character;
2535           /* Allocation is larger than neccessary, but enough even for split MIME words */
2536           buffer_capacity=16+4*subject.length;
2537           buffer=store_get(buffer_capacity);
2538           addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2539           addr->reply->oncelog=once;
2540           addr->reply->once_repeat=days*86400;
2541
2542           /* build body and MIME headers */
2543
2544           if (reason_is_mime)
2545             {
2546             uschar *mime_body,*reason_end;
2547 #ifdef RFC_EOL
2548             static const uschar nlnl[]="\r\n\r\n";
2549 #else
2550             static const uschar nlnl[]="\n\n";
2551 #endif
2552
2553             for
2554               (
2555               mime_body=reason.character,reason_end=reason.character+reason.length;
2556               mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
2557               ++mime_body
2558               );
2559             capacity = 0;
2560             start = 0;
2561             addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2562             addr->reply->headers[start] = '\0';
2563             capacity = 0;
2564             start = 0;
2565             if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
2566             else mime_body=reason_end-1;
2567             addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2568             addr->reply->text[start] = '\0';
2569             }
2570           else
2571             {
2572             struct String qp;
2573
2574             capacity = 0;
2575             start = reason.length;
2576             addr->reply->headers = US"MIME-Version: 1.0\n"
2577                                    "Content-Type: text/plain;\n"
2578                                    "\tcharset=\"utf-8\"\n"
2579                                    "Content-Transfer-Encoding: quoted-printable";
2580             addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2581             }
2582           }
2583         }
2584         else if (filter_test != FTEST_NONE)
2585           {
2586           debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
2587           }
2588       }
2589     }
2590     else break;
2591 #endif
2592   }
2593 return 1;
2594 }
2595
2596
2597 /*************************************************
2598 *       Parse and interpret a sieve filter       *
2599 *************************************************/
2600
2601 /*
2602 Arguments:
2603   filter      points to the Sieve filter including its state
2604   exec        Execute parsed statements
2605   generated   where to hang newly-generated addresses
2606
2607 Returns:      1                success
2608               -1               syntax or execution error
2609 */
2610
2611 static int parse_start(struct Sieve *filter, int exec,
2612   address_item **generated)
2613 {
2614 filter->pc=filter->filter;
2615 filter->line=1;
2616 filter->keep=1;
2617 filter->require_envelope=0;
2618 filter->require_fileinto=0;
2619 #ifdef NOTIFY
2620 filter->require_notify=0;
2621 #endif
2622 #ifdef SUBADDRESS
2623 filter->require_subaddress=0;
2624 #endif
2625 #ifdef VACATION
2626 filter->require_vacation=0;
2627 filter->vacation_ran=0;
2628 #endif
2629 filter->require_copy=0;
2630 filter->require_iascii_numeric=0;
2631
2632 if (parse_white(filter)==-1) return -1;
2633
2634 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
2635   {
2636   DIR *oncelogdir;
2637   struct dirent *oncelog;
2638   struct stat properties;
2639   time_t now;
2640
2641   /* clean up old vacation log databases */
2642
2643   oncelogdir=opendir(CS filter->vacation_directory);
2644
2645   if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2646     {
2647     filter->errmsg=CUS "unable to open vacation directory";
2648     return -1;
2649     }
2650
2651   if (oncelogdir != NULL)
2652     {
2653     time(&now);
2654
2655     while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2656       {
2657       if (strlen(oncelog->d_name)==32)
2658         {
2659         uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2660         if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2661           Uunlink(s);
2662         }
2663       }
2664     closedir(oncelogdir);
2665     }
2666   }
2667
2668 while (parse_identifier(filter,CUS "require"))
2669   {
2670   /*
2671   require-command = "require" <capabilities: string-list>
2672   */
2673
2674   struct String *cap,*check;
2675   int m;
2676
2677   if (parse_white(filter)==-1) return -1;
2678   if ((m=parse_stringlist(filter,&cap))!=1)
2679     {
2680     if (m==0) filter->errmsg=CUS "capability string list expected";
2681     return -1;
2682     }
2683   for (check=cap; check->character; ++check)
2684     {
2685     if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
2686     else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
2687 #ifdef NOTIFY
2688     else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
2689 #endif
2690 #ifdef SUBADDRESS
2691     else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
2692 #endif
2693 #ifdef VACATION
2694     else if (eq_octet(check,&str_vacation,0))
2695       {
2696       if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
2697         {
2698         filter->errmsg=CUS "vacation disabled";
2699         return -1;
2700         }
2701       filter->require_vacation=1;
2702       }
2703 #endif
2704     else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
2705     else if (eq_octet(check,&str_comparator_ioctet,0)) ;
2706     else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
2707     else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
2708     else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
2709     else
2710       {
2711       filter->errmsg=CUS "unknown capability";
2712       return -1;
2713       }
2714     }
2715     if (parse_semicolon(filter)==-1) return -1;
2716   }
2717   if (parse_commands(filter,exec,generated)==-1) return -1;
2718   if (*filter->pc)
2719     {
2720     filter->errmsg=CUS "syntax error";
2721     return -1;
2722     }
2723   return 1;
2724 }
2725
2726
2727 /*************************************************
2728 *            Interpret a sieve filter file       *
2729 *************************************************/
2730
2731 /*
2732 Arguments:
2733   filter      points to the entire file, read into store as a single string
2734   options     controls whether various special things are allowed, and requests
2735               special actions (not currently used)
2736   sieve_vacation_directory  where to store vacation "once" files
2737   useraddress string expression for :user part of address
2738   subaddress  string expression for :subaddress part of address
2739   generated   where to hang newly-generated addresses
2740   error       where to pass back an error text
2741
2742 Returns:      FF_DELIVERED     success, a significant action was taken
2743               FF_NOTDELIVERED  success, no significant action
2744               FF_DEFER         defer requested
2745               FF_FAIL          fail requested
2746               FF_FREEZE        freeze requested
2747               FF_ERROR         there was a problem
2748 */
2749
2750 int
2751 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
2752   uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
2753 {
2754 struct Sieve sieve;
2755 int r;
2756 uschar *msg;
2757
2758 options = options; /* Keep picky compilers happy */
2759 error = error;
2760
2761 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
2762 sieve.filter=filter;
2763
2764 if (vacation_directory == NULL)
2765   sieve.vacation_directory = NULL;
2766 else
2767   {
2768   sieve.vacation_directory=expand_string(vacation_directory);
2769   if (sieve.vacation_directory == NULL)
2770     {
2771     *error = string_sprintf("failed to expand \"%s\" "
2772       "(sieve_vacation_directory): %s", vacation_directory,
2773       expand_string_message);
2774     return FF_ERROR;
2775     }
2776   }
2777
2778 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
2779 sieve.subaddress = subaddress;
2780
2781 #ifdef COMPILE_SYNTAX_CHECKER
2782 if (parse_start(&sieve,0,generated)==1)
2783 #else
2784 if (parse_start(&sieve,1,generated)==1)
2785 #endif
2786   {
2787   if (sieve.keep)
2788     {
2789     add_addr(generated,US"inbox",1,0,0,0);
2790     msg = string_sprintf("Implicit keep");
2791     r = FF_DELIVERED;
2792     }
2793   else
2794     {
2795     msg = string_sprintf("No implicit keep");
2796     r = FF_DELIVERED;
2797     }
2798   }
2799 else
2800   {
2801   msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
2802 #ifdef COMPILE_SYNTAX_CHECKER
2803   r = FF_ERROR;
2804   *error = msg;
2805 #else
2806   add_addr(generated,US"inbox",1,0,0,0);
2807   r = FF_DELIVERED;
2808 #endif
2809   }
2810
2811 #ifndef COMPILE_SYNTAX_CHECKER
2812 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
2813   else debug_printf("%s\n", msg);
2814 #endif
2815
2816 DEBUG(D_route) debug_printf("Sieve: end of processing\n");
2817 return r;
2818 }