1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
6 * Copyright (c) The Exim Maintainers 2016 - 2022
7 * Copyright (c) Michael Haardt 2003 - 2015
8 * See the file NOTICE for conditions of use and distribution.
11 /* This code was contributed by Michael Haardt. */
14 /* Sieve mail filter. */
28 /* Define this for RFC compliant \r\n end-of-line terminators. */
29 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
32 /* Define this for development of the Sieve extension "encoded-character". */
33 #define ENCODED_CHARACTER
35 /* Define this for development of the Sieve extension "envelope-auth". */
38 /* Define this for development of the Sieve extension "enotify". */
41 /* Define this for the Sieve extension "subaddress". */
44 /* Define this for the Sieve extension "vacation". */
48 #define VACATION_MIN_DAYS 1
49 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
50 #define VACATION_MAX_DAYS 31
52 /* Keep this at 75 to accept only RFC compliant MIME words. */
53 /* Increase it if you want to match headers from buggy MUAs. */
54 #define MIMEWORD_LENGTH 75
65 #ifdef ENCODED_CHARACTER
66 int require_encoded_character;
69 int require_envelope_auth;
73 struct Notification *notified;
75 const uschar *enotify_mailto_owner;
77 int require_subaddress;
83 const uschar *vacation_directory;
84 const uschar *subaddress;
85 const uschar *useraddress;
87 int require_iascii_numeric;
90 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
91 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
93 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
95 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
97 enum RelOp { LT, LE, EQ, GE, GT, NE };
107 struct String method;
108 struct String importance;
109 struct String message;
110 struct Notification *next;
113 /* This should be a complete list of supported extensions, so that an external
114 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
115 list of extensions and provide correct information to a client.
117 We'll emit the list in the order given here; keep it alphabetically sorted, so
118 that callers don't get surprised.
120 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
122 const uschar *exim_sieve_extension_list[] = {
123 CUS"comparator-i;ascii-numeric",
125 #ifdef ENCODED_CHARACTER
126 CUS"encoded-character",
145 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
146 static int parse_test(struct Sieve *filter, int *cond, int exec);
147 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
149 static uschar str_from_c[]="From";
150 static const struct String str_from={ str_from_c, 4 };
151 static uschar str_to_c[]="To";
152 static const struct String str_to={ str_to_c, 2 };
153 static uschar str_cc_c[]="Cc";
154 static const struct String str_cc={ str_cc_c, 2 };
155 static uschar str_bcc_c[]="Bcc";
156 static const struct String str_bcc={ str_bcc_c, 3 };
158 static uschar str_auth_c[]="auth";
159 static const struct String str_auth={ str_auth_c, 4 };
161 static uschar str_sender_c[]="Sender";
162 static const struct String str_sender={ str_sender_c, 6 };
163 static uschar str_resent_from_c[]="Resent-From";
164 static const struct String str_resent_from={ str_resent_from_c, 11 };
165 static uschar str_resent_to_c[]="Resent-To";
166 static const struct String str_resent_to={ str_resent_to_c, 9 };
167 static uschar str_fileinto_c[]="fileinto";
168 static const struct String str_fileinto={ str_fileinto_c, 8 };
169 static uschar str_envelope_c[]="envelope";
170 static const struct String str_envelope={ str_envelope_c, 8 };
171 #ifdef ENCODED_CHARACTER
172 static uschar str_encoded_character_c[]="encoded-character";
173 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
176 static uschar str_envelope_auth_c[]="envelope-auth";
177 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
180 static uschar str_enotify_c[]="enotify";
181 static const struct String str_enotify={ str_enotify_c, 7 };
182 static uschar str_online_c[]="online";
183 static const struct String str_online={ str_online_c, 6 };
184 static uschar str_maybe_c[]="maybe";
185 static const struct String str_maybe={ str_maybe_c, 5 };
186 static uschar str_auto_submitted_c[]="Auto-Submitted";
187 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
190 static uschar str_subaddress_c[]="subaddress";
191 static const struct String str_subaddress={ str_subaddress_c, 10 };
194 static uschar str_vacation_c[]="vacation";
195 static const struct String str_vacation={ str_vacation_c, 8 };
196 static uschar str_subject_c[]="Subject";
197 static const struct String str_subject={ str_subject_c, 7 };
199 static uschar str_copy_c[]="copy";
200 static const struct String str_copy={ str_copy_c, 4 };
201 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
202 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
203 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
204 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
205 static uschar str_ioctet_c[]="i;octet";
206 static const struct String str_ioctet={ str_ioctet_c, 7 };
207 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
208 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
209 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
210 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
211 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
212 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
213 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
214 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
215 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
216 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
219 /*************************************************
220 * Encode to quoted-printable *
221 *************************************************/
232 static struct String *
233 quoted_printable_encode(const struct String *src, struct String *dst)
239 /* Two passes: one to count output allocation size, second
240 to do the encoding */
242 for (int pass = 0; pass <= 1; pass++)
249 dst->character = store_get(dst->length+1, src->character); /* plus one for \0 */
252 for (const uschar * start = src->character, * end = start + src->length;
253 start < end; ++start)
256 if (line>=73) /* line length limit */
262 *new++='='; /* line split */
267 if ( (ch>='!' && ch<='<')
268 || (ch>='>' && ch<='~')
269 || ( (ch=='\t' || ch==' ')
271 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
278 *new++=*start; /* copy char */
281 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
286 *new++='\n'; /* NL */
288 ++start; /* consume extra input char */
296 new += sprintf(CS new,"=%02X",ch);
302 *new='\0'; /* not included in length, but nice */
307 /*************************************************
308 * Check mail address for correct syntax *
309 *************************************************/
312 Check mail address for being syntactically correct.
315 filter points to the Sieve filter including its state
316 address String containing one address
319 1 Mail address is syntactically OK
323 int check_mail_address(struct Sieve *filter, const struct String *address)
325 int start, end, domain;
328 if (address->length>0)
330 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
334 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
335 address->character, error);
343 filter->errmsg=CUS "empty address";
349 /*************************************************
350 * Decode URI encoded string *
351 *************************************************/
355 str URI encoded string
358 0 Decoding successful
364 uri_decode(struct String *str)
368 if (str->length==0) return 0;
369 for (s=str->character,t=s,e=s+str->length; s<e; )
372 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
374 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
375 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
384 str->length=t-str->character;
389 /*************************************************
391 *************************************************/
396 mailtoURI = "mailto:" [ to ] [ headers ]
397 to = [ addr-spec *("%2C" addr-spec ) ]
398 headers = "?" header *( "&" header )
399 header = hname "=" hvalue
404 filter points to the Sieve filter including its state
405 uri URI, excluding scheme
410 1 URI is syntactically OK
416 parse_mailto_uri(struct Sieve *filter, const uschar *uri,
417 string_item **recipient, struct String *header, struct String *subject,
421 struct String to, hname;
422 struct String hvalue = {.character = NULL, .length = 0};
425 if (Ustrncmp(uri,"mailto:",7))
427 filter->errmsg=US "Unknown URI scheme";
432 if (*uri && *uri!='?')
436 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
439 gstring * g = string_catn(NULL, start, uri-start);
441 to.character = string_from_gstring(g);
443 if (uri_decode(&to)==-1)
445 filter->errmsg=US"Invalid URI encoding";
448 new = store_get(sizeof(string_item), GET_UNTAINTED);
449 new->text = store_get(to.length+1, to.character);
450 if (to.length) memcpy(new->text, to.character, to.length);
451 new->text[to.length] = '\0';
452 new->next = *recipient;
457 filter->errmsg = US"Missing addr-spec in URI";
460 if (*uri=='%') uri+=3;
469 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
472 gstring * g = string_catn(NULL, start, uri-start);
474 hname.character = string_from_gstring(g);
475 hname.length = g->ptr;
476 if (uri_decode(&hname)==-1)
478 filter->errmsg=US"Invalid URI encoding";
487 filter->errmsg=US"Missing equal after hname";
491 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
494 gstring * g = string_catn(NULL, start, uri-start);
496 hname.character = string_from_gstring(g);
497 hname.length = g->ptr;
498 if (uri_decode(&hvalue)==-1)
500 filter->errmsg=US"Invalid URI encoding";
504 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
506 new=store_get(sizeof(string_item), GET_UNTAINTED);
507 new->text = store_get(hvalue.length+1, hvalue.character);
508 if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length);
509 new->text[hvalue.length]='\0';
510 new->next=*recipient;
513 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
515 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
519 static struct String ignore[]=
525 {US"auto-submitted",14}
527 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
530 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
535 if (header->length==-1) header->length = 0;
537 g = string_catn(NULL, header->character, header->length);
538 g = string_catn(g, hname.character, hname.length);
539 g = string_catn(g, CUS ": ", 2);
540 g = string_catn(g, hvalue.character, hvalue.length);
541 g = string_catn(g, CUS "\n", 1);
543 header->character = string_from_gstring(g);
544 header->length = g->ptr;
547 if (*uri=='&') ++uri;
553 filter->errmsg=US"Syntactically invalid URI";
561 /*************************************************
562 * Octet-wise string comparison *
563 *************************************************/
567 needle UTF-8 string to search ...
568 haystack ... inside the haystack
569 match_prefix 1 to compare if needle is a prefix of haystack
571 Returns: 0 needle not found in haystack
575 static int eq_octet(const struct String *needle,
576 const struct String *haystack, int match_prefix)
584 h=haystack->character;
588 if (*n&0x80) return 0;
589 if (*h&0x80) return 0;
591 if (*n!=*h) return 0;
597 return (match_prefix ? nl==0 : nl==0 && hl==0);
601 /*************************************************
602 * ASCII case-insensitive string comparison *
603 *************************************************/
607 needle UTF-8 string to search ...
608 haystack ... inside the haystack
609 match_prefix 1 to compare if needle is a prefix of haystack
611 Returns: 0 needle not found in haystack
615 static int eq_asciicase(const struct String *needle,
616 const struct String *haystack, int match_prefix)
625 h=haystack->character;
631 if (nc&0x80) return 0;
632 if (hc&0x80) return 0;
634 /* tolower depends on the locale and only ASCII case must be insensitive */
635 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
641 return (match_prefix ? nl==0 : nl==0 && hl==0);
645 /*************************************************
646 * Glob pattern search *
647 *************************************************/
651 needle pattern to search ...
652 haystack ... inside the haystack
653 ascii_caseless ignore ASCII case
654 match_octet match octets, not UTF-8 multi-octet characters
656 Returns: 0 needle not found in haystack
661 static int eq_glob(const struct String *needle,
662 const struct String *haystack, int ascii_caseless, int match_octet)
664 const uschar *n,*h,*nend,*hend;
668 h=haystack->character;
669 nend=n+needle->length;
670 hend=h+haystack->length;
680 const uschar *npart,*hpart;
682 /* Try to match a non-star part of the needle at the current */
683 /* position in the haystack. */
687 while (npart<nend && *npart!='*') switch (*npart)
691 if (hpart==hend) return 0;
696 /* Match one UTF8 encoded character */
697 if ((*hpart&0xc0)==0xc0)
700 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
711 if (npart==nend) return -1;
716 if (hpart==hend) return 0;
717 /* tolower depends on the locale, but we need ASCII */
721 (*hpart&0x80) || (*npart&0x80) ||
724 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
729 /* string match after a star failed, advance and try again */
743 /* at this point, a part was matched successfully */
744 if (may_advance && npart==nend && hpart<hend)
745 /* needle ends, but haystack does not: if there was a star before, advance and try again */
755 return (h==hend ? 1 : may_advance);
759 /*************************************************
760 * ASCII numeric comparison *
761 *************************************************/
765 a first numeric string
766 b second numeric string
767 relop relational operator
769 Returns: 0 not (a relop b)
773 static int eq_asciinumeric(const struct String *a,
774 const struct String *b, enum RelOp relop)
777 const uschar *as,*aend,*bs,*bend;
781 aend=a->character+a->length;
783 bend=b->character+b->length;
785 while (*as>='0' && *as<='9' && as<aend) ++as;
787 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
790 if (al && bl==0) cmp=-1;
791 else if (al==0 && bl==0) cmp=0;
792 else if (al==0 && bl) cmp=1;
796 if (cmp==0) cmp=memcmp(a->character,b->character,al);
800 case LT: return cmp<0;
801 case LE: return cmp<=0;
802 case EQ: return cmp==0;
803 case GE: return cmp>=0;
804 case GT: return cmp>0;
805 case NE: return cmp!=0;
812 /*************************************************
814 *************************************************/
818 filter points to the Sieve filter including its state
819 needle UTF-8 pattern or string to search ...
820 haystack ... inside the haystack
824 Returns: 0 needle not found in haystack
826 -1 comparator does not offer matchtype
829 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
830 enum Comparator co, enum MatchType mt)
834 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
835 (debug_selector & D_filter) != 0)
837 debug_printf("String comparison (match ");
840 case MATCH_IS: debug_printf(":is"); break;
841 case MATCH_CONTAINS: debug_printf(":contains"); break;
842 case MATCH_MATCHES: debug_printf(":matches"); break;
844 debug_printf(", comparison \"");
847 case COMP_OCTET: debug_printf("i;octet"); break;
848 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
849 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
851 debug_printf("\"):\n");
852 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
853 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
861 if (eq_octet(needle,haystack,0)) r=1;
863 case COMP_EN_ASCII_CASEMAP:
864 if (eq_asciicase(needle,haystack,0)) r=1;
866 case COMP_ASCII_NUMERIC:
867 if (!filter->require_iascii_numeric)
869 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
872 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
884 for (h = *haystack; h.length; ++h.character,--h.length)
885 if (eq_octet(needle,&h,1)) { r=1; break; }
887 case COMP_EN_ASCII_CASEMAP:
888 for (h = *haystack; h.length; ++h.character, --h.length)
889 if (eq_asciicase(needle,&h,1)) { r=1; break; }
892 filter->errmsg=CUS "comparator does not offer specified matchtype";
902 if ((r=eq_glob(needle,haystack,0,1))==-1)
904 filter->errmsg=CUS "syntactically invalid pattern";
908 case COMP_EN_ASCII_CASEMAP:
909 if ((r=eq_glob(needle,haystack,1,1))==-1)
911 filter->errmsg=CUS "syntactically invalid pattern";
916 filter->errmsg=CUS "comparator does not offer specified matchtype";
921 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
922 (debug_selector & D_filter) != 0)
923 debug_printf(" Result %s\n",r?"true":"false");
928 /*************************************************
929 * Check header field syntax *
930 *************************************************/
933 RFC 2822, section 3.6.8 says:
937 ftext = %d33-57 / ; Any character except
938 %d59-126 ; controls, SP, and
941 That forbids 8-bit header fields. This implementation accepts them, since
942 all of Exim is 8-bit clean, so it adds %d128-%d255.
945 header header field to quote for suitable use in Exim expansions
947 Returns: 0 string is not a valid header field
948 1 string is a value header field
951 static int is_header(const struct String *header)
961 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
972 /*************************************************
973 * Quote special characters string *
974 *************************************************/
978 header header field to quote for suitable use in Exim expansions
981 Returns: quoted string
984 static const uschar *
985 quote(const struct String *header)
987 gstring * quoted = NULL;
998 quoted = string_catn(quoted, CUS "\\0", 2);
1003 quoted = string_catn(quoted, CUS "\\", 1);
1005 quoted = string_catn(quoted, h, 1);
1010 quoted = string_catn(quoted, CUS "", 1);
1011 return string_from_gstring(quoted);
1015 /*************************************************
1016 * Add address to list of generated addresses *
1017 *************************************************/
1020 According to RFC 5228, duplicate delivery to the same address must
1021 not happen, so the list is first searched for the address.
1024 generated list of generated addresses
1025 addr new address to add
1026 file address denotes a file
1032 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1034 address_item *new_addr;
1036 for (new_addr = *generated; new_addr; new_addr = new_addr->next)
1037 if ( Ustrcmp(new_addr->address,addr) == 0
1039 || testflag(new_addr, af_pfr)
1040 || testflag(new_addr, af_file)
1044 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1045 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1050 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1051 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1053 new_addr = deliver_make_addr(addr,TRUE);
1056 setflag(new_addr, af_pfr);
1057 setflag(new_addr, af_file);
1060 new_addr->prop.errors_address = NULL;
1061 new_addr->next = *generated;
1062 *generated = new_addr;
1066 /*************************************************
1067 * Return decoded header field *
1068 *************************************************/
1071 Unfold the header field as described in RFC 2822 and remove all
1072 leading and trailing white space, then perform MIME decoding and
1073 translate the header field to UTF-8.
1076 value returned value of the field
1077 header name of the header field
1079 Returns: nothing The expanded string is empty
1080 in case there is no such header
1083 static void expand_header(struct String *value, const struct String *header)
1089 value->character=(uschar*)0;
1091 t = r = s = expand_string(string_sprintf("$rheader_%s",quote(header)));
1093 while (*r==' ' || *r=='\t') ++r;
1101 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1103 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1107 /*************************************************
1108 * Parse remaining hash comment *
1109 *************************************************/
1113 Comment up to terminating CRLF
1116 filter points to the Sieve filter including its state
1122 static int parse_hashcomment(struct Sieve *filter)
1128 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1130 if (*filter->pc=='\n')
1143 filter->errmsg=CUS "missing end of comment";
1148 /*************************************************
1149 * Parse remaining C-style comment *
1150 *************************************************/
1154 Everything up to star slash
1157 filter points to the Sieve filter including its state
1163 static int parse_comment(struct Sieve *filter)
1168 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1175 filter->errmsg=CUS "missing end of comment";
1180 /*************************************************
1181 * Parse optional white space *
1182 *************************************************/
1186 Spaces, tabs, CRLFs, hash comments or C-style comments
1189 filter points to the Sieve filter including its state
1195 static int parse_white(struct Sieve *filter)
1199 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1201 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1203 else if (*filter->pc=='\n')
1213 else if (*filter->pc=='#')
1215 if (parse_hashcomment(filter)==-1) return -1;
1217 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1219 if (parse_comment(filter)==-1) return -1;
1227 #ifdef ENCODED_CHARACTER
1228 /*************************************************
1229 * Decode hex-encoded-character string *
1230 *************************************************/
1233 Encoding definition:
1234 blank = SP / TAB / CRLF
1235 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1236 hex-pair = 1*2HEXDIG
1239 src points to a hex-pair-seq
1240 end points to its end
1241 dst points to the destination of the decoded octets,
1242 optionally to (uschar*)0 for checking only
1244 Returns: >=0 number of decoded octets
1248 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1252 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1258 d<2 && src<end && isxdigit(n=tolower(*src));
1259 x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ;
1260 if (d==0) return -1;
1263 if (src==end) return decoded;
1264 if (*src==' ' || *src=='\t' || *src=='\n')
1265 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1274 /*************************************************
1275 * Decode unicode-encoded-character string *
1276 *************************************************/
1279 Encoding definition:
1280 blank = SP / TAB / CRLF
1281 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1282 unicode-hex = 1*HEXDIG
1284 It is an error for a script to use a hexadecimal value that isn't in
1285 either the range 0 to D7FF or the range E000 to 10FFFF.
1287 At this time, strings are already scanned, thus the CRLF is converted
1288 to the internally used \n (should RFC_EOL have been used).
1291 src points to a unicode-hex-seq
1292 end points to its end
1293 dst points to the destination of the decoded octets,
1294 optionally to (uschar*)0 for checking only
1296 Returns: >=0 number of decoded octets
1298 -2 semantic error (character range violation)
1302 unicode_decode(uschar *src, uschar *end, uschar *dst)
1306 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1313 for (hex_seq = src; src < end && *src=='0'; ) src++;
1315 d < 7 && src < end && isxdigit(n=tolower(*src));
1316 c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1317 if (src == hex_seq) return -1;
1318 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1324 else if (c>=0x80 && c<=0x7ff)
1329 *dst++=128+(c&0x3f);
1333 else if (c>=0x800 && c<=0xffff)
1338 *dst++=128+((c>>6)&0x3f);
1339 *dst++=128+(c&0x3f);
1343 else if (c>=0x10000 && c<=0x1fffff)
1348 *dst++=128+((c>>10)&0x3f);
1349 *dst++=128+((c>>6)&0x3f);
1350 *dst++=128+(c&0x3f);
1354 if (*src==' ' || *src=='\t' || *src=='\n')
1356 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1357 if (src==end) return decoded;
1366 /*************************************************
1367 * Decode encoded-character string *
1368 *************************************************/
1371 Encoding definition:
1372 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1373 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1376 encoded points to an encoded string, returns decoded string
1377 filter points to the Sieve filter including its state
1383 static int string_decode(struct Sieve *filter, struct String *data)
1385 uschar *src,*dst,*end;
1387 src=data->character;
1389 end=data->character+data->length;
1395 strncmpic(src,US "${hex:",6)==0
1396 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1397 && (hex_decode(src+6,brace,(uschar*)0))>=0
1400 dst+=hex_decode(src+6,brace,dst);
1404 strncmpic(src,US "${unicode:",10)==0
1405 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1408 switch (unicode_decode(src+10,brace,(uschar*)0))
1412 filter->errmsg=CUS "unicode character out of range";
1422 dst+=unicode_decode(src+10,brace,dst);
1429 data->length=dst-data->character;
1436 /*************************************************
1437 * Parse an optional string *
1438 *************************************************/
1442 quoted-string = DQUOTE *CHAR DQUOTE
1443 ;; in general, \ CHAR inside a string maps to CHAR
1444 ;; so \" maps to " and \\ maps to \
1445 ;; note that newlines and other characters are all allowed
1448 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1449 *(multi-line-literal / multi-line-dotstuff)
1451 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1452 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1453 ;; A line containing only "." ends the multi-line.
1454 ;; Remove a leading '.' if followed by another '.'.
1455 string = quoted-string / multi-line
1458 filter points to the Sieve filter including its state
1459 id specifies identifier to match
1463 0 identifier not matched
1467 parse_string(struct Sieve *filter, struct String *data)
1472 data->character = NULL;
1474 if (*filter->pc=='"') /* quoted string */
1479 if (*filter->pc=='"') /* end of string */
1485 data->character = string_from_gstring(g);
1486 data->length = g->ptr;
1489 data->character = US"\0";
1490 /* that way, there will be at least one character allocated */
1492 #ifdef ENCODED_CHARACTER
1493 if (filter->require_encoded_character
1494 && string_decode(filter,data)==-1)
1499 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1501 g = string_catn(g, filter->pc+1, 1);
1504 else /* regular character */
1507 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1509 if (*filter->pc=='\n')
1511 g = string_catn(g, US"\r", 1);
1515 g = string_catn(g, filter->pc, 1);
1519 filter->errmsg=CUS "missing end of string";
1522 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1525 /* skip optional white space followed by hashed comment or CRLF */
1526 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1527 if (*filter->pc=='#')
1529 if (parse_hashcomment(filter)==-1) return -1;
1532 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1534 else if (*filter->pc=='\n')
1546 filter->errmsg=CUS "syntax error";
1552 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1554 if (*filter->pc=='\n') /* end of line */
1557 g = string_catn(g, CUS "\r\n", 2);
1565 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1567 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1572 data->character = string_from_gstring(g);
1573 data->length = g->ptr;
1576 data->character = US"\0";
1577 /* that way, there will be at least one character allocated */
1585 #ifdef ENCODED_CHARACTER
1586 if (filter->require_encoded_character
1587 && string_decode(filter,data)==-1)
1592 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1594 g = string_catn(g, CUS ".", 1);
1598 else /* regular character */
1600 g = string_catn(g, filter->pc, 1);
1604 filter->errmsg=CUS "missing end of multi line string";
1611 /*************************************************
1612 * Parse a specific identifier *
1613 *************************************************/
1617 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1620 filter points to the Sieve filter including its state
1621 id specifies identifier to match
1624 0 identifier not matched
1627 static int parse_identifier(struct Sieve *filter, const uschar *id)
1629 size_t idlen=Ustrlen(id);
1631 if (strncmpic(US filter->pc,US id,idlen)==0)
1633 uschar next=filter->pc[idlen];
1635 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1643 /*************************************************
1645 *************************************************/
1649 number = 1*DIGIT [QUANTIFIER]
1650 QUANTIFIER = "K" / "M" / "G"
1653 filter points to the Sieve filter including its state
1657 -1 no string list found
1660 static int parse_number(struct Sieve *filter, unsigned long *data)
1664 if (*filter->pc>='0' && *filter->pc<='9')
1669 d=Ustrtoul(filter->pc,&e,10);
1672 filter->errmsg=CUstrerror(ERANGE);
1677 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1678 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1679 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1680 if (d>(ULONG_MAX/u))
1682 filter->errmsg=CUstrerror(ERANGE);
1691 filter->errmsg=CUS "missing number";
1697 /*************************************************
1698 * Parse a string list *
1699 *************************************************/
1703 string-list = "[" string *("," string) "]" / string
1706 filter points to the Sieve filter including its state
1707 data returns string list
1710 -1 no string list found
1714 parse_stringlist(struct Sieve *filter, struct String **data)
1716 const uschar *orig=filter->pc;
1717 int dataCapacity = 0;
1719 struct String *d = NULL;
1722 if (*filter->pc=='[') /* string list */
1727 if (parse_white(filter)==-1) goto error;
1728 if (dataLength+1 >= dataCapacity) /* increase buffer */
1732 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1733 new = store_get(sizeof(struct String) * dataCapacity, GET_UNTAINTED);
1735 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1739 m=parse_string(filter,&d[dataLength]);
1742 if (dataLength==0) break;
1745 filter->errmsg=CUS "missing string";
1749 else if (m==-1) goto error;
1751 if (parse_white(filter)==-1) goto error;
1752 if (*filter->pc==',') ++filter->pc;
1755 if (*filter->pc==']')
1757 d[dataLength].character=(uschar*)0;
1758 d[dataLength].length=-1;
1765 filter->errmsg=CUS "missing closing bracket";
1769 else /* single string */
1771 if (!(d=store_get(sizeof(struct String)*2, GET_UNTAINTED)))
1774 m=parse_string(filter,&d[0]);
1785 d[1].character=(uschar*)0;
1792 filter->errmsg=CUS "missing string list";
1797 /*************************************************
1798 * Parse an optional address part specifier *
1799 *************************************************/
1803 address-part = ":localpart" / ":domain" / ":all"
1804 address-part =/ ":user" / ":detail"
1807 filter points to the Sieve filter including its state
1808 a returns address part specified
1811 0 no comparator found
1815 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1818 if (parse_identifier(filter,CUS ":user")==1)
1820 if (!filter->require_subaddress)
1822 filter->errmsg=CUS "missing previous require \"subaddress\";";
1828 else if (parse_identifier(filter,CUS ":detail")==1)
1830 if (!filter->require_subaddress)
1832 filter->errmsg=CUS "missing previous require \"subaddress\";";
1840 if (parse_identifier(filter,CUS ":localpart")==1)
1842 *a=ADDRPART_LOCALPART;
1845 else if (parse_identifier(filter,CUS ":domain")==1)
1850 else if (parse_identifier(filter,CUS ":all")==1)
1859 /*************************************************
1860 * Parse an optional comparator *
1861 *************************************************/
1865 comparator = ":comparator" <comparator-name: string>
1868 filter points to the Sieve filter including its state
1869 c returns comparator
1872 0 no comparator found
1873 -1 incomplete comparator found
1876 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1878 struct String comparator_name;
1880 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1881 if (parse_white(filter)==-1) return -1;
1882 switch (parse_string(filter,&comparator_name))
1887 filter->errmsg=CUS "missing comparator";
1894 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1899 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1901 *c=COMP_EN_ASCII_CASEMAP;
1904 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1906 *c=COMP_EN_ASCII_CASEMAP;
1909 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1911 *c=COMP_ASCII_NUMERIC;
1916 filter->errmsg=CUS "invalid comparator";
1925 /*************************************************
1926 * Parse an optional match type *
1927 *************************************************/
1931 match-type = ":is" / ":contains" / ":matches"
1934 filter points to the Sieve filter including its state
1935 m returns match type
1938 0 no match type found
1941 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1943 if (parse_identifier(filter,CUS ":is")==1)
1948 else if (parse_identifier(filter,CUS ":contains")==1)
1953 else if (parse_identifier(filter,CUS ":matches")==1)
1962 /*************************************************
1963 * Parse and interpret an optional test list *
1964 *************************************************/
1968 test-list = "(" test *("," test) ")"
1971 filter points to the Sieve filter including its state
1972 n total number of tests
1973 num_true number of passed tests
1974 exec Execute parsed statements
1977 0 no test list found
1978 -1 syntax or execution error
1981 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1983 if (parse_white(filter)==-1) return -1;
1984 if (*filter->pc=='(')
1993 switch (parse_test(filter,&cond,exec))
1996 case 0: filter->errmsg=CUS "missing test"; return -1;
1997 default: ++*n; if (cond) ++*num_true; break;
1999 if (parse_white(filter)==-1) return -1;
2000 if (*filter->pc==',') ++filter->pc;
2003 if (*filter->pc==')')
2010 filter->errmsg=CUS "missing closing paren";
2018 /*************************************************
2019 * Parse and interpret an optional test *
2020 *************************************************/
2024 filter points to the Sieve filter including its state
2025 cond returned condition status
2026 exec Execute parsed statements
2030 -1 syntax or execution error
2034 parse_test(struct Sieve *filter, int *cond, int exec)
2036 if (parse_white(filter)==-1) return -1;
2037 if (parse_identifier(filter,CUS "address"))
2040 address-test = "address" { [address-part] [comparator] [match-type] }
2041 <header-list: string-list> <key-list: string-list>
2043 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2046 enum AddressPart addressPart=ADDRPART_ALL;
2047 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2048 enum MatchType matchType=MATCH_IS;
2049 struct String *hdr,*key;
2055 if (parse_white(filter)==-1) return -1;
2056 if ((m=parse_addresspart(filter,&addressPart))!=0)
2058 if (m==-1) return -1;
2061 filter->errmsg=CUS "address part already specified";
2066 else if ((m=parse_comparator(filter,&comparator))!=0)
2068 if (m==-1) return -1;
2071 filter->errmsg=CUS "comparator already specified";
2076 else if ((m=parse_matchtype(filter,&matchType))!=0)
2078 if (m==-1) return -1;
2081 filter->errmsg=CUS "match type already specified";
2088 if (parse_white(filter)==-1) return -1;
2089 if ((m=parse_stringlist(filter,&hdr))!=1)
2091 if (m==0) filter->errmsg=CUS "header string list expected";
2094 if (parse_white(filter)==-1) return -1;
2095 if ((m=parse_stringlist(filter,&key))!=1)
2097 if (m==0) filter->errmsg=CUS "key string list expected";
2101 for (struct String * h = hdr; h->length!=-1 && !*cond; ++h)
2103 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2107 !eq_asciicase(h,&str_from,0)
2108 && !eq_asciicase(h,&str_to,0)
2109 && !eq_asciicase(h,&str_cc,0)
2110 && !eq_asciicase(h,&str_bcc,0)
2111 && !eq_asciicase(h,&str_sender,0)
2112 && !eq_asciicase(h,&str_resent_from,0)
2113 && !eq_asciicase(h,&str_resent_to,0)
2116 filter->errmsg=CUS "invalid header field";
2121 /* We are only interested in addresses below, so no MIME decoding */
2122 if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h)))))
2124 filter->errmsg=CUS "header string expansion failed";
2127 f.parse_allow_group = TRUE;
2128 while (*header_value && !*cond)
2131 int start, end, domain;
2135 end_addr = parse_find_address_end(header_value, FALSE);
2136 saveend = *end_addr;
2138 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2140 if (extracted_addr) switch (addressPart)
2142 case ADDRPART_ALL: part=extracted_addr; break;
2146 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2147 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2149 case ADDRPART_DETAIL: part=NULL; break;
2153 *end_addr = saveend;
2156 for (struct String * k = key; k->length !=- 1; ++k)
2158 struct String partStr = {.character = part, .length = Ustrlen(part)};
2162 *cond=compare(filter,k,&partStr,comparator,matchType);
2163 if (*cond==-1) return -1;
2168 if (saveend == 0) break;
2169 header_value = end_addr + 1;
2171 f.parse_allow_group = FALSE;
2172 f.parse_found_group = FALSE;
2177 else if (parse_identifier(filter,CUS "allof"))
2180 allof-test = "allof" <tests: test-list>
2185 switch (parse_testlist(filter,&n,&num_true,exec))
2188 case 0: filter->errmsg=CUS "missing test list"; return -1;
2189 default: *cond=(n==num_true); return 1;
2192 else if (parse_identifier(filter,CUS "anyof"))
2195 anyof-test = "anyof" <tests: test-list>
2200 switch (parse_testlist(filter,&n,&num_true,exec))
2203 case 0: filter->errmsg=CUS "missing test list"; return -1;
2204 default: *cond=(num_true>0); return 1;
2207 else if (parse_identifier(filter,CUS "exists"))
2210 exists-test = "exists" <header-names: string-list>
2216 if (parse_white(filter)==-1) return -1;
2217 if ((m=parse_stringlist(filter,&hdr))!=1)
2219 if (m==0) filter->errmsg=CUS "header string list expected";
2225 for (struct String * h = hdr; h->length != -1 && *cond; ++h)
2229 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2232 filter->errmsg=CUS "header string expansion failed";
2235 if (Ustrcmp(header_def,"false")==0) *cond=0;
2240 else if (parse_identifier(filter,CUS "false"))
2243 false-test = "false"
2249 else if (parse_identifier(filter,CUS "header"))
2252 header-test = "header" { [comparator] [match-type] }
2253 <header-names: string-list> <key-list: string-list>
2256 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2257 enum MatchType matchType=MATCH_IS;
2258 struct String *hdr,*key;
2264 if (parse_white(filter)==-1) return -1;
2265 if ((m=parse_comparator(filter,&comparator))!=0)
2267 if (m==-1) return -1;
2270 filter->errmsg=CUS "comparator already specified";
2275 else if ((m=parse_matchtype(filter,&matchType))!=0)
2277 if (m==-1) return -1;
2280 filter->errmsg=CUS "match type already specified";
2287 if (parse_white(filter)==-1) return -1;
2288 if ((m=parse_stringlist(filter,&hdr))!=1)
2290 if (m==0) filter->errmsg=CUS "header string list expected";
2293 if (parse_white(filter)==-1) return -1;
2294 if ((m=parse_stringlist(filter,&key))!=1)
2296 if (m==0) filter->errmsg=CUS "key string list expected";
2300 for (struct String * h = hdr; h->length != -1 && !*cond; ++h)
2304 filter->errmsg=CUS "invalid header field";
2309 struct String header_value;
2312 expand_header(&header_value,h);
2313 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2314 if (!header_value.character || !header_def)
2316 filter->errmsg=CUS "header string expansion failed";
2319 for (struct String * k = key; k->length != -1; ++k)
2320 if (Ustrcmp(header_def,"true")==0)
2322 *cond=compare(filter,k,&header_value,comparator,matchType);
2323 if (*cond==-1) return -1;
2330 else if (parse_identifier(filter,CUS "not"))
2332 if (parse_white(filter)==-1) return -1;
2333 switch (parse_test(filter,cond,exec))
2336 case 0: filter->errmsg=CUS "missing test"; return -1;
2337 default: *cond=!*cond; return 1;
2340 else if (parse_identifier(filter,CUS "size"))
2343 relop = ":over" / ":under"
2344 size-test = "size" relop <limit: number>
2347 unsigned long limit;
2350 if (parse_white(filter)==-1) return -1;
2351 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2352 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2355 filter->errmsg=CUS "missing :over or :under";
2358 if (parse_white(filter)==-1) return -1;
2359 if (parse_number(filter,&limit)==-1) return -1;
2360 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2363 else if (parse_identifier(filter,CUS "true"))
2368 else if (parse_identifier(filter,CUS "envelope"))
2371 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2372 <envelope-part: string-list> <key-list: string-list>
2374 envelope-part is case insensitive "from" or "to"
2375 #ifdef ENVELOPE_AUTH
2376 envelope-part =/ "auth"
2380 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2381 enum AddressPart addressPart=ADDRPART_ALL;
2382 enum MatchType matchType=MATCH_IS;
2383 struct String *env,*key;
2387 if (!filter->require_envelope)
2389 filter->errmsg=CUS "missing previous require \"envelope\";";
2394 if (parse_white(filter)==-1) return -1;
2395 if ((m=parse_comparator(filter,&comparator))!=0)
2397 if (m==-1) return -1;
2400 filter->errmsg=CUS "comparator already specified";
2405 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2407 if (m==-1) return -1;
2410 filter->errmsg=CUS "address part already specified";
2415 else if ((m=parse_matchtype(filter,&matchType))!=0)
2417 if (m==-1) return -1;
2420 filter->errmsg=CUS "match type already specified";
2427 if (parse_white(filter)==-1) return -1;
2428 if ((m=parse_stringlist(filter,&env))!=1)
2430 if (m==0) filter->errmsg=CUS "envelope string list expected";
2433 if (parse_white(filter)==-1) return -1;
2434 if ((m=parse_stringlist(filter,&key))!=1)
2436 if (m==0) filter->errmsg=CUS "key string list expected";
2440 for (struct String * e = env; e->length != -1 && !*cond; ++e)
2442 const uschar *envelopeExpr=CUS 0;
2443 uschar *envelope=US 0;
2445 if (eq_asciicase(e,&str_from,0))
2447 switch (addressPart)
2449 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2453 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2454 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2456 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2460 else if (eq_asciicase(e,&str_to,0))
2462 switch (addressPart)
2464 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2466 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2467 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2469 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2470 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2473 #ifdef ENVELOPE_AUTH
2474 else if (eq_asciicase(e,&str_auth,0))
2476 switch (addressPart)
2478 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2482 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2483 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2485 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2492 filter->errmsg=CUS "invalid envelope string";
2495 if (exec && envelopeExpr)
2497 if (!(envelope=expand_string(US envelopeExpr)))
2499 filter->errmsg=CUS "header string expansion failed";
2502 for (struct String * k = key; k->length != -1; ++k)
2504 struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)};
2506 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2507 if (*cond==-1) return -1;
2515 else if (parse_identifier(filter,CUS "valid_notify_method"))
2518 valid_notify_method = "valid_notify_method"
2519 <notification-uris: string-list>
2522 struct String *uris;
2525 if (!filter->require_enotify)
2527 filter->errmsg=CUS "missing previous require \"enotify\";";
2530 if (parse_white(filter)==-1) return -1;
2531 if ((m=parse_stringlist(filter,&uris))!=1)
2533 if (m==0) filter->errmsg=CUS "URI string list expected";
2539 for (struct String * u = uris; u->length != -1 && *cond; ++u)
2541 string_item *recipient;
2542 struct String header,subject,body;
2546 header.character=(uschar*)0;
2548 subject.character=(uschar*)0;
2550 body.character=(uschar*)0;
2551 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2557 else if (parse_identifier(filter,CUS "notify_method_capability"))
2560 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2561 <notification-uri: string>
2562 <notification-capability: string>
2563 <key-list: string-list>
2569 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2570 enum MatchType matchType=MATCH_IS;
2571 struct String uri,capa,*keys;
2573 if (!filter->require_enotify)
2575 filter->errmsg=CUS "missing previous require \"enotify\";";
2580 if (parse_white(filter)==-1) return -1;
2581 if ((m=parse_comparator(filter,&comparator))!=0)
2583 if (m==-1) return -1;
2586 filter->errmsg=CUS "comparator already specified";
2591 else if ((m=parse_matchtype(filter,&matchType))!=0)
2593 if (m==-1) return -1;
2596 filter->errmsg=CUS "match type already specified";
2603 if ((m=parse_string(filter,&uri))!=1)
2605 if (m==0) filter->errmsg=CUS "missing notification URI string";
2608 if (parse_white(filter)==-1) return -1;
2609 if ((m=parse_string(filter,&capa))!=1)
2611 if (m==0) filter->errmsg=CUS "missing notification capability string";
2614 if (parse_white(filter)==-1) return -1;
2615 if ((m=parse_stringlist(filter,&keys))!=1)
2617 if (m==0) filter->errmsg=CUS "missing key string list";
2622 string_item *recipient;
2623 struct String header,subject,body;
2628 header.character=(uschar*)0;
2630 subject.character=(uschar*)0;
2632 body.character=(uschar*)0;
2633 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2634 if (eq_asciicase(&capa,&str_online,0)==1)
2635 for (struct String * k = keys; k->length != -1; ++k)
2637 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2638 if (*cond==-1) return -1;
2649 /*************************************************
2650 * Parse and interpret an optional block *
2651 *************************************************/
2655 filter points to the Sieve filter including its state
2656 exec Execute parsed statements
2657 generated where to hang newly-generated addresses
2659 Returns: 2 success by stop
2661 0 no block command found
2662 -1 syntax or execution error
2665 static int parse_block(struct Sieve *filter, int exec,
2666 address_item **generated)
2670 if (parse_white(filter)==-1) return -1;
2671 if (*filter->pc=='{')
2674 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2675 if (*filter->pc=='}')
2682 filter->errmsg=CUS "expecting command or closing brace";
2690 /*************************************************
2691 * Match a semicolon *
2692 *************************************************/
2696 filter points to the Sieve filter including its state
2702 static int parse_semicolon(struct Sieve *filter)
2704 if (parse_white(filter)==-1) return -1;
2705 if (*filter->pc==';')
2712 filter->errmsg=CUS "missing semicolon";
2718 /*************************************************
2719 * Parse and interpret a Sieve command *
2720 *************************************************/
2724 filter points to the Sieve filter including its state
2725 exec Execute parsed statements
2726 generated where to hang newly-generated addresses
2728 Returns: 2 success by stop
2730 -1 syntax or execution error
2733 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2737 if (parse_white(filter)==-1) return -1;
2738 if (parse_identifier(filter,CUS "if"))
2741 if-command = "if" test block *( "elsif" test block ) [ else block ]
2744 int cond,m,unsuccessful;
2747 if (parse_white(filter)==-1) return -1;
2748 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2751 filter->errmsg=CUS "missing test";
2754 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2755 (debug_selector & D_filter) != 0)
2757 if (exec) debug_printf("if %s\n",cond?"true":"false");
2759 m=parse_block(filter,exec ? cond : 0, generated);
2760 if (m==-1 || m==2) return m;
2763 filter->errmsg=CUS "missing block";
2766 unsuccessful = !cond;
2767 for (;;) /* elsif test block */
2769 if (parse_white(filter)==-1) return -1;
2770 if (parse_identifier(filter,CUS "elsif"))
2772 if (parse_white(filter)==-1) return -1;
2773 m=parse_test(filter,&cond,exec && unsuccessful);
2774 if (m==-1 || m==2) return m;
2777 filter->errmsg=CUS "missing test";
2780 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2781 (debug_selector & D_filter) != 0)
2783 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2785 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2786 if (m==-1 || m==2) return m;
2789 filter->errmsg=CUS "missing block";
2792 if (exec && unsuccessful && cond) unsuccessful = 0;
2797 if (parse_white(filter)==-1) return -1;
2798 if (parse_identifier(filter,CUS "else"))
2800 m=parse_block(filter,exec && unsuccessful, generated);
2801 if (m==-1 || m==2) return m;
2804 filter->errmsg=CUS "missing block";
2809 else if (parse_identifier(filter,CUS "stop"))
2812 stop-command = "stop" { stop-options } ";"
2816 if (parse_semicolon(filter)==-1) return -1;
2819 filter->pc+=Ustrlen(filter->pc);
2823 else if (parse_identifier(filter,CUS "keep"))
2826 keep-command = "keep" { keep-options } ";"
2830 if (parse_semicolon(filter)==-1) return -1;
2833 add_addr(generated,US"inbox",1,0,0,0);
2837 else if (parse_identifier(filter,CUS "discard"))
2840 discard-command = "discard" { discard-options } ";"
2844 if (parse_semicolon(filter)==-1) return -1;
2845 if (exec) filter->keep=0;
2847 else if (parse_identifier(filter,CUS "redirect"))
2850 redirect-command = "redirect" redirect-options "string" ";"
2852 redirect-options =) ":copy"
2855 struct String recipient;
2861 if (parse_white(filter)==-1) return -1;
2862 if (parse_identifier(filter,CUS ":copy")==1)
2864 if (!filter->require_copy)
2866 filter->errmsg=CUS "missing previous require \"copy\";";
2873 if (parse_white(filter)==-1) return -1;
2874 if ((m=parse_string(filter,&recipient))!=1)
2876 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2879 if (strchr(CCS recipient.character,'@')==(char*)0)
2881 filter->errmsg=CUS "unqualified recipient address";
2886 add_addr(generated,recipient.character,0,0,0,0);
2887 if (!copy) filter->keep = 0;
2889 if (parse_semicolon(filter)==-1) return -1;
2891 else if (parse_identifier(filter,CUS "fileinto"))
2894 fileinto-command = "fileinto" { fileinto-options } string ";"
2896 fileinto-options =) [ ":copy" ]
2899 struct String folder;
2902 unsigned long maxage, maxmessages, maxstorage;
2905 maxage = maxmessages = maxstorage = 0;
2906 if (!filter->require_fileinto)
2908 filter->errmsg=CUS "missing previous require \"fileinto\";";
2913 if (parse_white(filter)==-1) return -1;
2914 if (parse_identifier(filter,CUS ":copy")==1)
2916 if (!filter->require_copy)
2918 filter->errmsg=CUS "missing previous require \"copy\";";
2925 if (parse_white(filter)==-1) return -1;
2926 if ((m=parse_string(filter,&folder))!=1)
2928 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2931 m=0; s=folder.character;
2932 if (folder.length==0) m=1;
2933 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2936 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2941 filter->errmsg=CUS "invalid folder";
2946 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2947 if (!copy) filter->keep = 0;
2949 if (parse_semicolon(filter)==-1) return -1;
2952 else if (parse_identifier(filter,CUS "notify"))
2955 notify-command = "notify" { notify-options } <method: string> ";"
2956 notify-options = [":from" string]
2957 [":importance" <"1" / "2" / "3">]
2958 [":options" 1*(string-list / number)]
2964 struct String importance;
2965 struct String message;
2966 struct String method;
2967 struct Notification *already;
2968 string_item *recipient;
2969 struct String header;
2970 struct String subject;
2972 uschar *envelope_from;
2973 struct String auto_submitted_value;
2974 uschar *auto_submitted_def;
2976 if (!filter->require_enotify)
2978 filter->errmsg=CUS "missing previous require \"enotify\";";
2981 from.character=(uschar*)0;
2983 importance.character=(uschar*)0;
2984 importance.length=-1;
2985 message.character=(uschar*)0;
2989 header.character=(uschar*)0;
2991 subject.character=(uschar*)0;
2993 body.character=(uschar*)0;
2994 envelope_from = sender_address && sender_address[0]
2995 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2998 filter->errmsg=CUS "expansion failure for envelope from";
3003 if (parse_white(filter)==-1) return -1;
3004 if (parse_identifier(filter,CUS ":from")==1)
3006 if (parse_white(filter)==-1) return -1;
3007 if ((m=parse_string(filter,&from))!=1)
3009 if (m==0) filter->errmsg=CUS "from string expected";
3013 else if (parse_identifier(filter,CUS ":importance")==1)
3015 if (parse_white(filter)==-1) return -1;
3016 if ((m=parse_string(filter,&importance))!=1)
3018 if (m==0) filter->errmsg=CUS "importance string expected";
3021 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3023 filter->errmsg=CUS "invalid importance";
3027 else if (parse_identifier(filter,CUS ":options")==1)
3029 if (parse_white(filter)==-1) return -1;
3031 else if (parse_identifier(filter,CUS ":message")==1)
3033 if (parse_white(filter)==-1) return -1;
3034 if ((m=parse_string(filter,&message))!=1)
3036 if (m==0) filter->errmsg=CUS "message string expected";
3042 if (parse_white(filter)==-1) return -1;
3043 if ((m=parse_string(filter,&method))!=1)
3045 if (m==0) filter->errmsg=CUS "missing method string";
3048 if (parse_semicolon(filter)==-1) return -1;
3049 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3053 if (message.length==-1) message=subject;
3054 if (message.length==-1) expand_header(&message,&str_subject);
3055 expand_header(&auto_submitted_value,&str_auto_submitted);
3056 auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}");
3057 if (!auto_submitted_value.character || !auto_submitted_def)
3059 filter->errmsg=CUS "header string expansion failed";
3062 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3064 for (already=filter->notified; already; already=already->next)
3066 if (already->method.length==method.length
3067 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3068 && already->importance.length==importance.length
3069 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3070 && already->message.length==message.length
3071 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3075 /* New notification, process it */
3077 struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED);
3078 sent->method=method;
3079 sent->importance=importance;
3080 sent->message=message;
3081 sent->next=filter->notified;
3082 filter->notified=sent;
3083 #ifndef COMPILE_SYNTAX_CHECKER
3084 if (filter_test == FTEST_NONE)
3088 if ((pid = child_open_exim2(&fd, envelope_from, envelope_from,
3089 US"sieve-notify")) >= 1)
3091 FILE * f = fdopen(fd, "wb");
3093 fprintf(f,"From: %s\n", from.length == -1
3094 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
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 if (message.length != -1)
3106 fprintf(f, "Subject: %s\n", parse_quote_2047(message.character,
3107 message.length, US"utf-8", TRUE));
3109 if (body.length > 0) fprintf(f, "%s\n", body.character);
3112 (void)child_close(pid, 0);
3115 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3116 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3120 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3121 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3124 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3125 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3130 else if (parse_identifier(filter,CUS "vacation"))
3133 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3134 vacation-options = [":days" number]
3137 [":addresses" string-list]
3144 struct String subject;
3146 struct String *addresses;
3148 string_item *aliases;
3149 struct String handle;
3150 struct String reason;
3152 if (!filter->require_vacation)
3154 filter->errmsg=CUS "missing previous require \"vacation\";";
3159 if (filter->vacation_ran)
3161 filter->errmsg=CUS "trying to execute vacation more than once";
3164 filter->vacation_ran=1;
3166 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3167 subject.character=(uschar*)0;
3169 from.character=(uschar*)0;
3171 addresses=(struct String*)0;
3174 handle.character=(uschar*)0;
3178 if (parse_white(filter)==-1) return -1;
3179 if (parse_identifier(filter,CUS ":days")==1)
3181 if (parse_white(filter)==-1) return -1;
3182 if (parse_number(filter,&days)==-1) return -1;
3183 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3184 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3186 else if (parse_identifier(filter,CUS ":subject")==1)
3188 if (parse_white(filter)==-1) return -1;
3189 if ((m=parse_string(filter,&subject))!=1)
3191 if (m==0) filter->errmsg=CUS "subject string expected";
3195 else if (parse_identifier(filter,CUS ":from")==1)
3197 if (parse_white(filter)==-1) return -1;
3198 if ((m=parse_string(filter,&from))!=1)
3200 if (m==0) filter->errmsg=CUS "from string expected";
3203 if (check_mail_address(filter,&from)!=1)
3206 else if (parse_identifier(filter,CUS ":addresses")==1)
3208 if (parse_white(filter)==-1) return -1;
3209 if ((m=parse_stringlist(filter,&addresses))!=1)
3211 if (m==0) filter->errmsg=CUS "addresses string list expected";
3214 for (struct String * a = addresses; a->length != -1; ++a)
3216 string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
3218 new->text = store_get(a->length+1, a->character);
3219 if (a->length) memcpy(new->text,a->character,a->length);
3220 new->text[a->length]='\0';
3225 else if (parse_identifier(filter,CUS ":mime")==1)
3227 else if (parse_identifier(filter,CUS ":handle")==1)
3229 if (parse_white(filter)==-1) return -1;
3230 if ((m=parse_string(filter,&from))!=1)
3232 if (m==0) filter->errmsg=CUS "handle string expected";
3238 if (parse_white(filter)==-1) return -1;
3239 if ((m=parse_string(filter,&reason))!=1)
3241 if (m==0) filter->errmsg=CUS "missing reason string";
3248 for (s = reason.character, end = reason.character + reason.length;
3249 s<end && (*s&0x80)==0; ) s++;
3252 filter->errmsg=CUS "MIME reason string contains 8bit text";
3256 if (parse_semicolon(filter)==-1) return -1;
3263 uschar hexdigest[33];
3266 if (filter_personal(aliases,TRUE))
3268 if (filter_test == FTEST_NONE)
3270 /* ensure oncelog directory exists; failure will be detected later */
3272 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3274 /* build oncelog filename */
3278 if (handle.length==-1)
3280 gstring * key = NULL;
3281 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3282 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3283 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3284 key = string_catn(key, reason.character, reason.length);
3285 md5_end(&base, key->s, key->ptr, digest);
3288 md5_end(&base, handle.character, handle.length, digest);
3290 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3292 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3293 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3295 if (filter_test == FTEST_NONE)
3297 once = string_cat (NULL, filter->vacation_directory);
3298 once = string_catn(once, US"/", 1);
3299 once = string_catn(once, hexdigest, 33);
3301 /* process subject */
3303 if (subject.length==-1)
3305 uschar *subject_def;
3307 subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3308 if (subject_def && Ustrcmp(subject_def,"true")==0)
3310 gstring * g = string_catn(NULL, US"Auto: ", 6);
3312 expand_header(&subject,&str_subject);
3313 g = string_catn(g, subject.character, subject.length);
3314 subject.character = string_from_gstring(g);
3315 subject.length = g->ptr;
3319 subject.character=US"Automated reply";
3320 subject.length=Ustrlen(subject.character);
3324 /* add address to list of generated addresses */
3326 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3327 setflag(addr, af_pfr);
3328 addr->prop.ignore_error = TRUE;
3329 addr->next = *generated;
3331 addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
3332 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3333 addr->reply->to = string_copy(sender_address);
3334 if (from.length==-1)
3335 addr->reply->from = expand_string(US"$local_part@$domain");
3337 addr->reply->from = from.character;
3338 /* deconst cast safe as we pass in a non-const item */
3339 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE);
3340 addr->reply->oncelog = string_from_gstring(once);
3341 addr->reply->once_repeat=days*86400;
3343 /* build body and MIME headers */
3347 uschar *mime_body,*reason_end;
3348 static const uschar nlnl[]="\r\n\r\n";
3352 mime_body = reason.character, reason_end = reason.character + reason.length;
3353 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3356 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3358 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3359 else mime_body=reason_end-1;
3360 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3364 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3366 addr->reply->headers = US"MIME-Version: 1.0\n"
3367 "Content-Type: text/plain;\n"
3368 "\tcharset=\"utf-8\"\n"
3369 "Content-Transfer-Encoding: quoted-printable";
3370 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3374 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3375 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3385 /*************************************************
3386 * Parse and interpret a sieve filter *
3387 *************************************************/
3391 filter points to the Sieve filter including its state
3392 exec Execute parsed statements
3393 generated where to hang newly-generated addresses
3396 -1 syntax or execution error
3400 parse_start(struct Sieve *filter, int exec, address_item **generated)
3402 filter->pc=filter->filter;
3405 filter->require_envelope=0;
3406 filter->require_fileinto=0;
3407 #ifdef ENCODED_CHARACTER
3408 filter->require_encoded_character=0;
3410 #ifdef ENVELOPE_AUTH
3411 filter->require_envelope_auth=0;
3414 filter->require_enotify=0;
3415 filter->notified=(struct Notification*)0;
3418 filter->require_subaddress=0;
3421 filter->require_vacation=0;
3422 filter->vacation_ran=0;
3424 filter->require_copy=0;
3425 filter->require_iascii_numeric=0;
3427 if (parse_white(filter)==-1) return -1;
3429 if (exec && filter->vacation_directory && filter_test == FTEST_NONE)
3432 struct dirent *oncelog;
3433 struct stat properties;
3436 /* clean up old vacation log databases */
3438 if ( !(oncelogdir = exim_opendir(filter->vacation_directory))
3441 filter->errmsg = CUS "unable to open vacation directory";
3449 while ((oncelog = readdir(oncelogdir)))
3450 if (strlen(oncelog->d_name)==32)
3452 uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name);
3453 if (Ustat(s,&properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now)
3456 closedir(oncelogdir);
3460 while (parse_identifier(filter,CUS "require"))
3463 require-command = "require" <capabilities: string-list>
3469 if (parse_white(filter)==-1) return -1;
3470 if ((m=parse_stringlist(filter,&cap))!=1)
3472 if (m==0) filter->errmsg=CUS "capability string list expected";
3475 for (struct String * check = cap; check->character; ++check)
3477 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3478 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3479 #ifdef ENCODED_CHARACTER
3480 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3482 #ifdef ENVELOPE_AUTH
3483 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3486 else if (eq_octet(check,&str_enotify,0))
3488 if (!filter->enotify_mailto_owner)
3490 filter->errmsg=CUS "enotify disabled";
3493 filter->require_enotify=1;
3497 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3500 else if (eq_octet(check,&str_vacation,0))
3502 if (filter_test == FTEST_NONE && !filter->vacation_directory)
3504 filter->errmsg=CUS "vacation disabled";
3507 filter->require_vacation=1;
3510 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3511 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3512 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3513 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3514 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3517 filter->errmsg=CUS "unknown capability";
3521 if (parse_semicolon(filter)==-1) return -1;
3523 if (parse_commands(filter,exec,generated)==-1) return -1;
3526 filter->errmsg=CUS "syntax error";
3533 /*************************************************
3534 * Interpret a sieve filter file *
3535 *************************************************/
3539 filter points to the entire file, read into store as a single string
3540 options controls whether various special things are allowed, and requests
3541 special actions (not currently used)
3542 vacation_directory where to store vacation "once" files
3543 enotify_mailto_owner owner of mailto notifications
3544 useraddress string expression for :user part of address
3545 subaddress string expression for :subaddress part of address
3546 generated where to hang newly-generated addresses
3547 error where to pass back an error text
3549 Returns: FF_DELIVERED success, a significant action was taken
3550 FF_NOTDELIVERED success, no significant action
3551 FF_DEFER defer requested
3552 FF_FAIL fail requested
3553 FF_FREEZE freeze requested
3554 FF_ERROR there was a problem
3558 sieve_interpret(const uschar * filter, int options,
3559 const uschar * vacation_directory, const uschar * enotify_mailto_owner,
3560 const uschar * useraddress, const uschar * subaddress,
3561 address_item ** generated, uschar ** error)
3567 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3568 sieve.filter = filter;
3570 if (!vacation_directory)
3571 sieve.vacation_directory = NULL;
3572 else if (!(sieve.vacation_directory = expand_cstring(vacation_directory)))
3574 *error = string_sprintf("failed to expand \"%s\" "
3575 "(sieve_vacation_directory): %s", vacation_directory,
3576 expand_string_message);
3580 if (!enotify_mailto_owner)
3581 sieve.enotify_mailto_owner = NULL;
3582 else if (!(sieve.enotify_mailto_owner = expand_cstring(enotify_mailto_owner)))
3584 *error = string_sprintf("failed to expand \"%s\" "
3585 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3586 expand_string_message);
3590 sieve.useraddress = useraddress
3591 ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3592 sieve.subaddress = subaddress;
3594 #ifdef COMPILE_SYNTAX_CHECKER
3595 if (parse_start(&sieve, 0, generated) == 1)
3597 if (parse_start(&sieve, 1, generated) == 1)
3601 add_addr(generated, US"inbox", 1, 0, 0, 0);
3602 msg = US"Implicit keep";
3607 msg = US"No implicit keep";
3612 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3613 #ifdef COMPILE_SYNTAX_CHECKER
3617 add_addr(generated,US"inbox",1,0,0,0);
3622 #ifndef COMPILE_SYNTAX_CHECKER
3623 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3624 else debug_printf("%s\n", msg);
3627 DEBUG(D_route) debug_printf("Sieve: end of processing\n");