1 /* $Cambridge: exim/src/src/sieve.c,v 1.29 2007/08/17 11:16:45 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2006 */
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 #undef 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 *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)
483 static struct String ignore[]=
489 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
492 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
495 if (header->length==-1) header->length=0;
496 capacity=header->length;
497 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
498 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
499 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
500 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
501 header->character[header->length]='\0';
504 if (*uri=='&') ++uri;
510 filter->errmsg=US"Syntactically invalid URI";
518 /*************************************************
519 * Octet-wise string comparison *
520 *************************************************/
524 needle UTF-8 string to search ...
525 haystack ... inside the haystack
526 match_prefix 1 to compare if needle is a prefix of haystack
528 Returns: 0 needle not found in haystack
532 static int eq_octet(const struct String *needle,
533 const struct String *haystack, int match_prefix)
541 h=haystack->character;
545 if (*n&0x80) return 0;
546 if (*h&0x80) return 0;
548 if (*n!=*h) return 0;
554 return (match_prefix ? nl==0 : nl==0 && hl==0);
558 /*************************************************
559 * ASCII case-insensitive string comparison *
560 *************************************************/
564 needle UTF-8 string to search ...
565 haystack ... inside the haystack
566 match_prefix 1 to compare if needle is a prefix of haystack
568 Returns: 0 needle not found in haystack
572 static int eq_asciicase(const struct String *needle,
573 const struct String *haystack, int match_prefix)
582 h=haystack->character;
588 if (nc&0x80) return 0;
589 if (hc&0x80) return 0;
591 /* tolower depends on the locale and only ASCII case must be insensitive */
592 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
598 return (match_prefix ? nl==0 : nl==0 && hl==0);
602 /*************************************************
603 * Glob pattern search *
604 *************************************************/
608 needle pattern to search ...
609 haystack ... inside the haystack
611 Returns: 0 needle not found in haystack
616 static int eq_glob(const struct String *needle,
617 const struct String *haystack, int ascii_caseless, int match_octet)
619 const uschar *n,*h,*nend,*hend;
623 h=haystack->character;
624 nend=n+needle->length;
625 hend=h+haystack->length;
635 const uschar *npart,*hpart;
637 /* Try to match a non-star part of the needle at the current */
638 /* position in the haystack. */
642 while (npart<nend && *npart!='*') switch (*npart)
646 if (hpart==hend) return 0;
651 /* Match one UTF8 encoded character */
652 if ((*hpart&0xc0)==0xc0)
655 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
666 if (npart==nend) return -1;
671 if (hpart==hend) return 0;
672 /* tolower depends on the locale, but we need ASCII */
676 (*hpart&0x80) || (*npart&0x80) ||
679 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
684 /* string match after a star failed, advance and try again */
698 /* at this point, a part was matched successfully */
699 if (may_advance && npart==nend && hpart<hend)
700 /* needle ends, but haystack does not: if there was a star before, advance and try again */
710 return (h==hend ? 1 : may_advance);
714 /*************************************************
715 * ASCII numeric comparison *
716 *************************************************/
720 a first numeric string
721 b second numeric string
722 relop relational operator
724 Returns: 0 not (a relop b)
728 static int eq_asciinumeric(const struct String *a,
729 const struct String *b, enum RelOp relop)
732 const uschar *as,*aend,*bs,*bend;
736 aend=a->character+a->length;
738 bend=b->character+b->length;
740 while (*as>='0' && *as<='9' && as<aend) ++as;
742 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
745 if (al && bl==0) cmp=-1;
746 else if (al==0 && bl==0) cmp=0;
747 else if (al==0 && bl) cmp=1;
751 if (cmp==0) cmp=memcmp(a->character,b->character,al);
755 case LT: return cmp<0;
756 case LE: return cmp<=0;
757 case EQ: return cmp==0;
758 case GE: return cmp>=0;
759 case GT: return cmp>0;
760 case NE: return cmp!=0;
767 /*************************************************
769 *************************************************/
773 filter points to the Sieve filter including its state
774 needle UTF-8 pattern or string to search ...
775 haystack ... inside the haystack
779 Returns: 0 needle not found in haystack
781 -1 comparator does not offer matchtype
784 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
785 enum Comparator co, enum MatchType mt)
789 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
790 (debug_selector & D_filter) != 0)
792 debug_printf("String comparison (match ");
795 case MATCH_IS: debug_printf(":is"); break;
796 case MATCH_CONTAINS: debug_printf(":contains"); break;
797 case MATCH_MATCHES: debug_printf(":matches"); break;
799 debug_printf(", comparison \"");
802 case COMP_OCTET: debug_printf("i;octet"); break;
803 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
804 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
806 debug_printf("\"):\n");
807 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
808 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
818 if (eq_octet(needle,haystack,0)) r=1;
821 case COMP_EN_ASCII_CASEMAP:
823 if (eq_asciicase(needle,haystack,0)) r=1;
826 case COMP_ASCII_NUMERIC:
828 if (!filter->require_iascii_numeric)
830 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
833 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
847 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
850 case COMP_EN_ASCII_CASEMAP:
852 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
857 filter->errmsg=CUS "comparator does not offer specified matchtype";
869 if ((r=eq_glob(needle,haystack,0,1))==-1)
871 filter->errmsg=CUS "syntactically invalid pattern";
876 case COMP_EN_ASCII_CASEMAP:
878 if ((r=eq_glob(needle,haystack,1,1))==-1)
880 filter->errmsg=CUS "syntactically invalid pattern";
887 filter->errmsg=CUS "comparator does not offer specified matchtype";
894 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
895 (debug_selector & D_filter) != 0)
896 debug_printf(" Result %s\n",r?"true":"false");
901 /*************************************************
902 * Check header field syntax *
903 *************************************************/
906 RFC 2822, section 3.6.8 says:
910 ftext = %d33-57 / ; Any character except
911 %d59-126 ; controls, SP, and
914 That forbids 8-bit header fields. This implementation accepts them, since
915 all of Exim is 8-bit clean, so it adds %d128-%d255.
918 header header field to quote for suitable use in Exim expansions
920 Returns: 0 string is not a valid header field
921 1 string is a value header field
924 static int is_header(const struct String *header)
934 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
945 /*************************************************
946 * Quote special characters string *
947 *************************************************/
951 header header field to quote for suitable use in Exim expansions
954 Returns: quoted string
957 static const uschar *quote(const struct String *header)
972 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
979 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
983 quoted=string_cat(quoted,&size,&ptr,h,1);
989 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
994 /*************************************************
995 * Add address to list of generated addresses *
996 *************************************************/
999 According to RFC 3028, duplicate delivery to the same address must
1000 not happen, so the list is first searched for the address.
1003 generated list of generated addresses
1004 addr new address to add
1005 file address denotes a file
1010 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1012 address_item *new_addr;
1014 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1016 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1018 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1020 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1026 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1028 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1030 new_addr=deliver_make_addr(addr,TRUE);
1033 setflag(new_addr, af_pfr|af_file);
1036 new_addr->p.errors_address = NULL;
1037 new_addr->next = *generated;
1038 *generated = new_addr;
1042 /*************************************************
1043 * Return decoded header field *
1044 *************************************************/
1047 Unfold the header field as described in RFC 2822 and remove all
1048 leading and trailing white space, then perform MIME decoding and
1049 translate the header field to UTF-8.
1052 value returned value of the field
1053 header name of the header field
1055 Returns: nothing The expanded string is empty
1056 in case there is no such header
1059 static void expand_header(struct String *value, const struct String *header)
1065 value->character=(uschar*)0;
1067 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1068 while (*r==' ' || *r=='\t') ++r;
1076 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1078 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1082 /*************************************************
1083 * Parse remaining hash comment *
1084 *************************************************/
1088 Comment up to terminating CRLF
1091 filter points to the Sieve filter including its state
1097 static int parse_hashcomment(struct Sieve *filter)
1103 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1105 if (*filter->pc=='\n')
1118 filter->errmsg=CUS "missing end of comment";
1123 /*************************************************
1124 * Parse remaining C-style comment *
1125 *************************************************/
1129 Everything up to star slash
1132 filter points to the Sieve filter including its state
1138 static int parse_comment(struct Sieve *filter)
1143 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1150 filter->errmsg=CUS "missing end of comment";
1155 /*************************************************
1156 * Parse optional white space *
1157 *************************************************/
1161 Spaces, tabs, CRLFs, hash comments or C-style comments
1164 filter points to the Sieve filter including its state
1170 static int parse_white(struct Sieve *filter)
1174 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1176 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1178 else if (*filter->pc=='\n')
1188 else if (*filter->pc=='#')
1190 if (parse_hashcomment(filter)==-1) return -1;
1192 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1194 if (parse_comment(filter)==-1) return -1;
1202 #ifdef ENCODED_CHARACTER
1203 /*************************************************
1204 * Decode hex-encoded-character string *
1205 *************************************************/
1208 Encoding definition:
1209 blank = SP / TAB / CRLF
1210 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1211 hex-pair = 1*2HEXDIG
1214 src points to a hex-pair-seq
1215 end points to its end
1216 dst points to the destination of the decoded octets,
1217 optionally to (uschar*)0 for checking only
1219 Returns: >=0 number of decoded octets
1223 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1227 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1232 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);
1233 if (d==0) return -1;
1236 if (src==end) return decoded;
1237 if (*src==' ' || *src=='\t' || *src=='\n')
1238 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1247 /*************************************************
1248 * Decode unicode-encoded-character string *
1249 *************************************************/
1252 Encoding definition:
1253 blank = SP / TAB / CRLF
1254 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1255 unicode-hex = 1*HEXDIG
1257 It is an error for a script to use a hexadecimal value that isn't in
1258 either the range 0 to D7FF or the range E000 to 10FFFF.
1260 At this time, strings are already scanned, thus the CRLF is converted
1261 to the internally used \n (should RFC_EOL have been used).
1264 src points to a unicode-hex-seq
1265 end points to its end
1266 dst points to the destination of the decoded octets,
1267 optionally to (uschar*)0 for checking only
1269 Returns: >=0 number of decoded octets
1271 -2 semantic error (character range violation)
1274 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1278 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1285 for (hex_seq=src; src<end && *src=='0'; ++src);
1286 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);
1287 if (src==hex_seq) return -1;
1288 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1294 else if (c>=0x80 && c<=0x7ff)
1299 *dst++=128+(c&0x3f);
1303 else if (c>=0x800 && c<=0xffff)
1308 *dst++=128+((c>>6)&0x3f);
1309 *dst++=128+(c&0x3f);
1313 else if (c>=0x10000 && c<=0x1fffff)
1318 *dst++=128+((c>>10)&0x3f);
1319 *dst++=128+((c>>6)&0x3f);
1320 *dst++=128+(c&0x3f);
1324 if (*src==' ' || *src=='\t' || *src=='\n')
1326 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1327 if (src==end) return decoded;
1336 /*************************************************
1337 * Decode encoded-character string *
1338 *************************************************/
1341 Encoding definition:
1342 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1343 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1346 encoded points to an encoded string, returns decoded string
1347 filter points to the Sieve filter including its state
1353 static int string_decode(struct Sieve *filter, struct String *data)
1355 uschar *src,*dst,*end;
1357 src=data->character;
1359 end=data->character+data->length;
1365 strncmpic(src,US "${hex:",6)==0
1366 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1367 && (hex_decode(src+6,brace,(uschar*)0))>=0
1370 dst+=hex_decode(src+6,brace,dst);
1374 strncmpic(src,US "${unicode:",10)==0
1375 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1378 switch (unicode_decode(src+10,brace,(uschar*)0))
1382 filter->errmsg=CUS "unicode character out of range";
1392 dst+=unicode_decode(src+10,brace,dst);
1399 data->length=dst-data->character;
1406 /*************************************************
1407 * Parse an optional string *
1408 *************************************************/
1412 quoted-string = DQUOTE *CHAR DQUOTE
1413 ;; in general, \ CHAR inside a string maps to CHAR
1414 ;; so \" maps to " and \\ maps to \
1415 ;; note that newlines and other characters are all allowed
1418 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1419 *(multi-line-literal / multi-line-dotstuff)
1421 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1422 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1423 ;; A line containing only "." ends the multi-line.
1424 ;; Remove a leading '.' if followed by another '.'.
1425 string = quoted-string / multi-line
1428 filter points to the Sieve filter including its state
1429 id specifies identifier to match
1433 0 identifier not matched
1436 static int parse_string(struct Sieve *filter, struct String *data)
1441 data->character=(uschar*)0;
1442 if (*filter->pc=='"') /* quoted string */
1447 if (*filter->pc=='"') /* end of string */
1449 int foo=data->length;
1452 /* that way, there will be at least one character allocated */
1453 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1454 #ifdef ENCODED_CHARACTER
1455 if (filter->require_encoded_character
1456 && string_decode(filter,data)==-1)
1461 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1463 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1464 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1467 else /* regular character */
1470 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1472 if (*filter->pc=='\n')
1474 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1478 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1482 filter->errmsg=CUS "missing end of string";
1485 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1488 /* skip optional white space followed by hashed comment or CRLF */
1489 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1490 if (*filter->pc=='#')
1492 if (parse_hashcomment(filter)==-1) return -1;
1495 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1497 else if (*filter->pc=='\n')
1509 filter->errmsg=CUS "syntax error";
1515 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1517 if (*filter->pc=='\n') /* end of line */
1520 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1528 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1530 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1533 int foo=data->length;
1535 /* that way, there will be at least one character allocated */
1536 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1543 #ifdef ENCODED_CHARACTER
1544 if (filter->require_encoded_character
1545 && string_decode(filter,data)==-1)
1550 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1552 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1556 else /* regular character */
1558 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1562 filter->errmsg=CUS "missing end of multi line string";
1569 /*************************************************
1570 * Parse a specific identifier *
1571 *************************************************/
1575 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1578 filter points to the Sieve filter including its state
1579 id specifies identifier to match
1582 0 identifier not matched
1585 static int parse_identifier(struct Sieve *filter, const uschar *id)
1587 size_t idlen=Ustrlen(id);
1589 if (strncmpic(US filter->pc,US id,idlen)==0)
1591 uschar next=filter->pc[idlen];
1593 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1601 /*************************************************
1603 *************************************************/
1607 number = 1*DIGIT [QUANTIFIER]
1608 QUANTIFIER = "K" / "M" / "G"
1611 filter points to the Sieve filter including its state
1615 -1 no string list found
1618 static int parse_number(struct Sieve *filter, unsigned long *data)
1622 if (*filter->pc>='0' && *filter->pc<='9')
1627 d=Ustrtoul(filter->pc,&e,10);
1630 filter->errmsg=CUstrerror(ERANGE);
1635 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1636 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1637 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1638 if (d>(ULONG_MAX/u))
1640 filter->errmsg=CUstrerror(ERANGE);
1649 filter->errmsg=CUS "missing number";
1655 /*************************************************
1656 * Parse a string list *
1657 *************************************************/
1661 string-list = "[" string *("," string) "]" / string
1664 filter points to the Sieve filter including its state
1665 data returns string list
1668 -1 no string list found
1671 static int parse_stringlist(struct Sieve *filter, struct String **data)
1673 const uschar *orig=filter->pc;
1676 struct String *d=(struct String*)0;
1679 if (*filter->pc=='[') /* string list */
1684 if (parse_white(filter)==-1) goto error;
1685 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1688 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1689 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1690 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1692 filter->errmsg=CUstrerror(errno);
1695 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1697 dataCapacity=newCapacity;
1699 m=parse_string(filter,&d[dataLength]);
1702 if (dataLength==0) break;
1705 filter->errmsg=CUS "missing string";
1709 else if (m==-1) goto error;
1711 if (parse_white(filter)==-1) goto error;
1712 if (*filter->pc==',') ++filter->pc;
1715 if (*filter->pc==']')
1717 d[dataLength].character=(uschar*)0;
1718 d[dataLength].length=-1;
1725 filter->errmsg=CUS "missing closing bracket";
1729 else /* single string */
1731 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1735 m=parse_string(filter,&d[0]);
1747 d[1].character=(uschar*)0;
1754 filter->errmsg=CUS "missing string list";
1759 /*************************************************
1760 * Parse an optional address part specifier *
1761 *************************************************/
1765 address-part = ":localpart" / ":domain" / ":all"
1766 address-part =/ ":user" / ":detail"
1769 filter points to the Sieve filter including its state
1770 a returns address part specified
1773 0 no comparator found
1777 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1780 if (parse_identifier(filter,CUS ":user")==1)
1782 if (!filter->require_subaddress)
1784 filter->errmsg=CUS "missing previous require \"subaddress\";";
1790 else if (parse_identifier(filter,CUS ":detail")==1)
1792 if (!filter->require_subaddress)
1794 filter->errmsg=CUS "missing previous require \"subaddress\";";
1802 if (parse_identifier(filter,CUS ":localpart")==1)
1804 *a=ADDRPART_LOCALPART;
1807 else if (parse_identifier(filter,CUS ":domain")==1)
1812 else if (parse_identifier(filter,CUS ":all")==1)
1821 /*************************************************
1822 * Parse an optional comparator *
1823 *************************************************/
1827 comparator = ":comparator" <comparator-name: string>
1830 filter points to the Sieve filter including its state
1831 c returns comparator
1834 0 no comparator found
1835 -1 incomplete comparator found
1838 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1840 struct String comparator_name;
1842 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1843 if (parse_white(filter)==-1) return -1;
1844 switch (parse_string(filter,&comparator_name))
1849 filter->errmsg=CUS "missing comparator";
1856 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1861 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1863 *c=COMP_EN_ASCII_CASEMAP;
1866 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1868 *c=COMP_EN_ASCII_CASEMAP;
1871 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1873 *c=COMP_ASCII_NUMERIC;
1878 filter->errmsg=CUS "invalid comparator";
1887 /*************************************************
1888 * Parse an optional match type *
1889 *************************************************/
1893 match-type = ":is" / ":contains" / ":matches"
1896 filter points to the Sieve filter including its state
1897 m returns match type
1900 0 no match type found
1903 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1905 if (parse_identifier(filter,CUS ":is")==1)
1910 else if (parse_identifier(filter,CUS ":contains")==1)
1915 else if (parse_identifier(filter,CUS ":matches")==1)
1924 /*************************************************
1925 * Parse and interpret an optional test list *
1926 *************************************************/
1930 test-list = "(" test *("," test) ")"
1933 filter points to the Sieve filter including its state
1934 n total number of tests
1935 true number of passed tests
1936 exec Execute parsed statements
1939 0 no test list found
1940 -1 syntax or execution error
1943 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1945 if (parse_white(filter)==-1) return -1;
1946 if (*filter->pc=='(')
1955 switch (parse_test(filter,&cond,exec))
1958 case 0: filter->errmsg=CUS "missing test"; return -1;
1959 default: ++*n; if (cond) ++*true; break;
1961 if (parse_white(filter)==-1) return -1;
1962 if (*filter->pc==',') ++filter->pc;
1965 if (*filter->pc==')')
1972 filter->errmsg=CUS "missing closing paren";
1980 /*************************************************
1981 * Parse and interpret an optional test *
1982 *************************************************/
1986 filter points to the Sieve filter including its state
1987 cond returned condition status
1988 exec Execute parsed statements
1992 -1 syntax or execution error
1995 static int parse_test(struct Sieve *filter, int *cond, int exec)
1997 if (parse_white(filter)==-1) return -1;
1998 if (parse_identifier(filter,CUS "address"))
2001 address-test = "address" { [address-part] [comparator] [match-type] }
2002 <header-list: string-list> <key-list: string-list>
2004 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2007 enum AddressPart addressPart=ADDRPART_ALL;
2008 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2009 enum MatchType matchType=MATCH_IS;
2010 struct String *hdr,*h,*key,*k;
2016 if (parse_white(filter)==-1) return -1;
2017 if ((m=parse_addresspart(filter,&addressPart))!=0)
2019 if (m==-1) return -1;
2022 filter->errmsg=CUS "address part already specified";
2027 else if ((m=parse_comparator(filter,&comparator))!=0)
2029 if (m==-1) return -1;
2032 filter->errmsg=CUS "comparator already specified";
2037 else if ((m=parse_matchtype(filter,&matchType))!=0)
2039 if (m==-1) return -1;
2042 filter->errmsg=CUS "match type already specified";
2049 if (parse_white(filter)==-1) return -1;
2050 if ((m=parse_stringlist(filter,&hdr))!=1)
2052 if (m==0) filter->errmsg=CUS "header string list expected";
2055 if (parse_white(filter)==-1) return -1;
2056 if ((m=parse_stringlist(filter,&key))!=1)
2058 if (m==0) filter->errmsg=CUS "key string list expected";
2062 for (h=hdr; h->length!=-1 && !*cond; ++h)
2064 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2068 !eq_asciicase(h,&str_from,0)
2069 && !eq_asciicase(h,&str_to,0)
2070 && !eq_asciicase(h,&str_cc,0)
2071 && !eq_asciicase(h,&str_bcc,0)
2072 && !eq_asciicase(h,&str_sender,0)
2073 && !eq_asciicase(h,&str_resent_from,0)
2074 && !eq_asciicase(h,&str_resent_to,0)
2077 filter->errmsg=CUS "invalid header field";
2082 /* We are only interested in addresses below, so no MIME decoding */
2083 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2084 if (header_value == NULL)
2086 filter->errmsg=CUS "header string expansion failed";
2089 parse_allow_group = TRUE;
2090 while (*header_value && !*cond)
2093 int start, end, domain;
2097 end_addr = parse_find_address_end(header_value, FALSE);
2098 saveend = *end_addr;
2100 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2102 if (extracted_addr) switch (addressPart)
2104 case ADDRPART_ALL: part=extracted_addr; break;
2108 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2109 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2111 case ADDRPART_DETAIL: part=NULL; break;
2115 *end_addr = saveend;
2118 for (k=key; k->length!=-1; ++k)
2120 struct String partStr;
2122 partStr.character=part;
2123 partStr.length=Ustrlen(part);
2126 *cond=compare(filter,k,&partStr,comparator,matchType);
2127 if (*cond==-1) return -1;
2132 if (saveend == 0) break;
2133 header_value = end_addr + 1;
2135 parse_allow_group = FALSE;
2136 parse_found_group = FALSE;
2141 else if (parse_identifier(filter,CUS "allof"))
2144 allof-test = "allof" <tests: test-list>
2149 switch (parse_testlist(filter,&n,&true,exec))
2152 case 0: filter->errmsg=CUS "missing test list"; return -1;
2153 default: *cond=(n==true); return 1;
2156 else if (parse_identifier(filter,CUS "anyof"))
2159 anyof-test = "anyof" <tests: test-list>
2164 switch (parse_testlist(filter,&n,&true,exec))
2167 case 0: filter->errmsg=CUS "missing test list"; return -1;
2168 default: *cond=(true>0); return 1;
2171 else if (parse_identifier(filter,CUS "exists"))
2174 exists-test = "exists" <header-names: string-list>
2177 struct String *hdr,*h;
2180 if (parse_white(filter)==-1) return -1;
2181 if ((m=parse_stringlist(filter,&hdr))!=1)
2183 if (m==0) filter->errmsg=CUS "header string list expected";
2189 for (h=hdr; h->length!=-1 && *cond; ++h)
2193 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2194 if (header_def == NULL)
2196 filter->errmsg=CUS "header string expansion failed";
2199 if (Ustrcmp(header_def,"false")==0) *cond=0;
2204 else if (parse_identifier(filter,CUS "false"))
2207 false-test = "false"
2213 else if (parse_identifier(filter,CUS "header"))
2216 header-test = "header" { [comparator] [match-type] }
2217 <header-names: string-list> <key-list: string-list>
2220 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2221 enum MatchType matchType=MATCH_IS;
2222 struct String *hdr,*h,*key,*k;
2228 if (parse_white(filter)==-1) return -1;
2229 if ((m=parse_comparator(filter,&comparator))!=0)
2231 if (m==-1) return -1;
2234 filter->errmsg=CUS "comparator already specified";
2239 else if ((m=parse_matchtype(filter,&matchType))!=0)
2241 if (m==-1) return -1;
2244 filter->errmsg=CUS "match type already specified";
2251 if (parse_white(filter)==-1) return -1;
2252 if ((m=parse_stringlist(filter,&hdr))!=1)
2254 if (m==0) filter->errmsg=CUS "header string list expected";
2257 if (parse_white(filter)==-1) return -1;
2258 if ((m=parse_stringlist(filter,&key))!=1)
2260 if (m==0) filter->errmsg=CUS "key string list expected";
2264 for (h=hdr; h->length!=-1 && !*cond; ++h)
2268 filter->errmsg=CUS "invalid header field";
2273 struct String header_value;
2276 expand_header(&header_value,h);
2277 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2278 if (header_value.character == NULL || header_def == NULL)
2280 filter->errmsg=CUS "header string expansion failed";
2283 for (k=key; k->length!=-1; ++k)
2285 if (Ustrcmp(header_def,"true")==0)
2287 *cond=compare(filter,k,&header_value,comparator,matchType);
2288 if (*cond==-1) return -1;
2296 else if (parse_identifier(filter,CUS "not"))
2298 if (parse_white(filter)==-1) return -1;
2299 switch (parse_test(filter,cond,exec))
2302 case 0: filter->errmsg=CUS "missing test"; return -1;
2303 default: *cond=!*cond; return 1;
2306 else if (parse_identifier(filter,CUS "size"))
2309 relop = ":over" / ":under"
2310 size-test = "size" relop <limit: number>
2313 unsigned long limit;
2316 if (parse_white(filter)==-1) return -1;
2317 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2318 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2321 filter->errmsg=CUS "missing :over or :under";
2324 if (parse_white(filter)==-1) return -1;
2325 if (parse_number(filter,&limit)==-1) return -1;
2326 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2329 else if (parse_identifier(filter,CUS "true"))
2334 else if (parse_identifier(filter,CUS "envelope"))
2337 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2338 <envelope-part: string-list> <key-list: string-list>
2340 envelope-part is case insensitive "from" or "to"
2341 #ifdef ENVELOPE_AUTH
2342 envelope-part =/ "auth"
2346 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2347 enum AddressPart addressPart=ADDRPART_ALL;
2348 enum MatchType matchType=MATCH_IS;
2349 struct String *env,*e,*key,*k;
2353 if (!filter->require_envelope)
2355 filter->errmsg=CUS "missing previous require \"envelope\";";
2360 if (parse_white(filter)==-1) return -1;
2361 if ((m=parse_comparator(filter,&comparator))!=0)
2363 if (m==-1) return -1;
2366 filter->errmsg=CUS "comparator already specified";
2371 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2373 if (m==-1) return -1;
2376 filter->errmsg=CUS "address part already specified";
2381 else if ((m=parse_matchtype(filter,&matchType))!=0)
2383 if (m==-1) return -1;
2386 filter->errmsg=CUS "match type already specified";
2393 if (parse_white(filter)==-1) return -1;
2394 if ((m=parse_stringlist(filter,&env))!=1)
2396 if (m==0) filter->errmsg=CUS "envelope string list expected";
2399 if (parse_white(filter)==-1) return -1;
2400 if ((m=parse_stringlist(filter,&key))!=1)
2402 if (m==0) filter->errmsg=CUS "key string list expected";
2406 for (e=env; e->length!=-1 && !*cond; ++e)
2408 const uschar *envelopeExpr=CUS 0;
2409 uschar *envelope=US 0;
2411 if (eq_asciicase(e,&str_from,0))
2413 switch (addressPart)
2415 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2419 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2420 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2422 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2426 else if (eq_asciicase(e,&str_to,0))
2428 switch (addressPart)
2430 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2432 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2433 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2435 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2436 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2439 #ifdef ENVELOPE_AUTH
2440 else if (eq_asciicase(e,&str_auth,0))
2442 switch (addressPart)
2444 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2448 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2449 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2451 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2458 filter->errmsg=CUS "invalid envelope string";
2461 if (exec && envelopeExpr)
2463 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2465 filter->errmsg=CUS "header string expansion failed";
2468 for (k=key; k->length!=-1; ++k)
2470 struct String envelopeStr;
2472 envelopeStr.character=envelope;
2473 envelopeStr.length=Ustrlen(envelope);
2474 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2475 if (*cond==-1) return -1;
2483 else if (parse_identifier(filter,CUS "valid_notify_method"))
2486 valid_notify_method = "valid_notify_method"
2487 <notification-uris: string-list>
2490 struct String *uris,*u;
2493 if (!filter->require_enotify)
2495 filter->errmsg=CUS "missing previous require \"enotify\";";
2498 if (parse_white(filter)==-1) return -1;
2499 if ((m=parse_stringlist(filter,&uris))!=1)
2501 if (m==0) filter->errmsg=CUS "URI string list expected";
2507 for (u=uris; u->length!=-1 && *cond; ++u)
2509 string_item *recipient;
2510 struct String header,body;
2514 header.character=(uschar*)0;
2516 body.character=(uschar*)0;
2517 if (parse_mailto_uri(filter,u->character,&recipient,&header,&body)!=1)
2523 else if (parse_identifier(filter,CUS "notify_method_capability"))
2526 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2527 <notification-uri: string>
2528 <notification-capability: string>
2529 <key-list: string-list>
2535 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2536 enum MatchType matchType=MATCH_IS;
2537 struct String uri,capa,*keys,*k;
2539 if (!filter->require_enotify)
2541 filter->errmsg=CUS "missing previous require \"enotify\";";
2546 if (parse_white(filter)==-1) return -1;
2547 if ((m=parse_comparator(filter,&comparator))!=0)
2549 if (m==-1) return -1;
2552 filter->errmsg=CUS "comparator already specified";
2557 else if ((m=parse_matchtype(filter,&matchType))!=0)
2559 if (m==-1) return -1;
2562 filter->errmsg=CUS "match type already specified";
2569 if ((m=parse_string(filter,&uri))!=1)
2571 if (m==0) filter->errmsg=CUS "missing notification URI string";
2574 if (parse_white(filter)==-1) return -1;
2575 if ((m=parse_string(filter,&capa))!=1)
2577 if (m==0) filter->errmsg=CUS "missing notification capability string";
2580 if (parse_white(filter)==-1) return -1;
2581 if ((m=parse_stringlist(filter,&keys))!=1)
2583 if (m==0) filter->errmsg=CUS "missing key string list";
2588 string_item *recipient;
2589 struct String header,body;
2594 header.character=(uschar*)0;
2596 body.character=(uschar*)0;
2597 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&body)==1)
2599 if (eq_asciicase(&capa,&str_online,0)==1)
2600 for (k=keys; k->length!=-1; ++k)
2602 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2603 if (*cond==-1) return -1;
2615 /*************************************************
2616 * Parse and interpret an optional block *
2617 *************************************************/
2621 filter points to the Sieve filter including its state
2622 exec Execute parsed statements
2623 generated where to hang newly-generated addresses
2625 Returns: 2 success by stop
2627 0 no block command found
2628 -1 syntax or execution error
2631 static int parse_block(struct Sieve *filter, int exec,
2632 address_item **generated)
2636 if (parse_white(filter)==-1) return -1;
2637 if (*filter->pc=='{')
2640 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2641 if (*filter->pc=='}')
2648 filter->errmsg=CUS "expecting command or closing brace";
2656 /*************************************************
2657 * Match a semicolon *
2658 *************************************************/
2662 filter points to the Sieve filter including its state
2668 static int parse_semicolon(struct Sieve *filter)
2670 if (parse_white(filter)==-1) return -1;
2671 if (*filter->pc==';')
2678 filter->errmsg=CUS "missing semicolon";
2684 /*************************************************
2685 * Parse and interpret a Sieve command *
2686 *************************************************/
2690 filter points to the Sieve filter including its state
2691 exec Execute parsed statements
2692 generated where to hang newly-generated addresses
2694 Returns: 2 success by stop
2696 -1 syntax or execution error
2698 static int parse_commands(struct Sieve *filter, int exec,
2699 address_item **generated)
2703 if (parse_white(filter)==-1) return -1;
2704 if (parse_identifier(filter,CUS "if"))
2707 if-command = "if" test block *( "elsif" test block ) [ else block ]
2710 int cond,m,unsuccessful;
2713 if (parse_white(filter)==-1) return -1;
2714 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2717 filter->errmsg=CUS "missing test";
2720 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2721 (debug_selector & D_filter) != 0)
2723 if (exec) debug_printf("if %s\n",cond?"true":"false");
2725 m=parse_block(filter,exec ? cond : 0, generated);
2726 if (m==-1 || m==2) return m;
2729 filter->errmsg=CUS "missing block";
2732 unsuccessful = !cond;
2733 for (;;) /* elsif test block */
2735 if (parse_white(filter)==-1) return -1;
2736 if (parse_identifier(filter,CUS "elsif"))
2738 if (parse_white(filter)==-1) return -1;
2739 m=parse_test(filter,&cond,exec && unsuccessful);
2740 if (m==-1 || m==2) return m;
2743 filter->errmsg=CUS "missing test";
2746 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2747 (debug_selector & D_filter) != 0)
2749 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2751 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2752 if (m==-1 || m==2) return m;
2755 filter->errmsg=CUS "missing block";
2758 if (exec && unsuccessful && cond) unsuccessful = 0;
2763 if (parse_white(filter)==-1) return -1;
2764 if (parse_identifier(filter,CUS "else"))
2766 m=parse_block(filter,exec && unsuccessful, generated);
2767 if (m==-1 || m==2) return m;
2770 filter->errmsg=CUS "missing block";
2775 else if (parse_identifier(filter,CUS "stop"))
2778 stop-command = "stop" { stop-options } ";"
2782 if (parse_semicolon(filter)==-1) return -1;
2785 filter->pc+=Ustrlen(filter->pc);
2789 else if (parse_identifier(filter,CUS "keep"))
2792 keep-command = "keep" { keep-options } ";"
2796 if (parse_semicolon(filter)==-1) return -1;
2799 add_addr(generated,US"inbox",1,0,0,0);
2803 else if (parse_identifier(filter,CUS "discard"))
2806 discard-command = "discard" { discard-options } ";"
2810 if (parse_semicolon(filter)==-1) return -1;
2811 if (exec) filter->keep=0;
2813 else if (parse_identifier(filter,CUS "redirect"))
2816 redirect-command = "redirect" redirect-options "string" ";"
2818 redirect-options =) ":copy"
2821 struct String recipient;
2827 if (parse_white(filter)==-1) return -1;
2828 if (parse_identifier(filter,CUS ":copy")==1)
2830 if (!filter->require_copy)
2832 filter->errmsg=CUS "missing previous require \"copy\";";
2839 if (parse_white(filter)==-1) return -1;
2840 if ((m=parse_string(filter,&recipient))!=1)
2842 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2845 if (strchr(CCS recipient.character,'@')==(char*)0)
2847 filter->errmsg=CUS "unqualified recipient address";
2852 add_addr(generated,recipient.character,0,0,0,0);
2853 if (!copy) filter->keep = 0;
2855 if (parse_semicolon(filter)==-1) return -1;
2857 else if (parse_identifier(filter,CUS "fileinto"))
2860 fileinto-command = "fileinto" { fileinto-options } string ";"
2862 fileinto-options =) [ ":copy" ]
2865 struct String folder;
2868 unsigned long maxage, maxmessages, maxstorage;
2871 maxage = maxmessages = maxstorage = 0;
2872 if (!filter->require_fileinto)
2874 filter->errmsg=CUS "missing previous require \"fileinto\";";
2879 if (parse_white(filter)==-1) return -1;
2880 if (parse_identifier(filter,CUS ":copy")==1)
2882 if (!filter->require_copy)
2884 filter->errmsg=CUS "missing previous require \"copy\";";
2891 if (parse_white(filter)==-1) return -1;
2892 if ((m=parse_string(filter,&folder))!=1)
2894 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2897 m=0; s=folder.character;
2898 if (folder.length==0) m=1;
2899 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2902 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2907 filter->errmsg=CUS "invalid folder";
2912 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2913 if (!copy) filter->keep = 0;
2915 if (parse_semicolon(filter)==-1) return -1;
2918 else if (parse_identifier(filter,CUS "notify"))
2921 notify-command = "notify" { notify-options } <method: string> ";"
2922 notify-options = [":from" string]
2923 [":importance" <"1" / "2" / "3">]
2924 [":options" 1*(string-list / number)]
2930 struct String importance;
2931 struct String *options;
2932 struct String message;
2933 struct String method;
2934 struct Notification *already;
2935 string_item *recipient;
2936 struct String header;
2938 uschar *envelope_from,*envelope_to;
2940 if (!filter->require_enotify)
2942 filter->errmsg=CUS "missing previous require \"enotify\";";
2945 from.character=(uschar*)0;
2947 importance.character=(uschar*)0;
2948 importance.length=-1;
2949 options=(struct String*)0;
2950 message.character=(uschar*)0;
2954 header.character=(uschar*)0;
2956 body.character=(uschar*)0;
2957 envelope_from=expand_string("$sender_address");
2958 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2961 if (parse_white(filter)==-1) return -1;
2962 if (parse_identifier(filter,CUS ":from")==1)
2964 if (parse_white(filter)==-1) return -1;
2965 if ((m=parse_string(filter,&from))!=1)
2967 if (m==0) filter->errmsg=CUS "from string expected";
2971 else if (parse_identifier(filter,CUS ":importance")==1)
2973 if (parse_white(filter)==-1) return -1;
2974 if ((m=parse_string(filter,&importance))!=1)
2976 if (m==0) filter->errmsg=CUS "importance string expected";
2979 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2981 filter->errmsg=CUS "invalid importance";
2985 else if (parse_identifier(filter,CUS ":options")==1)
2987 if (parse_white(filter)==-1) return -1;
2989 else if (parse_identifier(filter,CUS ":message")==1)
2991 if (parse_white(filter)==-1) return -1;
2992 if ((m=parse_string(filter,&message))!=1)
2994 if (m==0) filter->errmsg=CUS "message string expected";
3000 if (parse_white(filter)==-1) return -1;
3001 if ((m=parse_string(filter,&method))!=1)
3003 if (m==0) filter->errmsg=CUS "missing method string";
3006 if (parse_semicolon(filter)==-1) return -1;
3008 for (already=filter->notified; already; already=already->next)
3010 if (already->method.length==method.length
3011 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
3012 && already->importance.length==importance.length
3013 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
3014 && already->message.length==message.length
3015 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
3018 if (already==(struct Notification*)0)
3019 /* New notification, process it */
3021 if (parse_mailto_uri(filter,method.character,&recipient,&header,&body)!=1)
3023 struct Notification *sent;
3024 sent=store_get(sizeof(struct Notification));
3025 sent->method=method;
3026 sent->importance=importance;
3027 sent->message=message;
3028 sent->next=filter->notified;
3029 filter->notified=sent;
3030 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3032 debug_printf("Notification to `%s'.\n",method.character);
3034 #ifndef COMPILE_SYNTAX_CHECKER
3035 if (exec && filter_test == FTEST_NONE)
3041 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
3045 int buffer_capacity;
3047 f = fdopen(fd, "wb");
3048 for (h = header_list; h != NULL; h = h->next)
3049 if (h->type == htype_received) fprintf(f,"%s",h->text);
3050 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
3051 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3052 if (header.length>0) fprintf(f,"%s",header.character);
3053 if (message.length==-1)
3055 message.character=US"Notification";
3056 message.length=Ustrlen(message.character);
3058 /* Allocation is larger than neccessary, but enough even for split MIME words */
3059 buffer_capacity=32+4*message.length;
3060 buffer=store_get(buffer_capacity);
3061 fprintf(f,"Subject: %s\n\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3062 if (body.length>0) fprintf(f,"%s\n",body.character);
3065 (void)child_close(pid, 0);
3072 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3074 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3080 else if (parse_identifier(filter,CUS "vacation"))
3083 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3084 vacation-options = [":days" number]
3087 [":addresses" string-list]
3094 struct String subject;
3096 struct String *addresses;
3098 string_item *aliases;
3099 struct String handle;
3100 struct String reason;
3102 if (!filter->require_vacation)
3104 filter->errmsg=CUS "missing previous require \"vacation\";";
3109 if (filter->vacation_ran)
3111 filter->errmsg=CUS "trying to execute vacation more than once";
3114 filter->vacation_ran=1;
3116 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3117 subject.character=(uschar*)0;
3119 from.character=(uschar*)0;
3121 addresses=(struct String*)0;
3124 handle.character=(uschar*)0;
3128 if (parse_white(filter)==-1) return -1;
3129 if (parse_identifier(filter,CUS ":days")==1)
3131 if (parse_white(filter)==-1) return -1;
3132 if (parse_number(filter,&days)==-1) return -1;
3133 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3134 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3136 else if (parse_identifier(filter,CUS ":subject")==1)
3138 if (parse_white(filter)==-1) return -1;
3139 if ((m=parse_string(filter,&subject))!=1)
3141 if (m==0) filter->errmsg=CUS "subject string expected";
3145 else if (parse_identifier(filter,CUS ":from")==1)
3147 if (parse_white(filter)==-1) return -1;
3148 if ((m=parse_string(filter,&from))!=1)
3150 if (m==0) filter->errmsg=CUS "from string expected";
3153 if (check_mail_address(filter,&from)!=1)
3156 else if (parse_identifier(filter,CUS ":addresses")==1)
3160 if (parse_white(filter)==-1) return -1;
3161 if ((m=parse_stringlist(filter,&addresses))!=1)
3163 if (m==0) filter->errmsg=CUS "addresses string list expected";
3166 for (a=addresses; a->length!=-1; ++a)
3170 new=store_get(sizeof(string_item));
3171 new->text=store_get(a->length+1);
3172 if (a->length) memcpy(new->text,a->character,a->length);
3173 new->text[a->length]='\0';
3178 else if (parse_identifier(filter,CUS ":mime")==1)
3180 else if (parse_identifier(filter,CUS ":handle")==1)
3182 if (parse_white(filter)==-1) return -1;
3183 if ((m=parse_string(filter,&from))!=1)
3185 if (m==0) filter->errmsg=CUS "handle string expected";
3191 if (parse_white(filter)==-1) return -1;
3192 if ((m=parse_string(filter,&reason))!=1)
3194 if (m==0) filter->errmsg=CUS "missing reason string";
3201 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3204 filter->errmsg=CUS "MIME reason string contains 8bit text";
3208 if (parse_semicolon(filter)==-1) return -1;
3215 int buffer_capacity;
3219 uschar hexdigest[33];
3223 if (filter_personal(aliases,TRUE))
3225 if (filter_test == FTEST_NONE)
3227 /* ensure oncelog directory exists; failure will be detected later */
3229 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3231 /* build oncelog filename */
3233 key.character=(uschar*)0;
3236 if (handle.length==-1)
3238 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3239 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3240 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3241 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3246 md5_end(&base, key.character, key.length, digest);
3247 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3248 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3250 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3252 if (filter_test == FTEST_NONE)
3254 capacity=Ustrlen(filter->vacation_directory);
3256 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3257 once=string_cat(once,&capacity,&start,hexdigest,33);
3260 /* process subject */
3262 if (subject.length==-1)
3264 uschar *subject_def;
3266 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3267 if (Ustrcmp(subject_def,"true")==0)
3269 expand_header(&subject,&str_subject);
3272 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3273 subject.length=start;
3277 subject.character=US"Automated reply";
3278 subject.length=Ustrlen(subject.character);
3282 /* add address to list of generated addresses */
3284 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3285 setflag(addr, af_pfr);
3286 setflag(addr, af_ignore_error);
3287 addr->next = *generated;
3289 addr->reply = store_get(sizeof(reply_item));
3290 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3291 addr->reply->to = string_copy(sender_address);
3292 if (from.length==-1)
3293 addr->reply->from = expand_string(US"$local_part@$domain");
3295 addr->reply->from = from.character;
3296 /* Allocation is larger than neccessary, but enough even for split MIME words */
3297 buffer_capacity=32+4*subject.length;
3298 buffer=store_get(buffer_capacity);
3299 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3300 addr->reply->oncelog=once;
3301 addr->reply->once_repeat=days*86400;
3303 /* build body and MIME headers */
3307 uschar *mime_body,*reason_end;
3308 static const uschar nlnl[]="\r\n\r\n";
3312 mime_body=reason.character,reason_end=reason.character+reason.length;
3313 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3318 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3319 addr->reply->headers[start] = '\0';
3322 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3323 else mime_body=reason_end-1;
3324 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3325 addr->reply->text[start] = '\0';
3329 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3332 start = reason.length;
3333 addr->reply->headers = US"MIME-Version: 1.0\n"
3334 "Content-Type: text/plain;\n"
3335 "\tcharset=\"utf-8\"\n"
3336 "Content-Transfer-Encoding: quoted-printable";
3337 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3341 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3343 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3354 /*************************************************
3355 * Parse and interpret a sieve filter *
3356 *************************************************/
3360 filter points to the Sieve filter including its state
3361 exec Execute parsed statements
3362 generated where to hang newly-generated addresses
3365 -1 syntax or execution error
3368 static int parse_start(struct Sieve *filter, int exec,
3369 address_item **generated)
3371 filter->pc=filter->filter;
3374 filter->require_envelope=0;
3375 filter->require_fileinto=0;
3376 #ifdef ENCODED_CHARACTER
3377 filter->require_encoded_character=0;
3379 #ifdef ENVELOPE_AUTH
3380 filter->require_envelope_auth=0;
3383 filter->require_enotify=0;
3384 filter->notified=(struct Notification*)0;
3387 filter->require_subaddress=0;
3390 filter->require_vacation=0;
3391 filter->vacation_ran=0;
3393 filter->require_copy=0;
3394 filter->require_iascii_numeric=0;
3396 if (parse_white(filter)==-1) return -1;
3398 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3401 struct dirent *oncelog;
3402 struct stat properties;
3405 /* clean up old vacation log databases */
3407 oncelogdir=opendir(CS filter->vacation_directory);
3409 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3411 filter->errmsg=CUS "unable to open vacation directory";
3415 if (oncelogdir != NULL)
3419 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3421 if (strlen(oncelog->d_name)==32)
3423 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3424 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3428 closedir(oncelogdir);
3432 while (parse_identifier(filter,CUS "require"))
3435 require-command = "require" <capabilities: string-list>
3438 struct String *cap,*check;
3441 if (parse_white(filter)==-1) return -1;
3442 if ((m=parse_stringlist(filter,&cap))!=1)
3444 if (m==0) filter->errmsg=CUS "capability string list expected";
3447 for (check=cap; check->character; ++check)
3449 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3450 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3451 #ifdef ENCODED_CHARACTER
3452 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3454 #ifdef ENVELOPE_AUTH
3455 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3458 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3461 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3464 else if (eq_octet(check,&str_vacation,0))
3466 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3468 filter->errmsg=CUS "vacation disabled";
3471 filter->require_vacation=1;
3474 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3475 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3476 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3477 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3478 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3481 filter->errmsg=CUS "unknown capability";
3485 if (parse_semicolon(filter)==-1) return -1;
3487 if (parse_commands(filter,exec,generated)==-1) return -1;
3490 filter->errmsg=CUS "syntax error";
3497 /*************************************************
3498 * Interpret a sieve filter file *
3499 *************************************************/
3503 filter points to the entire file, read into store as a single string
3504 options controls whether various special things are allowed, and requests
3505 special actions (not currently used)
3506 sieve_vacation_directory where to store vacation "once" files
3507 useraddress string expression for :user part of address
3508 subaddress string expression for :subaddress part of address
3509 generated where to hang newly-generated addresses
3510 error where to pass back an error text
3512 Returns: FF_DELIVERED success, a significant action was taken
3513 FF_NOTDELIVERED success, no significant action
3514 FF_DEFER defer requested
3515 FF_FAIL fail requested
3516 FF_FREEZE freeze requested
3517 FF_ERROR there was a problem
3521 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3522 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3528 options = options; /* Keep picky compilers happy */
3531 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3532 sieve.filter=filter;
3534 if (vacation_directory == NULL)
3535 sieve.vacation_directory = NULL;
3538 sieve.vacation_directory=expand_string(vacation_directory);
3539 if (sieve.vacation_directory == NULL)
3541 *error = string_sprintf("failed to expand \"%s\" "
3542 "(sieve_vacation_directory): %s", vacation_directory,
3543 expand_string_message);
3548 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3549 sieve.subaddress = subaddress;
3551 #ifdef COMPILE_SYNTAX_CHECKER
3552 if (parse_start(&sieve,0,generated)==1)
3554 if (parse_start(&sieve,1,generated)==1)
3559 add_addr(generated,US"inbox",1,0,0,0);
3560 msg = string_sprintf("Implicit keep");
3565 msg = string_sprintf("No implicit keep");
3571 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3572 #ifdef COMPILE_SYNTAX_CHECKER
3576 add_addr(generated,US"inbox",1,0,0,0);
3581 #ifndef COMPILE_SYNTAX_CHECKER
3582 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3583 else debug_printf("%s\n", msg);
3586 DEBUG(D_route) debug_printf("Sieve: end of processing\n");