1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2016 - 2020
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,
3088 US"sieve-notify")) >= 1)
3090 FILE * f = fdopen(fd, "wb");
3092 fprintf(f,"From: %s\n", from.length == -1
3093 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
3095 for (string_item * p = recipient; p; p=p->next)
3096 fprintf(f,"To: %s\n",p->text);
3097 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3098 if (header.length>0) fprintf(f,"%s",header.character);
3099 if (message.length==-1)
3101 message.character=US"Notification";
3102 message.length=Ustrlen(message.character);
3104 if (message.length != -1)
3105 fprintf(f, "Subject: %s\n", parse_quote_2047(message.character,
3106 message.length, US"utf-8", TRUE));
3108 if (body.length>0) fprintf(f,"%s\n",body.character);
3111 (void)child_close(pid, 0);
3114 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3115 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3119 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3120 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3123 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3124 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3129 else if (parse_identifier(filter,CUS "vacation"))
3132 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3133 vacation-options = [":days" number]
3136 [":addresses" string-list]
3143 struct String subject;
3145 struct String *addresses;
3147 string_item *aliases;
3148 struct String handle;
3149 struct String reason;
3151 if (!filter->require_vacation)
3153 filter->errmsg=CUS "missing previous require \"vacation\";";
3158 if (filter->vacation_ran)
3160 filter->errmsg=CUS "trying to execute vacation more than once";
3163 filter->vacation_ran=1;
3165 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3166 subject.character=(uschar*)0;
3168 from.character=(uschar*)0;
3170 addresses=(struct String*)0;
3173 handle.character=(uschar*)0;
3177 if (parse_white(filter)==-1) return -1;
3178 if (parse_identifier(filter,CUS ":days")==1)
3180 if (parse_white(filter)==-1) return -1;
3181 if (parse_number(filter,&days)==-1) return -1;
3182 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3183 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3185 else if (parse_identifier(filter,CUS ":subject")==1)
3187 if (parse_white(filter)==-1) return -1;
3188 if ((m=parse_string(filter,&subject))!=1)
3190 if (m==0) filter->errmsg=CUS "subject string expected";
3194 else if (parse_identifier(filter,CUS ":from")==1)
3196 if (parse_white(filter)==-1) return -1;
3197 if ((m=parse_string(filter,&from))!=1)
3199 if (m==0) filter->errmsg=CUS "from string expected";
3202 if (check_mail_address(filter,&from)!=1)
3205 else if (parse_identifier(filter,CUS ":addresses")==1)
3207 if (parse_white(filter)==-1) return -1;
3208 if ((m=parse_stringlist(filter,&addresses))!=1)
3210 if (m==0) filter->errmsg=CUS "addresses string list expected";
3213 for (struct String * a = addresses; a->length != -1; ++a)
3215 string_item * new = store_get(sizeof(string_item), FALSE);
3217 new->text = store_get(a->length+1, is_tainted(a->character));
3218 if (a->length) memcpy(new->text,a->character,a->length);
3219 new->text[a->length]='\0';
3224 else if (parse_identifier(filter,CUS ":mime")==1)
3226 else if (parse_identifier(filter,CUS ":handle")==1)
3228 if (parse_white(filter)==-1) return -1;
3229 if ((m=parse_string(filter,&from))!=1)
3231 if (m==0) filter->errmsg=CUS "handle string expected";
3237 if (parse_white(filter)==-1) return -1;
3238 if ((m=parse_string(filter,&reason))!=1)
3240 if (m==0) filter->errmsg=CUS "missing reason string";
3247 for (s = reason.character, end = reason.character + reason.length;
3248 s<end && (*s&0x80)==0; ) s++;
3251 filter->errmsg=CUS "MIME reason string contains 8bit text";
3255 if (parse_semicolon(filter)==-1) return -1;
3262 uschar hexdigest[33];
3265 if (filter_personal(aliases,TRUE))
3267 if (filter_test == FTEST_NONE)
3269 /* ensure oncelog directory exists; failure will be detected later */
3271 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3273 /* build oncelog filename */
3277 if (handle.length==-1)
3279 gstring * key = NULL;
3280 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3281 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3282 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3283 key = string_catn(key, reason.character, reason.length);
3284 md5_end(&base, key->s, key->ptr, digest);
3287 md5_end(&base, handle.character, handle.length, digest);
3289 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3291 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3292 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3294 if (filter_test == FTEST_NONE)
3296 once = string_cat (NULL, filter->vacation_directory);
3297 once = string_catn(once, US"/", 1);
3298 once = string_catn(once, hexdigest, 33);
3300 /* process subject */
3302 if (subject.length==-1)
3304 uschar *subject_def;
3306 subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3307 if (subject_def && Ustrcmp(subject_def,"true")==0)
3309 gstring * g = string_catn(NULL, US"Auto: ", 6);
3311 expand_header(&subject,&str_subject);
3312 g = string_catn(g, subject.character, subject.length);
3313 subject.character = string_from_gstring(g);
3314 subject.length = g->ptr;
3318 subject.character=US"Automated reply";
3319 subject.length=Ustrlen(subject.character);
3323 /* add address to list of generated addresses */
3325 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3326 setflag(addr, af_pfr);
3327 addr->prop.ignore_error = TRUE;
3328 addr->next = *generated;
3330 addr->reply = store_get(sizeof(reply_item), FALSE);
3331 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3332 addr->reply->to = string_copy(sender_address);
3333 if (from.length==-1)
3334 addr->reply->from = expand_string(US"$local_part@$domain");
3336 addr->reply->from = from.character;
3337 /* deconst cast safe as we pass in a non-const item */
3338 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE);
3339 addr->reply->oncelog = string_from_gstring(once);
3340 addr->reply->once_repeat=days*86400;
3342 /* build body and MIME headers */
3346 uschar *mime_body,*reason_end;
3347 static const uschar nlnl[]="\r\n\r\n";
3351 mime_body = reason.character, reason_end = reason.character + reason.length;
3352 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3355 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3357 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3358 else mime_body=reason_end-1;
3359 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3363 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3365 addr->reply->headers = US"MIME-Version: 1.0\n"
3366 "Content-Type: text/plain;\n"
3367 "\tcharset=\"utf-8\"\n"
3368 "Content-Transfer-Encoding: quoted-printable";
3369 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3373 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3374 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3384 /*************************************************
3385 * Parse and interpret a sieve filter *
3386 *************************************************/
3390 filter points to the Sieve filter including its state
3391 exec Execute parsed statements
3392 generated where to hang newly-generated addresses
3395 -1 syntax or execution error
3399 parse_start(struct Sieve *filter, int exec, address_item **generated)
3401 filter->pc=filter->filter;
3404 filter->require_envelope=0;
3405 filter->require_fileinto=0;
3406 #ifdef ENCODED_CHARACTER
3407 filter->require_encoded_character=0;
3409 #ifdef ENVELOPE_AUTH
3410 filter->require_envelope_auth=0;
3413 filter->require_enotify=0;
3414 filter->notified=(struct Notification*)0;
3417 filter->require_subaddress=0;
3420 filter->require_vacation=0;
3421 filter->vacation_ran=0;
3423 filter->require_copy=0;
3424 filter->require_iascii_numeric=0;
3426 if (parse_white(filter)==-1) return -1;
3428 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3431 struct dirent *oncelog;
3432 struct stat properties;
3435 /* clean up old vacation log databases */
3437 if ( !(oncelogdir = exim_opendir(filter->vacation_directory))
3440 filter->errmsg = CUS "unable to open vacation directory";
3448 while ((oncelog = readdir(oncelogdir)))
3449 if (strlen(oncelog->d_name)==32)
3451 uschar *s = string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3452 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3455 closedir(oncelogdir);
3459 while (parse_identifier(filter,CUS "require"))
3462 require-command = "require" <capabilities: string-list>
3468 if (parse_white(filter)==-1) return -1;
3469 if ((m=parse_stringlist(filter,&cap))!=1)
3471 if (m==0) filter->errmsg=CUS "capability string list expected";
3474 for (struct String * check = cap; check->character; ++check)
3476 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3477 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3478 #ifdef ENCODED_CHARACTER
3479 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3481 #ifdef ENVELOPE_AUTH
3482 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3485 else if (eq_octet(check,&str_enotify,0))
3487 if (filter->enotify_mailto_owner == NULL)
3489 filter->errmsg=CUS "enotify disabled";
3492 filter->require_enotify=1;
3496 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3499 else if (eq_octet(check,&str_vacation,0))
3501 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3503 filter->errmsg=CUS "vacation disabled";
3506 filter->require_vacation=1;
3509 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3510 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3511 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3512 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3513 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3516 filter->errmsg=CUS "unknown capability";
3520 if (parse_semicolon(filter)==-1) return -1;
3522 if (parse_commands(filter,exec,generated)==-1) return -1;
3525 filter->errmsg=CUS "syntax error";
3532 /*************************************************
3533 * Interpret a sieve filter file *
3534 *************************************************/
3538 filter points to the entire file, read into store as a single string
3539 options controls whether various special things are allowed, and requests
3540 special actions (not currently used)
3541 vacation_directory where to store vacation "once" files
3542 enotify_mailto_owner owner of mailto notifications
3543 useraddress string expression for :user part of address
3544 subaddress string expression for :subaddress part of address
3545 generated where to hang newly-generated addresses
3546 error where to pass back an error text
3548 Returns: FF_DELIVERED success, a significant action was taken
3549 FF_NOTDELIVERED success, no significant action
3550 FF_DEFER defer requested
3551 FF_FAIL fail requested
3552 FF_FREEZE freeze requested
3553 FF_ERROR there was a problem
3557 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3558 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3559 address_item **generated, uschar **error)
3565 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3566 sieve.filter = filter;
3568 if (!vacation_directory)
3569 sieve.vacation_directory = NULL;
3572 if (!(sieve.vacation_directory = expand_string(vacation_directory)))
3574 *error = string_sprintf("failed to expand \"%s\" "
3575 "(sieve_vacation_directory): %s", vacation_directory,
3576 expand_string_message);
3581 if (!enotify_mailto_owner)
3582 sieve.enotify_mailto_owner = NULL;
3585 if (!(sieve.enotify_mailto_owner = expand_string(enotify_mailto_owner)))
3587 *error = string_sprintf("failed to expand \"%s\" "
3588 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3589 expand_string_message);
3594 sieve.useraddress = useraddress
3595 ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3596 sieve.subaddress = subaddress;
3598 #ifdef COMPILE_SYNTAX_CHECKER
3599 if (parse_start(&sieve,0,generated)==1)
3601 if (parse_start(&sieve,1,generated)==1)
3606 add_addr(generated,US"inbox",1,0,0,0);
3607 msg = US"Implicit keep";
3612 msg = US"No implicit keep";
3618 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3619 #ifdef COMPILE_SYNTAX_CHECKER
3623 add_addr(generated,US"inbox",1,0,0,0);
3628 #ifndef COMPILE_SYNTAX_CHECKER
3629 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3630 else debug_printf("%s\n", msg);
3633 DEBUG(D_route) debug_printf("Sieve: end of processing\n");