1 /* $Cambridge: exim/src/src/sieve.c,v 1.31 2007/09/28 12:21:57 tom Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2007 */
8 /* See the file NOTICE for conditions of use and distribution. */
10 /* This code was contributed by Michael Haardt. */
13 /* Sieve mail filter. */
27 /* Define this for RFC compliant \r\n end-of-line terminators. */
28 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 /* Define this for development of the Sieve extension "encoded-character". */
32 #define ENCODED_CHARACTER
34 /* Define this for development of the Sieve extension "envelope-auth". */
37 /* Define this for development of the Sieve extension "enotify". */
40 /* Define this for the Sieve extension "subaddress". */
43 /* Define this for the Sieve extension "vacation". */
47 #define VACATION_MIN_DAYS 1
48 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
49 #define VACATION_MAX_DAYS 31
51 /* Keep this at 75 to accept only RFC compliant MIME words. */
52 /* Increase it if you want to match headers from buggy MUAs. */
53 #define MIMEWORD_LENGTH 75
64 #ifdef ENCODED_CHARACTER
65 int require_encoded_character;
68 int require_envelope_auth;
72 struct Notification *notified;
75 int require_subaddress;
81 uschar *vacation_directory;
82 const uschar *subaddress;
83 const uschar *useraddress;
85 int require_iascii_numeric;
88 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
89 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
91 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
93 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
95 enum RelOp { LT, LE, EQ, GE, GT, NE };
105 struct String method;
106 struct String importance;
107 struct String message;
108 struct Notification *next;
111 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
112 static int parse_test(struct Sieve *filter, int *cond, int exec);
113 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
115 static uschar str_from_c[]="From";
116 static const struct String str_from={ str_from_c, 4 };
117 static uschar str_to_c[]="To";
118 static const struct String str_to={ str_to_c, 2 };
119 static uschar str_cc_c[]="Cc";
120 static const struct String str_cc={ str_cc_c, 2 };
121 static uschar str_bcc_c[]="Bcc";
122 static const struct String str_bcc={ str_bcc_c, 3 };
123 static uschar str_auth_c[]="auth";
124 static const struct String str_auth={ str_auth_c, 4 };
125 static uschar str_sender_c[]="Sender";
126 static const struct String str_sender={ str_sender_c, 6 };
127 static uschar str_resent_from_c[]="Resent-From";
128 static const struct String str_resent_from={ str_resent_from_c, 11 };
129 static uschar str_resent_to_c[]="Resent-To";
130 static const struct String str_resent_to={ str_resent_to_c, 9 };
131 static uschar str_fileinto_c[]="fileinto";
132 static const struct String str_fileinto={ str_fileinto_c, 8 };
133 static uschar str_envelope_c[]="envelope";
134 static const struct String str_envelope={ str_envelope_c, 8 };
135 #ifdef ENCODED_CHARACTER
136 static uschar str_encoded_character_c[]="encoded-character";
137 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
140 static uschar str_envelope_auth_c[]="envelope-auth";
141 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
144 static uschar str_enotify_c[]="enotify";
145 static const struct String str_enotify={ str_enotify_c, 7 };
146 static uschar str_online_c[]="online";
147 static const struct String str_online={ str_online_c, 6 };
148 static uschar str_maybe_c[]="maybe";
149 static const struct String str_maybe={ str_maybe_c, 5 };
152 static uschar str_subaddress_c[]="subaddress";
153 static const struct String str_subaddress={ str_subaddress_c, 10 };
156 static uschar str_vacation_c[]="vacation";
157 static const struct String str_vacation={ str_vacation_c, 8 };
158 static uschar str_subject_c[]="Subject";
159 static const struct String str_subject={ str_subject_c, 7 };
161 static uschar str_copy_c[]="copy";
162 static const struct String str_copy={ str_copy_c, 4 };
163 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
164 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
165 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
166 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
167 static uschar str_ioctet_c[]="i;octet";
168 static const struct String str_ioctet={ str_ioctet_c, 7 };
169 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
170 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
171 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
172 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
173 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
174 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
175 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
176 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
177 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
178 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
181 /*************************************************
182 * Encode to quoted-printable *
183 *************************************************/
194 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
197 const uschar *start,*end;
202 for (pass=0; pass<=1; ++pass)
209 dst->character=store_get(dst->length+1); /* plus one for \0 */
212 for (start=src->character,end=start+src->length; start<end; ++start)
229 || (ch>=62 && ch<=126)
234 && (*(start+1)!='\r' || *(start+2)!='\n')
244 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
262 sprintf(CS new,"=%02X",ch);
269 *new='\0'; /* not included in length, but nice */
274 /*************************************************
275 * Check mail address for correct syntax *
276 *************************************************/
279 Check mail address for being syntactically correct.
282 filter points to the Sieve filter including its state
283 address String containing one address
286 1 Mail address is syntactically OK
290 int check_mail_address(struct Sieve *filter, const struct String *address)
292 int start, end, domain;
295 if (address->length>0)
297 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
301 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
302 address->character, error);
310 filter->errmsg=CUS "empty address";
316 /*************************************************
317 * Decode URI encoded string *
318 *************************************************/
322 str URI encoded string
325 0 Decoding successful
330 static int uri_decode(struct String *str)
334 if (str->length==0) return 0;
335 for (s=str->character,t=s,e=s+str->length; s<e; )
339 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
341 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
342 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
351 str->length=t-str->character;
356 /*************************************************
358 *************************************************/
363 mailtoURI = "mailto:" [ to ] [ headers ]
364 to = [ addr-spec *("%2C" addr-spec ) ]
365 headers = "?" header *( "&" header )
366 header = hname "=" hvalue
371 filter points to the Sieve filter including its state
372 uri URI, excluding scheme
377 1 URI is syntactically OK
382 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
385 struct String to,hname,hvalue;
389 if (Ustrncmp(uri,"mailto:",7))
391 filter->errmsg=US "Unknown URI scheme";
395 if (*uri && *uri!='?')
399 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
403 to.character=(uschar*)0;
405 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
406 to.character[to.length]='\0';
407 if (uri_decode(&to)==-1)
409 filter->errmsg=US"Invalid URI encoding";
412 new=store_get(sizeof(string_item));
413 new->text=store_get(to.length+1);
414 if (to.length) memcpy(new->text,to.character,to.length);
415 new->text[to.length]='\0';
416 new->next=*recipient;
421 filter->errmsg=US"Missing addr-spec in URI";
424 if (*uri=='%') uri+=3;
433 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
437 hname.character=(uschar*)0;
439 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
440 hname.character[hname.length]='\0';
441 if (uri_decode(&hname)==-1)
443 filter->errmsg=US"Invalid URI encoding";
452 filter->errmsg=US"Missing equal after hname";
456 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
460 hvalue.character=(uschar*)0;
462 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
463 hvalue.character[hvalue.length]='\0';
464 if (uri_decode(&hvalue)==-1)
466 filter->errmsg=US"Invalid URI encoding";
470 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
472 new=store_get(sizeof(string_item));
473 new->text=store_get(hvalue.length+1);
474 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
475 new->text[hvalue.length]='\0';
476 new->next=*recipient;
479 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
481 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
485 static struct String ignore[]=
492 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
495 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
498 if (header->length==-1) header->length=0;
499 capacity=header->length;
500 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
501 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
502 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
503 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
504 header->character[header->length]='\0';
507 if (*uri=='&') ++uri;
513 filter->errmsg=US"Syntactically invalid URI";
521 /*************************************************
522 * Octet-wise string comparison *
523 *************************************************/
527 needle UTF-8 string to search ...
528 haystack ... inside the haystack
529 match_prefix 1 to compare if needle is a prefix of haystack
531 Returns: 0 needle not found in haystack
535 static int eq_octet(const struct String *needle,
536 const struct String *haystack, int match_prefix)
544 h=haystack->character;
548 if (*n&0x80) return 0;
549 if (*h&0x80) return 0;
551 if (*n!=*h) return 0;
557 return (match_prefix ? nl==0 : nl==0 && hl==0);
561 /*************************************************
562 * ASCII case-insensitive string comparison *
563 *************************************************/
567 needle UTF-8 string to search ...
568 haystack ... inside the haystack
569 match_prefix 1 to compare if needle is a prefix of haystack
571 Returns: 0 needle not found in haystack
575 static int eq_asciicase(const struct String *needle,
576 const struct String *haystack, int match_prefix)
585 h=haystack->character;
591 if (nc&0x80) return 0;
592 if (hc&0x80) return 0;
594 /* tolower depends on the locale and only ASCII case must be insensitive */
595 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
601 return (match_prefix ? nl==0 : nl==0 && hl==0);
605 /*************************************************
606 * Glob pattern search *
607 *************************************************/
611 needle pattern to search ...
612 haystack ... inside the haystack
614 Returns: 0 needle not found in haystack
619 static int eq_glob(const struct String *needle,
620 const struct String *haystack, int ascii_caseless, int match_octet)
622 const uschar *n,*h,*nend,*hend;
626 h=haystack->character;
627 nend=n+needle->length;
628 hend=h+haystack->length;
638 const uschar *npart,*hpart;
640 /* Try to match a non-star part of the needle at the current */
641 /* position in the haystack. */
645 while (npart<nend && *npart!='*') switch (*npart)
649 if (hpart==hend) return 0;
654 /* Match one UTF8 encoded character */
655 if ((*hpart&0xc0)==0xc0)
658 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
669 if (npart==nend) return -1;
674 if (hpart==hend) return 0;
675 /* tolower depends on the locale, but we need ASCII */
679 (*hpart&0x80) || (*npart&0x80) ||
682 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
687 /* string match after a star failed, advance and try again */
701 /* at this point, a part was matched successfully */
702 if (may_advance && npart==nend && hpart<hend)
703 /* needle ends, but haystack does not: if there was a star before, advance and try again */
713 return (h==hend ? 1 : may_advance);
717 /*************************************************
718 * ASCII numeric comparison *
719 *************************************************/
723 a first numeric string
724 b second numeric string
725 relop relational operator
727 Returns: 0 not (a relop b)
731 static int eq_asciinumeric(const struct String *a,
732 const struct String *b, enum RelOp relop)
735 const uschar *as,*aend,*bs,*bend;
739 aend=a->character+a->length;
741 bend=b->character+b->length;
743 while (*as>='0' && *as<='9' && as<aend) ++as;
745 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
748 if (al && bl==0) cmp=-1;
749 else if (al==0 && bl==0) cmp=0;
750 else if (al==0 && bl) cmp=1;
754 if (cmp==0) cmp=memcmp(a->character,b->character,al);
758 case LT: return cmp<0;
759 case LE: return cmp<=0;
760 case EQ: return cmp==0;
761 case GE: return cmp>=0;
762 case GT: return cmp>0;
763 case NE: return cmp!=0;
770 /*************************************************
772 *************************************************/
776 filter points to the Sieve filter including its state
777 needle UTF-8 pattern or string to search ...
778 haystack ... inside the haystack
782 Returns: 0 needle not found in haystack
784 -1 comparator does not offer matchtype
787 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
788 enum Comparator co, enum MatchType mt)
792 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
793 (debug_selector & D_filter) != 0)
795 debug_printf("String comparison (match ");
798 case MATCH_IS: debug_printf(":is"); break;
799 case MATCH_CONTAINS: debug_printf(":contains"); break;
800 case MATCH_MATCHES: debug_printf(":matches"); break;
802 debug_printf(", comparison \"");
805 case COMP_OCTET: debug_printf("i;octet"); break;
806 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
807 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
809 debug_printf("\"):\n");
810 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
811 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
821 if (eq_octet(needle,haystack,0)) r=1;
824 case COMP_EN_ASCII_CASEMAP:
826 if (eq_asciicase(needle,haystack,0)) r=1;
829 case COMP_ASCII_NUMERIC:
831 if (!filter->require_iascii_numeric)
833 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
836 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
850 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
853 case COMP_EN_ASCII_CASEMAP:
855 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
860 filter->errmsg=CUS "comparator does not offer specified matchtype";
872 if ((r=eq_glob(needle,haystack,0,1))==-1)
874 filter->errmsg=CUS "syntactically invalid pattern";
879 case COMP_EN_ASCII_CASEMAP:
881 if ((r=eq_glob(needle,haystack,1,1))==-1)
883 filter->errmsg=CUS "syntactically invalid pattern";
890 filter->errmsg=CUS "comparator does not offer specified matchtype";
897 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
898 (debug_selector & D_filter) != 0)
899 debug_printf(" Result %s\n",r?"true":"false");
904 /*************************************************
905 * Check header field syntax *
906 *************************************************/
909 RFC 2822, section 3.6.8 says:
913 ftext = %d33-57 / ; Any character except
914 %d59-126 ; controls, SP, and
917 That forbids 8-bit header fields. This implementation accepts them, since
918 all of Exim is 8-bit clean, so it adds %d128-%d255.
921 header header field to quote for suitable use in Exim expansions
923 Returns: 0 string is not a valid header field
924 1 string is a value header field
927 static int is_header(const struct String *header)
937 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
948 /*************************************************
949 * Quote special characters string *
950 *************************************************/
954 header header field to quote for suitable use in Exim expansions
957 Returns: quoted string
960 static const uschar *quote(const struct String *header)
975 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
982 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
986 quoted=string_cat(quoted,&size,&ptr,h,1);
992 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
997 /*************************************************
998 * Add address to list of generated addresses *
999 *************************************************/
1002 According to RFC 3028, duplicate delivery to the same address must
1003 not happen, so the list is first searched for the address.
1006 generated list of generated addresses
1007 addr new address to add
1008 file address denotes a file
1013 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1015 address_item *new_addr;
1017 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1019 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1021 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1023 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1029 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1031 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1033 new_addr=deliver_make_addr(addr,TRUE);
1036 setflag(new_addr, af_pfr|af_file);
1039 new_addr->p.errors_address = NULL;
1040 new_addr->next = *generated;
1041 *generated = new_addr;
1045 /*************************************************
1046 * Return decoded header field *
1047 *************************************************/
1050 Unfold the header field as described in RFC 2822 and remove all
1051 leading and trailing white space, then perform MIME decoding and
1052 translate the header field to UTF-8.
1055 value returned value of the field
1056 header name of the header field
1058 Returns: nothing The expanded string is empty
1059 in case there is no such header
1062 static void expand_header(struct String *value, const struct String *header)
1068 value->character=(uschar*)0;
1070 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1071 while (*r==' ' || *r=='\t') ++r;
1079 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1081 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1085 /*************************************************
1086 * Parse remaining hash comment *
1087 *************************************************/
1091 Comment up to terminating CRLF
1094 filter points to the Sieve filter including its state
1100 static int parse_hashcomment(struct Sieve *filter)
1106 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1108 if (*filter->pc=='\n')
1121 filter->errmsg=CUS "missing end of comment";
1126 /*************************************************
1127 * Parse remaining C-style comment *
1128 *************************************************/
1132 Everything up to star slash
1135 filter points to the Sieve filter including its state
1141 static int parse_comment(struct Sieve *filter)
1146 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1153 filter->errmsg=CUS "missing end of comment";
1158 /*************************************************
1159 * Parse optional white space *
1160 *************************************************/
1164 Spaces, tabs, CRLFs, hash comments or C-style comments
1167 filter points to the Sieve filter including its state
1173 static int parse_white(struct Sieve *filter)
1177 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1179 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1181 else if (*filter->pc=='\n')
1191 else if (*filter->pc=='#')
1193 if (parse_hashcomment(filter)==-1) return -1;
1195 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1197 if (parse_comment(filter)==-1) return -1;
1205 #ifdef ENCODED_CHARACTER
1206 /*************************************************
1207 * Decode hex-encoded-character string *
1208 *************************************************/
1211 Encoding definition:
1212 blank = SP / TAB / CRLF
1213 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1214 hex-pair = 1*2HEXDIG
1217 src points to a hex-pair-seq
1218 end points to its end
1219 dst points to the destination of the decoded octets,
1220 optionally to (uschar*)0 for checking only
1222 Returns: >=0 number of decoded octets
1226 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1230 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1235 for (x=0,d=0; d<2 && src<end && isxdigit(n=tolower(*src)); x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')),++d,++src);
1236 if (d==0) return -1;
1239 if (src==end) return decoded;
1240 if (*src==' ' || *src=='\t' || *src=='\n')
1241 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1250 /*************************************************
1251 * Decode unicode-encoded-character string *
1252 *************************************************/
1255 Encoding definition:
1256 blank = SP / TAB / CRLF
1257 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1258 unicode-hex = 1*HEXDIG
1260 It is an error for a script to use a hexadecimal value that isn't in
1261 either the range 0 to D7FF or the range E000 to 10FFFF.
1263 At this time, strings are already scanned, thus the CRLF is converted
1264 to the internally used \n (should RFC_EOL have been used).
1267 src points to a unicode-hex-seq
1268 end points to its end
1269 dst points to the destination of the decoded octets,
1270 optionally to (uschar*)0 for checking only
1272 Returns: >=0 number of decoded octets
1274 -2 semantic error (character range violation)
1277 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1281 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1288 for (hex_seq=src; src<end && *src=='0'; ++src);
1289 for (c=0,d=0; d<7 && src<end && isxdigit(n=tolower(*src)); c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')),++d,++src);
1290 if (src==hex_seq) return -1;
1291 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1297 else if (c>=0x80 && c<=0x7ff)
1302 *dst++=128+(c&0x3f);
1306 else if (c>=0x800 && c<=0xffff)
1311 *dst++=128+((c>>6)&0x3f);
1312 *dst++=128+(c&0x3f);
1316 else if (c>=0x10000 && c<=0x1fffff)
1321 *dst++=128+((c>>10)&0x3f);
1322 *dst++=128+((c>>6)&0x3f);
1323 *dst++=128+(c&0x3f);
1327 if (*src==' ' || *src=='\t' || *src=='\n')
1329 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1330 if (src==end) return decoded;
1339 /*************************************************
1340 * Decode encoded-character string *
1341 *************************************************/
1344 Encoding definition:
1345 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1346 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1349 encoded points to an encoded string, returns decoded string
1350 filter points to the Sieve filter including its state
1356 static int string_decode(struct Sieve *filter, struct String *data)
1358 uschar *src,*dst,*end;
1360 src=data->character;
1362 end=data->character+data->length;
1368 strncmpic(src,US "${hex:",6)==0
1369 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1370 && (hex_decode(src+6,brace,(uschar*)0))>=0
1373 dst+=hex_decode(src+6,brace,dst);
1377 strncmpic(src,US "${unicode:",10)==0
1378 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1381 switch (unicode_decode(src+10,brace,(uschar*)0))
1385 filter->errmsg=CUS "unicode character out of range";
1395 dst+=unicode_decode(src+10,brace,dst);
1402 data->length=dst-data->character;
1409 /*************************************************
1410 * Parse an optional string *
1411 *************************************************/
1415 quoted-string = DQUOTE *CHAR DQUOTE
1416 ;; in general, \ CHAR inside a string maps to CHAR
1417 ;; so \" maps to " and \\ maps to \
1418 ;; note that newlines and other characters are all allowed
1421 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1422 *(multi-line-literal / multi-line-dotstuff)
1424 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1425 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1426 ;; A line containing only "." ends the multi-line.
1427 ;; Remove a leading '.' if followed by another '.'.
1428 string = quoted-string / multi-line
1431 filter points to the Sieve filter including its state
1432 id specifies identifier to match
1436 0 identifier not matched
1439 static int parse_string(struct Sieve *filter, struct String *data)
1444 data->character=(uschar*)0;
1445 if (*filter->pc=='"') /* quoted string */
1450 if (*filter->pc=='"') /* end of string */
1452 int foo=data->length;
1455 /* that way, there will be at least one character allocated */
1456 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1457 #ifdef ENCODED_CHARACTER
1458 if (filter->require_encoded_character
1459 && string_decode(filter,data)==-1)
1464 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1466 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1469 else /* regular character */
1472 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1474 if (*filter->pc=='\n')
1476 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1480 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1484 filter->errmsg=CUS "missing end of string";
1487 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1490 /* skip optional white space followed by hashed comment or CRLF */
1491 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1492 if (*filter->pc=='#')
1494 if (parse_hashcomment(filter)==-1) return -1;
1497 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1499 else if (*filter->pc=='\n')
1511 filter->errmsg=CUS "syntax error";
1517 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1519 if (*filter->pc=='\n') /* end of line */
1522 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1530 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1532 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1535 int foo=data->length;
1537 /* that way, there will be at least one character allocated */
1538 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1545 #ifdef ENCODED_CHARACTER
1546 if (filter->require_encoded_character
1547 && string_decode(filter,data)==-1)
1552 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1554 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1558 else /* regular character */
1560 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1564 filter->errmsg=CUS "missing end of multi line string";
1571 /*************************************************
1572 * Parse a specific identifier *
1573 *************************************************/
1577 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1580 filter points to the Sieve filter including its state
1581 id specifies identifier to match
1584 0 identifier not matched
1587 static int parse_identifier(struct Sieve *filter, const uschar *id)
1589 size_t idlen=Ustrlen(id);
1591 if (strncmpic(US filter->pc,US id,idlen)==0)
1593 uschar next=filter->pc[idlen];
1595 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1603 /*************************************************
1605 *************************************************/
1609 number = 1*DIGIT [QUANTIFIER]
1610 QUANTIFIER = "K" / "M" / "G"
1613 filter points to the Sieve filter including its state
1617 -1 no string list found
1620 static int parse_number(struct Sieve *filter, unsigned long *data)
1624 if (*filter->pc>='0' && *filter->pc<='9')
1629 d=Ustrtoul(filter->pc,&e,10);
1632 filter->errmsg=CUstrerror(ERANGE);
1637 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1638 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1639 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1640 if (d>(ULONG_MAX/u))
1642 filter->errmsg=CUstrerror(ERANGE);
1651 filter->errmsg=CUS "missing number";
1657 /*************************************************
1658 * Parse a string list *
1659 *************************************************/
1663 string-list = "[" string *("," string) "]" / string
1666 filter points to the Sieve filter including its state
1667 data returns string list
1670 -1 no string list found
1673 static int parse_stringlist(struct Sieve *filter, struct String **data)
1675 const uschar *orig=filter->pc;
1678 struct String *d=(struct String*)0;
1681 if (*filter->pc=='[') /* string list */
1686 if (parse_white(filter)==-1) goto error;
1687 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1690 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1691 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1692 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1694 filter->errmsg=CUstrerror(errno);
1697 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1699 dataCapacity=newCapacity;
1701 m=parse_string(filter,&d[dataLength]);
1704 if (dataLength==0) break;
1707 filter->errmsg=CUS "missing string";
1711 else if (m==-1) goto error;
1713 if (parse_white(filter)==-1) goto error;
1714 if (*filter->pc==',') ++filter->pc;
1717 if (*filter->pc==']')
1719 d[dataLength].character=(uschar*)0;
1720 d[dataLength].length=-1;
1727 filter->errmsg=CUS "missing closing bracket";
1731 else /* single string */
1733 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1737 m=parse_string(filter,&d[0]);
1749 d[1].character=(uschar*)0;
1756 filter->errmsg=CUS "missing string list";
1761 /*************************************************
1762 * Parse an optional address part specifier *
1763 *************************************************/
1767 address-part = ":localpart" / ":domain" / ":all"
1768 address-part =/ ":user" / ":detail"
1771 filter points to the Sieve filter including its state
1772 a returns address part specified
1775 0 no comparator found
1779 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1782 if (parse_identifier(filter,CUS ":user")==1)
1784 if (!filter->require_subaddress)
1786 filter->errmsg=CUS "missing previous require \"subaddress\";";
1792 else if (parse_identifier(filter,CUS ":detail")==1)
1794 if (!filter->require_subaddress)
1796 filter->errmsg=CUS "missing previous require \"subaddress\";";
1804 if (parse_identifier(filter,CUS ":localpart")==1)
1806 *a=ADDRPART_LOCALPART;
1809 else if (parse_identifier(filter,CUS ":domain")==1)
1814 else if (parse_identifier(filter,CUS ":all")==1)
1823 /*************************************************
1824 * Parse an optional comparator *
1825 *************************************************/
1829 comparator = ":comparator" <comparator-name: string>
1832 filter points to the Sieve filter including its state
1833 c returns comparator
1836 0 no comparator found
1837 -1 incomplete comparator found
1840 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1842 struct String comparator_name;
1844 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1845 if (parse_white(filter)==-1) return -1;
1846 switch (parse_string(filter,&comparator_name))
1851 filter->errmsg=CUS "missing comparator";
1858 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1863 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1865 *c=COMP_EN_ASCII_CASEMAP;
1868 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1870 *c=COMP_EN_ASCII_CASEMAP;
1873 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1875 *c=COMP_ASCII_NUMERIC;
1880 filter->errmsg=CUS "invalid comparator";
1889 /*************************************************
1890 * Parse an optional match type *
1891 *************************************************/
1895 match-type = ":is" / ":contains" / ":matches"
1898 filter points to the Sieve filter including its state
1899 m returns match type
1902 0 no match type found
1905 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1907 if (parse_identifier(filter,CUS ":is")==1)
1912 else if (parse_identifier(filter,CUS ":contains")==1)
1917 else if (parse_identifier(filter,CUS ":matches")==1)
1926 /*************************************************
1927 * Parse and interpret an optional test list *
1928 *************************************************/
1932 test-list = "(" test *("," test) ")"
1935 filter points to the Sieve filter including its state
1936 n total number of tests
1937 true number of passed tests
1938 exec Execute parsed statements
1941 0 no test list found
1942 -1 syntax or execution error
1945 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1947 if (parse_white(filter)==-1) return -1;
1948 if (*filter->pc=='(')
1957 switch (parse_test(filter,&cond,exec))
1960 case 0: filter->errmsg=CUS "missing test"; return -1;
1961 default: ++*n; if (cond) ++*num_true; break;
1963 if (parse_white(filter)==-1) return -1;
1964 if (*filter->pc==',') ++filter->pc;
1967 if (*filter->pc==')')
1974 filter->errmsg=CUS "missing closing paren";
1982 /*************************************************
1983 * Parse and interpret an optional test *
1984 *************************************************/
1988 filter points to the Sieve filter including its state
1989 cond returned condition status
1990 exec Execute parsed statements
1994 -1 syntax or execution error
1997 static int parse_test(struct Sieve *filter, int *cond, int exec)
1999 if (parse_white(filter)==-1) return -1;
2000 if (parse_identifier(filter,CUS "address"))
2003 address-test = "address" { [address-part] [comparator] [match-type] }
2004 <header-list: string-list> <key-list: string-list>
2006 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2009 enum AddressPart addressPart=ADDRPART_ALL;
2010 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2011 enum MatchType matchType=MATCH_IS;
2012 struct String *hdr,*h,*key,*k;
2018 if (parse_white(filter)==-1) return -1;
2019 if ((m=parse_addresspart(filter,&addressPart))!=0)
2021 if (m==-1) return -1;
2024 filter->errmsg=CUS "address part already specified";
2029 else if ((m=parse_comparator(filter,&comparator))!=0)
2031 if (m==-1) return -1;
2034 filter->errmsg=CUS "comparator already specified";
2039 else if ((m=parse_matchtype(filter,&matchType))!=0)
2041 if (m==-1) return -1;
2044 filter->errmsg=CUS "match type already specified";
2051 if (parse_white(filter)==-1) return -1;
2052 if ((m=parse_stringlist(filter,&hdr))!=1)
2054 if (m==0) filter->errmsg=CUS "header string list expected";
2057 if (parse_white(filter)==-1) return -1;
2058 if ((m=parse_stringlist(filter,&key))!=1)
2060 if (m==0) filter->errmsg=CUS "key string list expected";
2064 for (h=hdr; h->length!=-1 && !*cond; ++h)
2066 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2070 !eq_asciicase(h,&str_from,0)
2071 && !eq_asciicase(h,&str_to,0)
2072 && !eq_asciicase(h,&str_cc,0)
2073 && !eq_asciicase(h,&str_bcc,0)
2074 && !eq_asciicase(h,&str_sender,0)
2075 && !eq_asciicase(h,&str_resent_from,0)
2076 && !eq_asciicase(h,&str_resent_to,0)
2079 filter->errmsg=CUS "invalid header field";
2084 /* We are only interested in addresses below, so no MIME decoding */
2085 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2086 if (header_value == NULL)
2088 filter->errmsg=CUS "header string expansion failed";
2091 parse_allow_group = TRUE;
2092 while (*header_value && !*cond)
2095 int start, end, domain;
2099 end_addr = parse_find_address_end(header_value, FALSE);
2100 saveend = *end_addr;
2102 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2104 if (extracted_addr) switch (addressPart)
2106 case ADDRPART_ALL: part=extracted_addr; break;
2110 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2111 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2113 case ADDRPART_DETAIL: part=NULL; break;
2117 *end_addr = saveend;
2120 for (k=key; k->length!=-1; ++k)
2122 struct String partStr;
2124 partStr.character=part;
2125 partStr.length=Ustrlen(part);
2128 *cond=compare(filter,k,&partStr,comparator,matchType);
2129 if (*cond==-1) return -1;
2134 if (saveend == 0) break;
2135 header_value = end_addr + 1;
2137 parse_allow_group = FALSE;
2138 parse_found_group = FALSE;
2143 else if (parse_identifier(filter,CUS "allof"))
2146 allof-test = "allof" <tests: test-list>
2151 switch (parse_testlist(filter,&n,&num_true,exec))
2154 case 0: filter->errmsg=CUS "missing test list"; return -1;
2155 default: *cond=(n==num_true); return 1;
2158 else if (parse_identifier(filter,CUS "anyof"))
2161 anyof-test = "anyof" <tests: test-list>
2166 switch (parse_testlist(filter,&n,&num_true,exec))
2169 case 0: filter->errmsg=CUS "missing test list"; return -1;
2170 default: *cond=(num_true>0); return 1;
2173 else if (parse_identifier(filter,CUS "exists"))
2176 exists-test = "exists" <header-names: string-list>
2179 struct String *hdr,*h;
2182 if (parse_white(filter)==-1) return -1;
2183 if ((m=parse_stringlist(filter,&hdr))!=1)
2185 if (m==0) filter->errmsg=CUS "header string list expected";
2191 for (h=hdr; h->length!=-1 && *cond; ++h)
2195 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2196 if (header_def == NULL)
2198 filter->errmsg=CUS "header string expansion failed";
2201 if (Ustrcmp(header_def,"false")==0) *cond=0;
2206 else if (parse_identifier(filter,CUS "false"))
2209 false-test = "false"
2215 else if (parse_identifier(filter,CUS "header"))
2218 header-test = "header" { [comparator] [match-type] }
2219 <header-names: string-list> <key-list: string-list>
2222 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2223 enum MatchType matchType=MATCH_IS;
2224 struct String *hdr,*h,*key,*k;
2230 if (parse_white(filter)==-1) return -1;
2231 if ((m=parse_comparator(filter,&comparator))!=0)
2233 if (m==-1) return -1;
2236 filter->errmsg=CUS "comparator already specified";
2241 else if ((m=parse_matchtype(filter,&matchType))!=0)
2243 if (m==-1) return -1;
2246 filter->errmsg=CUS "match type already specified";
2253 if (parse_white(filter)==-1) return -1;
2254 if ((m=parse_stringlist(filter,&hdr))!=1)
2256 if (m==0) filter->errmsg=CUS "header string list expected";
2259 if (parse_white(filter)==-1) return -1;
2260 if ((m=parse_stringlist(filter,&key))!=1)
2262 if (m==0) filter->errmsg=CUS "key string list expected";
2266 for (h=hdr; h->length!=-1 && !*cond; ++h)
2270 filter->errmsg=CUS "invalid header field";
2275 struct String header_value;
2278 expand_header(&header_value,h);
2279 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2280 if (header_value.character == NULL || header_def == NULL)
2282 filter->errmsg=CUS "header string expansion failed";
2285 for (k=key; k->length!=-1; ++k)
2287 if (Ustrcmp(header_def,"true")==0)
2289 *cond=compare(filter,k,&header_value,comparator,matchType);
2290 if (*cond==-1) return -1;
2298 else if (parse_identifier(filter,CUS "not"))
2300 if (parse_white(filter)==-1) return -1;
2301 switch (parse_test(filter,cond,exec))
2304 case 0: filter->errmsg=CUS "missing test"; return -1;
2305 default: *cond=!*cond; return 1;
2308 else if (parse_identifier(filter,CUS "size"))
2311 relop = ":over" / ":under"
2312 size-test = "size" relop <limit: number>
2315 unsigned long limit;
2318 if (parse_white(filter)==-1) return -1;
2319 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2320 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2323 filter->errmsg=CUS "missing :over or :under";
2326 if (parse_white(filter)==-1) return -1;
2327 if (parse_number(filter,&limit)==-1) return -1;
2328 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2331 else if (parse_identifier(filter,CUS "true"))
2336 else if (parse_identifier(filter,CUS "envelope"))
2339 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2340 <envelope-part: string-list> <key-list: string-list>
2342 envelope-part is case insensitive "from" or "to"
2343 #ifdef ENVELOPE_AUTH
2344 envelope-part =/ "auth"
2348 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2349 enum AddressPart addressPart=ADDRPART_ALL;
2350 enum MatchType matchType=MATCH_IS;
2351 struct String *env,*e,*key,*k;
2355 if (!filter->require_envelope)
2357 filter->errmsg=CUS "missing previous require \"envelope\";";
2362 if (parse_white(filter)==-1) return -1;
2363 if ((m=parse_comparator(filter,&comparator))!=0)
2365 if (m==-1) return -1;
2368 filter->errmsg=CUS "comparator already specified";
2373 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2375 if (m==-1) return -1;
2378 filter->errmsg=CUS "address part already specified";
2383 else if ((m=parse_matchtype(filter,&matchType))!=0)
2385 if (m==-1) return -1;
2388 filter->errmsg=CUS "match type already specified";
2395 if (parse_white(filter)==-1) return -1;
2396 if ((m=parse_stringlist(filter,&env))!=1)
2398 if (m==0) filter->errmsg=CUS "envelope string list expected";
2401 if (parse_white(filter)==-1) return -1;
2402 if ((m=parse_stringlist(filter,&key))!=1)
2404 if (m==0) filter->errmsg=CUS "key string list expected";
2408 for (e=env; e->length!=-1 && !*cond; ++e)
2410 const uschar *envelopeExpr=CUS 0;
2411 uschar *envelope=US 0;
2413 if (eq_asciicase(e,&str_from,0))
2415 switch (addressPart)
2417 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2421 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2422 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2424 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2428 else if (eq_asciicase(e,&str_to,0))
2430 switch (addressPart)
2432 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2434 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2435 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2437 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2438 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2441 #ifdef ENVELOPE_AUTH
2442 else if (eq_asciicase(e,&str_auth,0))
2444 switch (addressPart)
2446 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2450 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2451 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2453 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2460 filter->errmsg=CUS "invalid envelope string";
2463 if (exec && envelopeExpr)
2465 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2467 filter->errmsg=CUS "header string expansion failed";
2470 for (k=key; k->length!=-1; ++k)
2472 struct String envelopeStr;
2474 envelopeStr.character=envelope;
2475 envelopeStr.length=Ustrlen(envelope);
2476 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2477 if (*cond==-1) return -1;
2485 else if (parse_identifier(filter,CUS "valid_notify_method"))
2488 valid_notify_method = "valid_notify_method"
2489 <notification-uris: string-list>
2492 struct String *uris,*u;
2495 if (!filter->require_enotify)
2497 filter->errmsg=CUS "missing previous require \"enotify\";";
2500 if (parse_white(filter)==-1) return -1;
2501 if ((m=parse_stringlist(filter,&uris))!=1)
2503 if (m==0) filter->errmsg=CUS "URI string list expected";
2509 for (u=uris; u->length!=-1 && *cond; ++u)
2511 string_item *recipient;
2512 struct String header,subject,body;
2516 header.character=(uschar*)0;
2518 subject.character=(uschar*)0;
2520 body.character=(uschar*)0;
2521 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2527 else if (parse_identifier(filter,CUS "notify_method_capability"))
2530 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2531 <notification-uri: string>
2532 <notification-capability: string>
2533 <key-list: string-list>
2539 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2540 enum MatchType matchType=MATCH_IS;
2541 struct String uri,capa,*keys,*k;
2543 if (!filter->require_enotify)
2545 filter->errmsg=CUS "missing previous require \"enotify\";";
2550 if (parse_white(filter)==-1) return -1;
2551 if ((m=parse_comparator(filter,&comparator))!=0)
2553 if (m==-1) return -1;
2556 filter->errmsg=CUS "comparator already specified";
2561 else if ((m=parse_matchtype(filter,&matchType))!=0)
2563 if (m==-1) return -1;
2566 filter->errmsg=CUS "match type already specified";
2573 if ((m=parse_string(filter,&uri))!=1)
2575 if (m==0) filter->errmsg=CUS "missing notification URI string";
2578 if (parse_white(filter)==-1) return -1;
2579 if ((m=parse_string(filter,&capa))!=1)
2581 if (m==0) filter->errmsg=CUS "missing notification capability string";
2584 if (parse_white(filter)==-1) return -1;
2585 if ((m=parse_stringlist(filter,&keys))!=1)
2587 if (m==0) filter->errmsg=CUS "missing key string list";
2592 string_item *recipient;
2593 struct String header,subject,body;
2598 header.character=(uschar*)0;
2600 subject.character=(uschar*)0;
2602 body.character=(uschar*)0;
2603 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2605 if (eq_asciicase(&capa,&str_online,0)==1)
2606 for (k=keys; k->length!=-1; ++k)
2608 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2609 if (*cond==-1) return -1;
2621 /*************************************************
2622 * Parse and interpret an optional block *
2623 *************************************************/
2627 filter points to the Sieve filter including its state
2628 exec Execute parsed statements
2629 generated where to hang newly-generated addresses
2631 Returns: 2 success by stop
2633 0 no block command found
2634 -1 syntax or execution error
2637 static int parse_block(struct Sieve *filter, int exec,
2638 address_item **generated)
2642 if (parse_white(filter)==-1) return -1;
2643 if (*filter->pc=='{')
2646 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2647 if (*filter->pc=='}')
2654 filter->errmsg=CUS "expecting command or closing brace";
2662 /*************************************************
2663 * Match a semicolon *
2664 *************************************************/
2668 filter points to the Sieve filter including its state
2674 static int parse_semicolon(struct Sieve *filter)
2676 if (parse_white(filter)==-1) return -1;
2677 if (*filter->pc==';')
2684 filter->errmsg=CUS "missing semicolon";
2690 /*************************************************
2691 * Parse and interpret a Sieve command *
2692 *************************************************/
2696 filter points to the Sieve filter including its state
2697 exec Execute parsed statements
2698 generated where to hang newly-generated addresses
2700 Returns: 2 success by stop
2702 -1 syntax or execution error
2704 static int parse_commands(struct Sieve *filter, int exec,
2705 address_item **generated)
2709 if (parse_white(filter)==-1) return -1;
2710 if (parse_identifier(filter,CUS "if"))
2713 if-command = "if" test block *( "elsif" test block ) [ else block ]
2716 int cond,m,unsuccessful;
2719 if (parse_white(filter)==-1) return -1;
2720 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2723 filter->errmsg=CUS "missing test";
2726 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2727 (debug_selector & D_filter) != 0)
2729 if (exec) debug_printf("if %s\n",cond?"true":"false");
2731 m=parse_block(filter,exec ? cond : 0, generated);
2732 if (m==-1 || m==2) return m;
2735 filter->errmsg=CUS "missing block";
2738 unsuccessful = !cond;
2739 for (;;) /* elsif test block */
2741 if (parse_white(filter)==-1) return -1;
2742 if (parse_identifier(filter,CUS "elsif"))
2744 if (parse_white(filter)==-1) return -1;
2745 m=parse_test(filter,&cond,exec && unsuccessful);
2746 if (m==-1 || m==2) return m;
2749 filter->errmsg=CUS "missing test";
2752 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2753 (debug_selector & D_filter) != 0)
2755 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2757 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2758 if (m==-1 || m==2) return m;
2761 filter->errmsg=CUS "missing block";
2764 if (exec && unsuccessful && cond) unsuccessful = 0;
2769 if (parse_white(filter)==-1) return -1;
2770 if (parse_identifier(filter,CUS "else"))
2772 m=parse_block(filter,exec && unsuccessful, generated);
2773 if (m==-1 || m==2) return m;
2776 filter->errmsg=CUS "missing block";
2781 else if (parse_identifier(filter,CUS "stop"))
2784 stop-command = "stop" { stop-options } ";"
2788 if (parse_semicolon(filter)==-1) return -1;
2791 filter->pc+=Ustrlen(filter->pc);
2795 else if (parse_identifier(filter,CUS "keep"))
2798 keep-command = "keep" { keep-options } ";"
2802 if (parse_semicolon(filter)==-1) return -1;
2805 add_addr(generated,US"inbox",1,0,0,0);
2809 else if (parse_identifier(filter,CUS "discard"))
2812 discard-command = "discard" { discard-options } ";"
2816 if (parse_semicolon(filter)==-1) return -1;
2817 if (exec) filter->keep=0;
2819 else if (parse_identifier(filter,CUS "redirect"))
2822 redirect-command = "redirect" redirect-options "string" ";"
2824 redirect-options =) ":copy"
2827 struct String recipient;
2833 if (parse_white(filter)==-1) return -1;
2834 if (parse_identifier(filter,CUS ":copy")==1)
2836 if (!filter->require_copy)
2838 filter->errmsg=CUS "missing previous require \"copy\";";
2845 if (parse_white(filter)==-1) return -1;
2846 if ((m=parse_string(filter,&recipient))!=1)
2848 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2851 if (strchr(CCS recipient.character,'@')==(char*)0)
2853 filter->errmsg=CUS "unqualified recipient address";
2858 add_addr(generated,recipient.character,0,0,0,0);
2859 if (!copy) filter->keep = 0;
2861 if (parse_semicolon(filter)==-1) return -1;
2863 else if (parse_identifier(filter,CUS "fileinto"))
2866 fileinto-command = "fileinto" { fileinto-options } string ";"
2868 fileinto-options =) [ ":copy" ]
2871 struct String folder;
2874 unsigned long maxage, maxmessages, maxstorage;
2877 maxage = maxmessages = maxstorage = 0;
2878 if (!filter->require_fileinto)
2880 filter->errmsg=CUS "missing previous require \"fileinto\";";
2885 if (parse_white(filter)==-1) return -1;
2886 if (parse_identifier(filter,CUS ":copy")==1)
2888 if (!filter->require_copy)
2890 filter->errmsg=CUS "missing previous require \"copy\";";
2897 if (parse_white(filter)==-1) return -1;
2898 if ((m=parse_string(filter,&folder))!=1)
2900 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2903 m=0; s=folder.character;
2904 if (folder.length==0) m=1;
2905 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2908 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2913 filter->errmsg=CUS "invalid folder";
2918 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2919 if (!copy) filter->keep = 0;
2921 if (parse_semicolon(filter)==-1) return -1;
2924 else if (parse_identifier(filter,CUS "notify"))
2927 notify-command = "notify" { notify-options } <method: string> ";"
2928 notify-options = [":from" string]
2929 [":importance" <"1" / "2" / "3">]
2930 [":options" 1*(string-list / number)]
2936 struct String importance;
2937 struct String *options;
2938 struct String message;
2939 struct String method;
2940 struct Notification *already;
2941 string_item *recipient;
2942 struct String header;
2943 struct String subject;
2945 uschar *envelope_from,*envelope_to;
2947 if (!filter->require_enotify)
2949 filter->errmsg=CUS "missing previous require \"enotify\";";
2952 from.character=(uschar*)0;
2954 importance.character=(uschar*)0;
2955 importance.length=-1;
2956 options=(struct String*)0;
2957 message.character=(uschar*)0;
2961 header.character=(uschar*)0;
2963 subject.character=(uschar*)0;
2965 body.character=(uschar*)0;
2966 envelope_from=expand_string("$sender_address");
2967 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2970 if (parse_white(filter)==-1) return -1;
2971 if (parse_identifier(filter,CUS ":from")==1)
2973 if (parse_white(filter)==-1) return -1;
2974 if ((m=parse_string(filter,&from))!=1)
2976 if (m==0) filter->errmsg=CUS "from string expected";
2980 else if (parse_identifier(filter,CUS ":importance")==1)
2982 if (parse_white(filter)==-1) return -1;
2983 if ((m=parse_string(filter,&importance))!=1)
2985 if (m==0) filter->errmsg=CUS "importance string expected";
2988 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2990 filter->errmsg=CUS "invalid importance";
2994 else if (parse_identifier(filter,CUS ":options")==1)
2996 if (parse_white(filter)==-1) return -1;
2998 else if (parse_identifier(filter,CUS ":message")==1)
3000 if (parse_white(filter)==-1) return -1;
3001 if ((m=parse_string(filter,&message))!=1)
3003 if (m==0) filter->errmsg=CUS "message string expected";
3009 if (parse_white(filter)==-1) return -1;
3010 if ((m=parse_string(filter,&method))!=1)
3012 if (m==0) filter->errmsg=CUS "missing method string";
3015 if (parse_semicolon(filter)==-1) return -1;
3016 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3018 if (message.length==-1) message=subject;
3019 if (message.length==-1) expand_header(&message,&str_subject);
3021 for (already=filter->notified; already; already=already->next)
3023 if (already->method.length==method.length
3024 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
3025 && already->importance.length==importance.length
3026 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
3027 && already->message.length==message.length
3028 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
3031 if (already==(struct Notification*)0)
3032 /* New notification, process it */
3034 struct Notification *sent;
3035 sent=store_get(sizeof(struct Notification));
3036 sent->method=method;
3037 sent->importance=importance;
3038 sent->message=message;
3039 sent->next=filter->notified;
3040 filter->notified=sent;
3041 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3043 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3045 #ifndef COMPILE_SYNTAX_CHECKER
3046 if (exec && filter_test == FTEST_NONE)
3052 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
3056 int buffer_capacity;
3058 f = fdopen(fd, "wb");
3059 for (h = header_list; h != NULL; h = h->next)
3060 if (h->type == htype_received) fprintf(f,"%s",h->text);
3061 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
3062 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3063 if (header.length>0) fprintf(f,"%s",header.character);
3064 if (message.length==-1)
3066 message.character=US"Notification";
3067 message.length=Ustrlen(message.character);
3069 /* Allocation is larger than neccessary, but enough even for split MIME words */
3070 buffer_capacity=32+4*message.length;
3071 buffer=store_get(buffer_capacity);
3072 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3074 if (body.length>0) fprintf(f,"%s\n",body.character);
3077 (void)child_close(pid, 0);
3084 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3086 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3092 else if (parse_identifier(filter,CUS "vacation"))
3095 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3096 vacation-options = [":days" number]
3099 [":addresses" string-list]
3106 struct String subject;
3108 struct String *addresses;
3110 string_item *aliases;
3111 struct String handle;
3112 struct String reason;
3114 if (!filter->require_vacation)
3116 filter->errmsg=CUS "missing previous require \"vacation\";";
3121 if (filter->vacation_ran)
3123 filter->errmsg=CUS "trying to execute vacation more than once";
3126 filter->vacation_ran=1;
3128 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3129 subject.character=(uschar*)0;
3131 from.character=(uschar*)0;
3133 addresses=(struct String*)0;
3136 handle.character=(uschar*)0;
3140 if (parse_white(filter)==-1) return -1;
3141 if (parse_identifier(filter,CUS ":days")==1)
3143 if (parse_white(filter)==-1) return -1;
3144 if (parse_number(filter,&days)==-1) return -1;
3145 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3146 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3148 else if (parse_identifier(filter,CUS ":subject")==1)
3150 if (parse_white(filter)==-1) return -1;
3151 if ((m=parse_string(filter,&subject))!=1)
3153 if (m==0) filter->errmsg=CUS "subject string expected";
3157 else if (parse_identifier(filter,CUS ":from")==1)
3159 if (parse_white(filter)==-1) return -1;
3160 if ((m=parse_string(filter,&from))!=1)
3162 if (m==0) filter->errmsg=CUS "from string expected";
3165 if (check_mail_address(filter,&from)!=1)
3168 else if (parse_identifier(filter,CUS ":addresses")==1)
3172 if (parse_white(filter)==-1) return -1;
3173 if ((m=parse_stringlist(filter,&addresses))!=1)
3175 if (m==0) filter->errmsg=CUS "addresses string list expected";
3178 for (a=addresses; a->length!=-1; ++a)
3182 new=store_get(sizeof(string_item));
3183 new->text=store_get(a->length+1);
3184 if (a->length) memcpy(new->text,a->character,a->length);
3185 new->text[a->length]='\0';
3190 else if (parse_identifier(filter,CUS ":mime")==1)
3192 else if (parse_identifier(filter,CUS ":handle")==1)
3194 if (parse_white(filter)==-1) return -1;
3195 if ((m=parse_string(filter,&from))!=1)
3197 if (m==0) filter->errmsg=CUS "handle string expected";
3203 if (parse_white(filter)==-1) return -1;
3204 if ((m=parse_string(filter,&reason))!=1)
3206 if (m==0) filter->errmsg=CUS "missing reason string";
3213 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3216 filter->errmsg=CUS "MIME reason string contains 8bit text";
3220 if (parse_semicolon(filter)==-1) return -1;
3227 int buffer_capacity;
3231 uschar hexdigest[33];
3235 if (filter_personal(aliases,TRUE))
3237 if (filter_test == FTEST_NONE)
3239 /* ensure oncelog directory exists; failure will be detected later */
3241 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3243 /* build oncelog filename */
3245 key.character=(uschar*)0;
3248 if (handle.length==-1)
3250 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3251 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3252 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3253 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3258 md5_end(&base, key.character, key.length, digest);
3259 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3260 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3262 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3264 if (filter_test == FTEST_NONE)
3266 capacity=Ustrlen(filter->vacation_directory);
3268 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3269 once=string_cat(once,&capacity,&start,hexdigest,33);
3272 /* process subject */
3274 if (subject.length==-1)
3276 uschar *subject_def;
3278 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3279 if (Ustrcmp(subject_def,"true")==0)
3281 expand_header(&subject,&str_subject);
3284 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3285 subject.length=start;
3289 subject.character=US"Automated reply";
3290 subject.length=Ustrlen(subject.character);
3294 /* add address to list of generated addresses */
3296 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3297 setflag(addr, af_pfr);
3298 setflag(addr, af_ignore_error);
3299 addr->next = *generated;
3301 addr->reply = store_get(sizeof(reply_item));
3302 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3303 addr->reply->to = string_copy(sender_address);
3304 if (from.length==-1)
3305 addr->reply->from = expand_string(US"$local_part@$domain");
3307 addr->reply->from = from.character;
3308 /* Allocation is larger than neccessary, but enough even for split MIME words */
3309 buffer_capacity=32+4*subject.length;
3310 buffer=store_get(buffer_capacity);
3311 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3312 addr->reply->oncelog=once;
3313 addr->reply->once_repeat=days*86400;
3315 /* build body and MIME headers */
3319 uschar *mime_body,*reason_end;
3320 static const uschar nlnl[]="\r\n\r\n";
3324 mime_body=reason.character,reason_end=reason.character+reason.length;
3325 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3330 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3331 addr->reply->headers[start] = '\0';
3334 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3335 else mime_body=reason_end-1;
3336 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3337 addr->reply->text[start] = '\0';
3341 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3344 start = reason.length;
3345 addr->reply->headers = US"MIME-Version: 1.0\n"
3346 "Content-Type: text/plain;\n"
3347 "\tcharset=\"utf-8\"\n"
3348 "Content-Transfer-Encoding: quoted-printable";
3349 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3353 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3355 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3366 /*************************************************
3367 * Parse and interpret a sieve filter *
3368 *************************************************/
3372 filter points to the Sieve filter including its state
3373 exec Execute parsed statements
3374 generated where to hang newly-generated addresses
3377 -1 syntax or execution error
3380 static int parse_start(struct Sieve *filter, int exec,
3381 address_item **generated)
3383 filter->pc=filter->filter;
3386 filter->require_envelope=0;
3387 filter->require_fileinto=0;
3388 #ifdef ENCODED_CHARACTER
3389 filter->require_encoded_character=0;
3391 #ifdef ENVELOPE_AUTH
3392 filter->require_envelope_auth=0;
3395 filter->require_enotify=0;
3396 filter->notified=(struct Notification*)0;
3399 filter->require_subaddress=0;
3402 filter->require_vacation=0;
3403 filter->vacation_ran=0;
3405 filter->require_copy=0;
3406 filter->require_iascii_numeric=0;
3408 if (parse_white(filter)==-1) return -1;
3410 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3413 struct dirent *oncelog;
3414 struct stat properties;
3417 /* clean up old vacation log databases */
3419 oncelogdir=opendir(CS filter->vacation_directory);
3421 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3423 filter->errmsg=CUS "unable to open vacation directory";
3427 if (oncelogdir != NULL)
3431 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3433 if (strlen(oncelog->d_name)==32)
3435 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3436 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3440 closedir(oncelogdir);
3444 while (parse_identifier(filter,CUS "require"))
3447 require-command = "require" <capabilities: string-list>
3450 struct String *cap,*check;
3453 if (parse_white(filter)==-1) return -1;
3454 if ((m=parse_stringlist(filter,&cap))!=1)
3456 if (m==0) filter->errmsg=CUS "capability string list expected";
3459 for (check=cap; check->character; ++check)
3461 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3462 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3463 #ifdef ENCODED_CHARACTER
3464 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3466 #ifdef ENVELOPE_AUTH
3467 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3470 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3473 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3476 else if (eq_octet(check,&str_vacation,0))
3478 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3480 filter->errmsg=CUS "vacation disabled";
3483 filter->require_vacation=1;
3486 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3487 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3488 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3489 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3490 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3493 filter->errmsg=CUS "unknown capability";
3497 if (parse_semicolon(filter)==-1) return -1;
3499 if (parse_commands(filter,exec,generated)==-1) return -1;
3502 filter->errmsg=CUS "syntax error";
3509 /*************************************************
3510 * Interpret a sieve filter file *
3511 *************************************************/
3515 filter points to the entire file, read into store as a single string
3516 options controls whether various special things are allowed, and requests
3517 special actions (not currently used)
3518 sieve_vacation_directory where to store vacation "once" files
3519 useraddress string expression for :user part of address
3520 subaddress string expression for :subaddress part of address
3521 generated where to hang newly-generated addresses
3522 error where to pass back an error text
3524 Returns: FF_DELIVERED success, a significant action was taken
3525 FF_NOTDELIVERED success, no significant action
3526 FF_DEFER defer requested
3527 FF_FAIL fail requested
3528 FF_FREEZE freeze requested
3529 FF_ERROR there was a problem
3533 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3534 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3540 options = options; /* Keep picky compilers happy */
3543 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3544 sieve.filter=filter;
3546 if (vacation_directory == NULL)
3547 sieve.vacation_directory = NULL;
3550 sieve.vacation_directory=expand_string(vacation_directory);
3551 if (sieve.vacation_directory == NULL)
3553 *error = string_sprintf("failed to expand \"%s\" "
3554 "(sieve_vacation_directory): %s", vacation_directory,
3555 expand_string_message);
3560 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3561 sieve.subaddress = subaddress;
3563 #ifdef COMPILE_SYNTAX_CHECKER
3564 if (parse_start(&sieve,0,generated)==1)
3566 if (parse_start(&sieve,1,generated)==1)
3571 add_addr(generated,US"inbox",1,0,0,0);
3572 msg = string_sprintf("Implicit keep");
3577 msg = string_sprintf("No implicit keep");
3583 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3584 #ifdef COMPILE_SYNTAX_CHECKER
3588 add_addr(generated,US"inbox",1,0,0,0);
3593 #ifndef COMPILE_SYNTAX_CHECKER
3594 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3595 else debug_printf("%s\n", msg);
3598 DEBUG(D_route) debug_printf("Sieve: end of processing\n");