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 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
111 static int parse_test(struct Sieve *filter, int *cond, int exec);
112 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
114 static uschar str_from_c[]="From";
115 static const struct String str_from={ str_from_c, 4 };
116 static uschar str_to_c[]="To";
117 static const struct String str_to={ str_to_c, 2 };
118 static uschar str_cc_c[]="Cc";
119 static const struct String str_cc={ str_cc_c, 2 };
120 static uschar str_bcc_c[]="Bcc";
121 static const struct String str_bcc={ str_bcc_c, 3 };
122 static uschar str_auth_c[]="auth";
123 static const struct String str_auth={ str_auth_c, 4 };
124 static uschar str_sender_c[]="Sender";
125 static const struct String str_sender={ str_sender_c, 6 };
126 static uschar str_resent_from_c[]="Resent-From";
127 static const struct String str_resent_from={ str_resent_from_c, 11 };
128 static uschar str_resent_to_c[]="Resent-To";
129 static const struct String str_resent_to={ str_resent_to_c, 9 };
130 static uschar str_fileinto_c[]="fileinto";
131 static const struct String str_fileinto={ str_fileinto_c, 8 };
132 static uschar str_envelope_c[]="envelope";
133 static const struct String str_envelope={ str_envelope_c, 8 };
134 #ifdef ENCODED_CHARACTER
135 static uschar str_encoded_character_c[]="encoded-character";
136 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
139 static uschar str_envelope_auth_c[]="envelope-auth";
140 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
143 static uschar str_enotify_c[]="enotify";
144 static const struct String str_enotify={ str_enotify_c, 7 };
145 static uschar str_online_c[]="online";
146 static const struct String str_online={ str_online_c, 6 };
147 static uschar str_maybe_c[]="maybe";
148 static const struct String str_maybe={ str_maybe_c, 5 };
149 static uschar str_auto_submitted_c[]="Auto-Submitted";
150 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
153 static uschar str_subaddress_c[]="subaddress";
154 static const struct String str_subaddress={ str_subaddress_c, 10 };
157 static uschar str_vacation_c[]="vacation";
158 static const struct String str_vacation={ str_vacation_c, 8 };
159 static uschar str_subject_c[]="Subject";
160 static const struct String str_subject={ str_subject_c, 7 };
162 static uschar str_copy_c[]="copy";
163 static const struct String str_copy={ str_copy_c, 4 };
164 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
165 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
166 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
167 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
168 static uschar str_ioctet_c[]="i;octet";
169 static const struct String str_ioctet={ str_ioctet_c, 7 };
170 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
171 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
172 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
173 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
174 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
175 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
176 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
177 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
178 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
179 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
182 /*************************************************
183 * Encode to quoted-printable *
184 *************************************************/
195 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
198 const uschar *start,*end;
203 for (pass=0; pass<=1; ++pass)
210 dst->character=store_get(dst->length+1); /* plus one for \0 */
213 for (start=src->character,end=start+src->length; start<end; ++start)
230 || (ch>=62 && ch<=126)
235 && (*(start+1)!='\r' || *(start+2)!='\n')
245 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
263 sprintf(CS new,"=%02X",ch);
270 *new='\0'; /* not included in length, but nice */
275 /*************************************************
276 * Check mail address for correct syntax *
277 *************************************************/
280 Check mail address for being syntactically correct.
283 filter points to the Sieve filter including its state
284 address String containing one address
287 1 Mail address is syntactically OK
291 int check_mail_address(struct Sieve *filter, const struct String *address)
293 int start, end, domain;
296 if (address->length>0)
298 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
302 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
303 address->character, error);
311 filter->errmsg=CUS "empty address";
317 /*************************************************
318 * Decode URI encoded string *
319 *************************************************/
323 str URI encoded string
326 0 Decoding successful
331 static int uri_decode(struct String *str)
335 if (str->length==0) return 0;
336 for (s=str->character,t=s,e=s+str->length; s<e; )
340 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
342 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
343 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
352 str->length=t-str->character;
357 /*************************************************
359 *************************************************/
364 mailtoURI = "mailto:" [ to ] [ headers ]
365 to = [ addr-spec *("%2C" addr-spec ) ]
366 headers = "?" header *( "&" header )
367 header = hname "=" hvalue
372 filter points to the Sieve filter including its state
373 uri URI, excluding scheme
378 1 URI is syntactically OK
383 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
386 struct String to,hname,hvalue;
390 if (Ustrncmp(uri,"mailto:",7))
392 filter->errmsg=US "Unknown URI scheme";
396 if (*uri && *uri!='?')
400 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
404 to.character=(uschar*)0;
406 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
407 to.character[to.length]='\0';
408 if (uri_decode(&to)==-1)
410 filter->errmsg=US"Invalid URI encoding";
413 new=store_get(sizeof(string_item));
414 new->text=store_get(to.length+1);
415 if (to.length) memcpy(new->text,to.character,to.length);
416 new->text[to.length]='\0';
417 new->next=*recipient;
422 filter->errmsg=US"Missing addr-spec in URI";
425 if (*uri=='%') uri+=3;
434 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
438 hname.character=(uschar*)0;
440 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
441 hname.character[hname.length]='\0';
442 if (uri_decode(&hname)==-1)
444 filter->errmsg=US"Invalid URI encoding";
453 filter->errmsg=US"Missing equal after hname";
457 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
461 hvalue.character=(uschar*)0;
463 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
464 hvalue.character[hvalue.length]='\0';
465 if (uri_decode(&hvalue)==-1)
467 filter->errmsg=US"Invalid URI encoding";
471 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
473 new=store_get(sizeof(string_item));
474 new->text=store_get(hvalue.length+1);
475 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
476 new->text[hvalue.length]='\0';
477 new->next=*recipient;
480 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
482 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
486 static struct String ignore[]=
492 {US"auto-submitted",14}
494 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
497 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
500 if (header->length==-1) header->length=0;
501 capacity=header->length;
502 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
503 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
504 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
505 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
506 header->character[header->length]='\0';
509 if (*uri=='&') ++uri;
515 filter->errmsg=US"Syntactically invalid URI";
523 /*************************************************
524 * Octet-wise string comparison *
525 *************************************************/
529 needle UTF-8 string to search ...
530 haystack ... inside the haystack
531 match_prefix 1 to compare if needle is a prefix of haystack
533 Returns: 0 needle not found in haystack
537 static int eq_octet(const struct String *needle,
538 const struct String *haystack, int match_prefix)
546 h=haystack->character;
550 if (*n&0x80) return 0;
551 if (*h&0x80) return 0;
553 if (*n!=*h) return 0;
559 return (match_prefix ? nl==0 : nl==0 && hl==0);
563 /*************************************************
564 * ASCII case-insensitive string comparison *
565 *************************************************/
569 needle UTF-8 string to search ...
570 haystack ... inside the haystack
571 match_prefix 1 to compare if needle is a prefix of haystack
573 Returns: 0 needle not found in haystack
577 static int eq_asciicase(const struct String *needle,
578 const struct String *haystack, int match_prefix)
587 h=haystack->character;
593 if (nc&0x80) return 0;
594 if (hc&0x80) return 0;
596 /* tolower depends on the locale and only ASCII case must be insensitive */
597 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
603 return (match_prefix ? nl==0 : nl==0 && hl==0);
607 /*************************************************
608 * Glob pattern search *
609 *************************************************/
613 needle pattern to search ...
614 haystack ... inside the haystack
615 ascii_caseless ignore ASCII case
616 match_octet match octets, not UTF-8 multi-octet characters
618 Returns: 0 needle not found in haystack
623 static int eq_glob(const struct String *needle,
624 const struct String *haystack, int ascii_caseless, int match_octet)
626 const uschar *n,*h,*nend,*hend;
630 h=haystack->character;
631 nend=n+needle->length;
632 hend=h+haystack->length;
642 const uschar *npart,*hpart;
644 /* Try to match a non-star part of the needle at the current */
645 /* position in the haystack. */
649 while (npart<nend && *npart!='*') switch (*npart)
653 if (hpart==hend) return 0;
658 /* Match one UTF8 encoded character */
659 if ((*hpart&0xc0)==0xc0)
662 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
673 if (npart==nend) return -1;
678 if (hpart==hend) return 0;
679 /* tolower depends on the locale, but we need ASCII */
683 (*hpart&0x80) || (*npart&0x80) ||
686 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
691 /* string match after a star failed, advance and try again */
705 /* at this point, a part was matched successfully */
706 if (may_advance && npart==nend && hpart<hend)
707 /* needle ends, but haystack does not: if there was a star before, advance and try again */
717 return (h==hend ? 1 : may_advance);
721 /*************************************************
722 * ASCII numeric comparison *
723 *************************************************/
727 a first numeric string
728 b second numeric string
729 relop relational operator
731 Returns: 0 not (a relop b)
735 static int eq_asciinumeric(const struct String *a,
736 const struct String *b, enum RelOp relop)
739 const uschar *as,*aend,*bs,*bend;
743 aend=a->character+a->length;
745 bend=b->character+b->length;
747 while (*as>='0' && *as<='9' && as<aend) ++as;
749 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
752 if (al && bl==0) cmp=-1;
753 else if (al==0 && bl==0) cmp=0;
754 else if (al==0 && bl) cmp=1;
758 if (cmp==0) cmp=memcmp(a->character,b->character,al);
762 case LT: return cmp<0;
763 case LE: return cmp<=0;
764 case EQ: return cmp==0;
765 case GE: return cmp>=0;
766 case GT: return cmp>0;
767 case NE: return cmp!=0;
774 /*************************************************
776 *************************************************/
780 filter points to the Sieve filter including its state
781 needle UTF-8 pattern or string to search ...
782 haystack ... inside the haystack
786 Returns: 0 needle not found in haystack
788 -1 comparator does not offer matchtype
791 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
792 enum Comparator co, enum MatchType mt)
796 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
797 (debug_selector & D_filter) != 0)
799 debug_printf("String comparison (match ");
802 case MATCH_IS: debug_printf(":is"); break;
803 case MATCH_CONTAINS: debug_printf(":contains"); break;
804 case MATCH_MATCHES: debug_printf(":matches"); break;
806 debug_printf(", comparison \"");
809 case COMP_OCTET: debug_printf("i;octet"); break;
810 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
811 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
813 debug_printf("\"):\n");
814 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
815 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
825 if (eq_octet(needle,haystack,0)) r=1;
828 case COMP_EN_ASCII_CASEMAP:
830 if (eq_asciicase(needle,haystack,0)) r=1;
833 case COMP_ASCII_NUMERIC:
835 if (!filter->require_iascii_numeric)
837 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
840 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
854 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
857 case COMP_EN_ASCII_CASEMAP:
859 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
864 filter->errmsg=CUS "comparator does not offer specified matchtype";
876 if ((r=eq_glob(needle,haystack,0,1))==-1)
878 filter->errmsg=CUS "syntactically invalid pattern";
883 case COMP_EN_ASCII_CASEMAP:
885 if ((r=eq_glob(needle,haystack,1,1))==-1)
887 filter->errmsg=CUS "syntactically invalid pattern";
894 filter->errmsg=CUS "comparator does not offer specified matchtype";
901 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
902 (debug_selector & D_filter) != 0)
903 debug_printf(" Result %s\n",r?"true":"false");
908 /*************************************************
909 * Check header field syntax *
910 *************************************************/
913 RFC 2822, section 3.6.8 says:
917 ftext = %d33-57 / ; Any character except
918 %d59-126 ; controls, SP, and
921 That forbids 8-bit header fields. This implementation accepts them, since
922 all of Exim is 8-bit clean, so it adds %d128-%d255.
925 header header field to quote for suitable use in Exim expansions
927 Returns: 0 string is not a valid header field
928 1 string is a value header field
931 static int is_header(const struct String *header)
941 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
952 /*************************************************
953 * Quote special characters string *
954 *************************************************/
958 header header field to quote for suitable use in Exim expansions
961 Returns: quoted string
964 static const uschar *quote(const struct String *header)
979 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
986 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
990 quoted=string_cat(quoted,&size,&ptr,h,1);
996 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
1001 /*************************************************
1002 * Add address to list of generated addresses *
1003 *************************************************/
1006 According to RFC 5228, duplicate delivery to the same address must
1007 not happen, so the list is first searched for the address.
1010 generated list of generated addresses
1011 addr new address to add
1012 file address denotes a file
1017 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1019 address_item *new_addr;
1021 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1023 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1025 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1027 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1033 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1035 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1037 new_addr=deliver_make_addr(addr,TRUE);
1040 setflag(new_addr, af_pfr|af_file);
1043 new_addr->p.errors_address = NULL;
1044 new_addr->next = *generated;
1045 *generated = new_addr;
1049 /*************************************************
1050 * Return decoded header field *
1051 *************************************************/
1054 Unfold the header field as described in RFC 2822 and remove all
1055 leading and trailing white space, then perform MIME decoding and
1056 translate the header field to UTF-8.
1059 value returned value of the field
1060 header name of the header field
1062 Returns: nothing The expanded string is empty
1063 in case there is no such header
1066 static void expand_header(struct String *value, const struct String *header)
1072 value->character=(uschar*)0;
1074 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1075 while (*r==' ' || *r=='\t') ++r;
1083 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1085 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1089 /*************************************************
1090 * Parse remaining hash comment *
1091 *************************************************/
1095 Comment up to terminating CRLF
1098 filter points to the Sieve filter including its state
1104 static int parse_hashcomment(struct Sieve *filter)
1110 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1112 if (*filter->pc=='\n')
1125 filter->errmsg=CUS "missing end of comment";
1130 /*************************************************
1131 * Parse remaining C-style comment *
1132 *************************************************/
1136 Everything up to star slash
1139 filter points to the Sieve filter including its state
1145 static int parse_comment(struct Sieve *filter)
1150 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1157 filter->errmsg=CUS "missing end of comment";
1162 /*************************************************
1163 * Parse optional white space *
1164 *************************************************/
1168 Spaces, tabs, CRLFs, hash comments or C-style comments
1171 filter points to the Sieve filter including its state
1177 static int parse_white(struct Sieve *filter)
1181 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1183 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1185 else if (*filter->pc=='\n')
1195 else if (*filter->pc=='#')
1197 if (parse_hashcomment(filter)==-1) return -1;
1199 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1201 if (parse_comment(filter)==-1) return -1;
1209 #ifdef ENCODED_CHARACTER
1210 /*************************************************
1211 * Decode hex-encoded-character string *
1212 *************************************************/
1215 Encoding definition:
1216 blank = SP / TAB / CRLF
1217 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1218 hex-pair = 1*2HEXDIG
1221 src points to a hex-pair-seq
1222 end points to its end
1223 dst points to the destination of the decoded octets,
1224 optionally to (uschar*)0 for checking only
1226 Returns: >=0 number of decoded octets
1230 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1234 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1239 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);
1240 if (d==0) return -1;
1243 if (src==end) return decoded;
1244 if (*src==' ' || *src=='\t' || *src=='\n')
1245 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1254 /*************************************************
1255 * Decode unicode-encoded-character string *
1256 *************************************************/
1259 Encoding definition:
1260 blank = SP / TAB / CRLF
1261 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1262 unicode-hex = 1*HEXDIG
1264 It is an error for a script to use a hexadecimal value that isn't in
1265 either the range 0 to D7FF or the range E000 to 10FFFF.
1267 At this time, strings are already scanned, thus the CRLF is converted
1268 to the internally used \n (should RFC_EOL have been used).
1271 src points to a unicode-hex-seq
1272 end points to its end
1273 dst points to the destination of the decoded octets,
1274 optionally to (uschar*)0 for checking only
1276 Returns: >=0 number of decoded octets
1278 -2 semantic error (character range violation)
1281 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1285 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1292 for (hex_seq=src; src<end && *src=='0'; ++src);
1293 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);
1294 if (src==hex_seq) return -1;
1295 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1301 else if (c>=0x80 && c<=0x7ff)
1306 *dst++=128+(c&0x3f);
1310 else if (c>=0x800 && c<=0xffff)
1315 *dst++=128+((c>>6)&0x3f);
1316 *dst++=128+(c&0x3f);
1320 else if (c>=0x10000 && c<=0x1fffff)
1325 *dst++=128+((c>>10)&0x3f);
1326 *dst++=128+((c>>6)&0x3f);
1327 *dst++=128+(c&0x3f);
1331 if (*src==' ' || *src=='\t' || *src=='\n')
1333 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1334 if (src==end) return decoded;
1343 /*************************************************
1344 * Decode encoded-character string *
1345 *************************************************/
1348 Encoding definition:
1349 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1350 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1353 encoded points to an encoded string, returns decoded string
1354 filter points to the Sieve filter including its state
1360 static int string_decode(struct Sieve *filter, struct String *data)
1362 uschar *src,*dst,*end;
1364 src=data->character;
1366 end=data->character+data->length;
1372 strncmpic(src,US "${hex:",6)==0
1373 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1374 && (hex_decode(src+6,brace,(uschar*)0))>=0
1377 dst+=hex_decode(src+6,brace,dst);
1381 strncmpic(src,US "${unicode:",10)==0
1382 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1385 switch (unicode_decode(src+10,brace,(uschar*)0))
1389 filter->errmsg=CUS "unicode character out of range";
1399 dst+=unicode_decode(src+10,brace,dst);
1406 data->length=dst-data->character;
1413 /*************************************************
1414 * Parse an optional string *
1415 *************************************************/
1419 quoted-string = DQUOTE *CHAR DQUOTE
1420 ;; in general, \ CHAR inside a string maps to CHAR
1421 ;; so \" maps to " and \\ maps to \
1422 ;; note that newlines and other characters are all allowed
1425 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1426 *(multi-line-literal / multi-line-dotstuff)
1428 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1429 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1430 ;; A line containing only "." ends the multi-line.
1431 ;; Remove a leading '.' if followed by another '.'.
1432 string = quoted-string / multi-line
1435 filter points to the Sieve filter including its state
1436 id specifies identifier to match
1440 0 identifier not matched
1443 static int parse_string(struct Sieve *filter, struct String *data)
1448 data->character=(uschar*)0;
1449 if (*filter->pc=='"') /* quoted string */
1454 if (*filter->pc=='"') /* end of string */
1456 int foo=data->length;
1459 /* that way, there will be at least one character allocated */
1460 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1461 #ifdef ENCODED_CHARACTER
1462 if (filter->require_encoded_character
1463 && string_decode(filter,data)==-1)
1468 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1470 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1473 else /* regular character */
1476 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1478 if (*filter->pc=='\n')
1480 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1484 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1488 filter->errmsg=CUS "missing end of string";
1491 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1494 /* skip optional white space followed by hashed comment or CRLF */
1495 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1496 if (*filter->pc=='#')
1498 if (parse_hashcomment(filter)==-1) return -1;
1501 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1503 else if (*filter->pc=='\n')
1515 filter->errmsg=CUS "syntax error";
1521 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1523 if (*filter->pc=='\n') /* end of line */
1526 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1534 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1536 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1539 int foo=data->length;
1541 /* that way, there will be at least one character allocated */
1542 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1549 #ifdef ENCODED_CHARACTER
1550 if (filter->require_encoded_character
1551 && string_decode(filter,data)==-1)
1556 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1558 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1562 else /* regular character */
1564 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1568 filter->errmsg=CUS "missing end of multi line string";
1575 /*************************************************
1576 * Parse a specific identifier *
1577 *************************************************/
1581 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1584 filter points to the Sieve filter including its state
1585 id specifies identifier to match
1588 0 identifier not matched
1591 static int parse_identifier(struct Sieve *filter, const uschar *id)
1593 size_t idlen=Ustrlen(id);
1595 if (strncmpic(US filter->pc,US id,idlen)==0)
1597 uschar next=filter->pc[idlen];
1599 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1607 /*************************************************
1609 *************************************************/
1613 number = 1*DIGIT [QUANTIFIER]
1614 QUANTIFIER = "K" / "M" / "G"
1617 filter points to the Sieve filter including its state
1621 -1 no string list found
1624 static int parse_number(struct Sieve *filter, unsigned long *data)
1628 if (*filter->pc>='0' && *filter->pc<='9')
1633 d=Ustrtoul(filter->pc,&e,10);
1636 filter->errmsg=CUstrerror(ERANGE);
1641 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1642 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1643 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1644 if (d>(ULONG_MAX/u))
1646 filter->errmsg=CUstrerror(ERANGE);
1655 filter->errmsg=CUS "missing number";
1661 /*************************************************
1662 * Parse a string list *
1663 *************************************************/
1667 string-list = "[" string *("," string) "]" / string
1670 filter points to the Sieve filter including its state
1671 data returns string list
1674 -1 no string list found
1677 static int parse_stringlist(struct Sieve *filter, struct String **data)
1679 const uschar *orig=filter->pc;
1682 struct String *d=(struct String*)0;
1685 if (*filter->pc=='[') /* string list */
1690 if (parse_white(filter)==-1) goto error;
1691 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1694 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1695 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1696 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1698 filter->errmsg=CUstrerror(errno);
1701 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1703 dataCapacity=newCapacity;
1705 m=parse_string(filter,&d[dataLength]);
1708 if (dataLength==0) break;
1711 filter->errmsg=CUS "missing string";
1715 else if (m==-1) goto error;
1717 if (parse_white(filter)==-1) goto error;
1718 if (*filter->pc==',') ++filter->pc;
1721 if (*filter->pc==']')
1723 d[dataLength].character=(uschar*)0;
1724 d[dataLength].length=-1;
1731 filter->errmsg=CUS "missing closing bracket";
1735 else /* single string */
1737 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1741 m=parse_string(filter,&d[0]);
1753 d[1].character=(uschar*)0;
1760 filter->errmsg=CUS "missing string list";
1765 /*************************************************
1766 * Parse an optional address part specifier *
1767 *************************************************/
1771 address-part = ":localpart" / ":domain" / ":all"
1772 address-part =/ ":user" / ":detail"
1775 filter points to the Sieve filter including its state
1776 a returns address part specified
1779 0 no comparator found
1783 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1786 if (parse_identifier(filter,CUS ":user")==1)
1788 if (!filter->require_subaddress)
1790 filter->errmsg=CUS "missing previous require \"subaddress\";";
1796 else if (parse_identifier(filter,CUS ":detail")==1)
1798 if (!filter->require_subaddress)
1800 filter->errmsg=CUS "missing previous require \"subaddress\";";
1808 if (parse_identifier(filter,CUS ":localpart")==1)
1810 *a=ADDRPART_LOCALPART;
1813 else if (parse_identifier(filter,CUS ":domain")==1)
1818 else if (parse_identifier(filter,CUS ":all")==1)
1827 /*************************************************
1828 * Parse an optional comparator *
1829 *************************************************/
1833 comparator = ":comparator" <comparator-name: string>
1836 filter points to the Sieve filter including its state
1837 c returns comparator
1840 0 no comparator found
1841 -1 incomplete comparator found
1844 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1846 struct String comparator_name;
1848 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1849 if (parse_white(filter)==-1) return -1;
1850 switch (parse_string(filter,&comparator_name))
1855 filter->errmsg=CUS "missing comparator";
1862 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1867 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1869 *c=COMP_EN_ASCII_CASEMAP;
1872 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1874 *c=COMP_EN_ASCII_CASEMAP;
1877 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1879 *c=COMP_ASCII_NUMERIC;
1884 filter->errmsg=CUS "invalid comparator";
1893 /*************************************************
1894 * Parse an optional match type *
1895 *************************************************/
1899 match-type = ":is" / ":contains" / ":matches"
1902 filter points to the Sieve filter including its state
1903 m returns match type
1906 0 no match type found
1909 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1911 if (parse_identifier(filter,CUS ":is")==1)
1916 else if (parse_identifier(filter,CUS ":contains")==1)
1921 else if (parse_identifier(filter,CUS ":matches")==1)
1930 /*************************************************
1931 * Parse and interpret an optional test list *
1932 *************************************************/
1936 test-list = "(" test *("," test) ")"
1939 filter points to the Sieve filter including its state
1940 n total number of tests
1941 num_true number of passed tests
1942 exec Execute parsed statements
1945 0 no test list found
1946 -1 syntax or execution error
1949 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1951 if (parse_white(filter)==-1) return -1;
1952 if (*filter->pc=='(')
1961 switch (parse_test(filter,&cond,exec))
1964 case 0: filter->errmsg=CUS "missing test"; return -1;
1965 default: ++*n; if (cond) ++*num_true; break;
1967 if (parse_white(filter)==-1) return -1;
1968 if (*filter->pc==',') ++filter->pc;
1971 if (*filter->pc==')')
1978 filter->errmsg=CUS "missing closing paren";
1986 /*************************************************
1987 * Parse and interpret an optional test *
1988 *************************************************/
1992 filter points to the Sieve filter including its state
1993 cond returned condition status
1994 exec Execute parsed statements
1998 -1 syntax or execution error
2001 static int parse_test(struct Sieve *filter, int *cond, int exec)
2003 if (parse_white(filter)==-1) return -1;
2004 if (parse_identifier(filter,CUS "address"))
2007 address-test = "address" { [address-part] [comparator] [match-type] }
2008 <header-list: string-list> <key-list: string-list>
2010 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2013 enum AddressPart addressPart=ADDRPART_ALL;
2014 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2015 enum MatchType matchType=MATCH_IS;
2016 struct String *hdr,*h,*key,*k;
2022 if (parse_white(filter)==-1) return -1;
2023 if ((m=parse_addresspart(filter,&addressPart))!=0)
2025 if (m==-1) return -1;
2028 filter->errmsg=CUS "address part already specified";
2033 else if ((m=parse_comparator(filter,&comparator))!=0)
2035 if (m==-1) return -1;
2038 filter->errmsg=CUS "comparator already specified";
2043 else if ((m=parse_matchtype(filter,&matchType))!=0)
2045 if (m==-1) return -1;
2048 filter->errmsg=CUS "match type already specified";
2055 if (parse_white(filter)==-1) return -1;
2056 if ((m=parse_stringlist(filter,&hdr))!=1)
2058 if (m==0) filter->errmsg=CUS "header string list expected";
2061 if (parse_white(filter)==-1) return -1;
2062 if ((m=parse_stringlist(filter,&key))!=1)
2064 if (m==0) filter->errmsg=CUS "key string list expected";
2068 for (h=hdr; h->length!=-1 && !*cond; ++h)
2070 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2074 !eq_asciicase(h,&str_from,0)
2075 && !eq_asciicase(h,&str_to,0)
2076 && !eq_asciicase(h,&str_cc,0)
2077 && !eq_asciicase(h,&str_bcc,0)
2078 && !eq_asciicase(h,&str_sender,0)
2079 && !eq_asciicase(h,&str_resent_from,0)
2080 && !eq_asciicase(h,&str_resent_to,0)
2083 filter->errmsg=CUS "invalid header field";
2088 /* We are only interested in addresses below, so no MIME decoding */
2089 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2090 if (header_value == NULL)
2092 filter->errmsg=CUS "header string expansion failed";
2095 parse_allow_group = TRUE;
2096 while (*header_value && !*cond)
2099 int start, end, domain;
2103 end_addr = parse_find_address_end(header_value, FALSE);
2104 saveend = *end_addr;
2106 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2108 if (extracted_addr) switch (addressPart)
2110 case ADDRPART_ALL: part=extracted_addr; break;
2114 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2115 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2117 case ADDRPART_DETAIL: part=NULL; break;
2121 *end_addr = saveend;
2124 for (k=key; k->length!=-1; ++k)
2126 struct String partStr;
2128 partStr.character=part;
2129 partStr.length=Ustrlen(part);
2132 *cond=compare(filter,k,&partStr,comparator,matchType);
2133 if (*cond==-1) return -1;
2138 if (saveend == 0) break;
2139 header_value = end_addr + 1;
2141 parse_allow_group = FALSE;
2142 parse_found_group = FALSE;
2147 else if (parse_identifier(filter,CUS "allof"))
2150 allof-test = "allof" <tests: test-list>
2155 switch (parse_testlist(filter,&n,&num_true,exec))
2158 case 0: filter->errmsg=CUS "missing test list"; return -1;
2159 default: *cond=(n==num_true); return 1;
2162 else if (parse_identifier(filter,CUS "anyof"))
2165 anyof-test = "anyof" <tests: test-list>
2170 switch (parse_testlist(filter,&n,&num_true,exec))
2173 case 0: filter->errmsg=CUS "missing test list"; return -1;
2174 default: *cond=(num_true>0); return 1;
2177 else if (parse_identifier(filter,CUS "exists"))
2180 exists-test = "exists" <header-names: string-list>
2183 struct String *hdr,*h;
2186 if (parse_white(filter)==-1) return -1;
2187 if ((m=parse_stringlist(filter,&hdr))!=1)
2189 if (m==0) filter->errmsg=CUS "header string list expected";
2195 for (h=hdr; h->length!=-1 && *cond; ++h)
2199 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2200 if (header_def == NULL)
2202 filter->errmsg=CUS "header string expansion failed";
2205 if (Ustrcmp(header_def,"false")==0) *cond=0;
2210 else if (parse_identifier(filter,CUS "false"))
2213 false-test = "false"
2219 else if (parse_identifier(filter,CUS "header"))
2222 header-test = "header" { [comparator] [match-type] }
2223 <header-names: string-list> <key-list: string-list>
2226 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2227 enum MatchType matchType=MATCH_IS;
2228 struct String *hdr,*h,*key,*k;
2234 if (parse_white(filter)==-1) return -1;
2235 if ((m=parse_comparator(filter,&comparator))!=0)
2237 if (m==-1) return -1;
2240 filter->errmsg=CUS "comparator already specified";
2245 else if ((m=parse_matchtype(filter,&matchType))!=0)
2247 if (m==-1) return -1;
2250 filter->errmsg=CUS "match type already specified";
2257 if (parse_white(filter)==-1) return -1;
2258 if ((m=parse_stringlist(filter,&hdr))!=1)
2260 if (m==0) filter->errmsg=CUS "header string list expected";
2263 if (parse_white(filter)==-1) return -1;
2264 if ((m=parse_stringlist(filter,&key))!=1)
2266 if (m==0) filter->errmsg=CUS "key string list expected";
2270 for (h=hdr; h->length!=-1 && !*cond; ++h)
2274 filter->errmsg=CUS "invalid header field";
2279 struct String header_value;
2282 expand_header(&header_value,h);
2283 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2284 if (header_value.character == NULL || header_def == NULL)
2286 filter->errmsg=CUS "header string expansion failed";
2289 for (k=key; k->length!=-1; ++k)
2291 if (Ustrcmp(header_def,"true")==0)
2293 *cond=compare(filter,k,&header_value,comparator,matchType);
2294 if (*cond==-1) return -1;
2302 else if (parse_identifier(filter,CUS "not"))
2304 if (parse_white(filter)==-1) return -1;
2305 switch (parse_test(filter,cond,exec))
2308 case 0: filter->errmsg=CUS "missing test"; return -1;
2309 default: *cond=!*cond; return 1;
2312 else if (parse_identifier(filter,CUS "size"))
2315 relop = ":over" / ":under"
2316 size-test = "size" relop <limit: number>
2319 unsigned long limit;
2322 if (parse_white(filter)==-1) return -1;
2323 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2324 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2327 filter->errmsg=CUS "missing :over or :under";
2330 if (parse_white(filter)==-1) return -1;
2331 if (parse_number(filter,&limit)==-1) return -1;
2332 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2335 else if (parse_identifier(filter,CUS "true"))
2340 else if (parse_identifier(filter,CUS "envelope"))
2343 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2344 <envelope-part: string-list> <key-list: string-list>
2346 envelope-part is case insensitive "from" or "to"
2347 #ifdef ENVELOPE_AUTH
2348 envelope-part =/ "auth"
2352 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2353 enum AddressPart addressPart=ADDRPART_ALL;
2354 enum MatchType matchType=MATCH_IS;
2355 struct String *env,*e,*key,*k;
2359 if (!filter->require_envelope)
2361 filter->errmsg=CUS "missing previous require \"envelope\";";
2366 if (parse_white(filter)==-1) return -1;
2367 if ((m=parse_comparator(filter,&comparator))!=0)
2369 if (m==-1) return -1;
2372 filter->errmsg=CUS "comparator already specified";
2377 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2379 if (m==-1) return -1;
2382 filter->errmsg=CUS "address part already specified";
2387 else if ((m=parse_matchtype(filter,&matchType))!=0)
2389 if (m==-1) return -1;
2392 filter->errmsg=CUS "match type already specified";
2399 if (parse_white(filter)==-1) return -1;
2400 if ((m=parse_stringlist(filter,&env))!=1)
2402 if (m==0) filter->errmsg=CUS "envelope string list expected";
2405 if (parse_white(filter)==-1) return -1;
2406 if ((m=parse_stringlist(filter,&key))!=1)
2408 if (m==0) filter->errmsg=CUS "key string list expected";
2412 for (e=env; e->length!=-1 && !*cond; ++e)
2414 const uschar *envelopeExpr=CUS 0;
2415 uschar *envelope=US 0;
2417 if (eq_asciicase(e,&str_from,0))
2419 switch (addressPart)
2421 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2425 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2426 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2428 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2432 else if (eq_asciicase(e,&str_to,0))
2434 switch (addressPart)
2436 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2438 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2439 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2441 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2442 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2445 #ifdef ENVELOPE_AUTH
2446 else if (eq_asciicase(e,&str_auth,0))
2448 switch (addressPart)
2450 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2454 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2455 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2457 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2464 filter->errmsg=CUS "invalid envelope string";
2467 if (exec && envelopeExpr)
2469 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2471 filter->errmsg=CUS "header string expansion failed";
2474 for (k=key; k->length!=-1; ++k)
2476 struct String envelopeStr;
2478 envelopeStr.character=envelope;
2479 envelopeStr.length=Ustrlen(envelope);
2480 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2481 if (*cond==-1) return -1;
2489 else if (parse_identifier(filter,CUS "valid_notify_method"))
2492 valid_notify_method = "valid_notify_method"
2493 <notification-uris: string-list>
2496 struct String *uris,*u;
2499 if (!filter->require_enotify)
2501 filter->errmsg=CUS "missing previous require \"enotify\";";
2504 if (parse_white(filter)==-1) return -1;
2505 if ((m=parse_stringlist(filter,&uris))!=1)
2507 if (m==0) filter->errmsg=CUS "URI string list expected";
2513 for (u=uris; u->length!=-1 && *cond; ++u)
2515 string_item *recipient;
2516 struct String header,subject,body;
2520 header.character=(uschar*)0;
2522 subject.character=(uschar*)0;
2524 body.character=(uschar*)0;
2525 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2531 else if (parse_identifier(filter,CUS "notify_method_capability"))
2534 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2535 <notification-uri: string>
2536 <notification-capability: string>
2537 <key-list: string-list>
2543 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2544 enum MatchType matchType=MATCH_IS;
2545 struct String uri,capa,*keys,*k;
2547 if (!filter->require_enotify)
2549 filter->errmsg=CUS "missing previous require \"enotify\";";
2554 if (parse_white(filter)==-1) return -1;
2555 if ((m=parse_comparator(filter,&comparator))!=0)
2557 if (m==-1) return -1;
2560 filter->errmsg=CUS "comparator already specified";
2565 else if ((m=parse_matchtype(filter,&matchType))!=0)
2567 if (m==-1) return -1;
2570 filter->errmsg=CUS "match type already specified";
2577 if ((m=parse_string(filter,&uri))!=1)
2579 if (m==0) filter->errmsg=CUS "missing notification URI string";
2582 if (parse_white(filter)==-1) return -1;
2583 if ((m=parse_string(filter,&capa))!=1)
2585 if (m==0) filter->errmsg=CUS "missing notification capability string";
2588 if (parse_white(filter)==-1) return -1;
2589 if ((m=parse_stringlist(filter,&keys))!=1)
2591 if (m==0) filter->errmsg=CUS "missing key string list";
2596 string_item *recipient;
2597 struct String header,subject,body;
2602 header.character=(uschar*)0;
2604 subject.character=(uschar*)0;
2606 body.character=(uschar*)0;
2607 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2609 if (eq_asciicase(&capa,&str_online,0)==1)
2610 for (k=keys; k->length!=-1; ++k)
2612 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2613 if (*cond==-1) return -1;
2625 /*************************************************
2626 * Parse and interpret an optional block *
2627 *************************************************/
2631 filter points to the Sieve filter including its state
2632 exec Execute parsed statements
2633 generated where to hang newly-generated addresses
2635 Returns: 2 success by stop
2637 0 no block command found
2638 -1 syntax or execution error
2641 static int parse_block(struct Sieve *filter, int exec,
2642 address_item **generated)
2646 if (parse_white(filter)==-1) return -1;
2647 if (*filter->pc=='{')
2650 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2651 if (*filter->pc=='}')
2658 filter->errmsg=CUS "expecting command or closing brace";
2666 /*************************************************
2667 * Match a semicolon *
2668 *************************************************/
2672 filter points to the Sieve filter including its state
2678 static int parse_semicolon(struct Sieve *filter)
2680 if (parse_white(filter)==-1) return -1;
2681 if (*filter->pc==';')
2688 filter->errmsg=CUS "missing semicolon";
2694 /*************************************************
2695 * Parse and interpret a Sieve command *
2696 *************************************************/
2700 filter points to the Sieve filter including its state
2701 exec Execute parsed statements
2702 generated where to hang newly-generated addresses
2704 Returns: 2 success by stop
2706 -1 syntax or execution error
2708 static int parse_commands(struct Sieve *filter, int exec,
2709 address_item **generated)
2713 if (parse_white(filter)==-1) return -1;
2714 if (parse_identifier(filter,CUS "if"))
2717 if-command = "if" test block *( "elsif" test block ) [ else block ]
2720 int cond,m,unsuccessful;
2723 if (parse_white(filter)==-1) return -1;
2724 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2727 filter->errmsg=CUS "missing test";
2730 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2731 (debug_selector & D_filter) != 0)
2733 if (exec) debug_printf("if %s\n",cond?"true":"false");
2735 m=parse_block(filter,exec ? cond : 0, generated);
2736 if (m==-1 || m==2) return m;
2739 filter->errmsg=CUS "missing block";
2742 unsuccessful = !cond;
2743 for (;;) /* elsif test block */
2745 if (parse_white(filter)==-1) return -1;
2746 if (parse_identifier(filter,CUS "elsif"))
2748 if (parse_white(filter)==-1) return -1;
2749 m=parse_test(filter,&cond,exec && unsuccessful);
2750 if (m==-1 || m==2) return m;
2753 filter->errmsg=CUS "missing test";
2756 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2757 (debug_selector & D_filter) != 0)
2759 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2761 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2762 if (m==-1 || m==2) return m;
2765 filter->errmsg=CUS "missing block";
2768 if (exec && unsuccessful && cond) unsuccessful = 0;
2773 if (parse_white(filter)==-1) return -1;
2774 if (parse_identifier(filter,CUS "else"))
2776 m=parse_block(filter,exec && unsuccessful, generated);
2777 if (m==-1 || m==2) return m;
2780 filter->errmsg=CUS "missing block";
2785 else if (parse_identifier(filter,CUS "stop"))
2788 stop-command = "stop" { stop-options } ";"
2792 if (parse_semicolon(filter)==-1) return -1;
2795 filter->pc+=Ustrlen(filter->pc);
2799 else if (parse_identifier(filter,CUS "keep"))
2802 keep-command = "keep" { keep-options } ";"
2806 if (parse_semicolon(filter)==-1) return -1;
2809 add_addr(generated,US"inbox",1,0,0,0);
2813 else if (parse_identifier(filter,CUS "discard"))
2816 discard-command = "discard" { discard-options } ";"
2820 if (parse_semicolon(filter)==-1) return -1;
2821 if (exec) filter->keep=0;
2823 else if (parse_identifier(filter,CUS "redirect"))
2826 redirect-command = "redirect" redirect-options "string" ";"
2828 redirect-options =) ":copy"
2831 struct String recipient;
2837 if (parse_white(filter)==-1) return -1;
2838 if (parse_identifier(filter,CUS ":copy")==1)
2840 if (!filter->require_copy)
2842 filter->errmsg=CUS "missing previous require \"copy\";";
2849 if (parse_white(filter)==-1) return -1;
2850 if ((m=parse_string(filter,&recipient))!=1)
2852 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2855 if (strchr(CCS recipient.character,'@')==(char*)0)
2857 filter->errmsg=CUS "unqualified recipient address";
2862 add_addr(generated,recipient.character,0,0,0,0);
2863 if (!copy) filter->keep = 0;
2865 if (parse_semicolon(filter)==-1) return -1;
2867 else if (parse_identifier(filter,CUS "fileinto"))
2870 fileinto-command = "fileinto" { fileinto-options } string ";"
2872 fileinto-options =) [ ":copy" ]
2875 struct String folder;
2878 unsigned long maxage, maxmessages, maxstorage;
2881 maxage = maxmessages = maxstorage = 0;
2882 if (!filter->require_fileinto)
2884 filter->errmsg=CUS "missing previous require \"fileinto\";";
2889 if (parse_white(filter)==-1) return -1;
2890 if (parse_identifier(filter,CUS ":copy")==1)
2892 if (!filter->require_copy)
2894 filter->errmsg=CUS "missing previous require \"copy\";";
2901 if (parse_white(filter)==-1) return -1;
2902 if ((m=parse_string(filter,&folder))!=1)
2904 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2907 m=0; s=folder.character;
2908 if (folder.length==0) m=1;
2909 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2912 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2917 filter->errmsg=CUS "invalid folder";
2922 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2923 if (!copy) filter->keep = 0;
2925 if (parse_semicolon(filter)==-1) return -1;
2928 else if (parse_identifier(filter,CUS "notify"))
2931 notify-command = "notify" { notify-options } <method: string> ";"
2932 notify-options = [":from" string]
2933 [":importance" <"1" / "2" / "3">]
2934 [":options" 1*(string-list / number)]
2940 struct String importance;
2941 struct String *options;
2942 struct String message;
2943 struct String method;
2944 struct Notification *already;
2945 string_item *recipient;
2946 struct String header;
2947 struct String subject;
2949 uschar *envelope_from;
2950 struct String auto_submitted_value;
2951 uschar *auto_submitted_def;
2953 if (!filter->require_enotify)
2955 filter->errmsg=CUS "missing previous require \"enotify\";";
2958 from.character=(uschar*)0;
2960 importance.character=(uschar*)0;
2961 importance.length=-1;
2962 options=(struct String*)0;
2963 message.character=(uschar*)0;
2967 header.character=(uschar*)0;
2969 subject.character=(uschar*)0;
2971 body.character=(uschar*)0;
2972 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2975 if (parse_white(filter)==-1) return -1;
2976 if (parse_identifier(filter,CUS ":from")==1)
2978 if (parse_white(filter)==-1) return -1;
2979 if ((m=parse_string(filter,&from))!=1)
2981 if (m==0) filter->errmsg=CUS "from string expected";
2985 else if (parse_identifier(filter,CUS ":importance")==1)
2987 if (parse_white(filter)==-1) return -1;
2988 if ((m=parse_string(filter,&importance))!=1)
2990 if (m==0) filter->errmsg=CUS "importance string expected";
2993 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2995 filter->errmsg=CUS "invalid importance";
2999 else if (parse_identifier(filter,CUS ":options")==1)
3001 if (parse_white(filter)==-1) return -1;
3003 else if (parse_identifier(filter,CUS ":message")==1)
3005 if (parse_white(filter)==-1) return -1;
3006 if ((m=parse_string(filter,&message))!=1)
3008 if (m==0) filter->errmsg=CUS "message string expected";
3014 if (parse_white(filter)==-1) return -1;
3015 if ((m=parse_string(filter,&method))!=1)
3017 if (m==0) filter->errmsg=CUS "missing method string";
3020 if (parse_semicolon(filter)==-1) return -1;
3021 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3025 if (message.length==-1) message=subject;
3026 if (message.length==-1) expand_header(&message,&str_subject);
3027 expand_header(&auto_submitted_value,&str_auto_submitted);
3028 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3029 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3031 filter->errmsg=CUS "header string expansion failed";
3034 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3036 for (already=filter->notified; already; already=already->next)
3038 if (already->method.length==method.length
3039 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3040 && already->importance.length==importance.length
3041 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3042 && already->message.length==message.length
3043 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3046 if (already==(struct Notification*)0)
3047 /* New notification, process it */
3049 struct Notification *sent;
3050 sent=store_get(sizeof(struct Notification));
3051 sent->method=method;
3052 sent->importance=importance;
3053 sent->message=message;
3054 sent->next=filter->notified;
3055 filter->notified=sent;
3056 #ifndef COMPILE_SYNTAX_CHECKER
3057 if (filter_test == FTEST_NONE)
3062 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3066 int buffer_capacity;
3068 f = fdopen(fd, "wb");
3069 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3070 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3071 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3072 if (header.length>0) fprintf(f,"%s",header.character);
3073 if (message.length==-1)
3075 message.character=US"Notification";
3076 message.length=Ustrlen(message.character);
3078 /* Allocation is larger than neccessary, but enough even for split MIME words */
3079 buffer_capacity=32+4*message.length;
3080 buffer=store_get(buffer_capacity);
3081 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3083 if (body.length>0) fprintf(f,"%s\n",body.character);
3086 (void)child_close(pid, 0);
3089 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3091 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3097 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3099 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3105 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3107 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3114 else if (parse_identifier(filter,CUS "vacation"))
3117 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3118 vacation-options = [":days" number]
3121 [":addresses" string-list]
3128 struct String subject;
3130 struct String *addresses;
3132 string_item *aliases;
3133 struct String handle;
3134 struct String reason;
3136 if (!filter->require_vacation)
3138 filter->errmsg=CUS "missing previous require \"vacation\";";
3143 if (filter->vacation_ran)
3145 filter->errmsg=CUS "trying to execute vacation more than once";
3148 filter->vacation_ran=1;
3150 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3151 subject.character=(uschar*)0;
3153 from.character=(uschar*)0;
3155 addresses=(struct String*)0;
3158 handle.character=(uschar*)0;
3162 if (parse_white(filter)==-1) return -1;
3163 if (parse_identifier(filter,CUS ":days")==1)
3165 if (parse_white(filter)==-1) return -1;
3166 if (parse_number(filter,&days)==-1) return -1;
3167 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3168 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3170 else if (parse_identifier(filter,CUS ":subject")==1)
3172 if (parse_white(filter)==-1) return -1;
3173 if ((m=parse_string(filter,&subject))!=1)
3175 if (m==0) filter->errmsg=CUS "subject string expected";
3179 else if (parse_identifier(filter,CUS ":from")==1)
3181 if (parse_white(filter)==-1) return -1;
3182 if ((m=parse_string(filter,&from))!=1)
3184 if (m==0) filter->errmsg=CUS "from string expected";
3187 if (check_mail_address(filter,&from)!=1)
3190 else if (parse_identifier(filter,CUS ":addresses")==1)
3194 if (parse_white(filter)==-1) return -1;
3195 if ((m=parse_stringlist(filter,&addresses))!=1)
3197 if (m==0) filter->errmsg=CUS "addresses string list expected";
3200 for (a=addresses; a->length!=-1; ++a)
3204 new=store_get(sizeof(string_item));
3205 new->text=store_get(a->length+1);
3206 if (a->length) memcpy(new->text,a->character,a->length);
3207 new->text[a->length]='\0';
3212 else if (parse_identifier(filter,CUS ":mime")==1)
3214 else if (parse_identifier(filter,CUS ":handle")==1)
3216 if (parse_white(filter)==-1) return -1;
3217 if ((m=parse_string(filter,&from))!=1)
3219 if (m==0) filter->errmsg=CUS "handle string expected";
3225 if (parse_white(filter)==-1) return -1;
3226 if ((m=parse_string(filter,&reason))!=1)
3228 if (m==0) filter->errmsg=CUS "missing reason string";
3235 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3238 filter->errmsg=CUS "MIME reason string contains 8bit text";
3242 if (parse_semicolon(filter)==-1) return -1;
3249 int buffer_capacity;
3253 uschar hexdigest[33];
3257 if (filter_personal(aliases,TRUE))
3259 if (filter_test == FTEST_NONE)
3261 /* ensure oncelog directory exists; failure will be detected later */
3263 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3265 /* build oncelog filename */
3267 key.character=(uschar*)0;
3270 if (handle.length==-1)
3272 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3273 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3274 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3275 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3280 md5_end(&base, key.character, key.length, digest);
3281 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3282 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3284 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3286 if (filter_test == FTEST_NONE)
3288 capacity=Ustrlen(filter->vacation_directory);
3290 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3291 once=string_cat(once,&capacity,&start,hexdigest,33);
3294 /* process subject */
3296 if (subject.length==-1)
3298 uschar *subject_def;
3300 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3301 if (Ustrcmp(subject_def,"true")==0)
3303 expand_header(&subject,&str_subject);
3306 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3307 subject.length=start;
3311 subject.character=US"Automated reply";
3312 subject.length=Ustrlen(subject.character);
3316 /* add address to list of generated addresses */
3318 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3319 setflag(addr, af_pfr);
3320 setflag(addr, af_ignore_error);
3321 addr->next = *generated;
3323 addr->reply = store_get(sizeof(reply_item));
3324 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3325 addr->reply->to = string_copy(sender_address);
3326 if (from.length==-1)
3327 addr->reply->from = expand_string(US"$local_part@$domain");
3329 addr->reply->from = from.character;
3330 /* Allocation is larger than neccessary, but enough even for split MIME words */
3331 buffer_capacity=32+4*subject.length;
3332 buffer=store_get(buffer_capacity);
3333 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3334 addr->reply->oncelog=once;
3335 addr->reply->once_repeat=days*86400;
3337 /* build body and MIME headers */
3341 uschar *mime_body,*reason_end;
3342 static const uschar nlnl[]="\r\n\r\n";
3346 mime_body=reason.character,reason_end=reason.character+reason.length;
3347 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3352 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3353 addr->reply->headers[start] = '\0';
3356 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3357 else mime_body=reason_end-1;
3358 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3359 addr->reply->text[start] = '\0';
3363 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3366 start = reason.length;
3367 addr->reply->headers = US"MIME-Version: 1.0\n"
3368 "Content-Type: text/plain;\n"
3369 "\tcharset=\"utf-8\"\n"
3370 "Content-Transfer-Encoding: quoted-printable";
3371 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3375 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3377 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3388 /*************************************************
3389 * Parse and interpret a sieve filter *
3390 *************************************************/
3394 filter points to the Sieve filter including its state
3395 exec Execute parsed statements
3396 generated where to hang newly-generated addresses
3399 -1 syntax or execution error
3402 static int parse_start(struct Sieve *filter, int exec,
3403 address_item **generated)
3405 filter->pc=filter->filter;
3408 filter->require_envelope=0;
3409 filter->require_fileinto=0;
3410 #ifdef ENCODED_CHARACTER
3411 filter->require_encoded_character=0;
3413 #ifdef ENVELOPE_AUTH
3414 filter->require_envelope_auth=0;
3417 filter->require_enotify=0;
3418 filter->notified=(struct Notification*)0;
3421 filter->require_subaddress=0;
3424 filter->require_vacation=0;
3425 filter->vacation_ran=0;
3427 filter->require_copy=0;
3428 filter->require_iascii_numeric=0;
3430 if (parse_white(filter)==-1) return -1;
3432 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3435 struct dirent *oncelog;
3436 struct stat properties;
3439 /* clean up old vacation log databases */
3441 oncelogdir=opendir(CS filter->vacation_directory);
3443 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3445 filter->errmsg=CUS "unable to open vacation directory";
3449 if (oncelogdir != NULL)
3453 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3455 if (strlen(oncelog->d_name)==32)
3457 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3458 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3462 closedir(oncelogdir);
3466 while (parse_identifier(filter,CUS "require"))
3469 require-command = "require" <capabilities: string-list>
3472 struct String *cap,*check;
3475 if (parse_white(filter)==-1) return -1;
3476 if ((m=parse_stringlist(filter,&cap))!=1)
3478 if (m==0) filter->errmsg=CUS "capability string list expected";
3481 for (check=cap; check->character; ++check)
3483 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3484 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3485 #ifdef ENCODED_CHARACTER
3486 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3488 #ifdef ENVELOPE_AUTH
3489 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3492 else if (eq_octet(check,&str_enotify,0))
3494 if (filter->enotify_mailto_owner == NULL)
3496 filter->errmsg=CUS "enotify disabled";
3499 filter->require_enotify=1;
3503 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3506 else if (eq_octet(check,&str_vacation,0))
3508 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3510 filter->errmsg=CUS "vacation disabled";
3513 filter->require_vacation=1;
3516 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3517 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3518 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3519 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3520 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3523 filter->errmsg=CUS "unknown capability";
3527 if (parse_semicolon(filter)==-1) return -1;
3529 if (parse_commands(filter,exec,generated)==-1) return -1;
3532 filter->errmsg=CUS "syntax error";
3539 /*************************************************
3540 * Interpret a sieve filter file *
3541 *************************************************/
3545 filter points to the entire file, read into store as a single string
3546 options controls whether various special things are allowed, and requests
3547 special actions (not currently used)
3548 vacation_directory where to store vacation "once" files
3549 enotify_mailto_owner owner of mailto notifications
3550 useraddress string expression for :user part of address
3551 subaddress string expression for :subaddress part of address
3552 generated where to hang newly-generated addresses
3553 error where to pass back an error text
3555 Returns: FF_DELIVERED success, a significant action was taken
3556 FF_NOTDELIVERED success, no significant action
3557 FF_DEFER defer requested
3558 FF_FAIL fail requested
3559 FF_FREEZE freeze requested
3560 FF_ERROR there was a problem
3564 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3565 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3566 address_item **generated, uschar **error)
3572 options = options; /* Keep picky compilers happy */
3575 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3576 sieve.filter=filter;
3578 if (vacation_directory == NULL)
3579 sieve.vacation_directory = NULL;
3582 sieve.vacation_directory=expand_string(vacation_directory);
3583 if (sieve.vacation_directory == NULL)
3585 *error = string_sprintf("failed to expand \"%s\" "
3586 "(sieve_vacation_directory): %s", vacation_directory,
3587 expand_string_message);
3592 if (enotify_mailto_owner == NULL)
3593 sieve.enotify_mailto_owner = NULL;
3596 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3597 if (sieve.enotify_mailto_owner == NULL)
3599 *error = string_sprintf("failed to expand \"%s\" "
3600 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3601 expand_string_message);
3606 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3607 sieve.subaddress = subaddress;
3609 #ifdef COMPILE_SYNTAX_CHECKER
3610 if (parse_start(&sieve,0,generated)==1)
3612 if (parse_start(&sieve,1,generated)==1)
3617 add_addr(generated,US"inbox",1,0,0,0);
3618 msg = string_sprintf("Implicit keep");
3623 msg = string_sprintf("No implicit keep");
3629 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3630 #ifdef COMPILE_SYNTAX_CHECKER
3634 add_addr(generated,US"inbox",1,0,0,0);
3639 #ifndef COMPILE_SYNTAX_CHECKER
3640 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3641 else debug_printf("%s\n", msg);
3644 DEBUG(D_route) debug_printf("Sieve: end of processing\n");