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.
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 /* This code was contributed by Michael Haardt. */
15 /* Sieve mail filter. */
29 /* Define this for RFC compliant \r\n end-of-line terminators. */
30 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
33 /* Define this for development of the Sieve extension "encoded-character". */
34 #define ENCODED_CHARACTER
36 /* Define this for development of the Sieve extension "envelope-auth". */
39 /* Define this for development of the Sieve extension "enotify". */
42 /* Define this for the Sieve extension "subaddress". */
45 /* Define this for the Sieve extension "vacation". */
49 #define VACATION_MIN_DAYS 1
50 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
51 #define VACATION_MAX_DAYS 31
53 /* Keep this at 75 to accept only RFC compliant MIME words. */
54 /* Increase it if you want to match headers from buggy MUAs. */
55 #define MIMEWORD_LENGTH 75
66 #ifdef ENCODED_CHARACTER
67 int require_encoded_character;
70 int require_envelope_auth;
74 struct Notification *notified;
76 const uschar *enotify_mailto_owner;
78 int require_subaddress;
84 const uschar *vacation_directory;
85 const uschar *subaddress;
86 const uschar *useraddress;
88 int require_iascii_numeric;
91 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
92 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
94 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
96 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
98 enum RelOp { LT, LE, EQ, GE, GT, NE };
108 struct String method;
109 struct String importance;
110 struct String message;
111 struct Notification *next;
114 /* This should be a complete list of supported extensions, so that an external
115 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
116 list of extensions and provide correct information to a client.
118 We'll emit the list in the order given here; keep it alphabetically sorted, so
119 that callers don't get surprised.
121 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
123 const uschar *exim_sieve_extension_list[] = {
124 CUS"comparator-i;ascii-numeric",
126 #ifdef ENCODED_CHARACTER
127 CUS"encoded-character",
146 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
147 static int parse_test(struct Sieve *filter, int *cond, int exec);
148 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
150 static uschar str_from_c[]="From";
151 static const struct String str_from={ str_from_c, 4 };
152 static uschar str_to_c[]="To";
153 static const struct String str_to={ str_to_c, 2 };
154 static uschar str_cc_c[]="Cc";
155 static const struct String str_cc={ str_cc_c, 2 };
156 static uschar str_bcc_c[]="Bcc";
157 static const struct String str_bcc={ str_bcc_c, 3 };
159 static uschar str_auth_c[]="auth";
160 static const struct String str_auth={ str_auth_c, 4 };
162 static uschar str_sender_c[]="Sender";
163 static const struct String str_sender={ str_sender_c, 6 };
164 static uschar str_resent_from_c[]="Resent-From";
165 static const struct String str_resent_from={ str_resent_from_c, 11 };
166 static uschar str_resent_to_c[]="Resent-To";
167 static const struct String str_resent_to={ str_resent_to_c, 9 };
168 static uschar str_fileinto_c[]="fileinto";
169 static const struct String str_fileinto={ str_fileinto_c, 8 };
170 static uschar str_envelope_c[]="envelope";
171 static const struct String str_envelope={ str_envelope_c, 8 };
172 #ifdef ENCODED_CHARACTER
173 static uschar str_encoded_character_c[]="encoded-character";
174 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
177 static uschar str_envelope_auth_c[]="envelope-auth";
178 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
181 static uschar str_enotify_c[]="enotify";
182 static const struct String str_enotify={ str_enotify_c, 7 };
183 static uschar str_online_c[]="online";
184 static const struct String str_online={ str_online_c, 6 };
185 static uschar str_maybe_c[]="maybe";
186 static const struct String str_maybe={ str_maybe_c, 5 };
187 static uschar str_auto_submitted_c[]="Auto-Submitted";
188 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
191 static uschar str_subaddress_c[]="subaddress";
192 static const struct String str_subaddress={ str_subaddress_c, 10 };
195 static uschar str_vacation_c[]="vacation";
196 static const struct String str_vacation={ str_vacation_c, 8 };
197 static uschar str_subject_c[]="Subject";
198 static const struct String str_subject={ str_subject_c, 7 };
200 static uschar str_copy_c[]="copy";
201 static const struct String str_copy={ str_copy_c, 4 };
202 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
203 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
204 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
205 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
206 static uschar str_ioctet_c[]="i;octet";
207 static const struct String str_ioctet={ str_ioctet_c, 7 };
208 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
209 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
210 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
211 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
212 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
213 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
214 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
215 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
216 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
217 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
220 /*************************************************
221 * Encode to quoted-printable *
222 *************************************************/
233 static struct String *
234 quoted_printable_encode(const struct String *src, struct String *dst)
240 /* Two passes: one to count output allocation size, second
241 to do the encoding */
243 for (int pass = 0; pass <= 1; pass++)
250 dst->character = store_get(dst->length+1, src->character); /* plus one for \0 */
253 for (const uschar * start = src->character, * end = start + src->length;
254 start < end; ++start)
257 if (line>=73) /* line length limit */
263 *new++='='; /* line split */
268 if ( (ch>='!' && ch<='<')
269 || (ch>='>' && ch<='~')
270 || ( (ch=='\t' || ch==' ')
272 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
279 *new++=*start; /* copy char */
282 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
287 *new++='\n'; /* NL */
289 ++start; /* consume extra input char */
297 new += sprintf(CS new,"=%02X",ch);
303 *new='\0'; /* not included in length, but nice */
308 /*************************************************
309 * Check mail address for correct syntax *
310 *************************************************/
313 Check mail address for being syntactically correct.
316 filter points to the Sieve filter including its state
317 address String containing one address
320 1 Mail address is syntactically OK
324 int check_mail_address(struct Sieve *filter, const struct String *address)
326 int start, end, domain;
329 if (address->length>0)
331 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
335 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
336 address->character, error);
344 filter->errmsg=CUS "empty address";
350 /*************************************************
351 * Decode URI encoded string *
352 *************************************************/
356 str URI encoded string
359 0 Decoding successful
365 uri_decode(struct String *str)
369 if (str->length==0) return 0;
370 for (s=str->character,t=s,e=s+str->length; s<e; )
373 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
375 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
376 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
385 str->length=t-str->character;
390 /*************************************************
392 *************************************************/
397 mailtoURI = "mailto:" [ to ] [ headers ]
398 to = [ addr-spec *("%2C" addr-spec ) ]
399 headers = "?" header *( "&" header )
400 header = hname "=" hvalue
405 filter points to the Sieve filter including its state
406 uri URI, excluding scheme
411 1 URI is syntactically OK
417 parse_mailto_uri(struct Sieve *filter, const uschar *uri,
418 string_item **recipient, struct String *header, struct String *subject,
422 struct String to, hname;
423 struct String hvalue = {.character = NULL, .length = 0};
426 if (Ustrncmp(uri,"mailto:",7))
428 filter->errmsg=US "Unknown URI scheme";
433 if (*uri && *uri!='?')
437 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
440 gstring * g = string_catn(NULL, start, uri-start);
442 to.character = string_from_gstring(g);
444 if (uri_decode(&to)==-1)
446 filter->errmsg=US"Invalid URI encoding";
449 new = store_get(sizeof(string_item), GET_UNTAINTED);
450 new->text = store_get(to.length+1, to.character);
451 if (to.length) memcpy(new->text, to.character, to.length);
452 new->text[to.length] = '\0';
453 new->next = *recipient;
458 filter->errmsg = US"Missing addr-spec in URI";
461 if (*uri=='%') uri+=3;
470 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
473 gstring * g = string_catn(NULL, start, uri-start);
475 hname.character = string_from_gstring(g);
476 hname.length = g->ptr;
477 if (uri_decode(&hname)==-1)
479 filter->errmsg=US"Invalid URI encoding";
488 filter->errmsg=US"Missing equal after hname";
492 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
495 gstring * g = string_catn(NULL, start, uri-start);
497 hname.character = string_from_gstring(g);
498 hname.length = g->ptr;
499 if (uri_decode(&hvalue)==-1)
501 filter->errmsg=US"Invalid URI encoding";
505 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
507 new=store_get(sizeof(string_item), GET_UNTAINTED);
508 new->text = store_get(hvalue.length+1, hvalue.character);
509 if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length);
510 new->text[hvalue.length]='\0';
511 new->next=*recipient;
514 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
516 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
520 static struct String ignore[]=
526 {US"auto-submitted",14}
528 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
531 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
536 if (header->length==-1) header->length = 0;
538 g = string_catn(NULL, header->character, header->length);
539 g = string_catn(g, hname.character, hname.length);
540 g = string_catn(g, CUS ": ", 2);
541 g = string_catn(g, hvalue.character, hvalue.length);
542 g = string_catn(g, CUS "\n", 1);
544 header->character = string_from_gstring(g);
545 header->length = g->ptr;
548 if (*uri=='&') ++uri;
554 filter->errmsg=US"Syntactically invalid URI";
562 /*************************************************
563 * Octet-wise string comparison *
564 *************************************************/
568 needle UTF-8 string to search ...
569 haystack ... inside the haystack
570 match_prefix 1 to compare if needle is a prefix of haystack
572 Returns: 0 needle not found in haystack
576 static int eq_octet(const struct String *needle,
577 const struct String *haystack, int match_prefix)
585 h=haystack->character;
589 if (*n&0x80) return 0;
590 if (*h&0x80) return 0;
592 if (*n!=*h) return 0;
598 return (match_prefix ? nl==0 : nl==0 && hl==0);
602 /*************************************************
603 * ASCII case-insensitive string comparison *
604 *************************************************/
608 needle UTF-8 string to search ...
609 haystack ... inside the haystack
610 match_prefix 1 to compare if needle is a prefix of haystack
612 Returns: 0 needle not found in haystack
616 static int eq_asciicase(const struct String *needle,
617 const struct String *haystack, int match_prefix)
626 h=haystack->character;
632 if (nc&0x80) return 0;
633 if (hc&0x80) return 0;
635 /* tolower depends on the locale and only ASCII case must be insensitive */
636 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
642 return (match_prefix ? nl==0 : nl==0 && hl==0);
646 /*************************************************
647 * Glob pattern search *
648 *************************************************/
652 needle pattern to search ...
653 haystack ... inside the haystack
654 ascii_caseless ignore ASCII case
655 match_octet match octets, not UTF-8 multi-octet characters
657 Returns: 0 needle not found in haystack
662 static int eq_glob(const struct String *needle,
663 const struct String *haystack, int ascii_caseless, int match_octet)
665 const uschar *n,*h,*nend,*hend;
669 h=haystack->character;
670 nend=n+needle->length;
671 hend=h+haystack->length;
681 const uschar *npart,*hpart;
683 /* Try to match a non-star part of the needle at the current */
684 /* position in the haystack. */
688 while (npart<nend && *npart!='*') switch (*npart)
692 if (hpart==hend) return 0;
697 /* Match one UTF8 encoded character */
698 if ((*hpart&0xc0)==0xc0)
701 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
712 if (npart==nend) return -1;
717 if (hpart==hend) return 0;
718 /* tolower depends on the locale, but we need ASCII */
722 (*hpart&0x80) || (*npart&0x80) ||
725 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
730 /* string match after a star failed, advance and try again */
744 /* at this point, a part was matched successfully */
745 if (may_advance && npart==nend && hpart<hend)
746 /* needle ends, but haystack does not: if there was a star before, advance and try again */
756 return (h==hend ? 1 : may_advance);
760 /*************************************************
761 * ASCII numeric comparison *
762 *************************************************/
766 a first numeric string
767 b second numeric string
768 relop relational operator
770 Returns: 0 not (a relop b)
774 static int eq_asciinumeric(const struct String *a,
775 const struct String *b, enum RelOp relop)
778 const uschar *as,*aend,*bs,*bend;
782 aend=a->character+a->length;
784 bend=b->character+b->length;
786 while (*as>='0' && *as<='9' && as<aend) ++as;
788 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
791 if (al && bl==0) cmp=-1;
792 else if (al==0 && bl==0) cmp=0;
793 else if (al==0 && bl) cmp=1;
797 if (cmp==0) cmp=memcmp(a->character,b->character,al);
801 case LT: return cmp<0;
802 case LE: return cmp<=0;
803 case EQ: return cmp==0;
804 case GE: return cmp>=0;
805 case GT: return cmp>0;
806 case NE: return cmp!=0;
813 /*************************************************
815 *************************************************/
819 filter points to the Sieve filter including its state
820 needle UTF-8 pattern or string to search ...
821 haystack ... inside the haystack
825 Returns: 0 needle not found in haystack
827 -1 comparator does not offer matchtype
830 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
831 enum Comparator co, enum MatchType mt)
835 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
836 (debug_selector & D_filter) != 0)
838 debug_printf("String comparison (match ");
841 case MATCH_IS: debug_printf(":is"); break;
842 case MATCH_CONTAINS: debug_printf(":contains"); break;
843 case MATCH_MATCHES: debug_printf(":matches"); break;
845 debug_printf(", comparison \"");
848 case COMP_OCTET: debug_printf("i;octet"); break;
849 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
850 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
852 debug_printf("\"):\n");
853 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
854 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
862 if (eq_octet(needle,haystack,0)) r=1;
864 case COMP_EN_ASCII_CASEMAP:
865 if (eq_asciicase(needle,haystack,0)) r=1;
867 case COMP_ASCII_NUMERIC:
868 if (!filter->require_iascii_numeric)
870 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
873 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
885 for (h = *haystack; h.length; ++h.character,--h.length)
886 if (eq_octet(needle,&h,1)) { r=1; break; }
888 case COMP_EN_ASCII_CASEMAP:
889 for (h = *haystack; h.length; ++h.character, --h.length)
890 if (eq_asciicase(needle,&h,1)) { r=1; break; }
893 filter->errmsg=CUS "comparator does not offer specified matchtype";
903 if ((r=eq_glob(needle,haystack,0,1))==-1)
905 filter->errmsg=CUS "syntactically invalid pattern";
909 case COMP_EN_ASCII_CASEMAP:
910 if ((r=eq_glob(needle,haystack,1,1))==-1)
912 filter->errmsg=CUS "syntactically invalid pattern";
917 filter->errmsg=CUS "comparator does not offer specified matchtype";
922 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
923 (debug_selector & D_filter) != 0)
924 debug_printf(" Result %s\n",r?"true":"false");
929 /*************************************************
930 * Check header field syntax *
931 *************************************************/
934 RFC 2822, section 3.6.8 says:
938 ftext = %d33-57 / ; Any character except
939 %d59-126 ; controls, SP, and
942 That forbids 8-bit header fields. This implementation accepts them, since
943 all of Exim is 8-bit clean, so it adds %d128-%d255.
946 header header field to quote for suitable use in Exim expansions
948 Returns: 0 string is not a valid header field
949 1 string is a value header field
952 static int is_header(const struct String *header)
962 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
973 /*************************************************
974 * Quote special characters string *
975 *************************************************/
979 header header field to quote for suitable use in Exim expansions
982 Returns: quoted string
985 static const uschar *
986 quote(const struct String *header)
988 gstring * quoted = NULL;
999 quoted = string_catn(quoted, CUS "\\0", 2);
1004 quoted = string_catn(quoted, CUS "\\", 1);
1006 quoted = string_catn(quoted, h, 1);
1011 quoted = string_catn(quoted, CUS "", 1);
1012 return string_from_gstring(quoted);
1016 /*************************************************
1017 * Add address to list of generated addresses *
1018 *************************************************/
1021 According to RFC 5228, duplicate delivery to the same address must
1022 not happen, so the list is first searched for the address.
1025 generated list of generated addresses
1026 addr new address to add
1027 file address denotes a file
1033 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1035 address_item *new_addr;
1037 for (new_addr = *generated; new_addr; new_addr = new_addr->next)
1038 if ( Ustrcmp(new_addr->address,addr) == 0
1040 || testflag(new_addr, af_pfr)
1041 || testflag(new_addr, af_file)
1045 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1046 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1051 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1052 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1054 new_addr = deliver_make_addr(addr,TRUE);
1057 setflag(new_addr, af_pfr);
1058 setflag(new_addr, af_file);
1061 new_addr->prop.errors_address = NULL;
1062 new_addr->next = *generated;
1063 *generated = new_addr;
1067 /*************************************************
1068 * Return decoded header field *
1069 *************************************************/
1072 Unfold the header field as described in RFC 2822 and remove all
1073 leading and trailing white space, then perform MIME decoding and
1074 translate the header field to UTF-8.
1077 value returned value of the field
1078 header name of the header field
1080 Returns: nothing The expanded string is empty
1081 in case there is no such header
1084 static void expand_header(struct String *value, const struct String *header)
1090 value->character=(uschar*)0;
1092 t = r = s = expand_string(string_sprintf("$rheader_%s",quote(header)));
1094 while (*r==' ' || *r=='\t') ++r;
1102 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1104 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1108 /*************************************************
1109 * Parse remaining hash comment *
1110 *************************************************/
1114 Comment up to terminating CRLF
1117 filter points to the Sieve filter including its state
1123 static int parse_hashcomment(struct Sieve *filter)
1129 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1131 if (*filter->pc=='\n')
1144 filter->errmsg=CUS "missing end of comment";
1149 /*************************************************
1150 * Parse remaining C-style comment *
1151 *************************************************/
1155 Everything up to star slash
1158 filter points to the Sieve filter including its state
1164 static int parse_comment(struct Sieve *filter)
1169 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1176 filter->errmsg=CUS "missing end of comment";
1181 /*************************************************
1182 * Parse optional white space *
1183 *************************************************/
1187 Spaces, tabs, CRLFs, hash comments or C-style comments
1190 filter points to the Sieve filter including its state
1196 static int parse_white(struct Sieve *filter)
1200 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1202 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1204 else if (*filter->pc=='\n')
1214 else if (*filter->pc=='#')
1216 if (parse_hashcomment(filter)==-1) return -1;
1218 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1220 if (parse_comment(filter)==-1) return -1;
1228 #ifdef ENCODED_CHARACTER
1229 /*************************************************
1230 * Decode hex-encoded-character string *
1231 *************************************************/
1234 Encoding definition:
1235 blank = SP / TAB / CRLF
1236 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1237 hex-pair = 1*2HEXDIG
1240 src points to a hex-pair-seq
1241 end points to its end
1242 dst points to the destination of the decoded octets,
1243 optionally to (uschar*)0 for checking only
1245 Returns: >=0 number of decoded octets
1249 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1253 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1259 d<2 && src<end && isxdigit(n=tolower(*src));
1260 x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ;
1261 if (d==0) return -1;
1264 if (src==end) return decoded;
1265 if (*src==' ' || *src=='\t' || *src=='\n')
1266 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1275 /*************************************************
1276 * Decode unicode-encoded-character string *
1277 *************************************************/
1280 Encoding definition:
1281 blank = SP / TAB / CRLF
1282 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1283 unicode-hex = 1*HEXDIG
1285 It is an error for a script to use a hexadecimal value that isn't in
1286 either the range 0 to D7FF or the range E000 to 10FFFF.
1288 At this time, strings are already scanned, thus the CRLF is converted
1289 to the internally used \n (should RFC_EOL have been used).
1292 src points to a unicode-hex-seq
1293 end points to its end
1294 dst points to the destination of the decoded octets,
1295 optionally to (uschar*)0 for checking only
1297 Returns: >=0 number of decoded octets
1299 -2 semantic error (character range violation)
1303 unicode_decode(uschar *src, uschar *end, uschar *dst)
1307 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1314 for (hex_seq = src; src < end && *src=='0'; ) src++;
1316 d < 7 && src < end && isxdigit(n=tolower(*src));
1317 c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1318 if (src == hex_seq) return -1;
1319 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1325 else if (c>=0x80 && c<=0x7ff)
1330 *dst++=128+(c&0x3f);
1334 else if (c>=0x800 && c<=0xffff)
1339 *dst++=128+((c>>6)&0x3f);
1340 *dst++=128+(c&0x3f);
1344 else if (c>=0x10000 && c<=0x1fffff)
1349 *dst++=128+((c>>10)&0x3f);
1350 *dst++=128+((c>>6)&0x3f);
1351 *dst++=128+(c&0x3f);
1355 if (*src==' ' || *src=='\t' || *src=='\n')
1357 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1358 if (src==end) return decoded;
1367 /*************************************************
1368 * Decode encoded-character string *
1369 *************************************************/
1372 Encoding definition:
1373 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1374 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1377 encoded points to an encoded string, returns decoded string
1378 filter points to the Sieve filter including its state
1384 static int string_decode(struct Sieve *filter, struct String *data)
1386 uschar *src,*dst,*end;
1388 src=data->character;
1390 end=data->character+data->length;
1396 strncmpic(src,US "${hex:",6)==0
1397 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1398 && (hex_decode(src+6,brace,(uschar*)0))>=0
1401 dst+=hex_decode(src+6,brace,dst);
1405 strncmpic(src,US "${unicode:",10)==0
1406 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1409 switch (unicode_decode(src+10,brace,(uschar*)0))
1413 filter->errmsg=CUS "unicode character out of range";
1423 dst+=unicode_decode(src+10,brace,dst);
1430 data->length=dst-data->character;
1437 /*************************************************
1438 * Parse an optional string *
1439 *************************************************/
1443 quoted-string = DQUOTE *CHAR DQUOTE
1444 ;; in general, \ CHAR inside a string maps to CHAR
1445 ;; so \" maps to " and \\ maps to \
1446 ;; note that newlines and other characters are all allowed
1449 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1450 *(multi-line-literal / multi-line-dotstuff)
1452 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1453 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1454 ;; A line containing only "." ends the multi-line.
1455 ;; Remove a leading '.' if followed by another '.'.
1456 string = quoted-string / multi-line
1459 filter points to the Sieve filter including its state
1460 id specifies identifier to match
1464 0 identifier not matched
1468 parse_string(struct Sieve *filter, struct String *data)
1473 data->character = NULL;
1475 if (*filter->pc=='"') /* quoted string */
1480 if (*filter->pc=='"') /* end of string */
1486 data->character = string_from_gstring(g);
1487 data->length = g->ptr;
1490 data->character = US"\0";
1491 /* that way, there will be at least one character allocated */
1493 #ifdef ENCODED_CHARACTER
1494 if (filter->require_encoded_character
1495 && string_decode(filter,data)==-1)
1500 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1502 g = string_catn(g, filter->pc+1, 1);
1505 else /* regular character */
1508 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1510 if (*filter->pc=='\n')
1512 g = string_catn(g, US"\r", 1);
1516 g = string_catn(g, filter->pc, 1);
1520 filter->errmsg=CUS "missing end of string";
1523 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1526 /* skip optional white space followed by hashed comment or CRLF */
1527 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1528 if (*filter->pc=='#')
1530 if (parse_hashcomment(filter)==-1) return -1;
1533 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1535 else if (*filter->pc=='\n')
1547 filter->errmsg=CUS "syntax error";
1553 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1555 if (*filter->pc=='\n') /* end of line */
1558 g = string_catn(g, CUS "\r\n", 2);
1566 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1568 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1573 data->character = string_from_gstring(g);
1574 data->length = g->ptr;
1577 data->character = US"\0";
1578 /* that way, there will be at least one character allocated */
1586 #ifdef ENCODED_CHARACTER
1587 if (filter->require_encoded_character
1588 && string_decode(filter,data)==-1)
1593 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1595 g = string_catn(g, CUS ".", 1);
1599 else /* regular character */
1601 g = string_catn(g, filter->pc, 1);
1605 filter->errmsg=CUS "missing end of multi line string";
1612 /*************************************************
1613 * Parse a specific identifier *
1614 *************************************************/
1618 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1621 filter points to the Sieve filter including its state
1622 id specifies identifier to match
1625 0 identifier not matched
1628 static int parse_identifier(struct Sieve *filter, const uschar *id)
1630 size_t idlen=Ustrlen(id);
1632 if (strncmpic(US filter->pc,US id,idlen)==0)
1634 uschar next=filter->pc[idlen];
1636 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1644 /*************************************************
1646 *************************************************/
1650 number = 1*DIGIT [QUANTIFIER]
1651 QUANTIFIER = "K" / "M" / "G"
1654 filter points to the Sieve filter including its state
1658 -1 no string list found
1661 static int parse_number(struct Sieve *filter, unsigned long *data)
1665 if (*filter->pc>='0' && *filter->pc<='9')
1670 d=Ustrtoul(filter->pc,&e,10);
1673 filter->errmsg=CUstrerror(ERANGE);
1678 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1679 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1680 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1681 if (d>(ULONG_MAX/u))
1683 filter->errmsg=CUstrerror(ERANGE);
1692 filter->errmsg=CUS "missing number";
1698 /*************************************************
1699 * Parse a string list *
1700 *************************************************/
1704 string-list = "[" string *("," string) "]" / string
1707 filter points to the Sieve filter including its state
1708 data returns string list
1711 -1 no string list found
1715 parse_stringlist(struct Sieve *filter, struct String **data)
1717 const uschar *orig=filter->pc;
1718 int dataCapacity = 0;
1720 struct String *d = NULL;
1723 if (*filter->pc=='[') /* string list */
1728 if (parse_white(filter)==-1) goto error;
1729 if (dataLength+1 >= dataCapacity) /* increase buffer */
1733 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1734 new = store_get(sizeof(struct String) * dataCapacity, GET_UNTAINTED);
1736 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1740 m=parse_string(filter,&d[dataLength]);
1743 if (dataLength==0) break;
1746 filter->errmsg=CUS "missing string";
1750 else if (m==-1) goto error;
1752 if (parse_white(filter)==-1) goto error;
1753 if (*filter->pc==',') ++filter->pc;
1756 if (*filter->pc==']')
1758 d[dataLength].character=(uschar*)0;
1759 d[dataLength].length=-1;
1766 filter->errmsg=CUS "missing closing bracket";
1770 else /* single string */
1772 if (!(d=store_get(sizeof(struct String)*2, GET_UNTAINTED)))
1775 m=parse_string(filter,&d[0]);
1786 d[1].character=(uschar*)0;
1793 filter->errmsg=CUS "missing string list";
1798 /*************************************************
1799 * Parse an optional address part specifier *
1800 *************************************************/
1804 address-part = ":localpart" / ":domain" / ":all"
1805 address-part =/ ":user" / ":detail"
1808 filter points to the Sieve filter including its state
1809 a returns address part specified
1812 0 no comparator found
1816 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1819 if (parse_identifier(filter,CUS ":user")==1)
1821 if (!filter->require_subaddress)
1823 filter->errmsg=CUS "missing previous require \"subaddress\";";
1829 else if (parse_identifier(filter,CUS ":detail")==1)
1831 if (!filter->require_subaddress)
1833 filter->errmsg=CUS "missing previous require \"subaddress\";";
1841 if (parse_identifier(filter,CUS ":localpart")==1)
1843 *a=ADDRPART_LOCALPART;
1846 else if (parse_identifier(filter,CUS ":domain")==1)
1851 else if (parse_identifier(filter,CUS ":all")==1)
1860 /*************************************************
1861 * Parse an optional comparator *
1862 *************************************************/
1866 comparator = ":comparator" <comparator-name: string>
1869 filter points to the Sieve filter including its state
1870 c returns comparator
1873 0 no comparator found
1874 -1 incomplete comparator found
1877 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1879 struct String comparator_name;
1881 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1882 if (parse_white(filter)==-1) return -1;
1883 switch (parse_string(filter,&comparator_name))
1888 filter->errmsg=CUS "missing comparator";
1895 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1900 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1902 *c=COMP_EN_ASCII_CASEMAP;
1905 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1907 *c=COMP_EN_ASCII_CASEMAP;
1910 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1912 *c=COMP_ASCII_NUMERIC;
1917 filter->errmsg=CUS "invalid comparator";
1926 /*************************************************
1927 * Parse an optional match type *
1928 *************************************************/
1932 match-type = ":is" / ":contains" / ":matches"
1935 filter points to the Sieve filter including its state
1936 m returns match type
1939 0 no match type found
1942 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1944 if (parse_identifier(filter,CUS ":is")==1)
1949 else if (parse_identifier(filter,CUS ":contains")==1)
1954 else if (parse_identifier(filter,CUS ":matches")==1)
1963 /*************************************************
1964 * Parse and interpret an optional test list *
1965 *************************************************/
1969 test-list = "(" test *("," test) ")"
1972 filter points to the Sieve filter including its state
1973 n total number of tests
1974 num_true number of passed tests
1975 exec Execute parsed statements
1978 0 no test list found
1979 -1 syntax or execution error
1982 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1984 if (parse_white(filter)==-1) return -1;
1985 if (*filter->pc=='(')
1994 switch (parse_test(filter,&cond,exec))
1997 case 0: filter->errmsg=CUS "missing test"; return -1;
1998 default: ++*n; if (cond) ++*num_true; break;
2000 if (parse_white(filter)==-1) return -1;
2001 if (*filter->pc==',') ++filter->pc;
2004 if (*filter->pc==')')
2011 filter->errmsg=CUS "missing closing paren";
2019 /*************************************************
2020 * Parse and interpret an optional test *
2021 *************************************************/
2025 filter points to the Sieve filter including its state
2026 cond returned condition status
2027 exec Execute parsed statements
2031 -1 syntax or execution error
2035 parse_test(struct Sieve *filter, int *cond, int exec)
2037 if (parse_white(filter)==-1) return -1;
2038 if (parse_identifier(filter,CUS "address"))
2041 address-test = "address" { [address-part] [comparator] [match-type] }
2042 <header-list: string-list> <key-list: string-list>
2044 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2047 enum AddressPart addressPart=ADDRPART_ALL;
2048 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2049 enum MatchType matchType=MATCH_IS;
2050 struct String *hdr,*key;
2056 if (parse_white(filter)==-1) return -1;
2057 if ((m=parse_addresspart(filter,&addressPart))!=0)
2059 if (m==-1) return -1;
2062 filter->errmsg=CUS "address part already specified";
2067 else if ((m=parse_comparator(filter,&comparator))!=0)
2069 if (m==-1) return -1;
2072 filter->errmsg=CUS "comparator already specified";
2077 else if ((m=parse_matchtype(filter,&matchType))!=0)
2079 if (m==-1) return -1;
2082 filter->errmsg=CUS "match type already specified";
2089 if (parse_white(filter)==-1) return -1;
2090 if ((m=parse_stringlist(filter,&hdr))!=1)
2092 if (m==0) filter->errmsg=CUS "header string list expected";
2095 if (parse_white(filter)==-1) return -1;
2096 if ((m=parse_stringlist(filter,&key))!=1)
2098 if (m==0) filter->errmsg=CUS "key string list expected";
2102 for (struct String * h = hdr; h->length!=-1 && !*cond; ++h)
2104 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2108 !eq_asciicase(h,&str_from,0)
2109 && !eq_asciicase(h,&str_to,0)
2110 && !eq_asciicase(h,&str_cc,0)
2111 && !eq_asciicase(h,&str_bcc,0)
2112 && !eq_asciicase(h,&str_sender,0)
2113 && !eq_asciicase(h,&str_resent_from,0)
2114 && !eq_asciicase(h,&str_resent_to,0)
2117 filter->errmsg=CUS "invalid header field";
2122 /* We are only interested in addresses below, so no MIME decoding */
2123 if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h)))))
2125 filter->errmsg=CUS "header string expansion failed";
2128 f.parse_allow_group = TRUE;
2129 while (*header_value && !*cond)
2132 int start, end, domain;
2136 end_addr = parse_find_address_end(header_value, FALSE);
2137 saveend = *end_addr;
2139 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2141 if (extracted_addr) switch (addressPart)
2143 case ADDRPART_ALL: part=extracted_addr; break;
2147 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2148 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2150 case ADDRPART_DETAIL: part=NULL; break;
2154 *end_addr = saveend;
2157 for (struct String * k = key; k->length !=- 1; ++k)
2159 struct String partStr = {.character = part, .length = Ustrlen(part)};
2163 *cond=compare(filter,k,&partStr,comparator,matchType);
2164 if (*cond==-1) return -1;
2169 if (saveend == 0) break;
2170 header_value = end_addr + 1;
2172 f.parse_allow_group = FALSE;
2173 f.parse_found_group = FALSE;
2178 else if (parse_identifier(filter,CUS "allof"))
2181 allof-test = "allof" <tests: test-list>
2186 switch (parse_testlist(filter,&n,&num_true,exec))
2189 case 0: filter->errmsg=CUS "missing test list"; return -1;
2190 default: *cond=(n==num_true); return 1;
2193 else if (parse_identifier(filter,CUS "anyof"))
2196 anyof-test = "anyof" <tests: test-list>
2201 switch (parse_testlist(filter,&n,&num_true,exec))
2204 case 0: filter->errmsg=CUS "missing test list"; return -1;
2205 default: *cond=(num_true>0); return 1;
2208 else if (parse_identifier(filter,CUS "exists"))
2211 exists-test = "exists" <header-names: string-list>
2217 if (parse_white(filter)==-1) return -1;
2218 if ((m=parse_stringlist(filter,&hdr))!=1)
2220 if (m==0) filter->errmsg=CUS "header string list expected";
2226 for (struct String * h = hdr; h->length != -1 && *cond; ++h)
2230 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2233 filter->errmsg=CUS "header string expansion failed";
2236 if (Ustrcmp(header_def,"false")==0) *cond=0;
2241 else if (parse_identifier(filter,CUS "false"))
2244 false-test = "false"
2250 else if (parse_identifier(filter,CUS "header"))
2253 header-test = "header" { [comparator] [match-type] }
2254 <header-names: string-list> <key-list: string-list>
2257 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2258 enum MatchType matchType=MATCH_IS;
2259 struct String *hdr,*key;
2265 if (parse_white(filter)==-1) return -1;
2266 if ((m=parse_comparator(filter,&comparator))!=0)
2268 if (m==-1) return -1;
2271 filter->errmsg=CUS "comparator already specified";
2276 else if ((m=parse_matchtype(filter,&matchType))!=0)
2278 if (m==-1) return -1;
2281 filter->errmsg=CUS "match type already specified";
2288 if (parse_white(filter)==-1) return -1;
2289 if ((m=parse_stringlist(filter,&hdr))!=1)
2291 if (m==0) filter->errmsg=CUS "header string list expected";
2294 if (parse_white(filter)==-1) return -1;
2295 if ((m=parse_stringlist(filter,&key))!=1)
2297 if (m==0) filter->errmsg=CUS "key string list expected";
2301 for (struct String * h = hdr; h->length != -1 && !*cond; ++h)
2305 filter->errmsg=CUS "invalid header field";
2310 struct String header_value;
2313 expand_header(&header_value,h);
2314 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2315 if (!header_value.character || !header_def)
2317 filter->errmsg=CUS "header string expansion failed";
2320 for (struct String * k = key; k->length != -1; ++k)
2321 if (Ustrcmp(header_def,"true")==0)
2323 *cond=compare(filter,k,&header_value,comparator,matchType);
2324 if (*cond==-1) return -1;
2331 else if (parse_identifier(filter,CUS "not"))
2333 if (parse_white(filter)==-1) return -1;
2334 switch (parse_test(filter,cond,exec))
2337 case 0: filter->errmsg=CUS "missing test"; return -1;
2338 default: *cond=!*cond; return 1;
2341 else if (parse_identifier(filter,CUS "size"))
2344 relop = ":over" / ":under"
2345 size-test = "size" relop <limit: number>
2348 unsigned long limit;
2351 if (parse_white(filter)==-1) return -1;
2352 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2353 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2356 filter->errmsg=CUS "missing :over or :under";
2359 if (parse_white(filter)==-1) return -1;
2360 if (parse_number(filter,&limit)==-1) return -1;
2361 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2364 else if (parse_identifier(filter,CUS "true"))
2369 else if (parse_identifier(filter,CUS "envelope"))
2372 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2373 <envelope-part: string-list> <key-list: string-list>
2375 envelope-part is case insensitive "from" or "to"
2376 #ifdef ENVELOPE_AUTH
2377 envelope-part =/ "auth"
2381 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2382 enum AddressPart addressPart=ADDRPART_ALL;
2383 enum MatchType matchType=MATCH_IS;
2384 struct String *env,*key;
2388 if (!filter->require_envelope)
2390 filter->errmsg=CUS "missing previous require \"envelope\";";
2395 if (parse_white(filter)==-1) return -1;
2396 if ((m=parse_comparator(filter,&comparator))!=0)
2398 if (m==-1) return -1;
2401 filter->errmsg=CUS "comparator already specified";
2406 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2408 if (m==-1) return -1;
2411 filter->errmsg=CUS "address part already specified";
2416 else if ((m=parse_matchtype(filter,&matchType))!=0)
2418 if (m==-1) return -1;
2421 filter->errmsg=CUS "match type already specified";
2428 if (parse_white(filter)==-1) return -1;
2429 if ((m=parse_stringlist(filter,&env))!=1)
2431 if (m==0) filter->errmsg=CUS "envelope string list expected";
2434 if (parse_white(filter)==-1) return -1;
2435 if ((m=parse_stringlist(filter,&key))!=1)
2437 if (m==0) filter->errmsg=CUS "key string list expected";
2441 for (struct String * e = env; e->length != -1 && !*cond; ++e)
2443 const uschar *envelopeExpr=CUS 0;
2444 uschar *envelope=US 0;
2446 if (eq_asciicase(e,&str_from,0))
2448 switch (addressPart)
2450 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2454 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2455 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2457 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2461 else if (eq_asciicase(e,&str_to,0))
2463 switch (addressPart)
2465 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2467 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2468 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2470 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2471 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2474 #ifdef ENVELOPE_AUTH
2475 else if (eq_asciicase(e,&str_auth,0))
2477 switch (addressPart)
2479 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2483 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2484 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2486 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2493 filter->errmsg=CUS "invalid envelope string";
2496 if (exec && envelopeExpr)
2498 if (!(envelope=expand_string(US envelopeExpr)))
2500 filter->errmsg=CUS "header string expansion failed";
2503 for (struct String * k = key; k->length != -1; ++k)
2505 struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)};
2507 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2508 if (*cond==-1) return -1;
2516 else if (parse_identifier(filter,CUS "valid_notify_method"))
2519 valid_notify_method = "valid_notify_method"
2520 <notification-uris: string-list>
2523 struct String *uris;
2526 if (!filter->require_enotify)
2528 filter->errmsg=CUS "missing previous require \"enotify\";";
2531 if (parse_white(filter)==-1) return -1;
2532 if ((m=parse_stringlist(filter,&uris))!=1)
2534 if (m==0) filter->errmsg=CUS "URI string list expected";
2540 for (struct String * u = uris; u->length != -1 && *cond; ++u)
2542 string_item *recipient;
2543 struct String header,subject,body;
2547 header.character=(uschar*)0;
2549 subject.character=(uschar*)0;
2551 body.character=(uschar*)0;
2552 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2558 else if (parse_identifier(filter,CUS "notify_method_capability"))
2561 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2562 <notification-uri: string>
2563 <notification-capability: string>
2564 <key-list: string-list>
2570 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2571 enum MatchType matchType=MATCH_IS;
2572 struct String uri,capa,*keys;
2574 if (!filter->require_enotify)
2576 filter->errmsg=CUS "missing previous require \"enotify\";";
2581 if (parse_white(filter)==-1) return -1;
2582 if ((m=parse_comparator(filter,&comparator))!=0)
2584 if (m==-1) return -1;
2587 filter->errmsg=CUS "comparator already specified";
2592 else if ((m=parse_matchtype(filter,&matchType))!=0)
2594 if (m==-1) return -1;
2597 filter->errmsg=CUS "match type already specified";
2604 if ((m=parse_string(filter,&uri))!=1)
2606 if (m==0) filter->errmsg=CUS "missing notification URI string";
2609 if (parse_white(filter)==-1) return -1;
2610 if ((m=parse_string(filter,&capa))!=1)
2612 if (m==0) filter->errmsg=CUS "missing notification capability string";
2615 if (parse_white(filter)==-1) return -1;
2616 if ((m=parse_stringlist(filter,&keys))!=1)
2618 if (m==0) filter->errmsg=CUS "missing key string list";
2623 string_item *recipient;
2624 struct String header,subject,body;
2629 header.character=(uschar*)0;
2631 subject.character=(uschar*)0;
2633 body.character=(uschar*)0;
2634 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2635 if (eq_asciicase(&capa,&str_online,0)==1)
2636 for (struct String * k = keys; k->length != -1; ++k)
2638 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2639 if (*cond==-1) return -1;
2650 /*************************************************
2651 * Parse and interpret an optional block *
2652 *************************************************/
2656 filter points to the Sieve filter including its state
2657 exec Execute parsed statements
2658 generated where to hang newly-generated addresses
2660 Returns: 2 success by stop
2662 0 no block command found
2663 -1 syntax or execution error
2666 static int parse_block(struct Sieve *filter, int exec,
2667 address_item **generated)
2671 if (parse_white(filter)==-1) return -1;
2672 if (*filter->pc=='{')
2675 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2676 if (*filter->pc=='}')
2683 filter->errmsg=CUS "expecting command or closing brace";
2691 /*************************************************
2692 * Match a semicolon *
2693 *************************************************/
2697 filter points to the Sieve filter including its state
2703 static int parse_semicolon(struct Sieve *filter)
2705 if (parse_white(filter)==-1) return -1;
2706 if (*filter->pc==';')
2713 filter->errmsg=CUS "missing semicolon";
2719 /*************************************************
2720 * Parse and interpret a Sieve command *
2721 *************************************************/
2725 filter points to the Sieve filter including its state
2726 exec Execute parsed statements
2727 generated where to hang newly-generated addresses
2729 Returns: 2 success by stop
2731 -1 syntax or execution error
2734 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2738 if (parse_white(filter)==-1) return -1;
2739 if (parse_identifier(filter,CUS "if"))
2742 if-command = "if" test block *( "elsif" test block ) [ else block ]
2745 int cond,m,unsuccessful;
2748 if (parse_white(filter)==-1) return -1;
2749 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2752 filter->errmsg=CUS "missing test";
2755 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2756 (debug_selector & D_filter) != 0)
2758 if (exec) debug_printf("if %s\n",cond?"true":"false");
2760 m=parse_block(filter,exec ? cond : 0, generated);
2761 if (m==-1 || m==2) return m;
2764 filter->errmsg=CUS "missing block";
2767 unsuccessful = !cond;
2768 for (;;) /* elsif test block */
2770 if (parse_white(filter)==-1) return -1;
2771 if (parse_identifier(filter,CUS "elsif"))
2773 if (parse_white(filter)==-1) return -1;
2774 m=parse_test(filter,&cond,exec && unsuccessful);
2775 if (m==-1 || m==2) return m;
2778 filter->errmsg=CUS "missing test";
2781 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2782 (debug_selector & D_filter) != 0)
2784 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2786 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2787 if (m==-1 || m==2) return m;
2790 filter->errmsg=CUS "missing block";
2793 if (exec && unsuccessful && cond) unsuccessful = 0;
2798 if (parse_white(filter)==-1) return -1;
2799 if (parse_identifier(filter,CUS "else"))
2801 m=parse_block(filter,exec && unsuccessful, generated);
2802 if (m==-1 || m==2) return m;
2805 filter->errmsg=CUS "missing block";
2810 else if (parse_identifier(filter,CUS "stop"))
2813 stop-command = "stop" { stop-options } ";"
2817 if (parse_semicolon(filter)==-1) return -1;
2820 filter->pc+=Ustrlen(filter->pc);
2824 else if (parse_identifier(filter,CUS "keep"))
2827 keep-command = "keep" { keep-options } ";"
2831 if (parse_semicolon(filter)==-1) return -1;
2834 add_addr(generated,US"inbox",1,0,0,0);
2838 else if (parse_identifier(filter,CUS "discard"))
2841 discard-command = "discard" { discard-options } ";"
2845 if (parse_semicolon(filter)==-1) return -1;
2846 if (exec) filter->keep=0;
2848 else if (parse_identifier(filter,CUS "redirect"))
2851 redirect-command = "redirect" redirect-options "string" ";"
2853 redirect-options =) ":copy"
2856 struct String recipient;
2862 if (parse_white(filter)==-1) return -1;
2863 if (parse_identifier(filter,CUS ":copy")==1)
2865 if (!filter->require_copy)
2867 filter->errmsg=CUS "missing previous require \"copy\";";
2874 if (parse_white(filter)==-1) return -1;
2875 if ((m=parse_string(filter,&recipient))!=1)
2877 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2880 if (strchr(CCS recipient.character,'@')==(char*)0)
2882 filter->errmsg=CUS "unqualified recipient address";
2887 add_addr(generated,recipient.character,0,0,0,0);
2888 if (!copy) filter->keep = 0;
2890 if (parse_semicolon(filter)==-1) return -1;
2892 else if (parse_identifier(filter,CUS "fileinto"))
2895 fileinto-command = "fileinto" { fileinto-options } string ";"
2897 fileinto-options =) [ ":copy" ]
2900 struct String folder;
2903 unsigned long maxage, maxmessages, maxstorage;
2906 maxage = maxmessages = maxstorage = 0;
2907 if (!filter->require_fileinto)
2909 filter->errmsg=CUS "missing previous require \"fileinto\";";
2914 if (parse_white(filter)==-1) return -1;
2915 if (parse_identifier(filter,CUS ":copy")==1)
2917 if (!filter->require_copy)
2919 filter->errmsg=CUS "missing previous require \"copy\";";
2926 if (parse_white(filter)==-1) return -1;
2927 if ((m=parse_string(filter,&folder))!=1)
2929 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2932 m=0; s=folder.character;
2933 if (folder.length==0) m=1;
2934 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2937 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2942 filter->errmsg=CUS "invalid folder";
2947 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2948 if (!copy) filter->keep = 0;
2950 if (parse_semicolon(filter)==-1) return -1;
2953 else if (parse_identifier(filter,CUS "notify"))
2956 notify-command = "notify" { notify-options } <method: string> ";"
2957 notify-options = [":from" string]
2958 [":importance" <"1" / "2" / "3">]
2959 [":options" 1*(string-list / number)]
2965 struct String importance;
2966 struct String message;
2967 struct String method;
2968 struct Notification *already;
2969 string_item *recipient;
2970 struct String header;
2971 struct String subject;
2973 uschar *envelope_from;
2974 struct String auto_submitted_value;
2975 uschar *auto_submitted_def;
2977 if (!filter->require_enotify)
2979 filter->errmsg=CUS "missing previous require \"enotify\";";
2982 from.character=(uschar*)0;
2984 importance.character=(uschar*)0;
2985 importance.length=-1;
2986 message.character=(uschar*)0;
2990 header.character=(uschar*)0;
2992 subject.character=(uschar*)0;
2994 body.character=(uschar*)0;
2995 envelope_from = sender_address && sender_address[0]
2996 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2999 filter->errmsg=CUS "expansion failure for envelope from";
3004 if (parse_white(filter)==-1) return -1;
3005 if (parse_identifier(filter,CUS ":from")==1)
3007 if (parse_white(filter)==-1) return -1;
3008 if ((m=parse_string(filter,&from))!=1)
3010 if (m==0) filter->errmsg=CUS "from string expected";
3014 else if (parse_identifier(filter,CUS ":importance")==1)
3016 if (parse_white(filter)==-1) return -1;
3017 if ((m=parse_string(filter,&importance))!=1)
3019 if (m==0) filter->errmsg=CUS "importance string expected";
3022 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3024 filter->errmsg=CUS "invalid importance";
3028 else if (parse_identifier(filter,CUS ":options")==1)
3030 if (parse_white(filter)==-1) return -1;
3032 else if (parse_identifier(filter,CUS ":message")==1)
3034 if (parse_white(filter)==-1) return -1;
3035 if ((m=parse_string(filter,&message))!=1)
3037 if (m==0) filter->errmsg=CUS "message string expected";
3043 if (parse_white(filter)==-1) return -1;
3044 if ((m=parse_string(filter,&method))!=1)
3046 if (m==0) filter->errmsg=CUS "missing method string";
3049 if (parse_semicolon(filter)==-1) return -1;
3050 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3054 if (message.length==-1) message=subject;
3055 if (message.length==-1) expand_header(&message,&str_subject);
3056 expand_header(&auto_submitted_value,&str_auto_submitted);
3057 auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}");
3058 if (!auto_submitted_value.character || !auto_submitted_def)
3060 filter->errmsg=CUS "header string expansion failed";
3063 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3065 for (already=filter->notified; already; already=already->next)
3067 if (already->method.length==method.length
3068 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3069 && already->importance.length==importance.length
3070 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3071 && already->message.length==message.length
3072 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3076 /* New notification, process it */
3078 struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED);
3079 sent->method=method;
3080 sent->importance=importance;
3081 sent->message=message;
3082 sent->next=filter->notified;
3083 filter->notified=sent;
3084 #ifndef COMPILE_SYNTAX_CHECKER
3085 if (filter_test == FTEST_NONE)
3089 if ((pid = child_open_exim2(&fd, envelope_from, envelope_from,
3090 US"sieve-notify")) >= 1)
3092 FILE * f = fdopen(fd, "wb");
3094 fprintf(f,"From: %s\n", from.length == -1
3095 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
3097 for (string_item * p = recipient; p; p=p->next)
3098 fprintf(f, "To: %s\n",p->text);
3099 fprintf(f, "Auto-Submitted: auto-notified; %s\n", filter->enotify_mailto_owner);
3100 if (header.length > 0) fprintf(f, "%s", header.character);
3101 if (message.length==-1)
3103 message.character=US"Notification";
3104 message.length=Ustrlen(message.character);
3106 if (message.length != -1)
3107 fprintf(f, "Subject: %s\n", parse_quote_2047(message.character,
3108 message.length, US"utf-8", TRUE));
3110 if (body.length > 0) fprintf(f, "%s\n", body.character);
3113 (void)child_close(pid, 0);
3116 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3117 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3121 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3122 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3125 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3126 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3131 else if (parse_identifier(filter,CUS "vacation"))
3134 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3135 vacation-options = [":days" number]
3138 [":addresses" string-list]
3145 struct String subject;
3147 struct String *addresses;
3149 string_item *aliases;
3150 struct String handle;
3151 struct String reason;
3153 if (!filter->require_vacation)
3155 filter->errmsg=CUS "missing previous require \"vacation\";";
3160 if (filter->vacation_ran)
3162 filter->errmsg=CUS "trying to execute vacation more than once";
3165 filter->vacation_ran=1;
3167 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3168 subject.character=(uschar*)0;
3170 from.character=(uschar*)0;
3172 addresses=(struct String*)0;
3175 handle.character=(uschar*)0;
3179 if (parse_white(filter)==-1) return -1;
3180 if (parse_identifier(filter,CUS ":days")==1)
3182 if (parse_white(filter)==-1) return -1;
3183 if (parse_number(filter,&days)==-1) return -1;
3184 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3185 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3187 else if (parse_identifier(filter,CUS ":subject")==1)
3189 if (parse_white(filter)==-1) return -1;
3190 if ((m=parse_string(filter,&subject))!=1)
3192 if (m==0) filter->errmsg=CUS "subject string expected";
3196 else if (parse_identifier(filter,CUS ":from")==1)
3198 if (parse_white(filter)==-1) return -1;
3199 if ((m=parse_string(filter,&from))!=1)
3201 if (m==0) filter->errmsg=CUS "from string expected";
3204 if (check_mail_address(filter,&from)!=1)
3207 else if (parse_identifier(filter,CUS ":addresses")==1)
3209 if (parse_white(filter)==-1) return -1;
3210 if ((m=parse_stringlist(filter,&addresses))!=1)
3212 if (m==0) filter->errmsg=CUS "addresses string list expected";
3215 for (struct String * a = addresses; a->length != -1; ++a)
3217 string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
3219 new->text = store_get(a->length+1, a->character);
3220 if (a->length) memcpy(new->text,a->character,a->length);
3221 new->text[a->length]='\0';
3226 else if (parse_identifier(filter,CUS ":mime")==1)
3228 else if (parse_identifier(filter,CUS ":handle")==1)
3230 if (parse_white(filter)==-1) return -1;
3231 if ((m=parse_string(filter,&from))!=1)
3233 if (m==0) filter->errmsg=CUS "handle string expected";
3239 if (parse_white(filter)==-1) return -1;
3240 if ((m=parse_string(filter,&reason))!=1)
3242 if (m==0) filter->errmsg=CUS "missing reason string";
3249 for (s = reason.character, end = reason.character + reason.length;
3250 s<end && (*s&0x80)==0; ) s++;
3253 filter->errmsg=CUS "MIME reason string contains 8bit text";
3257 if (parse_semicolon(filter)==-1) return -1;
3264 uschar hexdigest[33];
3267 if (filter_personal(aliases,TRUE))
3269 if (filter_test == FTEST_NONE)
3271 /* ensure oncelog directory exists; failure will be detected later */
3273 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3275 /* build oncelog filename */
3279 if (handle.length==-1)
3281 gstring * key = NULL;
3282 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3283 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3284 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3285 key = string_catn(key, reason.character, reason.length);
3286 md5_end(&base, key->s, key->ptr, digest);
3289 md5_end(&base, handle.character, handle.length, digest);
3291 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3293 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3294 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3296 if (filter_test == FTEST_NONE)
3298 once = string_cat (NULL, filter->vacation_directory);
3299 once = string_catn(once, US"/", 1);
3300 once = string_catn(once, hexdigest, 33);
3302 /* process subject */
3304 if (subject.length==-1)
3306 uschar *subject_def;
3308 subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3309 if (subject_def && Ustrcmp(subject_def,"true")==0)
3311 gstring * g = string_catn(NULL, US"Auto: ", 6);
3313 expand_header(&subject,&str_subject);
3314 g = string_catn(g, subject.character, subject.length);
3315 subject.character = string_from_gstring(g);
3316 subject.length = g->ptr;
3320 subject.character=US"Automated reply";
3321 subject.length=Ustrlen(subject.character);
3325 /* add address to list of generated addresses */
3327 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3328 setflag(addr, af_pfr);
3329 addr->prop.ignore_error = TRUE;
3330 addr->next = *generated;
3332 addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
3333 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3334 addr->reply->to = string_copy(sender_address);
3335 if (from.length==-1)
3336 addr->reply->from = expand_string(US"$local_part@$domain");
3338 addr->reply->from = from.character;
3339 /* deconst cast safe as we pass in a non-const item */
3340 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE);
3341 addr->reply->oncelog = string_from_gstring(once);
3342 addr->reply->once_repeat=days*86400;
3344 /* build body and MIME headers */
3348 uschar *mime_body,*reason_end;
3349 static const uschar nlnl[]="\r\n\r\n";
3353 mime_body = reason.character, reason_end = reason.character + reason.length;
3354 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3357 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3359 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3360 else mime_body=reason_end-1;
3361 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3365 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3367 addr->reply->headers = US"MIME-Version: 1.0\n"
3368 "Content-Type: text/plain;\n"
3369 "\tcharset=\"utf-8\"\n"
3370 "Content-Transfer-Encoding: quoted-printable";
3371 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3375 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3376 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3386 /*************************************************
3387 * Parse and interpret a sieve filter *
3388 *************************************************/
3392 filter points to the Sieve filter including its state
3393 exec Execute parsed statements
3394 generated where to hang newly-generated addresses
3397 -1 syntax or execution error
3401 parse_start(struct Sieve *filter, int exec, address_item **generated)
3403 filter->pc=filter->filter;
3406 filter->require_envelope=0;
3407 filter->require_fileinto=0;
3408 #ifdef ENCODED_CHARACTER
3409 filter->require_encoded_character=0;
3411 #ifdef ENVELOPE_AUTH
3412 filter->require_envelope_auth=0;
3415 filter->require_enotify=0;
3416 filter->notified=(struct Notification*)0;
3419 filter->require_subaddress=0;
3422 filter->require_vacation=0;
3423 filter->vacation_ran=0;
3425 filter->require_copy=0;
3426 filter->require_iascii_numeric=0;
3428 if (parse_white(filter)==-1) return -1;
3430 if (exec && filter->vacation_directory && filter_test == FTEST_NONE)
3433 struct dirent *oncelog;
3434 struct stat properties;
3437 /* clean up old vacation log databases */
3439 if ( !(oncelogdir = exim_opendir(filter->vacation_directory))
3442 filter->errmsg = CUS "unable to open vacation directory";
3450 while ((oncelog = readdir(oncelogdir)))
3451 if (strlen(oncelog->d_name)==32)
3453 uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name);
3454 if (Ustat(s,&properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now)
3457 closedir(oncelogdir);
3461 while (parse_identifier(filter,CUS "require"))
3464 require-command = "require" <capabilities: string-list>
3470 if (parse_white(filter)==-1) return -1;
3471 if ((m=parse_stringlist(filter,&cap))!=1)
3473 if (m==0) filter->errmsg=CUS "capability string list expected";
3476 for (struct String * check = cap; check->character; ++check)
3478 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3479 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3480 #ifdef ENCODED_CHARACTER
3481 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3483 #ifdef ENVELOPE_AUTH
3484 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3487 else if (eq_octet(check,&str_enotify,0))
3489 if (!filter->enotify_mailto_owner)
3491 filter->errmsg=CUS "enotify disabled";
3494 filter->require_enotify=1;
3498 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3501 else if (eq_octet(check,&str_vacation,0))
3503 if (filter_test == FTEST_NONE && !filter->vacation_directory)
3505 filter->errmsg=CUS "vacation disabled";
3508 filter->require_vacation=1;
3511 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3512 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3513 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3514 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3515 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3518 filter->errmsg=CUS "unknown capability";
3522 if (parse_semicolon(filter)==-1) return -1;
3524 if (parse_commands(filter,exec,generated)==-1) return -1;
3527 filter->errmsg=CUS "syntax error";
3534 /*************************************************
3535 * Interpret a sieve filter file *
3536 *************************************************/
3540 filter points to the entire file, read into store as a single string
3541 options controls whether various special things are allowed, and requests
3542 special actions (not currently used)
3543 vacation_directory where to store vacation "once" files
3544 enotify_mailto_owner owner of mailto notifications
3545 useraddress string expression for :user part of address
3546 subaddress string expression for :subaddress part of address
3547 generated where to hang newly-generated addresses
3548 error where to pass back an error text
3550 Returns: FF_DELIVERED success, a significant action was taken
3551 FF_NOTDELIVERED success, no significant action
3552 FF_DEFER defer requested
3553 FF_FAIL fail requested
3554 FF_FREEZE freeze requested
3555 FF_ERROR there was a problem
3559 sieve_interpret(const uschar * filter, int options,
3560 const uschar * vacation_directory, const uschar * enotify_mailto_owner,
3561 const uschar * useraddress, const uschar * subaddress,
3562 address_item ** generated, uschar ** error)
3568 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3569 sieve.filter = filter;
3571 if (!vacation_directory)
3572 sieve.vacation_directory = NULL;
3573 else if (!(sieve.vacation_directory = expand_cstring(vacation_directory)))
3575 *error = string_sprintf("failed to expand \"%s\" "
3576 "(sieve_vacation_directory): %s", vacation_directory,
3577 expand_string_message);
3581 if (!enotify_mailto_owner)
3582 sieve.enotify_mailto_owner = NULL;
3583 else if (!(sieve.enotify_mailto_owner = expand_cstring(enotify_mailto_owner)))
3585 *error = string_sprintf("failed to expand \"%s\" "
3586 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3587 expand_string_message);
3591 sieve.useraddress = useraddress
3592 ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3593 sieve.subaddress = subaddress;
3595 #ifdef COMPILE_SYNTAX_CHECKER
3596 if (parse_start(&sieve, 0, generated) == 1)
3598 if (parse_start(&sieve, 1, generated) == 1)
3602 add_addr(generated, US"inbox", 1, 0, 0, 0);
3603 msg = US"Implicit keep";
3608 msg = US"No implicit keep";
3613 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3614 #ifdef COMPILE_SYNTAX_CHECKER
3618 add_addr(generated,US"inbox",1,0,0,0);
3623 #ifndef COMPILE_SYNTAX_CHECKER
3624 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3625 else debug_printf("%s\n", msg);
3628 DEBUG(D_route) debug_printf("Sieve: end of processing\n");