1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2016 - 2018
7 * See the file NOTICE for conditions of use and distribution.
10 /* This code was contributed by Michael Haardt. */
13 /* Sieve mail filter. */
27 /* Define this for RFC compliant \r\n end-of-line terminators. */
28 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 /* Define this for development of the Sieve extension "encoded-character". */
32 #define ENCODED_CHARACTER
34 /* Define this for development of the Sieve extension "envelope-auth". */
37 /* Define this for development of the Sieve extension "enotify". */
40 /* Define this for the Sieve extension "subaddress". */
43 /* Define this for the Sieve extension "vacation". */
47 #define VACATION_MIN_DAYS 1
48 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
49 #define VACATION_MAX_DAYS 31
51 /* Keep this at 75 to accept only RFC compliant MIME words. */
52 /* Increase it if you want to match headers from buggy MUAs. */
53 #define MIMEWORD_LENGTH 75
64 #ifdef ENCODED_CHARACTER
65 int require_encoded_character;
68 int require_envelope_auth;
72 struct Notification *notified;
74 uschar *enotify_mailto_owner;
76 int require_subaddress;
82 uschar *vacation_directory;
83 const uschar *subaddress;
84 const uschar *useraddress;
86 int require_iascii_numeric;
89 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
90 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
92 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
96 enum RelOp { LT, LE, EQ, GE, GT, NE };
106 struct String method;
107 struct String importance;
108 struct String message;
109 struct Notification *next;
112 /* This should be a complete list of supported extensions, so that an external
113 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
114 list of extensions and provide correct information to a client.
116 We'll emit the list in the order given here; keep it alphabetically sorted, so
117 that callers don't get surprised.
119 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
121 const uschar *exim_sieve_extension_list[] = {
122 CUS"comparator-i;ascii-numeric",
124 #ifdef ENCODED_CHARACTER
125 CUS"encoded-character",
144 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
145 static int parse_test(struct Sieve *filter, int *cond, int exec);
146 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
148 static uschar str_from_c[]="From";
149 static const struct String str_from={ str_from_c, 4 };
150 static uschar str_to_c[]="To";
151 static const struct String str_to={ str_to_c, 2 };
152 static uschar str_cc_c[]="Cc";
153 static const struct String str_cc={ str_cc_c, 2 };
154 static uschar str_bcc_c[]="Bcc";
155 static const struct String str_bcc={ str_bcc_c, 3 };
157 static uschar str_auth_c[]="auth";
158 static const struct String str_auth={ str_auth_c, 4 };
160 static uschar str_sender_c[]="Sender";
161 static const struct String str_sender={ str_sender_c, 6 };
162 static uschar str_resent_from_c[]="Resent-From";
163 static const struct String str_resent_from={ str_resent_from_c, 11 };
164 static uschar str_resent_to_c[]="Resent-To";
165 static const struct String str_resent_to={ str_resent_to_c, 9 };
166 static uschar str_fileinto_c[]="fileinto";
167 static const struct String str_fileinto={ str_fileinto_c, 8 };
168 static uschar str_envelope_c[]="envelope";
169 static const struct String str_envelope={ str_envelope_c, 8 };
170 #ifdef ENCODED_CHARACTER
171 static uschar str_encoded_character_c[]="encoded-character";
172 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
175 static uschar str_envelope_auth_c[]="envelope-auth";
176 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
179 static uschar str_enotify_c[]="enotify";
180 static const struct String str_enotify={ str_enotify_c, 7 };
181 static uschar str_online_c[]="online";
182 static const struct String str_online={ str_online_c, 6 };
183 static uschar str_maybe_c[]="maybe";
184 static const struct String str_maybe={ str_maybe_c, 5 };
185 static uschar str_auto_submitted_c[]="Auto-Submitted";
186 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
189 static uschar str_subaddress_c[]="subaddress";
190 static const struct String str_subaddress={ str_subaddress_c, 10 };
193 static uschar str_vacation_c[]="vacation";
194 static const struct String str_vacation={ str_vacation_c, 8 };
195 static uschar str_subject_c[]="Subject";
196 static const struct String str_subject={ str_subject_c, 7 };
198 static uschar str_copy_c[]="copy";
199 static const struct String str_copy={ str_copy_c, 4 };
200 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
201 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
202 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
203 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
204 static uschar str_ioctet_c[]="i;octet";
205 static const struct String str_ioctet={ str_ioctet_c, 7 };
206 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
207 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
208 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
209 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
210 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
211 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
212 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
213 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
214 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
215 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
218 /*************************************************
219 * Encode to quoted-printable *
220 *************************************************/
231 static struct String *
232 quoted_printable_encode(const struct String *src, struct String *dst)
238 /* Two passes: one to count output allocation size, second
239 to do the encoding */
241 for (int pass = 0; pass <= 1; pass++)
248 dst->character=store_get(dst->length+1); /* plus one for \0 */
251 for (const uschar * start = src->character, * end = start + src->length;
252 start < end; ++start)
255 if (line>=73) /* line length limit */
261 *new++='='; /* line split */
266 if ( (ch>='!' && ch<='<')
267 || (ch>='>' && ch<='~')
268 || ( (ch=='\t' || ch==' ')
270 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
277 *new++=*start; /* copy char */
280 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
285 *new++='\n'; /* NL */
287 ++start; /* consume extra input char */
295 new += sprintf(CS new,"=%02X",ch);
301 *new='\0'; /* not included in length, but nice */
306 /*************************************************
307 * Check mail address for correct syntax *
308 *************************************************/
311 Check mail address for being syntactically correct.
314 filter points to the Sieve filter including its state
315 address String containing one address
318 1 Mail address is syntactically OK
322 int check_mail_address(struct Sieve *filter, const struct String *address)
324 int start, end, domain;
327 if (address->length>0)
329 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
333 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
334 address->character, error);
342 filter->errmsg=CUS "empty address";
348 /*************************************************
349 * Decode URI encoded string *
350 *************************************************/
354 str URI encoded string
357 0 Decoding successful
363 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; )
371 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
373 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
374 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
383 str->length=t-str->character;
388 /*************************************************
390 *************************************************/
395 mailtoURI = "mailto:" [ to ] [ headers ]
396 to = [ addr-spec *("%2C" addr-spec ) ]
397 headers = "?" header *( "&" header )
398 header = hname "=" hvalue
403 filter points to the Sieve filter including its state
404 uri URI, excluding scheme
409 1 URI is syntactically OK
415 parse_mailto_uri(struct Sieve *filter, const uschar *uri,
416 string_item **recipient, struct String *header, struct String *subject,
420 struct String to, hname;
421 struct String hvalue = {.character = NULL, .length = 0};
424 if (Ustrncmp(uri,"mailto:",7))
426 filter->errmsg=US "Unknown URI scheme";
431 if (*uri && *uri!='?')
435 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
438 gstring * g = string_catn(NULL, start, uri-start);
440 to.character = string_from_gstring(g);
442 if (uri_decode(&to)==-1)
444 filter->errmsg=US"Invalid URI encoding";
447 new=store_get(sizeof(string_item));
448 new->text=store_get(to.length+1);
449 if (to.length) memcpy(new->text,to.character,to.length);
450 new->text[to.length]='\0';
451 new->next=*recipient;
456 filter->errmsg=US"Missing addr-spec in URI";
459 if (*uri=='%') uri+=3;
468 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
471 gstring * g = string_catn(NULL, start, uri-start);
473 hname.character = string_from_gstring(g);
474 hname.length = g->ptr;
475 if (uri_decode(&hname)==-1)
477 filter->errmsg=US"Invalid URI encoding";
486 filter->errmsg=US"Missing equal after hname";
490 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
493 gstring * g = string_catn(NULL, start, uri-start);
495 hname.character = string_from_gstring(g);
496 hname.length = g->ptr;
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);
534 if (header->length==-1) header->length = 0;
536 g = string_catn(NULL, header->character, header->length);
537 g = string_catn(g, hname.character, hname.length);
538 g = string_catn(g, CUS ": ", 2);
539 g = string_catn(g, hvalue.character, hvalue.length);
540 g = string_catn(g, CUS "\n", 1);
542 header->character = string_from_gstring(g);
543 header->length = g->ptr;
546 if (*uri=='&') ++uri;
552 filter->errmsg=US"Syntactically invalid URI";
560 /*************************************************
561 * Octet-wise string comparison *
562 *************************************************/
566 needle UTF-8 string to search ...
567 haystack ... inside the haystack
568 match_prefix 1 to compare if needle is a prefix of haystack
570 Returns: 0 needle not found in haystack
574 static int eq_octet(const struct String *needle,
575 const struct String *haystack, int match_prefix)
583 h=haystack->character;
587 if (*n&0x80) return 0;
588 if (*h&0x80) return 0;
590 if (*n!=*h) return 0;
596 return (match_prefix ? nl==0 : nl==0 && hl==0);
600 /*************************************************
601 * ASCII case-insensitive string comparison *
602 *************************************************/
606 needle UTF-8 string to search ...
607 haystack ... inside the haystack
608 match_prefix 1 to compare if needle is a prefix of haystack
610 Returns: 0 needle not found in haystack
614 static int eq_asciicase(const struct String *needle,
615 const struct String *haystack, int match_prefix)
624 h=haystack->character;
630 if (nc&0x80) return 0;
631 if (hc&0x80) return 0;
633 /* tolower depends on the locale and only ASCII case must be insensitive */
634 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
640 return (match_prefix ? nl==0 : nl==0 && hl==0);
644 /*************************************************
645 * Glob pattern search *
646 *************************************************/
650 needle pattern to search ...
651 haystack ... inside the haystack
652 ascii_caseless ignore ASCII case
653 match_octet match octets, not UTF-8 multi-octet characters
655 Returns: 0 needle not found in haystack
660 static int eq_glob(const struct String *needle,
661 const struct String *haystack, int ascii_caseless, int match_octet)
663 const uschar *n,*h,*nend,*hend;
667 h=haystack->character;
668 nend=n+needle->length;
669 hend=h+haystack->length;
679 const uschar *npart,*hpart;
681 /* Try to match a non-star part of the needle at the current */
682 /* position in the haystack. */
686 while (npart<nend && *npart!='*') switch (*npart)
690 if (hpart==hend) return 0;
695 /* Match one UTF8 encoded character */
696 if ((*hpart&0xc0)==0xc0)
699 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
710 if (npart==nend) return -1;
715 if (hpart==hend) return 0;
716 /* tolower depends on the locale, but we need ASCII */
720 (*hpart&0x80) || (*npart&0x80) ||
723 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
728 /* string match after a star failed, advance and try again */
742 /* at this point, a part was matched successfully */
743 if (may_advance && npart==nend && hpart<hend)
744 /* needle ends, but haystack does not: if there was a star before, advance and try again */
754 return (h==hend ? 1 : may_advance);
758 /*************************************************
759 * ASCII numeric comparison *
760 *************************************************/
764 a first numeric string
765 b second numeric string
766 relop relational operator
768 Returns: 0 not (a relop b)
772 static int eq_asciinumeric(const struct String *a,
773 const struct String *b, enum RelOp relop)
776 const uschar *as,*aend,*bs,*bend;
780 aend=a->character+a->length;
782 bend=b->character+b->length;
784 while (*as>='0' && *as<='9' && as<aend) ++as;
786 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
789 if (al && bl==0) cmp=-1;
790 else if (al==0 && bl==0) cmp=0;
791 else if (al==0 && bl) cmp=1;
795 if (cmp==0) cmp=memcmp(a->character,b->character,al);
799 case LT: return cmp<0;
800 case LE: return cmp<=0;
801 case EQ: return cmp==0;
802 case GE: return cmp>=0;
803 case GT: return cmp>0;
804 case NE: return cmp!=0;
811 /*************************************************
813 *************************************************/
817 filter points to the Sieve filter including its state
818 needle UTF-8 pattern or string to search ...
819 haystack ... inside the haystack
823 Returns: 0 needle not found in haystack
825 -1 comparator does not offer matchtype
828 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
829 enum Comparator co, enum MatchType mt)
833 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
834 (debug_selector & D_filter) != 0)
836 debug_printf("String comparison (match ");
839 case MATCH_IS: debug_printf(":is"); break;
840 case MATCH_CONTAINS: debug_printf(":contains"); break;
841 case MATCH_MATCHES: debug_printf(":matches"); break;
843 debug_printf(", comparison \"");
846 case COMP_OCTET: debug_printf("i;octet"); break;
847 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
848 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
850 debug_printf("\"):\n");
851 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
852 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
860 if (eq_octet(needle,haystack,0)) r=1;
862 case COMP_EN_ASCII_CASEMAP:
863 if (eq_asciicase(needle,haystack,0)) r=1;
865 case COMP_ASCII_NUMERIC:
866 if (!filter->require_iascii_numeric)
868 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
871 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
883 for (h = *haystack; h.length; ++h.character,--h.length)
884 if (eq_octet(needle,&h,1)) { r=1; break; }
886 case COMP_EN_ASCII_CASEMAP:
887 for (h = *haystack; h.length; ++h.character, --h.length)
888 if (eq_asciicase(needle,&h,1)) { r=1; break; }
891 filter->errmsg=CUS "comparator does not offer specified matchtype";
901 if ((r=eq_glob(needle,haystack,0,1))==-1)
903 filter->errmsg=CUS "syntactically invalid pattern";
907 case COMP_EN_ASCII_CASEMAP:
908 if ((r=eq_glob(needle,haystack,1,1))==-1)
910 filter->errmsg=CUS "syntactically invalid pattern";
915 filter->errmsg=CUS "comparator does not offer specified matchtype";
920 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
921 (debug_selector & D_filter) != 0)
922 debug_printf(" Result %s\n",r?"true":"false");
927 /*************************************************
928 * Check header field syntax *
929 *************************************************/
932 RFC 2822, section 3.6.8 says:
936 ftext = %d33-57 / ; Any character except
937 %d59-126 ; controls, SP, and
940 That forbids 8-bit header fields. This implementation accepts them, since
941 all of Exim is 8-bit clean, so it adds %d128-%d255.
944 header header field to quote for suitable use in Exim expansions
946 Returns: 0 string is not a valid header field
947 1 string is a value header field
950 static int is_header(const struct String *header)
960 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
971 /*************************************************
972 * Quote special characters string *
973 *************************************************/
977 header header field to quote for suitable use in Exim expansions
980 Returns: quoted string
983 static const uschar *
984 quote(const struct String *header)
986 gstring * quoted = NULL;
997 quoted = string_catn(quoted, CUS "\\0", 2);
1002 quoted = string_catn(quoted, CUS "\\", 1);
1004 quoted = string_catn(quoted, h, 1);
1009 quoted = string_catn(quoted, CUS "", 1);
1010 return string_from_gstring(quoted);
1014 /*************************************************
1015 * Add address to list of generated addresses *
1016 *************************************************/
1019 According to RFC 5228, duplicate delivery to the same address must
1020 not happen, so the list is first searched for the address.
1023 generated list of generated addresses
1024 addr new address to add
1025 file address denotes a file
1031 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1033 address_item *new_addr;
1035 for (new_addr = *generated; new_addr; new_addr = new_addr->next)
1036 if ( Ustrcmp(new_addr->address,addr) == 0
1038 || testflag(new_addr, af_pfr)
1039 || testflag(new_addr, af_file)
1043 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1044 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1049 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1050 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1052 new_addr = deliver_make_addr(addr,TRUE);
1055 setflag(new_addr, af_pfr);
1056 setflag(new_addr, af_file);
1059 new_addr->prop.errors_address = NULL;
1060 new_addr->next = *generated;
1061 *generated = new_addr;
1065 /*************************************************
1066 * Return decoded header field *
1067 *************************************************/
1070 Unfold the header field as described in RFC 2822 and remove all
1071 leading and trailing white space, then perform MIME decoding and
1072 translate the header field to UTF-8.
1075 value returned value of the field
1076 header name of the header field
1078 Returns: nothing The expanded string is empty
1079 in case there is no such header
1082 static void expand_header(struct String *value, const struct String *header)
1088 value->character=(uschar*)0;
1090 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1091 while (*r==' ' || *r=='\t') ++r;
1099 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1101 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1105 /*************************************************
1106 * Parse remaining hash comment *
1107 *************************************************/
1111 Comment up to terminating CRLF
1114 filter points to the Sieve filter including its state
1120 static int parse_hashcomment(struct Sieve *filter)
1126 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1128 if (*filter->pc=='\n')
1141 filter->errmsg=CUS "missing end of comment";
1146 /*************************************************
1147 * Parse remaining C-style comment *
1148 *************************************************/
1152 Everything up to star slash
1155 filter points to the Sieve filter including its state
1161 static int parse_comment(struct Sieve *filter)
1166 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1173 filter->errmsg=CUS "missing end of comment";
1178 /*************************************************
1179 * Parse optional white space *
1180 *************************************************/
1184 Spaces, tabs, CRLFs, hash comments or C-style comments
1187 filter points to the Sieve filter including its state
1193 static int parse_white(struct Sieve *filter)
1197 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1199 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1201 else if (*filter->pc=='\n')
1211 else if (*filter->pc=='#')
1213 if (parse_hashcomment(filter)==-1) return -1;
1215 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1217 if (parse_comment(filter)==-1) return -1;
1225 #ifdef ENCODED_CHARACTER
1226 /*************************************************
1227 * Decode hex-encoded-character string *
1228 *************************************************/
1231 Encoding definition:
1232 blank = SP / TAB / CRLF
1233 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1234 hex-pair = 1*2HEXDIG
1237 src points to a hex-pair-seq
1238 end points to its end
1239 dst points to the destination of the decoded octets,
1240 optionally to (uschar*)0 for checking only
1242 Returns: >=0 number of decoded octets
1246 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1250 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1256 d<2 && src<end && isxdigit(n=tolower(*src));
1257 x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ;
1258 if (d==0) return -1;
1261 if (src==end) return decoded;
1262 if (*src==' ' || *src=='\t' || *src=='\n')
1263 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1272 /*************************************************
1273 * Decode unicode-encoded-character string *
1274 *************************************************/
1277 Encoding definition:
1278 blank = SP / TAB / CRLF
1279 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1280 unicode-hex = 1*HEXDIG
1282 It is an error for a script to use a hexadecimal value that isn't in
1283 either the range 0 to D7FF or the range E000 to 10FFFF.
1285 At this time, strings are already scanned, thus the CRLF is converted
1286 to the internally used \n (should RFC_EOL have been used).
1289 src points to a unicode-hex-seq
1290 end points to its end
1291 dst points to the destination of the decoded octets,
1292 optionally to (uschar*)0 for checking only
1294 Returns: >=0 number of decoded octets
1296 -2 semantic error (character range violation)
1300 unicode_decode(uschar *src, uschar *end, uschar *dst)
1304 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1311 for (hex_seq = src; src < end && *src=='0'; ) src++;
1313 d < 7 && src < end && isxdigit(n=tolower(*src));
1314 c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1315 if (src == hex_seq) return -1;
1316 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1322 else if (c>=0x80 && c<=0x7ff)
1327 *dst++=128+(c&0x3f);
1331 else if (c>=0x800 && c<=0xffff)
1336 *dst++=128+((c>>6)&0x3f);
1337 *dst++=128+(c&0x3f);
1341 else if (c>=0x10000 && c<=0x1fffff)
1346 *dst++=128+((c>>10)&0x3f);
1347 *dst++=128+((c>>6)&0x3f);
1348 *dst++=128+(c&0x3f);
1352 if (*src==' ' || *src=='\t' || *src=='\n')
1354 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1355 if (src==end) return decoded;
1364 /*************************************************
1365 * Decode encoded-character string *
1366 *************************************************/
1369 Encoding definition:
1370 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1371 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1374 encoded points to an encoded string, returns decoded string
1375 filter points to the Sieve filter including its state
1381 static int string_decode(struct Sieve *filter, struct String *data)
1383 uschar *src,*dst,*end;
1385 src=data->character;
1387 end=data->character+data->length;
1393 strncmpic(src,US "${hex:",6)==0
1394 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1395 && (hex_decode(src+6,brace,(uschar*)0))>=0
1398 dst+=hex_decode(src+6,brace,dst);
1402 strncmpic(src,US "${unicode:",10)==0
1403 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1406 switch (unicode_decode(src+10,brace,(uschar*)0))
1410 filter->errmsg=CUS "unicode character out of range";
1420 dst+=unicode_decode(src+10,brace,dst);
1427 data->length=dst-data->character;
1434 /*************************************************
1435 * Parse an optional string *
1436 *************************************************/
1440 quoted-string = DQUOTE *CHAR DQUOTE
1441 ;; in general, \ CHAR inside a string maps to CHAR
1442 ;; so \" maps to " and \\ maps to \
1443 ;; note that newlines and other characters are all allowed
1446 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1447 *(multi-line-literal / multi-line-dotstuff)
1449 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1450 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1451 ;; A line containing only "." ends the multi-line.
1452 ;; Remove a leading '.' if followed by another '.'.
1453 string = quoted-string / multi-line
1456 filter points to the Sieve filter including its state
1457 id specifies identifier to match
1461 0 identifier not matched
1465 parse_string(struct Sieve *filter, struct String *data)
1470 data->character = NULL;
1472 if (*filter->pc=='"') /* quoted string */
1477 if (*filter->pc=='"') /* end of string */
1483 data->character = string_from_gstring(g);
1484 data->length = g->ptr;
1487 data->character = US"\0";
1488 /* that way, there will be at least one character allocated */
1490 #ifdef ENCODED_CHARACTER
1491 if (filter->require_encoded_character
1492 && string_decode(filter,data)==-1)
1497 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1499 g = string_catn(g, filter->pc+1, 1);
1502 else /* regular character */
1505 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1507 if (*filter->pc=='\n')
1509 g = string_catn(g, US"\r", 1);
1513 g = string_catn(g, filter->pc, 1);
1517 filter->errmsg=CUS "missing end of string";
1520 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1523 /* skip optional white space followed by hashed comment or CRLF */
1524 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1525 if (*filter->pc=='#')
1527 if (parse_hashcomment(filter)==-1) return -1;
1530 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1532 else if (*filter->pc=='\n')
1544 filter->errmsg=CUS "syntax error";
1550 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1552 if (*filter->pc=='\n') /* end of line */
1555 g = string_catn(g, CUS "\r\n", 2);
1563 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1565 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1570 data->character = string_from_gstring(g);
1571 data->length = g->ptr;
1574 data->character = US"\0";
1575 /* that way, there will be at least one character allocated */
1583 #ifdef ENCODED_CHARACTER
1584 if (filter->require_encoded_character
1585 && string_decode(filter,data)==-1)
1590 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1592 g = string_catn(g, CUS ".", 1);
1596 else /* regular character */
1598 g = string_catn(g, filter->pc, 1);
1602 filter->errmsg=CUS "missing end of multi line string";
1609 /*************************************************
1610 * Parse a specific identifier *
1611 *************************************************/
1615 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1618 filter points to the Sieve filter including its state
1619 id specifies identifier to match
1622 0 identifier not matched
1625 static int parse_identifier(struct Sieve *filter, const uschar *id)
1627 size_t idlen=Ustrlen(id);
1629 if (strncmpic(US filter->pc,US id,idlen)==0)
1631 uschar next=filter->pc[idlen];
1633 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1641 /*************************************************
1643 *************************************************/
1647 number = 1*DIGIT [QUANTIFIER]
1648 QUANTIFIER = "K" / "M" / "G"
1651 filter points to the Sieve filter including its state
1655 -1 no string list found
1658 static int parse_number(struct Sieve *filter, unsigned long *data)
1662 if (*filter->pc>='0' && *filter->pc<='9')
1667 d=Ustrtoul(filter->pc,&e,10);
1670 filter->errmsg=CUstrerror(ERANGE);
1675 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1676 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1677 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1678 if (d>(ULONG_MAX/u))
1680 filter->errmsg=CUstrerror(ERANGE);
1689 filter->errmsg=CUS "missing number";
1695 /*************************************************
1696 * Parse a string list *
1697 *************************************************/
1701 string-list = "[" string *("," string) "]" / string
1704 filter points to the Sieve filter including its state
1705 data returns string list
1708 -1 no string list found
1712 parse_stringlist(struct Sieve *filter, struct String **data)
1714 const uschar *orig=filter->pc;
1715 int dataCapacity = 0;
1717 struct String *d = NULL;
1720 if (*filter->pc=='[') /* string list */
1725 if (parse_white(filter)==-1) goto error;
1726 if (dataLength+1 >= dataCapacity) /* increase buffer */
1730 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1731 new = store_get(sizeof(struct String) * dataCapacity);
1733 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
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
2034 parse_test(struct Sieve *filter, int *cond, int exec)
2036 if (parse_white(filter)==-1) return -1;
2037 if (parse_identifier(filter,CUS "address"))
2040 address-test = "address" { [address-part] [comparator] [match-type] }
2041 <header-list: string-list> <key-list: string-list>
2043 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2046 enum AddressPart addressPart=ADDRPART_ALL;
2047 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2048 enum MatchType matchType=MATCH_IS;
2049 struct String *hdr,*key;
2055 if (parse_white(filter)==-1) return -1;
2056 if ((m=parse_addresspart(filter,&addressPart))!=0)
2058 if (m==-1) return -1;
2061 filter->errmsg=CUS "address part already specified";
2066 else if ((m=parse_comparator(filter,&comparator))!=0)
2068 if (m==-1) return -1;
2071 filter->errmsg=CUS "comparator already specified";
2076 else if ((m=parse_matchtype(filter,&matchType))!=0)
2078 if (m==-1) return -1;
2081 filter->errmsg=CUS "match type already specified";
2088 if (parse_white(filter)==-1) return -1;
2089 if ((m=parse_stringlist(filter,&hdr))!=1)
2091 if (m==0) filter->errmsg=CUS "header string list expected";
2094 if (parse_white(filter)==-1) return -1;
2095 if ((m=parse_stringlist(filter,&key))!=1)
2097 if (m==0) filter->errmsg=CUS "key string list expected";
2101 for (struct String * h = hdr; h->length!=-1 && !*cond; ++h)
2103 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2107 !eq_asciicase(h,&str_from,0)
2108 && !eq_asciicase(h,&str_to,0)
2109 && !eq_asciicase(h,&str_cc,0)
2110 && !eq_asciicase(h,&str_bcc,0)
2111 && !eq_asciicase(h,&str_sender,0)
2112 && !eq_asciicase(h,&str_resent_from,0)
2113 && !eq_asciicase(h,&str_resent_to,0)
2116 filter->errmsg=CUS "invalid header field";
2121 /* We are only interested in addresses below, so no MIME decoding */
2122 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2123 if (header_value == NULL)
2125 filter->errmsg=CUS "header string expansion failed";
2128 f.parse_allow_group = TRUE;
2129 while (*header_value && !*cond)
2132 int start, end, domain;
2136 end_addr = parse_find_address_end(header_value, FALSE);
2137 saveend = *end_addr;
2139 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2141 if (extracted_addr) switch (addressPart)
2143 case ADDRPART_ALL: part=extracted_addr; break;
2147 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2148 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2150 case ADDRPART_DETAIL: part=NULL; break;
2154 *end_addr = saveend;
2157 for (struct String * k = key; k->length !=- 1; ++k)
2159 struct String partStr = {.character = part, .length = Ustrlen(part)};
2163 *cond=compare(filter,k,&partStr,comparator,matchType);
2164 if (*cond==-1) return -1;
2169 if (saveend == 0) break;
2170 header_value = end_addr + 1;
2172 f.parse_allow_group = FALSE;
2173 f.parse_found_group = FALSE;
2178 else if (parse_identifier(filter,CUS "allof"))
2181 allof-test = "allof" <tests: test-list>
2186 switch (parse_testlist(filter,&n,&num_true,exec))
2189 case 0: filter->errmsg=CUS "missing test list"; return -1;
2190 default: *cond=(n==num_true); return 1;
2193 else if (parse_identifier(filter,CUS "anyof"))
2196 anyof-test = "anyof" <tests: test-list>
2201 switch (parse_testlist(filter,&n,&num_true,exec))
2204 case 0: filter->errmsg=CUS "missing test list"; return -1;
2205 default: *cond=(num_true>0); return 1;
2208 else if (parse_identifier(filter,CUS "exists"))
2211 exists-test = "exists" <header-names: string-list>
2217 if (parse_white(filter)==-1) return -1;
2218 if ((m=parse_stringlist(filter,&hdr))!=1)
2220 if (m==0) filter->errmsg=CUS "header string list expected";
2226 for (struct String * h = hdr; h->length != -1 && *cond; ++h)
2230 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2231 if (header_def == NULL)
2233 filter->errmsg=CUS "header string expansion failed";
2236 if (Ustrcmp(header_def,"false")==0) *cond=0;
2241 else if (parse_identifier(filter,CUS "false"))
2244 false-test = "false"
2250 else if (parse_identifier(filter,CUS "header"))
2253 header-test = "header" { [comparator] [match-type] }
2254 <header-names: string-list> <key-list: string-list>
2257 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2258 enum MatchType matchType=MATCH_IS;
2259 struct String *hdr,*key;
2265 if (parse_white(filter)==-1) return -1;
2266 if ((m=parse_comparator(filter,&comparator))!=0)
2268 if (m==-1) return -1;
2271 filter->errmsg=CUS "comparator already specified";
2276 else if ((m=parse_matchtype(filter,&matchType))!=0)
2278 if (m==-1) return -1;
2281 filter->errmsg=CUS "match type already specified";
2288 if (parse_white(filter)==-1) return -1;
2289 if ((m=parse_stringlist(filter,&hdr))!=1)
2291 if (m==0) filter->errmsg=CUS "header string list expected";
2294 if (parse_white(filter)==-1) return -1;
2295 if ((m=parse_stringlist(filter,&key))!=1)
2297 if (m==0) filter->errmsg=CUS "key string list expected";
2301 for (struct String * h = hdr; h->length != -1 && !*cond; ++h)
2305 filter->errmsg=CUS "invalid header field";
2310 struct String header_value;
2313 expand_header(&header_value,h);
2314 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2315 if (header_value.character == NULL || header_def == NULL)
2317 filter->errmsg=CUS "header string expansion failed";
2320 for (struct String * k = key; k->length != -1; ++k)
2321 if (Ustrcmp(header_def,"true")==0)
2323 *cond=compare(filter,k,&header_value,comparator,matchType);
2324 if (*cond==-1) return -1;
2331 else if (parse_identifier(filter,CUS "not"))
2333 if (parse_white(filter)==-1) return -1;
2334 switch (parse_test(filter,cond,exec))
2337 case 0: filter->errmsg=CUS "missing test"; return -1;
2338 default: *cond=!*cond; return 1;
2341 else if (parse_identifier(filter,CUS "size"))
2344 relop = ":over" / ":under"
2345 size-test = "size" relop <limit: number>
2348 unsigned long limit;
2351 if (parse_white(filter)==-1) return -1;
2352 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2353 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2356 filter->errmsg=CUS "missing :over or :under";
2359 if (parse_white(filter)==-1) return -1;
2360 if (parse_number(filter,&limit)==-1) return -1;
2361 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2364 else if (parse_identifier(filter,CUS "true"))
2369 else if (parse_identifier(filter,CUS "envelope"))
2372 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2373 <envelope-part: string-list> <key-list: string-list>
2375 envelope-part is case insensitive "from" or "to"
2376 #ifdef ENVELOPE_AUTH
2377 envelope-part =/ "auth"
2381 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2382 enum AddressPart addressPart=ADDRPART_ALL;
2383 enum MatchType matchType=MATCH_IS;
2384 struct String *env,*key;
2388 if (!filter->require_envelope)
2390 filter->errmsg=CUS "missing previous require \"envelope\";";
2395 if (parse_white(filter)==-1) return -1;
2396 if ((m=parse_comparator(filter,&comparator))!=0)
2398 if (m==-1) return -1;
2401 filter->errmsg=CUS "comparator already specified";
2406 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2408 if (m==-1) return -1;
2411 filter->errmsg=CUS "address part already specified";
2416 else if ((m=parse_matchtype(filter,&matchType))!=0)
2418 if (m==-1) return -1;
2421 filter->errmsg=CUS "match type already specified";
2428 if (parse_white(filter)==-1) return -1;
2429 if ((m=parse_stringlist(filter,&env))!=1)
2431 if (m==0) filter->errmsg=CUS "envelope string list expected";
2434 if (parse_white(filter)==-1) return -1;
2435 if ((m=parse_stringlist(filter,&key))!=1)
2437 if (m==0) filter->errmsg=CUS "key string list expected";
2441 for (struct String * e = env; e->length != -1 && !*cond; ++e)
2443 const uschar *envelopeExpr=CUS 0;
2444 uschar *envelope=US 0;
2446 if (eq_asciicase(e,&str_from,0))
2448 switch (addressPart)
2450 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2454 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2455 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2457 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2461 else if (eq_asciicase(e,&str_to,0))
2463 switch (addressPart)
2465 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2467 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2468 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2470 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2471 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2474 #ifdef ENVELOPE_AUTH
2475 else if (eq_asciicase(e,&str_auth,0))
2477 switch (addressPart)
2479 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2483 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2484 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2486 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2493 filter->errmsg=CUS "invalid envelope string";
2496 if (exec && envelopeExpr)
2498 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2500 filter->errmsg=CUS "header string expansion failed";
2503 for (struct String * k = key; k->length != -1; ++k)
2505 struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)};
2507 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2508 if (*cond==-1) return -1;
2516 else if (parse_identifier(filter,CUS "valid_notify_method"))
2519 valid_notify_method = "valid_notify_method"
2520 <notification-uris: string-list>
2523 struct String *uris;
2526 if (!filter->require_enotify)
2528 filter->errmsg=CUS "missing previous require \"enotify\";";
2531 if (parse_white(filter)==-1) return -1;
2532 if ((m=parse_stringlist(filter,&uris))!=1)
2534 if (m==0) filter->errmsg=CUS "URI string list expected";
2540 for (struct String * u = uris; u->length != -1 && *cond; ++u)
2542 string_item *recipient;
2543 struct String header,subject,body;
2547 header.character=(uschar*)0;
2549 subject.character=(uschar*)0;
2551 body.character=(uschar*)0;
2552 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2558 else if (parse_identifier(filter,CUS "notify_method_capability"))
2561 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2562 <notification-uri: string>
2563 <notification-capability: string>
2564 <key-list: string-list>
2570 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2571 enum MatchType matchType=MATCH_IS;
2572 struct String uri,capa,*keys;
2574 if (!filter->require_enotify)
2576 filter->errmsg=CUS "missing previous require \"enotify\";";
2581 if (parse_white(filter)==-1) return -1;
2582 if ((m=parse_comparator(filter,&comparator))!=0)
2584 if (m==-1) return -1;
2587 filter->errmsg=CUS "comparator already specified";
2592 else if ((m=parse_matchtype(filter,&matchType))!=0)
2594 if (m==-1) return -1;
2597 filter->errmsg=CUS "match type already specified";
2604 if ((m=parse_string(filter,&uri))!=1)
2606 if (m==0) filter->errmsg=CUS "missing notification URI string";
2609 if (parse_white(filter)==-1) return -1;
2610 if ((m=parse_string(filter,&capa))!=1)
2612 if (m==0) filter->errmsg=CUS "missing notification capability string";
2615 if (parse_white(filter)==-1) return -1;
2616 if ((m=parse_stringlist(filter,&keys))!=1)
2618 if (m==0) filter->errmsg=CUS "missing key string list";
2623 string_item *recipient;
2624 struct String header,subject,body;
2629 header.character=(uschar*)0;
2631 subject.character=(uschar*)0;
2633 body.character=(uschar*)0;
2634 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2635 if (eq_asciicase(&capa,&str_online,0)==1)
2636 for (struct String * k = keys; k->length != -1; ++k)
2638 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2639 if (*cond==-1) return -1;
2650 /*************************************************
2651 * Parse and interpret an optional block *
2652 *************************************************/
2656 filter points to the Sieve filter including its state
2657 exec Execute parsed statements
2658 generated where to hang newly-generated addresses
2660 Returns: 2 success by stop
2662 0 no block command found
2663 -1 syntax or execution error
2666 static int parse_block(struct Sieve *filter, int exec,
2667 address_item **generated)
2671 if (parse_white(filter)==-1) return -1;
2672 if (*filter->pc=='{')
2675 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2676 if (*filter->pc=='}')
2683 filter->errmsg=CUS "expecting command or closing brace";
2691 /*************************************************
2692 * Match a semicolon *
2693 *************************************************/
2697 filter points to the Sieve filter including its state
2703 static int parse_semicolon(struct Sieve *filter)
2705 if (parse_white(filter)==-1) return -1;
2706 if (*filter->pc==';')
2713 filter->errmsg=CUS "missing semicolon";
2719 /*************************************************
2720 * Parse and interpret a Sieve command *
2721 *************************************************/
2725 filter points to the Sieve filter including its state
2726 exec Execute parsed statements
2727 generated where to hang newly-generated addresses
2729 Returns: 2 success by stop
2731 -1 syntax or execution error
2734 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2738 if (parse_white(filter)==-1) return -1;
2739 if (parse_identifier(filter,CUS "if"))
2742 if-command = "if" test block *( "elsif" test block ) [ else block ]
2745 int cond,m,unsuccessful;
2748 if (parse_white(filter)==-1) return -1;
2749 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2752 filter->errmsg=CUS "missing test";
2755 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2756 (debug_selector & D_filter) != 0)
2758 if (exec) debug_printf("if %s\n",cond?"true":"false");
2760 m=parse_block(filter,exec ? cond : 0, generated);
2761 if (m==-1 || m==2) return m;
2764 filter->errmsg=CUS "missing block";
2767 unsuccessful = !cond;
2768 for (;;) /* elsif test block */
2770 if (parse_white(filter)==-1) return -1;
2771 if (parse_identifier(filter,CUS "elsif"))
2773 if (parse_white(filter)==-1) return -1;
2774 m=parse_test(filter,&cond,exec && unsuccessful);
2775 if (m==-1 || m==2) return m;
2778 filter->errmsg=CUS "missing test";
2781 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2782 (debug_selector & D_filter) != 0)
2784 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2786 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2787 if (m==-1 || m==2) return m;
2790 filter->errmsg=CUS "missing block";
2793 if (exec && unsuccessful && cond) unsuccessful = 0;
2798 if (parse_white(filter)==-1) return -1;
2799 if (parse_identifier(filter,CUS "else"))
2801 m=parse_block(filter,exec && unsuccessful, generated);
2802 if (m==-1 || m==2) return m;
2805 filter->errmsg=CUS "missing block";
2810 else if (parse_identifier(filter,CUS "stop"))
2813 stop-command = "stop" { stop-options } ";"
2817 if (parse_semicolon(filter)==-1) return -1;
2820 filter->pc+=Ustrlen(filter->pc);
2824 else if (parse_identifier(filter,CUS "keep"))
2827 keep-command = "keep" { keep-options } ";"
2831 if (parse_semicolon(filter)==-1) return -1;
2834 add_addr(generated,US"inbox",1,0,0,0);
2838 else if (parse_identifier(filter,CUS "discard"))
2841 discard-command = "discard" { discard-options } ";"
2845 if (parse_semicolon(filter)==-1) return -1;
2846 if (exec) filter->keep=0;
2848 else if (parse_identifier(filter,CUS "redirect"))
2851 redirect-command = "redirect" redirect-options "string" ";"
2853 redirect-options =) ":copy"
2856 struct String recipient;
2862 if (parse_white(filter)==-1) return -1;
2863 if (parse_identifier(filter,CUS ":copy")==1)
2865 if (!filter->require_copy)
2867 filter->errmsg=CUS "missing previous require \"copy\";";
2874 if (parse_white(filter)==-1) return -1;
2875 if ((m=parse_string(filter,&recipient))!=1)
2877 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2880 if (strchr(CCS recipient.character,'@')==(char*)0)
2882 filter->errmsg=CUS "unqualified recipient address";
2887 add_addr(generated,recipient.character,0,0,0,0);
2888 if (!copy) filter->keep = 0;
2890 if (parse_semicolon(filter)==-1) return -1;
2892 else if (parse_identifier(filter,CUS "fileinto"))
2895 fileinto-command = "fileinto" { fileinto-options } string ";"
2897 fileinto-options =) [ ":copy" ]
2900 struct String folder;
2903 unsigned long maxage, maxmessages, maxstorage;
2906 maxage = maxmessages = maxstorage = 0;
2907 if (!filter->require_fileinto)
2909 filter->errmsg=CUS "missing previous require \"fileinto\";";
2914 if (parse_white(filter)==-1) return -1;
2915 if (parse_identifier(filter,CUS ":copy")==1)
2917 if (!filter->require_copy)
2919 filter->errmsg=CUS "missing previous require \"copy\";";
2926 if (parse_white(filter)==-1) return -1;
2927 if ((m=parse_string(filter,&folder))!=1)
2929 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2932 m=0; s=folder.character;
2933 if (folder.length==0) m=1;
2934 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2937 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2942 filter->errmsg=CUS "invalid folder";
2947 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2948 if (!copy) filter->keep = 0;
2950 if (parse_semicolon(filter)==-1) return -1;
2953 else if (parse_identifier(filter,CUS "notify"))
2956 notify-command = "notify" { notify-options } <method: string> ";"
2957 notify-options = [":from" string]
2958 [":importance" <"1" / "2" / "3">]
2959 [":options" 1*(string-list / number)]
2965 struct String importance;
2966 struct String message;
2967 struct String method;
2968 struct Notification *already;
2969 string_item *recipient;
2970 struct String header;
2971 struct String subject;
2973 uschar *envelope_from;
2974 struct String auto_submitted_value;
2975 uschar *auto_submitted_def;
2977 if (!filter->require_enotify)
2979 filter->errmsg=CUS "missing previous require \"enotify\";";
2982 from.character=(uschar*)0;
2984 importance.character=(uschar*)0;
2985 importance.length=-1;
2986 message.character=(uschar*)0;
2990 header.character=(uschar*)0;
2992 subject.character=(uschar*)0;
2994 body.character=(uschar*)0;
2995 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2998 if (parse_white(filter)==-1) return -1;
2999 if (parse_identifier(filter,CUS ":from")==1)
3001 if (parse_white(filter)==-1) return -1;
3002 if ((m=parse_string(filter,&from))!=1)
3004 if (m==0) filter->errmsg=CUS "from string expected";
3008 else if (parse_identifier(filter,CUS ":importance")==1)
3010 if (parse_white(filter)==-1) return -1;
3011 if ((m=parse_string(filter,&importance))!=1)
3013 if (m==0) filter->errmsg=CUS "importance string expected";
3016 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3018 filter->errmsg=CUS "invalid importance";
3022 else if (parse_identifier(filter,CUS ":options")==1)
3024 if (parse_white(filter)==-1) return -1;
3026 else if (parse_identifier(filter,CUS ":message")==1)
3028 if (parse_white(filter)==-1) return -1;
3029 if ((m=parse_string(filter,&message))!=1)
3031 if (m==0) filter->errmsg=CUS "message string expected";
3037 if (parse_white(filter)==-1) return -1;
3038 if ((m=parse_string(filter,&method))!=1)
3040 if (m==0) filter->errmsg=CUS "missing method string";
3043 if (parse_semicolon(filter)==-1) return -1;
3044 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3048 if (message.length==-1) message=subject;
3049 if (message.length==-1) expand_header(&message,&str_subject);
3050 expand_header(&auto_submitted_value,&str_auto_submitted);
3051 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3052 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3054 filter->errmsg=CUS "header string expansion failed";
3057 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3059 for (already=filter->notified; already; already=already->next)
3061 if (already->method.length==method.length
3062 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3063 && already->importance.length==importance.length
3064 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3065 && already->message.length==message.length
3066 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3070 /* New notification, process it */
3072 struct Notification *sent;
3073 sent=store_get(sizeof(struct Notification));
3074 sent->method=method;
3075 sent->importance=importance;
3076 sent->message=message;
3077 sent->next=filter->notified;
3078 filter->notified=sent;
3079 #ifndef COMPILE_SYNTAX_CHECKER
3080 if (filter_test == FTEST_NONE)
3084 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3088 int buffer_capacity;
3090 f = fdopen(fd, "wb");
3091 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3092 for (string_item * p = recipient; p; p=p->next)
3093 fprintf(f,"To: %s\n",p->text);
3094 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3095 if (header.length>0) fprintf(f,"%s",header.character);
3096 if (message.length==-1)
3098 message.character=US"Notification";
3099 message.length=Ustrlen(message.character);
3101 /* Allocation is larger than necessary, but enough even for split MIME words */
3102 buffer_capacity=32+4*message.length;
3103 buffer=store_get(buffer_capacity);
3104 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3106 if (body.length>0) fprintf(f,"%s\n",body.character);
3109 (void)child_close(pid, 0);
3112 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3114 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3120 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3122 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3128 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3130 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3137 else if (parse_identifier(filter,CUS "vacation"))
3140 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3141 vacation-options = [":days" number]
3144 [":addresses" string-list]
3151 struct String subject;
3153 struct String *addresses;
3155 string_item *aliases;
3156 struct String handle;
3157 struct String reason;
3159 if (!filter->require_vacation)
3161 filter->errmsg=CUS "missing previous require \"vacation\";";
3166 if (filter->vacation_ran)
3168 filter->errmsg=CUS "trying to execute vacation more than once";
3171 filter->vacation_ran=1;
3173 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3174 subject.character=(uschar*)0;
3176 from.character=(uschar*)0;
3178 addresses=(struct String*)0;
3181 handle.character=(uschar*)0;
3185 if (parse_white(filter)==-1) return -1;
3186 if (parse_identifier(filter,CUS ":days")==1)
3188 if (parse_white(filter)==-1) return -1;
3189 if (parse_number(filter,&days)==-1) return -1;
3190 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3191 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3193 else if (parse_identifier(filter,CUS ":subject")==1)
3195 if (parse_white(filter)==-1) return -1;
3196 if ((m=parse_string(filter,&subject))!=1)
3198 if (m==0) filter->errmsg=CUS "subject string expected";
3202 else if (parse_identifier(filter,CUS ":from")==1)
3204 if (parse_white(filter)==-1) return -1;
3205 if ((m=parse_string(filter,&from))!=1)
3207 if (m==0) filter->errmsg=CUS "from string expected";
3210 if (check_mail_address(filter,&from)!=1)
3213 else if (parse_identifier(filter,CUS ":addresses")==1)
3215 if (parse_white(filter)==-1) return -1;
3216 if ((m=parse_stringlist(filter,&addresses))!=1)
3218 if (m==0) filter->errmsg=CUS "addresses string list expected";
3221 for (struct String * a = addresses; a->length != -1; ++a)
3223 string_item * new = store_get(sizeof(string_item));
3225 new->text=store_get(a->length+1);
3226 if (a->length) memcpy(new->text,a->character,a->length);
3227 new->text[a->length]='\0';
3232 else if (parse_identifier(filter,CUS ":mime")==1)
3234 else if (parse_identifier(filter,CUS ":handle")==1)
3236 if (parse_white(filter)==-1) return -1;
3237 if ((m=parse_string(filter,&from))!=1)
3239 if (m==0) filter->errmsg=CUS "handle string expected";
3245 if (parse_white(filter)==-1) return -1;
3246 if ((m=parse_string(filter,&reason))!=1)
3248 if (m==0) filter->errmsg=CUS "missing reason string";
3255 for (s = reason.character, end = reason.character + reason.length;
3256 s<end && (*s&0x80)==0; ) s++;
3259 filter->errmsg=CUS "MIME reason string contains 8bit text";
3263 if (parse_semicolon(filter)==-1) return -1;
3269 int buffer_capacity;
3272 uschar hexdigest[33];
3275 if (filter_personal(aliases,TRUE))
3277 if (filter_test == FTEST_NONE)
3279 /* ensure oncelog directory exists; failure will be detected later */
3281 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3283 /* build oncelog filename */
3287 if (handle.length==-1)
3289 gstring * key = NULL;
3290 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3291 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3292 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3293 key = string_catn(key, reason.character, reason.length);
3294 md5_end(&base, key->s, key->ptr, digest);
3297 md5_end(&base, handle.character, handle.length, digest);
3299 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3301 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3302 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3304 if (filter_test == FTEST_NONE)
3306 once = string_cat (NULL, filter->vacation_directory);
3307 once = string_catn(once, US"/", 1);
3308 once = string_catn(once, hexdigest, 33);
3310 /* process subject */
3312 if (subject.length==-1)
3314 uschar *subject_def;
3316 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3317 if (Ustrcmp(subject_def,"true")==0)
3319 gstring * g = string_catn(NULL, US"Auto: ", 6);
3321 expand_header(&subject,&str_subject);
3322 g = string_catn(g, subject.character, subject.length);
3323 subject.character = string_from_gstring(g);
3324 subject.length = g->ptr;
3328 subject.character=US"Automated reply";
3329 subject.length=Ustrlen(subject.character);
3333 /* add address to list of generated addresses */
3335 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3336 setflag(addr, af_pfr);
3337 addr->prop.ignore_error = TRUE;
3338 addr->next = *generated;
3340 addr->reply = store_get(sizeof(reply_item));
3341 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3342 addr->reply->to = string_copy(sender_address);
3343 if (from.length==-1)
3344 addr->reply->from = expand_string(US"$local_part@$domain");
3346 addr->reply->from = from.character;
3347 /* Allocation is larger than necessary, but enough even for split MIME words */
3348 buffer_capacity=32+4*subject.length;
3349 buffer=store_get(buffer_capacity);
3350 /* deconst cast safe as we pass in a non-const item */
3351 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3352 addr->reply->oncelog = string_from_gstring(once);
3353 addr->reply->once_repeat=days*86400;
3355 /* build body and MIME headers */
3359 uschar *mime_body,*reason_end;
3360 static const uschar nlnl[]="\r\n\r\n";
3364 mime_body = reason.character, reason_end = reason.character + reason.length;
3365 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3368 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3370 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3371 else mime_body=reason_end-1;
3372 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3376 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3378 addr->reply->headers = US"MIME-Version: 1.0\n"
3379 "Content-Type: text/plain;\n"
3380 "\tcharset=\"utf-8\"\n"
3381 "Content-Transfer-Encoding: quoted-printable";
3382 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3386 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3387 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3397 /*************************************************
3398 * Parse and interpret a sieve filter *
3399 *************************************************/
3403 filter points to the Sieve filter including its state
3404 exec Execute parsed statements
3405 generated where to hang newly-generated addresses
3408 -1 syntax or execution error
3412 parse_start(struct Sieve *filter, int exec, address_item **generated)
3414 filter->pc=filter->filter;
3417 filter->require_envelope=0;
3418 filter->require_fileinto=0;
3419 #ifdef ENCODED_CHARACTER
3420 filter->require_encoded_character=0;
3422 #ifdef ENVELOPE_AUTH
3423 filter->require_envelope_auth=0;
3426 filter->require_enotify=0;
3427 filter->notified=(struct Notification*)0;
3430 filter->require_subaddress=0;
3433 filter->require_vacation=0;
3434 filter->vacation_ran=0;
3436 filter->require_copy=0;
3437 filter->require_iascii_numeric=0;
3439 if (parse_white(filter)==-1) return -1;
3441 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3444 struct dirent *oncelog;
3445 struct stat properties;
3448 /* clean up old vacation log databases */
3450 oncelogdir=opendir(CS filter->vacation_directory);
3452 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3454 filter->errmsg=CUS "unable to open vacation directory";
3458 if (oncelogdir != NULL)
3462 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3464 if (strlen(oncelog->d_name)==32)
3466 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3467 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3471 closedir(oncelogdir);
3475 while (parse_identifier(filter,CUS "require"))
3478 require-command = "require" <capabilities: string-list>
3484 if (parse_white(filter)==-1) return -1;
3485 if ((m=parse_stringlist(filter,&cap))!=1)
3487 if (m==0) filter->errmsg=CUS "capability string list expected";
3490 for (struct String * check = cap; check->character; ++check)
3492 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3493 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3494 #ifdef ENCODED_CHARACTER
3495 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3497 #ifdef ENVELOPE_AUTH
3498 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3501 else if (eq_octet(check,&str_enotify,0))
3503 if (filter->enotify_mailto_owner == NULL)
3505 filter->errmsg=CUS "enotify disabled";
3508 filter->require_enotify=1;
3512 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3515 else if (eq_octet(check,&str_vacation,0))
3517 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3519 filter->errmsg=CUS "vacation disabled";
3522 filter->require_vacation=1;
3525 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3526 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3527 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3528 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3529 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3532 filter->errmsg=CUS "unknown capability";
3536 if (parse_semicolon(filter)==-1) return -1;
3538 if (parse_commands(filter,exec,generated)==-1) return -1;
3541 filter->errmsg=CUS "syntax error";
3548 /*************************************************
3549 * Interpret a sieve filter file *
3550 *************************************************/
3554 filter points to the entire file, read into store as a single string
3555 options controls whether various special things are allowed, and requests
3556 special actions (not currently used)
3557 vacation_directory where to store vacation "once" files
3558 enotify_mailto_owner owner of mailto notifications
3559 useraddress string expression for :user part of address
3560 subaddress string expression for :subaddress part of address
3561 generated where to hang newly-generated addresses
3562 error where to pass back an error text
3564 Returns: FF_DELIVERED success, a significant action was taken
3565 FF_NOTDELIVERED success, no significant action
3566 FF_DEFER defer requested
3567 FF_FAIL fail requested
3568 FF_FREEZE freeze requested
3569 FF_ERROR there was a problem
3573 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3574 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3575 address_item **generated, uschar **error)
3581 options = options; /* Keep picky compilers happy */
3584 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3585 sieve.filter=filter;
3587 if (vacation_directory == NULL)
3588 sieve.vacation_directory = NULL;
3591 sieve.vacation_directory=expand_string(vacation_directory);
3592 if (sieve.vacation_directory == NULL)
3594 *error = string_sprintf("failed to expand \"%s\" "
3595 "(sieve_vacation_directory): %s", vacation_directory,
3596 expand_string_message);
3601 if (enotify_mailto_owner == NULL)
3602 sieve.enotify_mailto_owner = NULL;
3605 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3606 if (sieve.enotify_mailto_owner == NULL)
3608 *error = string_sprintf("failed to expand \"%s\" "
3609 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3610 expand_string_message);
3615 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3616 sieve.subaddress = subaddress;
3618 #ifdef COMPILE_SYNTAX_CHECKER
3619 if (parse_start(&sieve,0,generated)==1)
3621 if (parse_start(&sieve,1,generated)==1)
3626 add_addr(generated,US"inbox",1,0,0,0);
3627 msg = string_sprintf("Implicit keep");
3632 msg = string_sprintf("No implicit keep");
3638 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3639 #ifdef COMPILE_SYNTAX_CHECKER
3643 add_addr(generated,US"inbox",1,0,0,0);
3648 #ifndef COMPILE_SYNTAX_CHECKER
3649 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3650 else debug_printf("%s\n", msg);
3653 DEBUG(D_route) debug_printf("Sieve: end of processing\n");