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, is_tainted(src->character)); /* 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), FALSE);
448 new->text = store_get(to.length+1, is_tainted(to.character));
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), FALSE);
506 new->text = store_get(hvalue.length+1, is_tainted(hvalue.character));
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)));
1092 while (*r==' ' || *r=='\t') ++r;
1100 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1102 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1106 /*************************************************
1107 * Parse remaining hash comment *
1108 *************************************************/
1112 Comment up to terminating CRLF
1115 filter points to the Sieve filter including its state
1121 static int parse_hashcomment(struct Sieve *filter)
1127 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1129 if (*filter->pc=='\n')
1142 filter->errmsg=CUS "missing end of comment";
1147 /*************************************************
1148 * Parse remaining C-style comment *
1149 *************************************************/
1153 Everything up to star slash
1156 filter points to the Sieve filter including its state
1162 static int parse_comment(struct Sieve *filter)
1167 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1174 filter->errmsg=CUS "missing end of comment";
1179 /*************************************************
1180 * Parse optional white space *
1181 *************************************************/
1185 Spaces, tabs, CRLFs, hash comments or C-style comments
1188 filter points to the Sieve filter including its state
1194 static int parse_white(struct Sieve *filter)
1198 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1200 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1202 else if (*filter->pc=='\n')
1212 else if (*filter->pc=='#')
1214 if (parse_hashcomment(filter)==-1) return -1;
1216 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1218 if (parse_comment(filter)==-1) return -1;
1226 #ifdef ENCODED_CHARACTER
1227 /*************************************************
1228 * Decode hex-encoded-character string *
1229 *************************************************/
1232 Encoding definition:
1233 blank = SP / TAB / CRLF
1234 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1235 hex-pair = 1*2HEXDIG
1238 src points to a hex-pair-seq
1239 end points to its end
1240 dst points to the destination of the decoded octets,
1241 optionally to (uschar*)0 for checking only
1243 Returns: >=0 number of decoded octets
1247 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1251 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1257 d<2 && src<end && isxdigit(n=tolower(*src));
1258 x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ;
1259 if (d==0) return -1;
1262 if (src==end) return decoded;
1263 if (*src==' ' || *src=='\t' || *src=='\n')
1264 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1273 /*************************************************
1274 * Decode unicode-encoded-character string *
1275 *************************************************/
1278 Encoding definition:
1279 blank = SP / TAB / CRLF
1280 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1281 unicode-hex = 1*HEXDIG
1283 It is an error for a script to use a hexadecimal value that isn't in
1284 either the range 0 to D7FF or the range E000 to 10FFFF.
1286 At this time, strings are already scanned, thus the CRLF is converted
1287 to the internally used \n (should RFC_EOL have been used).
1290 src points to a unicode-hex-seq
1291 end points to its end
1292 dst points to the destination of the decoded octets,
1293 optionally to (uschar*)0 for checking only
1295 Returns: >=0 number of decoded octets
1297 -2 semantic error (character range violation)
1301 unicode_decode(uschar *src, uschar *end, uschar *dst)
1305 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1312 for (hex_seq = src; src < end && *src=='0'; ) src++;
1314 d < 7 && src < end && isxdigit(n=tolower(*src));
1315 c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1316 if (src == hex_seq) return -1;
1317 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1323 else if (c>=0x80 && c<=0x7ff)
1328 *dst++=128+(c&0x3f);
1332 else if (c>=0x800 && c<=0xffff)
1337 *dst++=128+((c>>6)&0x3f);
1338 *dst++=128+(c&0x3f);
1342 else if (c>=0x10000 && c<=0x1fffff)
1347 *dst++=128+((c>>10)&0x3f);
1348 *dst++=128+((c>>6)&0x3f);
1349 *dst++=128+(c&0x3f);
1353 if (*src==' ' || *src=='\t' || *src=='\n')
1355 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1356 if (src==end) return decoded;
1365 /*************************************************
1366 * Decode encoded-character string *
1367 *************************************************/
1370 Encoding definition:
1371 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1372 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1375 encoded points to an encoded string, returns decoded string
1376 filter points to the Sieve filter including its state
1382 static int string_decode(struct Sieve *filter, struct String *data)
1384 uschar *src,*dst,*end;
1386 src=data->character;
1388 end=data->character+data->length;
1394 strncmpic(src,US "${hex:",6)==0
1395 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1396 && (hex_decode(src+6,brace,(uschar*)0))>=0
1399 dst+=hex_decode(src+6,brace,dst);
1403 strncmpic(src,US "${unicode:",10)==0
1404 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1407 switch (unicode_decode(src+10,brace,(uschar*)0))
1411 filter->errmsg=CUS "unicode character out of range";
1421 dst+=unicode_decode(src+10,brace,dst);
1428 data->length=dst-data->character;
1435 /*************************************************
1436 * Parse an optional string *
1437 *************************************************/
1441 quoted-string = DQUOTE *CHAR DQUOTE
1442 ;; in general, \ CHAR inside a string maps to CHAR
1443 ;; so \" maps to " and \\ maps to \
1444 ;; note that newlines and other characters are all allowed
1447 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1448 *(multi-line-literal / multi-line-dotstuff)
1450 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1451 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1452 ;; A line containing only "." ends the multi-line.
1453 ;; Remove a leading '.' if followed by another '.'.
1454 string = quoted-string / multi-line
1457 filter points to the Sieve filter including its state
1458 id specifies identifier to match
1462 0 identifier not matched
1466 parse_string(struct Sieve *filter, struct String *data)
1471 data->character = NULL;
1473 if (*filter->pc=='"') /* quoted string */
1478 if (*filter->pc=='"') /* end of string */
1484 data->character = string_from_gstring(g);
1485 data->length = g->ptr;
1488 data->character = US"\0";
1489 /* that way, there will be at least one character allocated */
1491 #ifdef ENCODED_CHARACTER
1492 if (filter->require_encoded_character
1493 && string_decode(filter,data)==-1)
1498 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1500 g = string_catn(g, filter->pc+1, 1);
1503 else /* regular character */
1506 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1508 if (*filter->pc=='\n')
1510 g = string_catn(g, US"\r", 1);
1514 g = string_catn(g, filter->pc, 1);
1518 filter->errmsg=CUS "missing end of string";
1521 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1524 /* skip optional white space followed by hashed comment or CRLF */
1525 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1526 if (*filter->pc=='#')
1528 if (parse_hashcomment(filter)==-1) return -1;
1531 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1533 else if (*filter->pc=='\n')
1545 filter->errmsg=CUS "syntax error";
1551 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1553 if (*filter->pc=='\n') /* end of line */
1556 g = string_catn(g, CUS "\r\n", 2);
1564 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1566 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1571 data->character = string_from_gstring(g);
1572 data->length = g->ptr;
1575 data->character = US"\0";
1576 /* that way, there will be at least one character allocated */
1584 #ifdef ENCODED_CHARACTER
1585 if (filter->require_encoded_character
1586 && string_decode(filter,data)==-1)
1591 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1593 g = string_catn(g, CUS ".", 1);
1597 else /* regular character */
1599 g = string_catn(g, filter->pc, 1);
1603 filter->errmsg=CUS "missing end of multi line string";
1610 /*************************************************
1611 * Parse a specific identifier *
1612 *************************************************/
1616 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1619 filter points to the Sieve filter including its state
1620 id specifies identifier to match
1623 0 identifier not matched
1626 static int parse_identifier(struct Sieve *filter, const uschar *id)
1628 size_t idlen=Ustrlen(id);
1630 if (strncmpic(US filter->pc,US id,idlen)==0)
1632 uschar next=filter->pc[idlen];
1634 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1642 /*************************************************
1644 *************************************************/
1648 number = 1*DIGIT [QUANTIFIER]
1649 QUANTIFIER = "K" / "M" / "G"
1652 filter points to the Sieve filter including its state
1656 -1 no string list found
1659 static int parse_number(struct Sieve *filter, unsigned long *data)
1663 if (*filter->pc>='0' && *filter->pc<='9')
1668 d=Ustrtoul(filter->pc,&e,10);
1671 filter->errmsg=CUstrerror(ERANGE);
1676 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1677 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1678 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1679 if (d>(ULONG_MAX/u))
1681 filter->errmsg=CUstrerror(ERANGE);
1690 filter->errmsg=CUS "missing number";
1696 /*************************************************
1697 * Parse a string list *
1698 *************************************************/
1702 string-list = "[" string *("," string) "]" / string
1705 filter points to the Sieve filter including its state
1706 data returns string list
1709 -1 no string list found
1713 parse_stringlist(struct Sieve *filter, struct String **data)
1715 const uschar *orig=filter->pc;
1716 int dataCapacity = 0;
1718 struct String *d = NULL;
1721 if (*filter->pc=='[') /* string list */
1726 if (parse_white(filter)==-1) goto error;
1727 if (dataLength+1 >= dataCapacity) /* increase buffer */
1731 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1732 new = store_get(sizeof(struct String) * dataCapacity, FALSE);
1734 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1738 m=parse_string(filter,&d[dataLength]);
1741 if (dataLength==0) break;
1744 filter->errmsg=CUS "missing string";
1748 else if (m==-1) goto error;
1750 if (parse_white(filter)==-1) goto error;
1751 if (*filter->pc==',') ++filter->pc;
1754 if (*filter->pc==']')
1756 d[dataLength].character=(uschar*)0;
1757 d[dataLength].length=-1;
1764 filter->errmsg=CUS "missing closing bracket";
1768 else /* single string */
1770 if (!(d=store_get(sizeof(struct String)*2, FALSE)))
1773 m=parse_string(filter,&d[0]);
1784 d[1].character=(uschar*)0;
1791 filter->errmsg=CUS "missing string list";
1796 /*************************************************
1797 * Parse an optional address part specifier *
1798 *************************************************/
1802 address-part = ":localpart" / ":domain" / ":all"
1803 address-part =/ ":user" / ":detail"
1806 filter points to the Sieve filter including its state
1807 a returns address part specified
1810 0 no comparator found
1814 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1817 if (parse_identifier(filter,CUS ":user")==1)
1819 if (!filter->require_subaddress)
1821 filter->errmsg=CUS "missing previous require \"subaddress\";";
1827 else if (parse_identifier(filter,CUS ":detail")==1)
1829 if (!filter->require_subaddress)
1831 filter->errmsg=CUS "missing previous require \"subaddress\";";
1839 if (parse_identifier(filter,CUS ":localpart")==1)
1841 *a=ADDRPART_LOCALPART;
1844 else if (parse_identifier(filter,CUS ":domain")==1)
1849 else if (parse_identifier(filter,CUS ":all")==1)
1858 /*************************************************
1859 * Parse an optional comparator *
1860 *************************************************/
1864 comparator = ":comparator" <comparator-name: string>
1867 filter points to the Sieve filter including its state
1868 c returns comparator
1871 0 no comparator found
1872 -1 incomplete comparator found
1875 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1877 struct String comparator_name;
1879 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1880 if (parse_white(filter)==-1) return -1;
1881 switch (parse_string(filter,&comparator_name))
1886 filter->errmsg=CUS "missing comparator";
1893 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1898 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1900 *c=COMP_EN_ASCII_CASEMAP;
1903 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1905 *c=COMP_EN_ASCII_CASEMAP;
1908 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1910 *c=COMP_ASCII_NUMERIC;
1915 filter->errmsg=CUS "invalid comparator";
1924 /*************************************************
1925 * Parse an optional match type *
1926 *************************************************/
1930 match-type = ":is" / ":contains" / ":matches"
1933 filter points to the Sieve filter including its state
1934 m returns match type
1937 0 no match type found
1940 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1942 if (parse_identifier(filter,CUS ":is")==1)
1947 else if (parse_identifier(filter,CUS ":contains")==1)
1952 else if (parse_identifier(filter,CUS ":matches")==1)
1961 /*************************************************
1962 * Parse and interpret an optional test list *
1963 *************************************************/
1967 test-list = "(" test *("," test) ")"
1970 filter points to the Sieve filter including its state
1971 n total number of tests
1972 num_true number of passed tests
1973 exec Execute parsed statements
1976 0 no test list found
1977 -1 syntax or execution error
1980 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1982 if (parse_white(filter)==-1) return -1;
1983 if (*filter->pc=='(')
1992 switch (parse_test(filter,&cond,exec))
1995 case 0: filter->errmsg=CUS "missing test"; return -1;
1996 default: ++*n; if (cond) ++*num_true; break;
1998 if (parse_white(filter)==-1) return -1;
1999 if (*filter->pc==',') ++filter->pc;
2002 if (*filter->pc==')')
2009 filter->errmsg=CUS "missing closing paren";
2017 /*************************************************
2018 * Parse and interpret an optional test *
2019 *************************************************/
2023 filter points to the Sieve filter including its state
2024 cond returned condition status
2025 exec Execute parsed statements
2029 -1 syntax or execution error
2033 parse_test(struct Sieve *filter, int *cond, int exec)
2035 if (parse_white(filter)==-1) return -1;
2036 if (parse_identifier(filter,CUS "address"))
2039 address-test = "address" { [address-part] [comparator] [match-type] }
2040 <header-list: string-list> <key-list: string-list>
2042 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2045 enum AddressPart addressPart=ADDRPART_ALL;
2046 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2047 enum MatchType matchType=MATCH_IS;
2048 struct String *hdr,*key;
2054 if (parse_white(filter)==-1) return -1;
2055 if ((m=parse_addresspart(filter,&addressPart))!=0)
2057 if (m==-1) return -1;
2060 filter->errmsg=CUS "address part already specified";
2065 else if ((m=parse_comparator(filter,&comparator))!=0)
2067 if (m==-1) return -1;
2070 filter->errmsg=CUS "comparator already specified";
2075 else if ((m=parse_matchtype(filter,&matchType))!=0)
2077 if (m==-1) return -1;
2080 filter->errmsg=CUS "match type already specified";
2087 if (parse_white(filter)==-1) return -1;
2088 if ((m=parse_stringlist(filter,&hdr))!=1)
2090 if (m==0) filter->errmsg=CUS "header string list expected";
2093 if (parse_white(filter)==-1) return -1;
2094 if ((m=parse_stringlist(filter,&key))!=1)
2096 if (m==0) filter->errmsg=CUS "key string list expected";
2100 for (struct String * h = hdr; h->length!=-1 && !*cond; ++h)
2102 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2106 !eq_asciicase(h,&str_from,0)
2107 && !eq_asciicase(h,&str_to,0)
2108 && !eq_asciicase(h,&str_cc,0)
2109 && !eq_asciicase(h,&str_bcc,0)
2110 && !eq_asciicase(h,&str_sender,0)
2111 && !eq_asciicase(h,&str_resent_from,0)
2112 && !eq_asciicase(h,&str_resent_to,0)
2115 filter->errmsg=CUS "invalid header field";
2120 /* We are only interested in addresses below, so no MIME decoding */
2121 if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h)))))
2123 filter->errmsg=CUS "header string expansion failed";
2126 f.parse_allow_group = TRUE;
2127 while (*header_value && !*cond)
2130 int start, end, domain;
2134 end_addr = parse_find_address_end(header_value, FALSE);
2135 saveend = *end_addr;
2137 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2139 if (extracted_addr) switch (addressPart)
2141 case ADDRPART_ALL: part=extracted_addr; break;
2145 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2146 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2148 case ADDRPART_DETAIL: part=NULL; break;
2152 *end_addr = saveend;
2155 for (struct String * k = key; k->length !=- 1; ++k)
2157 struct String partStr = {.character = part, .length = Ustrlen(part)};
2161 *cond=compare(filter,k,&partStr,comparator,matchType);
2162 if (*cond==-1) return -1;
2167 if (saveend == 0) break;
2168 header_value = end_addr + 1;
2170 f.parse_allow_group = FALSE;
2171 f.parse_found_group = FALSE;
2176 else if (parse_identifier(filter,CUS "allof"))
2179 allof-test = "allof" <tests: test-list>
2184 switch (parse_testlist(filter,&n,&num_true,exec))
2187 case 0: filter->errmsg=CUS "missing test list"; return -1;
2188 default: *cond=(n==num_true); return 1;
2191 else if (parse_identifier(filter,CUS "anyof"))
2194 anyof-test = "anyof" <tests: test-list>
2199 switch (parse_testlist(filter,&n,&num_true,exec))
2202 case 0: filter->errmsg=CUS "missing test list"; return -1;
2203 default: *cond=(num_true>0); return 1;
2206 else if (parse_identifier(filter,CUS "exists"))
2209 exists-test = "exists" <header-names: string-list>
2215 if (parse_white(filter)==-1) return -1;
2216 if ((m=parse_stringlist(filter,&hdr))!=1)
2218 if (m==0) filter->errmsg=CUS "header string list expected";
2224 for (struct String * h = hdr; h->length != -1 && *cond; ++h)
2228 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2231 filter->errmsg=CUS "header string expansion failed";
2234 if (Ustrcmp(header_def,"false")==0) *cond=0;
2239 else if (parse_identifier(filter,CUS "false"))
2242 false-test = "false"
2248 else if (parse_identifier(filter,CUS "header"))
2251 header-test = "header" { [comparator] [match-type] }
2252 <header-names: string-list> <key-list: string-list>
2255 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2256 enum MatchType matchType=MATCH_IS;
2257 struct String *hdr,*key;
2263 if (parse_white(filter)==-1) return -1;
2264 if ((m=parse_comparator(filter,&comparator))!=0)
2266 if (m==-1) return -1;
2269 filter->errmsg=CUS "comparator already specified";
2274 else if ((m=parse_matchtype(filter,&matchType))!=0)
2276 if (m==-1) return -1;
2279 filter->errmsg=CUS "match type already specified";
2286 if (parse_white(filter)==-1) return -1;
2287 if ((m=parse_stringlist(filter,&hdr))!=1)
2289 if (m==0) filter->errmsg=CUS "header string list expected";
2292 if (parse_white(filter)==-1) return -1;
2293 if ((m=parse_stringlist(filter,&key))!=1)
2295 if (m==0) filter->errmsg=CUS "key string list expected";
2299 for (struct String * h = hdr; h->length != -1 && !*cond; ++h)
2303 filter->errmsg=CUS "invalid header field";
2308 struct String header_value;
2311 expand_header(&header_value,h);
2312 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2313 if (!header_value.character || !header_def)
2315 filter->errmsg=CUS "header string expansion failed";
2318 for (struct String * k = key; k->length != -1; ++k)
2319 if (Ustrcmp(header_def,"true")==0)
2321 *cond=compare(filter,k,&header_value,comparator,matchType);
2322 if (*cond==-1) return -1;
2329 else if (parse_identifier(filter,CUS "not"))
2331 if (parse_white(filter)==-1) return -1;
2332 switch (parse_test(filter,cond,exec))
2335 case 0: filter->errmsg=CUS "missing test"; return -1;
2336 default: *cond=!*cond; return 1;
2339 else if (parse_identifier(filter,CUS "size"))
2342 relop = ":over" / ":under"
2343 size-test = "size" relop <limit: number>
2346 unsigned long limit;
2349 if (parse_white(filter)==-1) return -1;
2350 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2351 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2354 filter->errmsg=CUS "missing :over or :under";
2357 if (parse_white(filter)==-1) return -1;
2358 if (parse_number(filter,&limit)==-1) return -1;
2359 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2362 else if (parse_identifier(filter,CUS "true"))
2367 else if (parse_identifier(filter,CUS "envelope"))
2370 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2371 <envelope-part: string-list> <key-list: string-list>
2373 envelope-part is case insensitive "from" or "to"
2374 #ifdef ENVELOPE_AUTH
2375 envelope-part =/ "auth"
2379 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2380 enum AddressPart addressPart=ADDRPART_ALL;
2381 enum MatchType matchType=MATCH_IS;
2382 struct String *env,*key;
2386 if (!filter->require_envelope)
2388 filter->errmsg=CUS "missing previous require \"envelope\";";
2393 if (parse_white(filter)==-1) return -1;
2394 if ((m=parse_comparator(filter,&comparator))!=0)
2396 if (m==-1) return -1;
2399 filter->errmsg=CUS "comparator already specified";
2404 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2406 if (m==-1) return -1;
2409 filter->errmsg=CUS "address part already specified";
2414 else if ((m=parse_matchtype(filter,&matchType))!=0)
2416 if (m==-1) return -1;
2419 filter->errmsg=CUS "match type already specified";
2426 if (parse_white(filter)==-1) return -1;
2427 if ((m=parse_stringlist(filter,&env))!=1)
2429 if (m==0) filter->errmsg=CUS "envelope string list expected";
2432 if (parse_white(filter)==-1) return -1;
2433 if ((m=parse_stringlist(filter,&key))!=1)
2435 if (m==0) filter->errmsg=CUS "key string list expected";
2439 for (struct String * e = env; e->length != -1 && !*cond; ++e)
2441 const uschar *envelopeExpr=CUS 0;
2442 uschar *envelope=US 0;
2444 if (eq_asciicase(e,&str_from,0))
2446 switch (addressPart)
2448 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2452 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2453 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2455 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2459 else if (eq_asciicase(e,&str_to,0))
2461 switch (addressPart)
2463 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2465 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2466 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2468 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2469 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2472 #ifdef ENVELOPE_AUTH
2473 else if (eq_asciicase(e,&str_auth,0))
2475 switch (addressPart)
2477 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2481 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2482 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2484 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2491 filter->errmsg=CUS "invalid envelope string";
2494 if (exec && envelopeExpr)
2496 if (!(envelope=expand_string(US envelopeExpr)))
2498 filter->errmsg=CUS "header string expansion failed";
2501 for (struct String * k = key; k->length != -1; ++k)
2503 struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)};
2505 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2506 if (*cond==-1) return -1;
2514 else if (parse_identifier(filter,CUS "valid_notify_method"))
2517 valid_notify_method = "valid_notify_method"
2518 <notification-uris: string-list>
2521 struct String *uris;
2524 if (!filter->require_enotify)
2526 filter->errmsg=CUS "missing previous require \"enotify\";";
2529 if (parse_white(filter)==-1) return -1;
2530 if ((m=parse_stringlist(filter,&uris))!=1)
2532 if (m==0) filter->errmsg=CUS "URI string list expected";
2538 for (struct String * u = uris; u->length != -1 && *cond; ++u)
2540 string_item *recipient;
2541 struct String header,subject,body;
2545 header.character=(uschar*)0;
2547 subject.character=(uschar*)0;
2549 body.character=(uschar*)0;
2550 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2556 else if (parse_identifier(filter,CUS "notify_method_capability"))
2559 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2560 <notification-uri: string>
2561 <notification-capability: string>
2562 <key-list: string-list>
2568 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2569 enum MatchType matchType=MATCH_IS;
2570 struct String uri,capa,*keys;
2572 if (!filter->require_enotify)
2574 filter->errmsg=CUS "missing previous require \"enotify\";";
2579 if (parse_white(filter)==-1) return -1;
2580 if ((m=parse_comparator(filter,&comparator))!=0)
2582 if (m==-1) return -1;
2585 filter->errmsg=CUS "comparator already specified";
2590 else if ((m=parse_matchtype(filter,&matchType))!=0)
2592 if (m==-1) return -1;
2595 filter->errmsg=CUS "match type already specified";
2602 if ((m=parse_string(filter,&uri))!=1)
2604 if (m==0) filter->errmsg=CUS "missing notification URI string";
2607 if (parse_white(filter)==-1) return -1;
2608 if ((m=parse_string(filter,&capa))!=1)
2610 if (m==0) filter->errmsg=CUS "missing notification capability string";
2613 if (parse_white(filter)==-1) return -1;
2614 if ((m=parse_stringlist(filter,&keys))!=1)
2616 if (m==0) filter->errmsg=CUS "missing key string list";
2621 string_item *recipient;
2622 struct String header,subject,body;
2627 header.character=(uschar*)0;
2629 subject.character=(uschar*)0;
2631 body.character=(uschar*)0;
2632 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2633 if (eq_asciicase(&capa,&str_online,0)==1)
2634 for (struct String * k = keys; k->length != -1; ++k)
2636 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2637 if (*cond==-1) return -1;
2648 /*************************************************
2649 * Parse and interpret an optional block *
2650 *************************************************/
2654 filter points to the Sieve filter including its state
2655 exec Execute parsed statements
2656 generated where to hang newly-generated addresses
2658 Returns: 2 success by stop
2660 0 no block command found
2661 -1 syntax or execution error
2664 static int parse_block(struct Sieve *filter, int exec,
2665 address_item **generated)
2669 if (parse_white(filter)==-1) return -1;
2670 if (*filter->pc=='{')
2673 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2674 if (*filter->pc=='}')
2681 filter->errmsg=CUS "expecting command or closing brace";
2689 /*************************************************
2690 * Match a semicolon *
2691 *************************************************/
2695 filter points to the Sieve filter including its state
2701 static int parse_semicolon(struct Sieve *filter)
2703 if (parse_white(filter)==-1) return -1;
2704 if (*filter->pc==';')
2711 filter->errmsg=CUS "missing semicolon";
2717 /*************************************************
2718 * Parse and interpret a Sieve command *
2719 *************************************************/
2723 filter points to the Sieve filter including its state
2724 exec Execute parsed statements
2725 generated where to hang newly-generated addresses
2727 Returns: 2 success by stop
2729 -1 syntax or execution error
2732 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2736 if (parse_white(filter)==-1) return -1;
2737 if (parse_identifier(filter,CUS "if"))
2740 if-command = "if" test block *( "elsif" test block ) [ else block ]
2743 int cond,m,unsuccessful;
2746 if (parse_white(filter)==-1) return -1;
2747 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2750 filter->errmsg=CUS "missing test";
2753 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2754 (debug_selector & D_filter) != 0)
2756 if (exec) debug_printf("if %s\n",cond?"true":"false");
2758 m=parse_block(filter,exec ? cond : 0, generated);
2759 if (m==-1 || m==2) return m;
2762 filter->errmsg=CUS "missing block";
2765 unsuccessful = !cond;
2766 for (;;) /* elsif test block */
2768 if (parse_white(filter)==-1) return -1;
2769 if (parse_identifier(filter,CUS "elsif"))
2771 if (parse_white(filter)==-1) return -1;
2772 m=parse_test(filter,&cond,exec && unsuccessful);
2773 if (m==-1 || m==2) return m;
2776 filter->errmsg=CUS "missing test";
2779 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2780 (debug_selector & D_filter) != 0)
2782 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2784 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2785 if (m==-1 || m==2) return m;
2788 filter->errmsg=CUS "missing block";
2791 if (exec && unsuccessful && cond) unsuccessful = 0;
2796 if (parse_white(filter)==-1) return -1;
2797 if (parse_identifier(filter,CUS "else"))
2799 m=parse_block(filter,exec && unsuccessful, generated);
2800 if (m==-1 || m==2) return m;
2803 filter->errmsg=CUS "missing block";
2808 else if (parse_identifier(filter,CUS "stop"))
2811 stop-command = "stop" { stop-options } ";"
2815 if (parse_semicolon(filter)==-1) return -1;
2818 filter->pc+=Ustrlen(filter->pc);
2822 else if (parse_identifier(filter,CUS "keep"))
2825 keep-command = "keep" { keep-options } ";"
2829 if (parse_semicolon(filter)==-1) return -1;
2832 add_addr(generated,US"inbox",1,0,0,0);
2836 else if (parse_identifier(filter,CUS "discard"))
2839 discard-command = "discard" { discard-options } ";"
2843 if (parse_semicolon(filter)==-1) return -1;
2844 if (exec) filter->keep=0;
2846 else if (parse_identifier(filter,CUS "redirect"))
2849 redirect-command = "redirect" redirect-options "string" ";"
2851 redirect-options =) ":copy"
2854 struct String recipient;
2860 if (parse_white(filter)==-1) return -1;
2861 if (parse_identifier(filter,CUS ":copy")==1)
2863 if (!filter->require_copy)
2865 filter->errmsg=CUS "missing previous require \"copy\";";
2872 if (parse_white(filter)==-1) return -1;
2873 if ((m=parse_string(filter,&recipient))!=1)
2875 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2878 if (strchr(CCS recipient.character,'@')==(char*)0)
2880 filter->errmsg=CUS "unqualified recipient address";
2885 add_addr(generated,recipient.character,0,0,0,0);
2886 if (!copy) filter->keep = 0;
2888 if (parse_semicolon(filter)==-1) return -1;
2890 else if (parse_identifier(filter,CUS "fileinto"))
2893 fileinto-command = "fileinto" { fileinto-options } string ";"
2895 fileinto-options =) [ ":copy" ]
2898 struct String folder;
2901 unsigned long maxage, maxmessages, maxstorage;
2904 maxage = maxmessages = maxstorage = 0;
2905 if (!filter->require_fileinto)
2907 filter->errmsg=CUS "missing previous require \"fileinto\";";
2912 if (parse_white(filter)==-1) return -1;
2913 if (parse_identifier(filter,CUS ":copy")==1)
2915 if (!filter->require_copy)
2917 filter->errmsg=CUS "missing previous require \"copy\";";
2924 if (parse_white(filter)==-1) return -1;
2925 if ((m=parse_string(filter,&folder))!=1)
2927 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2930 m=0; s=folder.character;
2931 if (folder.length==0) m=1;
2932 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2935 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2940 filter->errmsg=CUS "invalid folder";
2945 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2946 if (!copy) filter->keep = 0;
2948 if (parse_semicolon(filter)==-1) return -1;
2951 else if (parse_identifier(filter,CUS "notify"))
2954 notify-command = "notify" { notify-options } <method: string> ";"
2955 notify-options = [":from" string]
2956 [":importance" <"1" / "2" / "3">]
2957 [":options" 1*(string-list / number)]
2963 struct String importance;
2964 struct String message;
2965 struct String method;
2966 struct Notification *already;
2967 string_item *recipient;
2968 struct String header;
2969 struct String subject;
2971 uschar *envelope_from;
2972 struct String auto_submitted_value;
2973 uschar *auto_submitted_def;
2975 if (!filter->require_enotify)
2977 filter->errmsg=CUS "missing previous require \"enotify\";";
2980 from.character=(uschar*)0;
2982 importance.character=(uschar*)0;
2983 importance.length=-1;
2984 message.character=(uschar*)0;
2988 header.character=(uschar*)0;
2990 subject.character=(uschar*)0;
2992 body.character=(uschar*)0;
2993 envelope_from = sender_address && sender_address[0]
2994 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2997 filter->errmsg=CUS "expansion failure for envelope from";
3002 if (parse_white(filter)==-1) return -1;
3003 if (parse_identifier(filter,CUS ":from")==1)
3005 if (parse_white(filter)==-1) return -1;
3006 if ((m=parse_string(filter,&from))!=1)
3008 if (m==0) filter->errmsg=CUS "from string expected";
3012 else if (parse_identifier(filter,CUS ":importance")==1)
3014 if (parse_white(filter)==-1) return -1;
3015 if ((m=parse_string(filter,&importance))!=1)
3017 if (m==0) filter->errmsg=CUS "importance string expected";
3020 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3022 filter->errmsg=CUS "invalid importance";
3026 else if (parse_identifier(filter,CUS ":options")==1)
3028 if (parse_white(filter)==-1) return -1;
3030 else if (parse_identifier(filter,CUS ":message")==1)
3032 if (parse_white(filter)==-1) return -1;
3033 if ((m=parse_string(filter,&message))!=1)
3035 if (m==0) filter->errmsg=CUS "message string expected";
3041 if (parse_white(filter)==-1) return -1;
3042 if ((m=parse_string(filter,&method))!=1)
3044 if (m==0) filter->errmsg=CUS "missing method string";
3047 if (parse_semicolon(filter)==-1) return -1;
3048 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3052 if (message.length==-1) message=subject;
3053 if (message.length==-1) expand_header(&message,&str_subject);
3054 expand_header(&auto_submitted_value,&str_auto_submitted);
3055 auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}");
3056 if (!auto_submitted_value.character || !auto_submitted_def)
3058 filter->errmsg=CUS "header string expansion failed";
3061 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3063 for (already=filter->notified; already; already=already->next)
3065 if (already->method.length==method.length
3066 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3067 && already->importance.length==importance.length
3068 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3069 && already->message.length==message.length
3070 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3074 /* New notification, process it */
3076 struct Notification * sent = store_get(sizeof(struct Notification), FALSE);
3077 sent->method=method;
3078 sent->importance=importance;
3079 sent->message=message;
3080 sent->next=filter->notified;
3081 filter->notified=sent;
3082 #ifndef COMPILE_SYNTAX_CHECKER
3083 if (filter_test == FTEST_NONE)
3087 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3091 int buffer_capacity;
3093 f = fdopen(fd, "wb");
3094 fprintf(f,"From: %s\n", from.length == -1
3095 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3096 for (string_item * p = recipient; p; p=p->next)
3097 fprintf(f,"To: %s\n",p->text);
3098 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3099 if (header.length>0) fprintf(f,"%s",header.character);
3100 if (message.length==-1)
3102 message.character=US"Notification";
3103 message.length=Ustrlen(message.character);
3105 /* Allocation is larger than necessary, but enough even for split MIME words */
3106 buffer_capacity=32+4*message.length;
3107 buffer=store_get(buffer_capacity, TRUE);
3108 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3110 if (body.length>0) fprintf(f,"%s\n",body.character);
3113 (void)child_close(pid, 0);
3116 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3118 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3124 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3126 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3132 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3134 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3141 else if (parse_identifier(filter,CUS "vacation"))
3144 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3145 vacation-options = [":days" number]
3148 [":addresses" string-list]
3155 struct String subject;
3157 struct String *addresses;
3159 string_item *aliases;
3160 struct String handle;
3161 struct String reason;
3163 if (!filter->require_vacation)
3165 filter->errmsg=CUS "missing previous require \"vacation\";";
3170 if (filter->vacation_ran)
3172 filter->errmsg=CUS "trying to execute vacation more than once";
3175 filter->vacation_ran=1;
3177 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3178 subject.character=(uschar*)0;
3180 from.character=(uschar*)0;
3182 addresses=(struct String*)0;
3185 handle.character=(uschar*)0;
3189 if (parse_white(filter)==-1) return -1;
3190 if (parse_identifier(filter,CUS ":days")==1)
3192 if (parse_white(filter)==-1) return -1;
3193 if (parse_number(filter,&days)==-1) return -1;
3194 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3195 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3197 else if (parse_identifier(filter,CUS ":subject")==1)
3199 if (parse_white(filter)==-1) return -1;
3200 if ((m=parse_string(filter,&subject))!=1)
3202 if (m==0) filter->errmsg=CUS "subject string expected";
3206 else if (parse_identifier(filter,CUS ":from")==1)
3208 if (parse_white(filter)==-1) return -1;
3209 if ((m=parse_string(filter,&from))!=1)
3211 if (m==0) filter->errmsg=CUS "from string expected";
3214 if (check_mail_address(filter,&from)!=1)
3217 else if (parse_identifier(filter,CUS ":addresses")==1)
3219 if (parse_white(filter)==-1) return -1;
3220 if ((m=parse_stringlist(filter,&addresses))!=1)
3222 if (m==0) filter->errmsg=CUS "addresses string list expected";
3225 for (struct String * a = addresses; a->length != -1; ++a)
3227 string_item * new = store_get(sizeof(string_item), FALSE);
3229 new->text = store_get(a->length+1, is_tainted(a->character));
3230 if (a->length) memcpy(new->text,a->character,a->length);
3231 new->text[a->length]='\0';
3236 else if (parse_identifier(filter,CUS ":mime")==1)
3238 else if (parse_identifier(filter,CUS ":handle")==1)
3240 if (parse_white(filter)==-1) return -1;
3241 if ((m=parse_string(filter,&from))!=1)
3243 if (m==0) filter->errmsg=CUS "handle string expected";
3249 if (parse_white(filter)==-1) return -1;
3250 if ((m=parse_string(filter,&reason))!=1)
3252 if (m==0) filter->errmsg=CUS "missing reason string";
3259 for (s = reason.character, end = reason.character + reason.length;
3260 s<end && (*s&0x80)==0; ) s++;
3263 filter->errmsg=CUS "MIME reason string contains 8bit text";
3267 if (parse_semicolon(filter)==-1) return -1;
3273 int buffer_capacity;
3276 uschar hexdigest[33];
3279 if (filter_personal(aliases,TRUE))
3281 if (filter_test == FTEST_NONE)
3283 /* ensure oncelog directory exists; failure will be detected later */
3285 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3287 /* build oncelog filename */
3291 if (handle.length==-1)
3293 gstring * key = NULL;
3294 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3295 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3296 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3297 key = string_catn(key, reason.character, reason.length);
3298 md5_end(&base, key->s, key->ptr, digest);
3301 md5_end(&base, handle.character, handle.length, digest);
3303 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3305 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3306 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3308 if (filter_test == FTEST_NONE)
3310 once = string_cat (NULL, filter->vacation_directory);
3311 once = string_catn(once, US"/", 1);
3312 once = string_catn(once, hexdigest, 33);
3314 /* process subject */
3316 if (subject.length==-1)
3318 uschar *subject_def;
3320 subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3321 if (subject_def && Ustrcmp(subject_def,"true")==0)
3323 gstring * g = string_catn(NULL, US"Auto: ", 6);
3325 expand_header(&subject,&str_subject);
3326 g = string_catn(g, subject.character, subject.length);
3327 subject.character = string_from_gstring(g);
3328 subject.length = g->ptr;
3332 subject.character=US"Automated reply";
3333 subject.length=Ustrlen(subject.character);
3337 /* add address to list of generated addresses */
3339 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3340 setflag(addr, af_pfr);
3341 addr->prop.ignore_error = TRUE;
3342 addr->next = *generated;
3344 addr->reply = store_get(sizeof(reply_item), FALSE);
3345 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3346 addr->reply->to = string_copy(sender_address);
3347 if (from.length==-1)
3348 addr->reply->from = expand_string(US"$local_part@$domain");
3350 addr->reply->from = from.character;
3351 /* Allocation is larger than necessary, but enough even for split MIME words */
3352 buffer_capacity=32+4*subject.length;
3353 buffer = store_get(buffer_capacity, is_tainted(subject.character));
3354 /* deconst cast safe as we pass in a non-const item */
3355 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3356 addr->reply->oncelog = string_from_gstring(once);
3357 addr->reply->once_repeat=days*86400;
3359 /* build body and MIME headers */
3363 uschar *mime_body,*reason_end;
3364 static const uschar nlnl[]="\r\n\r\n";
3368 mime_body = reason.character, reason_end = reason.character + reason.length;
3369 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3372 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3374 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3375 else mime_body=reason_end-1;
3376 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3380 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3382 addr->reply->headers = US"MIME-Version: 1.0\n"
3383 "Content-Type: text/plain;\n"
3384 "\tcharset=\"utf-8\"\n"
3385 "Content-Transfer-Encoding: quoted-printable";
3386 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3390 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3391 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3401 /*************************************************
3402 * Parse and interpret a sieve filter *
3403 *************************************************/
3407 filter points to the Sieve filter including its state
3408 exec Execute parsed statements
3409 generated where to hang newly-generated addresses
3412 -1 syntax or execution error
3416 parse_start(struct Sieve *filter, int exec, address_item **generated)
3418 filter->pc=filter->filter;
3421 filter->require_envelope=0;
3422 filter->require_fileinto=0;
3423 #ifdef ENCODED_CHARACTER
3424 filter->require_encoded_character=0;
3426 #ifdef ENVELOPE_AUTH
3427 filter->require_envelope_auth=0;
3430 filter->require_enotify=0;
3431 filter->notified=(struct Notification*)0;
3434 filter->require_subaddress=0;
3437 filter->require_vacation=0;
3438 filter->vacation_ran=0;
3440 filter->require_copy=0;
3441 filter->require_iascii_numeric=0;
3443 if (parse_white(filter)==-1) return -1;
3445 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3448 struct dirent *oncelog;
3449 struct stat properties;
3452 /* clean up old vacation log databases */
3454 oncelogdir=opendir(CS filter->vacation_directory);
3456 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3458 filter->errmsg=CUS "unable to open vacation directory";
3462 if (oncelogdir != NULL)
3466 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3468 if (strlen(oncelog->d_name)==32)
3470 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3471 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3475 closedir(oncelogdir);
3479 while (parse_identifier(filter,CUS "require"))
3482 require-command = "require" <capabilities: string-list>
3488 if (parse_white(filter)==-1) return -1;
3489 if ((m=parse_stringlist(filter,&cap))!=1)
3491 if (m==0) filter->errmsg=CUS "capability string list expected";
3494 for (struct String * check = cap; check->character; ++check)
3496 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3497 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3498 #ifdef ENCODED_CHARACTER
3499 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3501 #ifdef ENVELOPE_AUTH
3502 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3505 else if (eq_octet(check,&str_enotify,0))
3507 if (filter->enotify_mailto_owner == NULL)
3509 filter->errmsg=CUS "enotify disabled";
3512 filter->require_enotify=1;
3516 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3519 else if (eq_octet(check,&str_vacation,0))
3521 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3523 filter->errmsg=CUS "vacation disabled";
3526 filter->require_vacation=1;
3529 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3530 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3531 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3532 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3533 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3536 filter->errmsg=CUS "unknown capability";
3540 if (parse_semicolon(filter)==-1) return -1;
3542 if (parse_commands(filter,exec,generated)==-1) return -1;
3545 filter->errmsg=CUS "syntax error";
3552 /*************************************************
3553 * Interpret a sieve filter file *
3554 *************************************************/
3558 filter points to the entire file, read into store as a single string
3559 options controls whether various special things are allowed, and requests
3560 special actions (not currently used)
3561 vacation_directory where to store vacation "once" files
3562 enotify_mailto_owner owner of mailto notifications
3563 useraddress string expression for :user part of address
3564 subaddress string expression for :subaddress part of address
3565 generated where to hang newly-generated addresses
3566 error where to pass back an error text
3568 Returns: FF_DELIVERED success, a significant action was taken
3569 FF_NOTDELIVERED success, no significant action
3570 FF_DEFER defer requested
3571 FF_FAIL fail requested
3572 FF_FREEZE freeze requested
3573 FF_ERROR there was a problem
3577 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3578 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3579 address_item **generated, uschar **error)
3585 options = options; /* Keep picky compilers happy */
3588 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3589 sieve.filter = filter;
3591 if (!vacation_directory)
3592 sieve.vacation_directory = NULL;
3595 if (!(sieve.vacation_directory = expand_string(vacation_directory)))
3597 *error = string_sprintf("failed to expand \"%s\" "
3598 "(sieve_vacation_directory): %s", vacation_directory,
3599 expand_string_message);
3604 if (!enotify_mailto_owner)
3605 sieve.enotify_mailto_owner = NULL;
3608 if (!(sieve.enotify_mailto_owner = expand_string(enotify_mailto_owner)))
3610 *error = string_sprintf("failed to expand \"%s\" "
3611 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3612 expand_string_message);
3617 sieve.useraddress = useraddress
3618 ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3619 sieve.subaddress = subaddress;
3621 #ifdef COMPILE_SYNTAX_CHECKER
3622 if (parse_start(&sieve,0,generated)==1)
3624 if (parse_start(&sieve,1,generated)==1)
3629 add_addr(generated,US"inbox",1,0,0,0);
3630 msg = US"Implicit keep";
3635 msg = US"No implicit keep";
3641 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3642 #ifdef COMPILE_SYNTAX_CHECKER
3646 add_addr(generated,US"inbox",1,0,0,0);
3651 #ifndef COMPILE_SYNTAX_CHECKER
3652 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3653 else debug_printf("%s\n", msg);
3656 DEBUG(D_route) debug_printf("Sieve: end of processing\n");