1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003-2008 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* This code was contributed by Michael Haardt. */
11 /* Sieve mail filter. */
25 /* Define this for RFC compliant \r\n end-of-line terminators. */
26 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
29 /* Define this for development of the Sieve extension "encoded-character". */
30 #define ENCODED_CHARACTER
32 /* Define this for development of the Sieve extension "envelope-auth". */
35 /* Define this for development of the Sieve extension "enotify". */
38 /* Define this for the Sieve extension "subaddress". */
41 /* Define this for the Sieve extension "vacation". */
45 #define VACATION_MIN_DAYS 1
46 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
47 #define VACATION_MAX_DAYS 31
49 /* Keep this at 75 to accept only RFC compliant MIME words. */
50 /* Increase it if you want to match headers from buggy MUAs. */
51 #define MIMEWORD_LENGTH 75
62 #ifdef ENCODED_CHARACTER
63 int require_encoded_character;
66 int require_envelope_auth;
70 struct Notification *notified;
72 uschar *enotify_mailto_owner;
74 int require_subaddress;
80 uschar *vacation_directory;
81 const uschar *subaddress;
82 const uschar *useraddress;
84 int require_iascii_numeric;
87 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
88 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
90 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
92 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94 enum RelOp { LT, LE, EQ, GE, GT, NE };
104 struct String method;
105 struct String importance;
106 struct String message;
107 struct Notification *next;
110 /* This should be a complete list of supported extensions, so that an external
111 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
112 list of extensions and provide correct information to a client.
114 We'll emit the list in the order given here; keep it alphabetically sorted, so
115 that callers don't get surprised.
117 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
119 const uschar *exim_sieve_extension_list[] = {
120 CUS"comparator-i;ascii-numeric",
122 #ifdef ENCODED_CHARACTER
123 CUS"encoded-character",
142 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
143 static int parse_test(struct Sieve *filter, int *cond, int exec);
144 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
146 static uschar str_from_c[]="From";
147 static const struct String str_from={ str_from_c, 4 };
148 static uschar str_to_c[]="To";
149 static const struct String str_to={ str_to_c, 2 };
150 static uschar str_cc_c[]="Cc";
151 static const struct String str_cc={ str_cc_c, 2 };
152 static uschar str_bcc_c[]="Bcc";
153 static const struct String str_bcc={ str_bcc_c, 3 };
154 static uschar str_auth_c[]="auth";
155 static const struct String str_auth={ str_auth_c, 4 };
156 static uschar str_sender_c[]="Sender";
157 static const struct String str_sender={ str_sender_c, 6 };
158 static uschar str_resent_from_c[]="Resent-From";
159 static const struct String str_resent_from={ str_resent_from_c, 11 };
160 static uschar str_resent_to_c[]="Resent-To";
161 static const struct String str_resent_to={ str_resent_to_c, 9 };
162 static uschar str_fileinto_c[]="fileinto";
163 static const struct String str_fileinto={ str_fileinto_c, 8 };
164 static uschar str_envelope_c[]="envelope";
165 static const struct String str_envelope={ str_envelope_c, 8 };
166 #ifdef ENCODED_CHARACTER
167 static uschar str_encoded_character_c[]="encoded-character";
168 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
171 static uschar str_envelope_auth_c[]="envelope-auth";
172 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
175 static uschar str_enotify_c[]="enotify";
176 static const struct String str_enotify={ str_enotify_c, 7 };
177 static uschar str_online_c[]="online";
178 static const struct String str_online={ str_online_c, 6 };
179 static uschar str_maybe_c[]="maybe";
180 static const struct String str_maybe={ str_maybe_c, 5 };
181 static uschar str_auto_submitted_c[]="Auto-Submitted";
182 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
185 static uschar str_subaddress_c[]="subaddress";
186 static const struct String str_subaddress={ str_subaddress_c, 10 };
189 static uschar str_vacation_c[]="vacation";
190 static const struct String str_vacation={ str_vacation_c, 8 };
191 static uschar str_subject_c[]="Subject";
192 static const struct String str_subject={ str_subject_c, 7 };
194 static uschar str_copy_c[]="copy";
195 static const struct String str_copy={ str_copy_c, 4 };
196 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
197 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
198 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
199 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
200 static uschar str_ioctet_c[]="i;octet";
201 static const struct String str_ioctet={ str_ioctet_c, 7 };
202 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
203 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
204 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
205 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
206 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
207 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
208 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
209 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
210 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
211 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
214 /*************************************************
215 * Encode to quoted-printable *
216 *************************************************/
227 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
230 const uschar *start,*end;
235 for (pass=0; pass<=1; ++pass)
242 dst->character=store_get(dst->length+1); /* plus one for \0 */
245 for (start=src->character,end=start+src->length; start<end; ++start)
262 || (ch>=62 && ch<=126)
267 && (*(start+1)!='\r' || *(start+2)!='\n')
277 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
295 sprintf(CS new,"=%02X",ch);
302 *new='\0'; /* not included in length, but nice */
307 /*************************************************
308 * Check mail address for correct syntax *
309 *************************************************/
312 Check mail address for being syntactically correct.
315 filter points to the Sieve filter including its state
316 address String containing one address
319 1 Mail address is syntactically OK
323 int check_mail_address(struct Sieve *filter, const struct String *address)
325 int start, end, domain;
328 if (address->length>0)
330 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
334 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
335 address->character, error);
343 filter->errmsg=CUS "empty address";
349 /*************************************************
350 * Decode URI encoded string *
351 *************************************************/
355 str URI encoded string
358 0 Decoding successful
363 static int uri_decode(struct String *str)
367 if (str->length==0) return 0;
368 for (s=str->character,t=s,e=s+str->length; s<e; )
372 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
374 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
375 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
384 str->length=t-str->character;
389 /*************************************************
391 *************************************************/
396 mailtoURI = "mailto:" [ to ] [ headers ]
397 to = [ addr-spec *("%2C" addr-spec ) ]
398 headers = "?" header *( "&" header )
399 header = hname "=" hvalue
404 filter points to the Sieve filter including its state
405 uri URI, excluding scheme
410 1 URI is syntactically OK
415 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
418 struct String to,hname,hvalue;
422 if (Ustrncmp(uri,"mailto:",7))
424 filter->errmsg=US "Unknown URI scheme";
428 if (*uri && *uri!='?')
432 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
436 to.character=(uschar*)0;
438 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
439 to.character[to.length]='\0';
440 if (uri_decode(&to)==-1)
442 filter->errmsg=US"Invalid URI encoding";
445 new=store_get(sizeof(string_item));
446 new->text=store_get(to.length+1);
447 if (to.length) memcpy(new->text,to.character,to.length);
448 new->text[to.length]='\0';
449 new->next=*recipient;
454 filter->errmsg=US"Missing addr-spec in URI";
457 if (*uri=='%') uri+=3;
466 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
470 hname.character=(uschar*)0;
472 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
473 hname.character[hname.length]='\0';
474 if (uri_decode(&hname)==-1)
476 filter->errmsg=US"Invalid URI encoding";
485 filter->errmsg=US"Missing equal after hname";
489 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
493 hvalue.character=(uschar*)0;
495 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
496 hvalue.character[hvalue.length]='\0';
497 if (uri_decode(&hvalue)==-1)
499 filter->errmsg=US"Invalid URI encoding";
503 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
505 new=store_get(sizeof(string_item));
506 new->text=store_get(hvalue.length+1);
507 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
508 new->text[hvalue.length]='\0';
509 new->next=*recipient;
512 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
514 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
518 static struct String ignore[]=
524 {US"auto-submitted",14}
526 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
529 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
532 if (header->length==-1) header->length=0;
533 capacity=header->length;
534 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
535 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
536 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
537 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
538 header->character[header->length]='\0';
541 if (*uri=='&') ++uri;
547 filter->errmsg=US"Syntactically invalid URI";
555 /*************************************************
556 * Octet-wise string comparison *
557 *************************************************/
561 needle UTF-8 string to search ...
562 haystack ... inside the haystack
563 match_prefix 1 to compare if needle is a prefix of haystack
565 Returns: 0 needle not found in haystack
569 static int eq_octet(const struct String *needle,
570 const struct String *haystack, int match_prefix)
578 h=haystack->character;
582 if (*n&0x80) return 0;
583 if (*h&0x80) return 0;
585 if (*n!=*h) return 0;
591 return (match_prefix ? nl==0 : nl==0 && hl==0);
595 /*************************************************
596 * ASCII case-insensitive string comparison *
597 *************************************************/
601 needle UTF-8 string to search ...
602 haystack ... inside the haystack
603 match_prefix 1 to compare if needle is a prefix of haystack
605 Returns: 0 needle not found in haystack
609 static int eq_asciicase(const struct String *needle,
610 const struct String *haystack, int match_prefix)
619 h=haystack->character;
625 if (nc&0x80) return 0;
626 if (hc&0x80) return 0;
628 /* tolower depends on the locale and only ASCII case must be insensitive */
629 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
635 return (match_prefix ? nl==0 : nl==0 && hl==0);
639 /*************************************************
640 * Glob pattern search *
641 *************************************************/
645 needle pattern to search ...
646 haystack ... inside the haystack
647 ascii_caseless ignore ASCII case
648 match_octet match octets, not UTF-8 multi-octet characters
650 Returns: 0 needle not found in haystack
655 static int eq_glob(const struct String *needle,
656 const struct String *haystack, int ascii_caseless, int match_octet)
658 const uschar *n,*h,*nend,*hend;
662 h=haystack->character;
663 nend=n+needle->length;
664 hend=h+haystack->length;
674 const uschar *npart,*hpart;
676 /* Try to match a non-star part of the needle at the current */
677 /* position in the haystack. */
681 while (npart<nend && *npart!='*') switch (*npart)
685 if (hpart==hend) return 0;
690 /* Match one UTF8 encoded character */
691 if ((*hpart&0xc0)==0xc0)
694 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
705 if (npart==nend) return -1;
710 if (hpart==hend) return 0;
711 /* tolower depends on the locale, but we need ASCII */
715 (*hpart&0x80) || (*npart&0x80) ||
718 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
723 /* string match after a star failed, advance and try again */
737 /* at this point, a part was matched successfully */
738 if (may_advance && npart==nend && hpart<hend)
739 /* needle ends, but haystack does not: if there was a star before, advance and try again */
749 return (h==hend ? 1 : may_advance);
753 /*************************************************
754 * ASCII numeric comparison *
755 *************************************************/
759 a first numeric string
760 b second numeric string
761 relop relational operator
763 Returns: 0 not (a relop b)
767 static int eq_asciinumeric(const struct String *a,
768 const struct String *b, enum RelOp relop)
771 const uschar *as,*aend,*bs,*bend;
775 aend=a->character+a->length;
777 bend=b->character+b->length;
779 while (*as>='0' && *as<='9' && as<aend) ++as;
781 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
784 if (al && bl==0) cmp=-1;
785 else if (al==0 && bl==0) cmp=0;
786 else if (al==0 && bl) cmp=1;
790 if (cmp==0) cmp=memcmp(a->character,b->character,al);
794 case LT: return cmp<0;
795 case LE: return cmp<=0;
796 case EQ: return cmp==0;
797 case GE: return cmp>=0;
798 case GT: return cmp>0;
799 case NE: return cmp!=0;
806 /*************************************************
808 *************************************************/
812 filter points to the Sieve filter including its state
813 needle UTF-8 pattern or string to search ...
814 haystack ... inside the haystack
818 Returns: 0 needle not found in haystack
820 -1 comparator does not offer matchtype
823 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
824 enum Comparator co, enum MatchType mt)
828 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
829 (debug_selector & D_filter) != 0)
831 debug_printf("String comparison (match ");
834 case MATCH_IS: debug_printf(":is"); break;
835 case MATCH_CONTAINS: debug_printf(":contains"); break;
836 case MATCH_MATCHES: debug_printf(":matches"); break;
838 debug_printf(", comparison \"");
841 case COMP_OCTET: debug_printf("i;octet"); break;
842 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
843 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
845 debug_printf("\"):\n");
846 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
847 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
857 if (eq_octet(needle,haystack,0)) r=1;
860 case COMP_EN_ASCII_CASEMAP:
862 if (eq_asciicase(needle,haystack,0)) r=1;
865 case COMP_ASCII_NUMERIC:
867 if (!filter->require_iascii_numeric)
869 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
872 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
886 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
889 case COMP_EN_ASCII_CASEMAP:
891 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
896 filter->errmsg=CUS "comparator does not offer specified matchtype";
908 if ((r=eq_glob(needle,haystack,0,1))==-1)
910 filter->errmsg=CUS "syntactically invalid pattern";
915 case COMP_EN_ASCII_CASEMAP:
917 if ((r=eq_glob(needle,haystack,1,1))==-1)
919 filter->errmsg=CUS "syntactically invalid pattern";
926 filter->errmsg=CUS "comparator does not offer specified matchtype";
933 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
934 (debug_selector & D_filter) != 0)
935 debug_printf(" Result %s\n",r?"true":"false");
940 /*************************************************
941 * Check header field syntax *
942 *************************************************/
945 RFC 2822, section 3.6.8 says:
949 ftext = %d33-57 / ; Any character except
950 %d59-126 ; controls, SP, and
953 That forbids 8-bit header fields. This implementation accepts them, since
954 all of Exim is 8-bit clean, so it adds %d128-%d255.
957 header header field to quote for suitable use in Exim expansions
959 Returns: 0 string is not a valid header field
960 1 string is a value header field
963 static int is_header(const struct String *header)
973 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
984 /*************************************************
985 * Quote special characters string *
986 *************************************************/
990 header header field to quote for suitable use in Exim expansions
993 Returns: quoted string
996 static const uschar *quote(const struct String *header)
1004 h=header->character;
1011 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
1018 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
1022 quoted=string_cat(quoted,&size,&ptr,h,1);
1028 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
1033 /*************************************************
1034 * Add address to list of generated addresses *
1035 *************************************************/
1038 According to RFC 5228, duplicate delivery to the same address must
1039 not happen, so the list is first searched for the address.
1042 generated list of generated addresses
1043 addr new address to add
1044 file address denotes a file
1049 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1051 address_item *new_addr;
1053 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1055 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1057 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1059 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1065 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1067 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1069 new_addr=deliver_make_addr(addr,TRUE);
1072 setflag(new_addr, af_pfr|af_file);
1075 new_addr->p.errors_address = NULL;
1076 new_addr->next = *generated;
1077 *generated = new_addr;
1081 /*************************************************
1082 * Return decoded header field *
1083 *************************************************/
1086 Unfold the header field as described in RFC 2822 and remove all
1087 leading and trailing white space, then perform MIME decoding and
1088 translate the header field to UTF-8.
1091 value returned value of the field
1092 header name of the header field
1094 Returns: nothing The expanded string is empty
1095 in case there is no such header
1098 static void expand_header(struct String *value, const struct String *header)
1104 value->character=(uschar*)0;
1106 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1107 while (*r==' ' || *r=='\t') ++r;
1115 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1117 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1121 /*************************************************
1122 * Parse remaining hash comment *
1123 *************************************************/
1127 Comment up to terminating CRLF
1130 filter points to the Sieve filter including its state
1136 static int parse_hashcomment(struct Sieve *filter)
1142 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1144 if (*filter->pc=='\n')
1157 filter->errmsg=CUS "missing end of comment";
1162 /*************************************************
1163 * Parse remaining C-style comment *
1164 *************************************************/
1168 Everything up to star slash
1171 filter points to the Sieve filter including its state
1177 static int parse_comment(struct Sieve *filter)
1182 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1189 filter->errmsg=CUS "missing end of comment";
1194 /*************************************************
1195 * Parse optional white space *
1196 *************************************************/
1200 Spaces, tabs, CRLFs, hash comments or C-style comments
1203 filter points to the Sieve filter including its state
1209 static int parse_white(struct Sieve *filter)
1213 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1215 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1217 else if (*filter->pc=='\n')
1227 else if (*filter->pc=='#')
1229 if (parse_hashcomment(filter)==-1) return -1;
1231 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1233 if (parse_comment(filter)==-1) return -1;
1241 #ifdef ENCODED_CHARACTER
1242 /*************************************************
1243 * Decode hex-encoded-character string *
1244 *************************************************/
1247 Encoding definition:
1248 blank = SP / TAB / CRLF
1249 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1250 hex-pair = 1*2HEXDIG
1253 src points to a hex-pair-seq
1254 end points to its end
1255 dst points to the destination of the decoded octets,
1256 optionally to (uschar*)0 for checking only
1258 Returns: >=0 number of decoded octets
1262 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1266 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1271 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);
1272 if (d==0) return -1;
1275 if (src==end) return decoded;
1276 if (*src==' ' || *src=='\t' || *src=='\n')
1277 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1286 /*************************************************
1287 * Decode unicode-encoded-character string *
1288 *************************************************/
1291 Encoding definition:
1292 blank = SP / TAB / CRLF
1293 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1294 unicode-hex = 1*HEXDIG
1296 It is an error for a script to use a hexadecimal value that isn't in
1297 either the range 0 to D7FF or the range E000 to 10FFFF.
1299 At this time, strings are already scanned, thus the CRLF is converted
1300 to the internally used \n (should RFC_EOL have been used).
1303 src points to a unicode-hex-seq
1304 end points to its end
1305 dst points to the destination of the decoded octets,
1306 optionally to (uschar*)0 for checking only
1308 Returns: >=0 number of decoded octets
1310 -2 semantic error (character range violation)
1313 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1317 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1324 for (hex_seq=src; src<end && *src=='0'; ++src);
1325 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);
1326 if (src==hex_seq) return -1;
1327 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1333 else if (c>=0x80 && c<=0x7ff)
1338 *dst++=128+(c&0x3f);
1342 else if (c>=0x800 && c<=0xffff)
1347 *dst++=128+((c>>6)&0x3f);
1348 *dst++=128+(c&0x3f);
1352 else if (c>=0x10000 && c<=0x1fffff)
1357 *dst++=128+((c>>10)&0x3f);
1358 *dst++=128+((c>>6)&0x3f);
1359 *dst++=128+(c&0x3f);
1363 if (*src==' ' || *src=='\t' || *src=='\n')
1365 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1366 if (src==end) return decoded;
1375 /*************************************************
1376 * Decode encoded-character string *
1377 *************************************************/
1380 Encoding definition:
1381 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1382 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1385 encoded points to an encoded string, returns decoded string
1386 filter points to the Sieve filter including its state
1392 static int string_decode(struct Sieve *filter, struct String *data)
1394 uschar *src,*dst,*end;
1396 src=data->character;
1398 end=data->character+data->length;
1404 strncmpic(src,US "${hex:",6)==0
1405 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1406 && (hex_decode(src+6,brace,(uschar*)0))>=0
1409 dst+=hex_decode(src+6,brace,dst);
1413 strncmpic(src,US "${unicode:",10)==0
1414 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1417 switch (unicode_decode(src+10,brace,(uschar*)0))
1421 filter->errmsg=CUS "unicode character out of range";
1431 dst+=unicode_decode(src+10,brace,dst);
1438 data->length=dst-data->character;
1445 /*************************************************
1446 * Parse an optional string *
1447 *************************************************/
1451 quoted-string = DQUOTE *CHAR DQUOTE
1452 ;; in general, \ CHAR inside a string maps to CHAR
1453 ;; so \" maps to " and \\ maps to \
1454 ;; note that newlines and other characters are all allowed
1457 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1458 *(multi-line-literal / multi-line-dotstuff)
1460 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1461 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1462 ;; A line containing only "." ends the multi-line.
1463 ;; Remove a leading '.' if followed by another '.'.
1464 string = quoted-string / multi-line
1467 filter points to the Sieve filter including its state
1468 id specifies identifier to match
1472 0 identifier not matched
1475 static int parse_string(struct Sieve *filter, struct String *data)
1480 data->character=(uschar*)0;
1481 if (*filter->pc=='"') /* quoted string */
1486 if (*filter->pc=='"') /* end of string */
1488 int foo=data->length;
1491 /* that way, there will be at least one character allocated */
1492 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1493 #ifdef ENCODED_CHARACTER
1494 if (filter->require_encoded_character
1495 && string_decode(filter,data)==-1)
1500 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1502 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1505 else /* regular character */
1508 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1510 if (*filter->pc=='\n')
1512 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1516 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1520 filter->errmsg=CUS "missing end of string";
1523 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1526 /* skip optional white space followed by hashed comment or CRLF */
1527 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1528 if (*filter->pc=='#')
1530 if (parse_hashcomment(filter)==-1) return -1;
1533 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1535 else if (*filter->pc=='\n')
1547 filter->errmsg=CUS "syntax error";
1553 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1555 if (*filter->pc=='\n') /* end of line */
1558 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1566 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1568 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1571 int foo=data->length;
1573 /* that way, there will be at least one character allocated */
1574 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1581 #ifdef ENCODED_CHARACTER
1582 if (filter->require_encoded_character
1583 && string_decode(filter,data)==-1)
1588 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1590 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1594 else /* regular character */
1596 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1600 filter->errmsg=CUS "missing end of multi line string";
1607 /*************************************************
1608 * Parse a specific identifier *
1609 *************************************************/
1613 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1616 filter points to the Sieve filter including its state
1617 id specifies identifier to match
1620 0 identifier not matched
1623 static int parse_identifier(struct Sieve *filter, const uschar *id)
1625 size_t idlen=Ustrlen(id);
1627 if (strncmpic(US filter->pc,US id,idlen)==0)
1629 uschar next=filter->pc[idlen];
1631 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1639 /*************************************************
1641 *************************************************/
1645 number = 1*DIGIT [QUANTIFIER]
1646 QUANTIFIER = "K" / "M" / "G"
1649 filter points to the Sieve filter including its state
1653 -1 no string list found
1656 static int parse_number(struct Sieve *filter, unsigned long *data)
1660 if (*filter->pc>='0' && *filter->pc<='9')
1665 d=Ustrtoul(filter->pc,&e,10);
1668 filter->errmsg=CUstrerror(ERANGE);
1673 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1674 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1675 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1676 if (d>(ULONG_MAX/u))
1678 filter->errmsg=CUstrerror(ERANGE);
1687 filter->errmsg=CUS "missing number";
1693 /*************************************************
1694 * Parse a string list *
1695 *************************************************/
1699 string-list = "[" string *("," string) "]" / string
1702 filter points to the Sieve filter including its state
1703 data returns string list
1706 -1 no string list found
1709 static int parse_stringlist(struct Sieve *filter, struct String **data)
1711 const uschar *orig=filter->pc;
1714 struct String *d=(struct String*)0;
1717 if (*filter->pc=='[') /* string list */
1722 if (parse_white(filter)==-1) goto error;
1723 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1726 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1727 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1728 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1730 filter->errmsg=CUstrerror(errno);
1733 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1735 dataCapacity=newCapacity;
1737 m=parse_string(filter,&d[dataLength]);
1740 if (dataLength==0) break;
1743 filter->errmsg=CUS "missing string";
1747 else if (m==-1) goto error;
1749 if (parse_white(filter)==-1) goto error;
1750 if (*filter->pc==',') ++filter->pc;
1753 if (*filter->pc==']')
1755 d[dataLength].character=(uschar*)0;
1756 d[dataLength].length=-1;
1763 filter->errmsg=CUS "missing closing bracket";
1767 else /* single string */
1769 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1773 m=parse_string(filter,&d[0]);
1785 d[1].character=(uschar*)0;
1792 filter->errmsg=CUS "missing string list";
1797 /*************************************************
1798 * Parse an optional address part specifier *
1799 *************************************************/
1803 address-part = ":localpart" / ":domain" / ":all"
1804 address-part =/ ":user" / ":detail"
1807 filter points to the Sieve filter including its state
1808 a returns address part specified
1811 0 no comparator found
1815 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1818 if (parse_identifier(filter,CUS ":user")==1)
1820 if (!filter->require_subaddress)
1822 filter->errmsg=CUS "missing previous require \"subaddress\";";
1828 else if (parse_identifier(filter,CUS ":detail")==1)
1830 if (!filter->require_subaddress)
1832 filter->errmsg=CUS "missing previous require \"subaddress\";";
1840 if (parse_identifier(filter,CUS ":localpart")==1)
1842 *a=ADDRPART_LOCALPART;
1845 else if (parse_identifier(filter,CUS ":domain")==1)
1850 else if (parse_identifier(filter,CUS ":all")==1)
1859 /*************************************************
1860 * Parse an optional comparator *
1861 *************************************************/
1865 comparator = ":comparator" <comparator-name: string>
1868 filter points to the Sieve filter including its state
1869 c returns comparator
1872 0 no comparator found
1873 -1 incomplete comparator found
1876 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1878 struct String comparator_name;
1880 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1881 if (parse_white(filter)==-1) return -1;
1882 switch (parse_string(filter,&comparator_name))
1887 filter->errmsg=CUS "missing comparator";
1894 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1899 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1901 *c=COMP_EN_ASCII_CASEMAP;
1904 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1906 *c=COMP_EN_ASCII_CASEMAP;
1909 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1911 *c=COMP_ASCII_NUMERIC;
1916 filter->errmsg=CUS "invalid comparator";
1925 /*************************************************
1926 * Parse an optional match type *
1927 *************************************************/
1931 match-type = ":is" / ":contains" / ":matches"
1934 filter points to the Sieve filter including its state
1935 m returns match type
1938 0 no match type found
1941 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1943 if (parse_identifier(filter,CUS ":is")==1)
1948 else if (parse_identifier(filter,CUS ":contains")==1)
1953 else if (parse_identifier(filter,CUS ":matches")==1)
1962 /*************************************************
1963 * Parse and interpret an optional test list *
1964 *************************************************/
1968 test-list = "(" test *("," test) ")"
1971 filter points to the Sieve filter including its state
1972 n total number of tests
1973 num_true number of passed tests
1974 exec Execute parsed statements
1977 0 no test list found
1978 -1 syntax or execution error
1981 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1983 if (parse_white(filter)==-1) return -1;
1984 if (*filter->pc=='(')
1993 switch (parse_test(filter,&cond,exec))
1996 case 0: filter->errmsg=CUS "missing test"; return -1;
1997 default: ++*n; if (cond) ++*num_true; break;
1999 if (parse_white(filter)==-1) return -1;
2000 if (*filter->pc==',') ++filter->pc;
2003 if (*filter->pc==')')
2010 filter->errmsg=CUS "missing closing paren";
2018 /*************************************************
2019 * Parse and interpret an optional test *
2020 *************************************************/
2024 filter points to the Sieve filter including its state
2025 cond returned condition status
2026 exec Execute parsed statements
2030 -1 syntax or execution error
2033 static int parse_test(struct Sieve *filter, int *cond, int exec)
2035 if (parse_white(filter)==-1) return -1;
2036 if (parse_identifier(filter,CUS "address"))
2039 address-test = "address" { [address-part] [comparator] [match-type] }
2040 <header-list: string-list> <key-list: string-list>
2042 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2045 enum AddressPart addressPart=ADDRPART_ALL;
2046 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2047 enum MatchType matchType=MATCH_IS;
2048 struct String *hdr,*h,*key,*k;
2054 if (parse_white(filter)==-1) return -1;
2055 if ((m=parse_addresspart(filter,&addressPart))!=0)
2057 if (m==-1) return -1;
2060 filter->errmsg=CUS "address part already specified";
2065 else if ((m=parse_comparator(filter,&comparator))!=0)
2067 if (m==-1) return -1;
2070 filter->errmsg=CUS "comparator already specified";
2075 else if ((m=parse_matchtype(filter,&matchType))!=0)
2077 if (m==-1) return -1;
2080 filter->errmsg=CUS "match type already specified";
2087 if (parse_white(filter)==-1) return -1;
2088 if ((m=parse_stringlist(filter,&hdr))!=1)
2090 if (m==0) filter->errmsg=CUS "header string list expected";
2093 if (parse_white(filter)==-1) return -1;
2094 if ((m=parse_stringlist(filter,&key))!=1)
2096 if (m==0) filter->errmsg=CUS "key string list expected";
2100 for (h=hdr; h->length!=-1 && !*cond; ++h)
2102 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2106 !eq_asciicase(h,&str_from,0)
2107 && !eq_asciicase(h,&str_to,0)
2108 && !eq_asciicase(h,&str_cc,0)
2109 && !eq_asciicase(h,&str_bcc,0)
2110 && !eq_asciicase(h,&str_sender,0)
2111 && !eq_asciicase(h,&str_resent_from,0)
2112 && !eq_asciicase(h,&str_resent_to,0)
2115 filter->errmsg=CUS "invalid header field";
2120 /* We are only interested in addresses below, so no MIME decoding */
2121 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2122 if (header_value == NULL)
2124 filter->errmsg=CUS "header string expansion failed";
2127 parse_allow_group = TRUE;
2128 while (*header_value && !*cond)
2131 int start, end, domain;
2135 end_addr = parse_find_address_end(header_value, FALSE);
2136 saveend = *end_addr;
2138 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2140 if (extracted_addr) switch (addressPart)
2142 case ADDRPART_ALL: part=extracted_addr; break;
2146 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2147 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2149 case ADDRPART_DETAIL: part=NULL; break;
2153 *end_addr = saveend;
2156 for (k=key; k->length!=-1; ++k)
2158 struct String partStr;
2160 partStr.character=part;
2161 partStr.length=Ustrlen(part);
2164 *cond=compare(filter,k,&partStr,comparator,matchType);
2165 if (*cond==-1) return -1;
2170 if (saveend == 0) break;
2171 header_value = end_addr + 1;
2173 parse_allow_group = FALSE;
2174 parse_found_group = FALSE;
2179 else if (parse_identifier(filter,CUS "allof"))
2182 allof-test = "allof" <tests: test-list>
2187 switch (parse_testlist(filter,&n,&num_true,exec))
2190 case 0: filter->errmsg=CUS "missing test list"; return -1;
2191 default: *cond=(n==num_true); return 1;
2194 else if (parse_identifier(filter,CUS "anyof"))
2197 anyof-test = "anyof" <tests: test-list>
2202 switch (parse_testlist(filter,&n,&num_true,exec))
2205 case 0: filter->errmsg=CUS "missing test list"; return -1;
2206 default: *cond=(num_true>0); return 1;
2209 else if (parse_identifier(filter,CUS "exists"))
2212 exists-test = "exists" <header-names: string-list>
2215 struct String *hdr,*h;
2218 if (parse_white(filter)==-1) return -1;
2219 if ((m=parse_stringlist(filter,&hdr))!=1)
2221 if (m==0) filter->errmsg=CUS "header string list expected";
2227 for (h=hdr; h->length!=-1 && *cond; ++h)
2231 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2232 if (header_def == NULL)
2234 filter->errmsg=CUS "header string expansion failed";
2237 if (Ustrcmp(header_def,"false")==0) *cond=0;
2242 else if (parse_identifier(filter,CUS "false"))
2245 false-test = "false"
2251 else if (parse_identifier(filter,CUS "header"))
2254 header-test = "header" { [comparator] [match-type] }
2255 <header-names: string-list> <key-list: string-list>
2258 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2259 enum MatchType matchType=MATCH_IS;
2260 struct String *hdr,*h,*key,*k;
2266 if (parse_white(filter)==-1) return -1;
2267 if ((m=parse_comparator(filter,&comparator))!=0)
2269 if (m==-1) return -1;
2272 filter->errmsg=CUS "comparator already specified";
2277 else if ((m=parse_matchtype(filter,&matchType))!=0)
2279 if (m==-1) return -1;
2282 filter->errmsg=CUS "match type already specified";
2289 if (parse_white(filter)==-1) return -1;
2290 if ((m=parse_stringlist(filter,&hdr))!=1)
2292 if (m==0) filter->errmsg=CUS "header string list expected";
2295 if (parse_white(filter)==-1) return -1;
2296 if ((m=parse_stringlist(filter,&key))!=1)
2298 if (m==0) filter->errmsg=CUS "key string list expected";
2302 for (h=hdr; h->length!=-1 && !*cond; ++h)
2306 filter->errmsg=CUS "invalid header field";
2311 struct String header_value;
2314 expand_header(&header_value,h);
2315 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2316 if (header_value.character == NULL || header_def == NULL)
2318 filter->errmsg=CUS "header string expansion failed";
2321 for (k=key; k->length!=-1; ++k)
2323 if (Ustrcmp(header_def,"true")==0)
2325 *cond=compare(filter,k,&header_value,comparator,matchType);
2326 if (*cond==-1) return -1;
2334 else if (parse_identifier(filter,CUS "not"))
2336 if (parse_white(filter)==-1) return -1;
2337 switch (parse_test(filter,cond,exec))
2340 case 0: filter->errmsg=CUS "missing test"; return -1;
2341 default: *cond=!*cond; return 1;
2344 else if (parse_identifier(filter,CUS "size"))
2347 relop = ":over" / ":under"
2348 size-test = "size" relop <limit: number>
2351 unsigned long limit;
2354 if (parse_white(filter)==-1) return -1;
2355 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2356 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2359 filter->errmsg=CUS "missing :over or :under";
2362 if (parse_white(filter)==-1) return -1;
2363 if (parse_number(filter,&limit)==-1) return -1;
2364 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2367 else if (parse_identifier(filter,CUS "true"))
2372 else if (parse_identifier(filter,CUS "envelope"))
2375 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2376 <envelope-part: string-list> <key-list: string-list>
2378 envelope-part is case insensitive "from" or "to"
2379 #ifdef ENVELOPE_AUTH
2380 envelope-part =/ "auth"
2384 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2385 enum AddressPart addressPart=ADDRPART_ALL;
2386 enum MatchType matchType=MATCH_IS;
2387 struct String *env,*e,*key,*k;
2391 if (!filter->require_envelope)
2393 filter->errmsg=CUS "missing previous require \"envelope\";";
2398 if (parse_white(filter)==-1) return -1;
2399 if ((m=parse_comparator(filter,&comparator))!=0)
2401 if (m==-1) return -1;
2404 filter->errmsg=CUS "comparator already specified";
2409 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2411 if (m==-1) return -1;
2414 filter->errmsg=CUS "address part already specified";
2419 else if ((m=parse_matchtype(filter,&matchType))!=0)
2421 if (m==-1) return -1;
2424 filter->errmsg=CUS "match type already specified";
2431 if (parse_white(filter)==-1) return -1;
2432 if ((m=parse_stringlist(filter,&env))!=1)
2434 if (m==0) filter->errmsg=CUS "envelope string list expected";
2437 if (parse_white(filter)==-1) return -1;
2438 if ((m=parse_stringlist(filter,&key))!=1)
2440 if (m==0) filter->errmsg=CUS "key string list expected";
2444 for (e=env; e->length!=-1 && !*cond; ++e)
2446 const uschar *envelopeExpr=CUS 0;
2447 uschar *envelope=US 0;
2449 if (eq_asciicase(e,&str_from,0))
2451 switch (addressPart)
2453 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2457 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2458 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2460 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2464 else if (eq_asciicase(e,&str_to,0))
2466 switch (addressPart)
2468 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2470 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2471 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2473 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2474 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2477 #ifdef ENVELOPE_AUTH
2478 else if (eq_asciicase(e,&str_auth,0))
2480 switch (addressPart)
2482 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2486 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2487 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2489 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2496 filter->errmsg=CUS "invalid envelope string";
2499 if (exec && envelopeExpr)
2501 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2503 filter->errmsg=CUS "header string expansion failed";
2506 for (k=key; k->length!=-1; ++k)
2508 struct String envelopeStr;
2510 envelopeStr.character=envelope;
2511 envelopeStr.length=Ustrlen(envelope);
2512 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2513 if (*cond==-1) return -1;
2521 else if (parse_identifier(filter,CUS "valid_notify_method"))
2524 valid_notify_method = "valid_notify_method"
2525 <notification-uris: string-list>
2528 struct String *uris,*u;
2531 if (!filter->require_enotify)
2533 filter->errmsg=CUS "missing previous require \"enotify\";";
2536 if (parse_white(filter)==-1) return -1;
2537 if ((m=parse_stringlist(filter,&uris))!=1)
2539 if (m==0) filter->errmsg=CUS "URI string list expected";
2545 for (u=uris; u->length!=-1 && *cond; ++u)
2547 string_item *recipient;
2548 struct String header,subject,body;
2552 header.character=(uschar*)0;
2554 subject.character=(uschar*)0;
2556 body.character=(uschar*)0;
2557 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2563 else if (parse_identifier(filter,CUS "notify_method_capability"))
2566 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2567 <notification-uri: string>
2568 <notification-capability: string>
2569 <key-list: string-list>
2575 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2576 enum MatchType matchType=MATCH_IS;
2577 struct String uri,capa,*keys,*k;
2579 if (!filter->require_enotify)
2581 filter->errmsg=CUS "missing previous require \"enotify\";";
2586 if (parse_white(filter)==-1) return -1;
2587 if ((m=parse_comparator(filter,&comparator))!=0)
2589 if (m==-1) return -1;
2592 filter->errmsg=CUS "comparator already specified";
2597 else if ((m=parse_matchtype(filter,&matchType))!=0)
2599 if (m==-1) return -1;
2602 filter->errmsg=CUS "match type already specified";
2609 if ((m=parse_string(filter,&uri))!=1)
2611 if (m==0) filter->errmsg=CUS "missing notification URI string";
2614 if (parse_white(filter)==-1) return -1;
2615 if ((m=parse_string(filter,&capa))!=1)
2617 if (m==0) filter->errmsg=CUS "missing notification capability string";
2620 if (parse_white(filter)==-1) return -1;
2621 if ((m=parse_stringlist(filter,&keys))!=1)
2623 if (m==0) filter->errmsg=CUS "missing key string list";
2628 string_item *recipient;
2629 struct String header,subject,body;
2634 header.character=(uschar*)0;
2636 subject.character=(uschar*)0;
2638 body.character=(uschar*)0;
2639 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2641 if (eq_asciicase(&capa,&str_online,0)==1)
2642 for (k=keys; k->length!=-1; ++k)
2644 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2645 if (*cond==-1) return -1;
2657 /*************************************************
2658 * Parse and interpret an optional block *
2659 *************************************************/
2663 filter points to the Sieve filter including its state
2664 exec Execute parsed statements
2665 generated where to hang newly-generated addresses
2667 Returns: 2 success by stop
2669 0 no block command found
2670 -1 syntax or execution error
2673 static int parse_block(struct Sieve *filter, int exec,
2674 address_item **generated)
2678 if (parse_white(filter)==-1) return -1;
2679 if (*filter->pc=='{')
2682 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2683 if (*filter->pc=='}')
2690 filter->errmsg=CUS "expecting command or closing brace";
2698 /*************************************************
2699 * Match a semicolon *
2700 *************************************************/
2704 filter points to the Sieve filter including its state
2710 static int parse_semicolon(struct Sieve *filter)
2712 if (parse_white(filter)==-1) return -1;
2713 if (*filter->pc==';')
2720 filter->errmsg=CUS "missing semicolon";
2726 /*************************************************
2727 * Parse and interpret a Sieve command *
2728 *************************************************/
2732 filter points to the Sieve filter including its state
2733 exec Execute parsed statements
2734 generated where to hang newly-generated addresses
2736 Returns: 2 success by stop
2738 -1 syntax or execution error
2740 static int parse_commands(struct Sieve *filter, int exec,
2741 address_item **generated)
2745 if (parse_white(filter)==-1) return -1;
2746 if (parse_identifier(filter,CUS "if"))
2749 if-command = "if" test block *( "elsif" test block ) [ else block ]
2752 int cond,m,unsuccessful;
2755 if (parse_white(filter)==-1) return -1;
2756 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2759 filter->errmsg=CUS "missing test";
2762 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2763 (debug_selector & D_filter) != 0)
2765 if (exec) debug_printf("if %s\n",cond?"true":"false");
2767 m=parse_block(filter,exec ? cond : 0, generated);
2768 if (m==-1 || m==2) return m;
2771 filter->errmsg=CUS "missing block";
2774 unsuccessful = !cond;
2775 for (;;) /* elsif test block */
2777 if (parse_white(filter)==-1) return -1;
2778 if (parse_identifier(filter,CUS "elsif"))
2780 if (parse_white(filter)==-1) return -1;
2781 m=parse_test(filter,&cond,exec && unsuccessful);
2782 if (m==-1 || m==2) return m;
2785 filter->errmsg=CUS "missing test";
2788 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2789 (debug_selector & D_filter) != 0)
2791 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2793 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2794 if (m==-1 || m==2) return m;
2797 filter->errmsg=CUS "missing block";
2800 if (exec && unsuccessful && cond) unsuccessful = 0;
2805 if (parse_white(filter)==-1) return -1;
2806 if (parse_identifier(filter,CUS "else"))
2808 m=parse_block(filter,exec && unsuccessful, generated);
2809 if (m==-1 || m==2) return m;
2812 filter->errmsg=CUS "missing block";
2817 else if (parse_identifier(filter,CUS "stop"))
2820 stop-command = "stop" { stop-options } ";"
2824 if (parse_semicolon(filter)==-1) return -1;
2827 filter->pc+=Ustrlen(filter->pc);
2831 else if (parse_identifier(filter,CUS "keep"))
2834 keep-command = "keep" { keep-options } ";"
2838 if (parse_semicolon(filter)==-1) return -1;
2841 add_addr(generated,US"inbox",1,0,0,0);
2845 else if (parse_identifier(filter,CUS "discard"))
2848 discard-command = "discard" { discard-options } ";"
2852 if (parse_semicolon(filter)==-1) return -1;
2853 if (exec) filter->keep=0;
2855 else if (parse_identifier(filter,CUS "redirect"))
2858 redirect-command = "redirect" redirect-options "string" ";"
2860 redirect-options =) ":copy"
2863 struct String recipient;
2869 if (parse_white(filter)==-1) return -1;
2870 if (parse_identifier(filter,CUS ":copy")==1)
2872 if (!filter->require_copy)
2874 filter->errmsg=CUS "missing previous require \"copy\";";
2881 if (parse_white(filter)==-1) return -1;
2882 if ((m=parse_string(filter,&recipient))!=1)
2884 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2887 if (strchr(CCS recipient.character,'@')==(char*)0)
2889 filter->errmsg=CUS "unqualified recipient address";
2894 add_addr(generated,recipient.character,0,0,0,0);
2895 if (!copy) filter->keep = 0;
2897 if (parse_semicolon(filter)==-1) return -1;
2899 else if (parse_identifier(filter,CUS "fileinto"))
2902 fileinto-command = "fileinto" { fileinto-options } string ";"
2904 fileinto-options =) [ ":copy" ]
2907 struct String folder;
2910 unsigned long maxage, maxmessages, maxstorage;
2913 maxage = maxmessages = maxstorage = 0;
2914 if (!filter->require_fileinto)
2916 filter->errmsg=CUS "missing previous require \"fileinto\";";
2921 if (parse_white(filter)==-1) return -1;
2922 if (parse_identifier(filter,CUS ":copy")==1)
2924 if (!filter->require_copy)
2926 filter->errmsg=CUS "missing previous require \"copy\";";
2933 if (parse_white(filter)==-1) return -1;
2934 if ((m=parse_string(filter,&folder))!=1)
2936 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2939 m=0; s=folder.character;
2940 if (folder.length==0) m=1;
2941 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2944 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2949 filter->errmsg=CUS "invalid folder";
2954 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2955 if (!copy) filter->keep = 0;
2957 if (parse_semicolon(filter)==-1) return -1;
2960 else if (parse_identifier(filter,CUS "notify"))
2963 notify-command = "notify" { notify-options } <method: string> ";"
2964 notify-options = [":from" string]
2965 [":importance" <"1" / "2" / "3">]
2966 [":options" 1*(string-list / number)]
2972 struct String importance;
2973 struct String *options;
2974 struct String message;
2975 struct String method;
2976 struct Notification *already;
2977 string_item *recipient;
2978 struct String header;
2979 struct String subject;
2981 uschar *envelope_from;
2982 struct String auto_submitted_value;
2983 uschar *auto_submitted_def;
2985 if (!filter->require_enotify)
2987 filter->errmsg=CUS "missing previous require \"enotify\";";
2990 from.character=(uschar*)0;
2992 importance.character=(uschar*)0;
2993 importance.length=-1;
2994 options=(struct String*)0;
2995 message.character=(uschar*)0;
2999 header.character=(uschar*)0;
3001 subject.character=(uschar*)0;
3003 body.character=(uschar*)0;
3004 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3007 if (parse_white(filter)==-1) return -1;
3008 if (parse_identifier(filter,CUS ":from")==1)
3010 if (parse_white(filter)==-1) return -1;
3011 if ((m=parse_string(filter,&from))!=1)
3013 if (m==0) filter->errmsg=CUS "from string expected";
3017 else if (parse_identifier(filter,CUS ":importance")==1)
3019 if (parse_white(filter)==-1) return -1;
3020 if ((m=parse_string(filter,&importance))!=1)
3022 if (m==0) filter->errmsg=CUS "importance string expected";
3025 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3027 filter->errmsg=CUS "invalid importance";
3031 else if (parse_identifier(filter,CUS ":options")==1)
3033 if (parse_white(filter)==-1) return -1;
3035 else if (parse_identifier(filter,CUS ":message")==1)
3037 if (parse_white(filter)==-1) return -1;
3038 if ((m=parse_string(filter,&message))!=1)
3040 if (m==0) filter->errmsg=CUS "message string expected";
3046 if (parse_white(filter)==-1) return -1;
3047 if ((m=parse_string(filter,&method))!=1)
3049 if (m==0) filter->errmsg=CUS "missing method string";
3052 if (parse_semicolon(filter)==-1) return -1;
3053 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3057 if (message.length==-1) message=subject;
3058 if (message.length==-1) expand_header(&message,&str_subject);
3059 expand_header(&auto_submitted_value,&str_auto_submitted);
3060 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3061 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3063 filter->errmsg=CUS "header string expansion failed";
3066 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3068 for (already=filter->notified; already; already=already->next)
3070 if (already->method.length==method.length
3071 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3072 && already->importance.length==importance.length
3073 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3074 && already->message.length==message.length
3075 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3078 if (already==(struct Notification*)0)
3079 /* New notification, process it */
3081 struct Notification *sent;
3082 sent=store_get(sizeof(struct Notification));
3083 sent->method=method;
3084 sent->importance=importance;
3085 sent->message=message;
3086 sent->next=filter->notified;
3087 filter->notified=sent;
3088 #ifndef COMPILE_SYNTAX_CHECKER
3089 if (filter_test == FTEST_NONE)
3094 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3098 int buffer_capacity;
3100 f = fdopen(fd, "wb");
3101 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3102 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3103 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3104 if (header.length>0) fprintf(f,"%s",header.character);
3105 if (message.length==-1)
3107 message.character=US"Notification";
3108 message.length=Ustrlen(message.character);
3110 /* Allocation is larger than neccessary, but enough even for split MIME words */
3111 buffer_capacity=32+4*message.length;
3112 buffer=store_get(buffer_capacity);
3113 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3115 if (body.length>0) fprintf(f,"%s\n",body.character);
3118 (void)child_close(pid, 0);
3121 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3123 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3129 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3131 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3137 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3139 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3146 else if (parse_identifier(filter,CUS "vacation"))
3149 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3150 vacation-options = [":days" number]
3153 [":addresses" string-list]
3160 struct String subject;
3162 struct String *addresses;
3164 string_item *aliases;
3165 struct String handle;
3166 struct String reason;
3168 if (!filter->require_vacation)
3170 filter->errmsg=CUS "missing previous require \"vacation\";";
3175 if (filter->vacation_ran)
3177 filter->errmsg=CUS "trying to execute vacation more than once";
3180 filter->vacation_ran=1;
3182 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3183 subject.character=(uschar*)0;
3185 from.character=(uschar*)0;
3187 addresses=(struct String*)0;
3190 handle.character=(uschar*)0;
3194 if (parse_white(filter)==-1) return -1;
3195 if (parse_identifier(filter,CUS ":days")==1)
3197 if (parse_white(filter)==-1) return -1;
3198 if (parse_number(filter,&days)==-1) return -1;
3199 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3200 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3202 else if (parse_identifier(filter,CUS ":subject")==1)
3204 if (parse_white(filter)==-1) return -1;
3205 if ((m=parse_string(filter,&subject))!=1)
3207 if (m==0) filter->errmsg=CUS "subject string expected";
3211 else if (parse_identifier(filter,CUS ":from")==1)
3213 if (parse_white(filter)==-1) return -1;
3214 if ((m=parse_string(filter,&from))!=1)
3216 if (m==0) filter->errmsg=CUS "from string expected";
3219 if (check_mail_address(filter,&from)!=1)
3222 else if (parse_identifier(filter,CUS ":addresses")==1)
3226 if (parse_white(filter)==-1) return -1;
3227 if ((m=parse_stringlist(filter,&addresses))!=1)
3229 if (m==0) filter->errmsg=CUS "addresses string list expected";
3232 for (a=addresses; a->length!=-1; ++a)
3236 new=store_get(sizeof(string_item));
3237 new->text=store_get(a->length+1);
3238 if (a->length) memcpy(new->text,a->character,a->length);
3239 new->text[a->length]='\0';
3244 else if (parse_identifier(filter,CUS ":mime")==1)
3246 else if (parse_identifier(filter,CUS ":handle")==1)
3248 if (parse_white(filter)==-1) return -1;
3249 if ((m=parse_string(filter,&from))!=1)
3251 if (m==0) filter->errmsg=CUS "handle string expected";
3257 if (parse_white(filter)==-1) return -1;
3258 if ((m=parse_string(filter,&reason))!=1)
3260 if (m==0) filter->errmsg=CUS "missing reason string";
3267 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3270 filter->errmsg=CUS "MIME reason string contains 8bit text";
3274 if (parse_semicolon(filter)==-1) return -1;
3281 int buffer_capacity;
3285 uschar hexdigest[33];
3289 if (filter_personal(aliases,TRUE))
3291 if (filter_test == FTEST_NONE)
3293 /* ensure oncelog directory exists; failure will be detected later */
3295 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3297 /* build oncelog filename */
3299 key.character=(uschar*)0;
3302 if (handle.length==-1)
3304 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3305 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3306 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3307 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3312 md5_end(&base, key.character, key.length, digest);
3313 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3314 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3316 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3318 if (filter_test == FTEST_NONE)
3320 capacity=Ustrlen(filter->vacation_directory);
3322 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3323 once=string_cat(once,&capacity,&start,hexdigest,33);
3326 /* process subject */
3328 if (subject.length==-1)
3330 uschar *subject_def;
3332 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3333 if (Ustrcmp(subject_def,"true")==0)
3335 expand_header(&subject,&str_subject);
3338 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3339 subject.length=start;
3343 subject.character=US"Automated reply";
3344 subject.length=Ustrlen(subject.character);
3348 /* add address to list of generated addresses */
3350 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3351 setflag(addr, af_pfr);
3352 setflag(addr, af_ignore_error);
3353 addr->next = *generated;
3355 addr->reply = store_get(sizeof(reply_item));
3356 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3357 addr->reply->to = string_copy(sender_address);
3358 if (from.length==-1)
3359 addr->reply->from = expand_string(US"$local_part@$domain");
3361 addr->reply->from = from.character;
3362 /* Allocation is larger than neccessary, but enough even for split MIME words */
3363 buffer_capacity=32+4*subject.length;
3364 buffer=store_get(buffer_capacity);
3365 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3366 addr->reply->oncelog=once;
3367 addr->reply->once_repeat=days*86400;
3369 /* build body and MIME headers */
3373 uschar *mime_body,*reason_end;
3374 static const uschar nlnl[]="\r\n\r\n";
3378 mime_body=reason.character,reason_end=reason.character+reason.length;
3379 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3384 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3385 addr->reply->headers[start] = '\0';
3388 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3389 else mime_body=reason_end-1;
3390 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3391 addr->reply->text[start] = '\0';
3395 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3398 start = reason.length;
3399 addr->reply->headers = US"MIME-Version: 1.0\n"
3400 "Content-Type: text/plain;\n"
3401 "\tcharset=\"utf-8\"\n"
3402 "Content-Transfer-Encoding: quoted-printable";
3403 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3407 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3409 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3420 /*************************************************
3421 * Parse and interpret a sieve filter *
3422 *************************************************/
3426 filter points to the Sieve filter including its state
3427 exec Execute parsed statements
3428 generated where to hang newly-generated addresses
3431 -1 syntax or execution error
3434 static int parse_start(struct Sieve *filter, int exec,
3435 address_item **generated)
3437 filter->pc=filter->filter;
3440 filter->require_envelope=0;
3441 filter->require_fileinto=0;
3442 #ifdef ENCODED_CHARACTER
3443 filter->require_encoded_character=0;
3445 #ifdef ENVELOPE_AUTH
3446 filter->require_envelope_auth=0;
3449 filter->require_enotify=0;
3450 filter->notified=(struct Notification*)0;
3453 filter->require_subaddress=0;
3456 filter->require_vacation=0;
3457 filter->vacation_ran=0;
3459 filter->require_copy=0;
3460 filter->require_iascii_numeric=0;
3462 if (parse_white(filter)==-1) return -1;
3464 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3467 struct dirent *oncelog;
3468 struct stat properties;
3471 /* clean up old vacation log databases */
3473 oncelogdir=opendir(CS filter->vacation_directory);
3475 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3477 filter->errmsg=CUS "unable to open vacation directory";
3481 if (oncelogdir != NULL)
3485 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3487 if (strlen(oncelog->d_name)==32)
3489 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3490 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3494 closedir(oncelogdir);
3498 while (parse_identifier(filter,CUS "require"))
3501 require-command = "require" <capabilities: string-list>
3504 struct String *cap,*check;
3507 if (parse_white(filter)==-1) return -1;
3508 if ((m=parse_stringlist(filter,&cap))!=1)
3510 if (m==0) filter->errmsg=CUS "capability string list expected";
3513 for (check=cap; check->character; ++check)
3515 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3516 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3517 #ifdef ENCODED_CHARACTER
3518 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3520 #ifdef ENVELOPE_AUTH
3521 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3524 else if (eq_octet(check,&str_enotify,0))
3526 if (filter->enotify_mailto_owner == NULL)
3528 filter->errmsg=CUS "enotify disabled";
3531 filter->require_enotify=1;
3535 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3538 else if (eq_octet(check,&str_vacation,0))
3540 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3542 filter->errmsg=CUS "vacation disabled";
3545 filter->require_vacation=1;
3548 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3549 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3550 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3551 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3552 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3555 filter->errmsg=CUS "unknown capability";
3559 if (parse_semicolon(filter)==-1) return -1;
3561 if (parse_commands(filter,exec,generated)==-1) return -1;
3564 filter->errmsg=CUS "syntax error";
3571 /*************************************************
3572 * Interpret a sieve filter file *
3573 *************************************************/
3577 filter points to the entire file, read into store as a single string
3578 options controls whether various special things are allowed, and requests
3579 special actions (not currently used)
3580 vacation_directory where to store vacation "once" files
3581 enotify_mailto_owner owner of mailto notifications
3582 useraddress string expression for :user part of address
3583 subaddress string expression for :subaddress part of address
3584 generated where to hang newly-generated addresses
3585 error where to pass back an error text
3587 Returns: FF_DELIVERED success, a significant action was taken
3588 FF_NOTDELIVERED success, no significant action
3589 FF_DEFER defer requested
3590 FF_FAIL fail requested
3591 FF_FREEZE freeze requested
3592 FF_ERROR there was a problem
3596 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3597 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3598 address_item **generated, uschar **error)
3604 options = options; /* Keep picky compilers happy */
3607 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3608 sieve.filter=filter;
3610 if (vacation_directory == NULL)
3611 sieve.vacation_directory = NULL;
3614 sieve.vacation_directory=expand_string(vacation_directory);
3615 if (sieve.vacation_directory == NULL)
3617 *error = string_sprintf("failed to expand \"%s\" "
3618 "(sieve_vacation_directory): %s", vacation_directory,
3619 expand_string_message);
3624 if (enotify_mailto_owner == NULL)
3625 sieve.enotify_mailto_owner = NULL;
3628 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3629 if (sieve.enotify_mailto_owner == NULL)
3631 *error = string_sprintf("failed to expand \"%s\" "
3632 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3633 expand_string_message);
3638 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3639 sieve.subaddress = subaddress;
3641 #ifdef COMPILE_SYNTAX_CHECKER
3642 if (parse_start(&sieve,0,generated)==1)
3644 if (parse_start(&sieve,1,generated)==1)
3649 add_addr(generated,US"inbox",1,0,0,0);
3650 msg = string_sprintf("Implicit keep");
3655 msg = string_sprintf("No implicit keep");
3661 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3662 #ifdef COMPILE_SYNTAX_CHECKER
3666 add_addr(generated,US"inbox",1,0,0,0);
3671 #ifndef COMPILE_SYNTAX_CHECKER
3672 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3673 else debug_printf("%s\n", msg);
3676 DEBUG(D_route) debug_printf("Sieve: end of processing\n");