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