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