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.length = len_string_from_gstring(g, &to.character);
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.length = len_string_from_gstring(g, &hname.character);
475 if (uri_decode(&hname)==-1)
477 filter->errmsg=US"Invalid URI encoding";
486 filter->errmsg=US"Missing equal after hname";
490 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
493 gstring * g = string_catn(NULL, start, uri-start);
495 hname.length = len_string_from_gstring(g, &hname.character);
496 if (uri_decode(&hvalue)==-1)
498 filter->errmsg=US"Invalid URI encoding";
502 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
504 new=store_get(sizeof(string_item), GET_UNTAINTED);
505 new->text = store_get(hvalue.length+1, hvalue.character);
506 if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length);
507 new->text[hvalue.length]='\0';
508 new->next=*recipient;
511 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
513 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
517 static struct String ignore[]=
523 {US"auto-submitted",14}
525 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
528 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
533 if (header->length==-1) header->length = 0;
535 g = string_catn(NULL, header->character, header->length);
536 g = string_catn(g, hname.character, hname.length);
537 g = string_catn(g, CUS ": ", 2);
538 g = string_catn(g, hvalue.character, hvalue.length);
539 g = string_catn(g, CUS "\n", 1);
541 hname.length = len_string_from_gstring(g, &hname.character);
544 if (*uri=='&') ++uri;
550 filter->errmsg=US"Syntactically invalid URI";
558 /*************************************************
559 * Octet-wise string comparison *
560 *************************************************/
564 needle UTF-8 string to search ...
565 haystack ... inside the haystack
566 match_prefix 1 to compare if needle is a prefix of haystack
568 Returns: 0 needle not found in haystack
572 static int eq_octet(const struct String *needle,
573 const struct String *haystack, int match_prefix)
581 h=haystack->character;
585 if (*n&0x80) return 0;
586 if (*h&0x80) return 0;
588 if (*n!=*h) return 0;
594 return (match_prefix ? nl==0 : nl==0 && hl==0);
598 /*************************************************
599 * ASCII case-insensitive string comparison *
600 *************************************************/
604 needle UTF-8 string to search ...
605 haystack ... inside the haystack
606 match_prefix 1 to compare if needle is a prefix of haystack
608 Returns: 0 needle not found in haystack
612 static int eq_asciicase(const struct String *needle,
613 const struct String *haystack, int match_prefix)
622 h=haystack->character;
628 if (nc&0x80) return 0;
629 if (hc&0x80) return 0;
631 /* tolower depends on the locale and only ASCII case must be insensitive */
632 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
638 return (match_prefix ? nl==0 : nl==0 && hl==0);
642 /*************************************************
643 * Glob pattern search *
644 *************************************************/
648 needle pattern to search ...
649 haystack ... inside the haystack
650 ascii_caseless ignore ASCII case
651 match_octet match octets, not UTF-8 multi-octet characters
653 Returns: 0 needle not found in haystack
658 static int eq_glob(const struct String *needle,
659 const struct String *haystack, int ascii_caseless, int match_octet)
661 const uschar *n,*h,*nend,*hend;
665 h=haystack->character;
666 nend=n+needle->length;
667 hend=h+haystack->length;
677 const uschar *npart,*hpart;
679 /* Try to match a non-star part of the needle at the current */
680 /* position in the haystack. */
684 while (npart<nend && *npart!='*') switch (*npart)
688 if (hpart==hend) return 0;
693 /* Match one UTF8 encoded character */
694 if ((*hpart&0xc0)==0xc0)
697 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
708 if (npart==nend) return -1;
713 if (hpart==hend) return 0;
714 /* tolower depends on the locale, but we need ASCII */
718 (*hpart&0x80) || (*npart&0x80) ||
721 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
726 /* string match after a star failed, advance and try again */
740 /* at this point, a part was matched successfully */
741 if (may_advance && npart==nend && hpart<hend)
742 /* needle ends, but haystack does not: if there was a star before, advance and try again */
752 return (h==hend ? 1 : may_advance);
756 /*************************************************
757 * ASCII numeric comparison *
758 *************************************************/
762 a first numeric string
763 b second numeric string
764 relop relational operator
766 Returns: 0 not (a relop b)
770 static int eq_asciinumeric(const struct String *a,
771 const struct String *b, enum RelOp relop)
774 const uschar *as,*aend,*bs,*bend;
778 aend=a->character+a->length;
780 bend=b->character+b->length;
782 while (*as>='0' && *as<='9' && as<aend) ++as;
784 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
787 if (al && bl==0) cmp=-1;
788 else if (al==0 && bl==0) cmp=0;
789 else if (al==0 && bl) cmp=1;
793 if (cmp==0) cmp=memcmp(a->character,b->character,al);
797 case LT: return cmp<0;
798 case LE: return cmp<=0;
799 case EQ: return cmp==0;
800 case GE: return cmp>=0;
801 case GT: return cmp>0;
802 case NE: return cmp!=0;
809 /*************************************************
811 *************************************************/
815 filter points to the Sieve filter including its state
816 needle UTF-8 pattern or string to search ...
817 haystack ... inside the haystack
821 Returns: 0 needle not found in haystack
823 -1 comparator does not offer matchtype
826 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
827 enum Comparator co, enum MatchType mt)
831 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
832 (debug_selector & D_filter) != 0)
834 debug_printf_indent("String comparison (match ");
837 case MATCH_IS: debug_printf_indent(":is"); break;
838 case MATCH_CONTAINS: debug_printf_indent(":contains"); break;
839 case MATCH_MATCHES: debug_printf_indent(":matches"); break;
841 debug_printf_indent(", comparison \"");
844 case COMP_OCTET: debug_printf_indent("i;octet"); break;
845 case COMP_EN_ASCII_CASEMAP: debug_printf_indent("en;ascii-casemap"); break;
846 case COMP_ASCII_NUMERIC: debug_printf_indent("i;ascii-numeric"); break;
848 debug_printf_indent("\"):\n");
849 debug_printf_indent(" Search = %s (%d chars)\n", needle->character,needle->length);
850 debug_printf_indent(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
858 if (eq_octet(needle,haystack,0)) r=1;
860 case COMP_EN_ASCII_CASEMAP:
861 if (eq_asciicase(needle,haystack,0)) r=1;
863 case COMP_ASCII_NUMERIC:
864 if (!filter->require_iascii_numeric)
866 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
869 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
881 for (h = *haystack; h.length; ++h.character,--h.length)
882 if (eq_octet(needle,&h,1)) { r=1; break; }
884 case COMP_EN_ASCII_CASEMAP:
885 for (h = *haystack; h.length; ++h.character, --h.length)
886 if (eq_asciicase(needle,&h,1)) { r=1; break; }
889 filter->errmsg=CUS "comparator does not offer specified matchtype";
899 if ((r=eq_glob(needle,haystack,0,1))==-1)
901 filter->errmsg=CUS "syntactically invalid pattern";
905 case COMP_EN_ASCII_CASEMAP:
906 if ((r=eq_glob(needle,haystack,1,1))==-1)
908 filter->errmsg=CUS "syntactically invalid pattern";
913 filter->errmsg=CUS "comparator does not offer specified matchtype";
918 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
919 (debug_selector & D_filter) != 0)
920 debug_printf_indent(" Result %s\n",r?"true":"false");
925 /*************************************************
926 * Check header field syntax *
927 *************************************************/
930 RFC 2822, section 3.6.8 says:
934 ftext = %d33-57 / ; Any character except
935 %d59-126 ; controls, SP, and
938 That forbids 8-bit header fields. This implementation accepts them, since
939 all of Exim is 8-bit clean, so it adds %d128-%d255.
942 header header field to quote for suitable use in Exim expansions
944 Returns: 0 string is not a valid header field
945 1 string is a value header field
948 static int is_header(const struct String *header)
958 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
969 /*************************************************
970 * Quote special characters string *
971 *************************************************/
975 header header field to quote for suitable use in Exim expansions
978 Returns: quoted string
981 static const uschar *
982 quote(const struct String *header)
984 gstring * quoted = NULL;
995 quoted = string_catn(quoted, CUS "\\0", 2);
1000 quoted = string_catn(quoted, CUS "\\", 1);
1002 quoted = string_catn(quoted, h, 1);
1007 quoted = string_catn(quoted, CUS "", 1);
1008 return string_from_gstring(quoted);
1012 /*************************************************
1013 * Add address to list of generated addresses *
1014 *************************************************/
1017 According to RFC 5228, duplicate delivery to the same address must
1018 not happen, so the list is first searched for the address.
1021 generated list of generated addresses
1022 addr new address to add
1023 file address denotes a file
1029 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1031 address_item *new_addr;
1033 for (new_addr = *generated; new_addr; new_addr = new_addr->next)
1034 if ( Ustrcmp(new_addr->address,addr) == 0
1036 || testflag(new_addr, af_pfr)
1037 || testflag(new_addr, af_file)
1041 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1042 debug_printf_indent("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1047 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1048 debug_printf_indent("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1050 new_addr = deliver_make_addr(addr,TRUE);
1053 setflag(new_addr, af_pfr);
1054 setflag(new_addr, af_file);
1057 new_addr->prop.errors_address = NULL;
1058 new_addr->next = *generated;
1059 *generated = new_addr;
1063 /*************************************************
1064 * Return decoded header field *
1065 *************************************************/
1068 Unfold the header field as described in RFC 2822 and remove all
1069 leading and trailing white space, then perform MIME decoding and
1070 translate the header field to UTF-8.
1073 value returned value of the field
1074 header name of the header field
1076 Returns: nothing The expanded string is empty
1077 in case there is no such header
1080 static void expand_header(struct String *value, const struct String *header)
1086 value->character=(uschar*)0;
1088 t = r = s = expand_string(string_sprintf("$rheader_%s",quote(header)));
1090 while (*r==' ' || *r=='\t') ++r;
1098 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1100 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1104 /*************************************************
1105 * Parse remaining hash comment *
1106 *************************************************/
1110 Comment up to terminating CRLF
1113 filter points to the Sieve filter including its state
1119 static int parse_hashcomment(struct Sieve *filter)
1125 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1127 if (*filter->pc=='\n')
1140 filter->errmsg=CUS "missing end of comment";
1145 /*************************************************
1146 * Parse remaining C-style comment *
1147 *************************************************/
1151 Everything up to star slash
1154 filter points to the Sieve filter including its state
1160 static int parse_comment(struct Sieve *filter)
1165 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1172 filter->errmsg=CUS "missing end of comment";
1177 /*************************************************
1178 * Parse optional white space *
1179 *************************************************/
1183 Spaces, tabs, CRLFs, hash comments or C-style comments
1186 filter points to the Sieve filter including its state
1192 static int parse_white(struct Sieve *filter)
1196 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1198 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1200 else if (*filter->pc=='\n')
1210 else if (*filter->pc=='#')
1212 if (parse_hashcomment(filter)==-1) return -1;
1214 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1216 if (parse_comment(filter)==-1) return -1;
1224 #ifdef ENCODED_CHARACTER
1225 /*************************************************
1226 * Decode hex-encoded-character string *
1227 *************************************************/
1230 Encoding definition:
1231 blank = SP / TAB / CRLF
1232 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1233 hex-pair = 1*2HEXDIG
1236 src points to a hex-pair-seq
1237 end points to its end
1238 dst points to the destination of the decoded octets,
1239 optionally to (uschar*)0 for checking only
1241 Returns: >=0 number of decoded octets
1245 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1249 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1255 d<2 && src<end && isxdigit(n=tolower(*src));
1256 x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ;
1257 if (d==0) return -1;
1260 if (src==end) return decoded;
1261 if (*src==' ' || *src=='\t' || *src=='\n')
1262 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1271 /*************************************************
1272 * Decode unicode-encoded-character string *
1273 *************************************************/
1276 Encoding definition:
1277 blank = SP / TAB / CRLF
1278 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1279 unicode-hex = 1*HEXDIG
1281 It is an error for a script to use a hexadecimal value that isn't in
1282 either the range 0 to D7FF or the range E000 to 10FFFF.
1284 At this time, strings are already scanned, thus the CRLF is converted
1285 to the internally used \n (should RFC_EOL have been used).
1288 src points to a unicode-hex-seq
1289 end points to its end
1290 dst points to the destination of the decoded octets,
1291 optionally to (uschar*)0 for checking only
1293 Returns: >=0 number of decoded octets
1295 -2 semantic error (character range violation)
1299 unicode_decode(uschar *src, uschar *end, uschar *dst)
1303 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1310 for (hex_seq = src; src < end && *src=='0'; ) src++;
1312 d < 7 && src < end && isxdigit(n=tolower(*src));
1313 c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1314 if (src == hex_seq) return -1;
1315 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1321 else if (c>=0x80 && c<=0x7ff)
1326 *dst++=128+(c&0x3f);
1330 else if (c>=0x800 && c<=0xffff)
1335 *dst++=128+((c>>6)&0x3f);
1336 *dst++=128+(c&0x3f);
1340 else if (c>=0x10000 && c<=0x1fffff)
1345 *dst++=128+((c>>10)&0x3f);
1346 *dst++=128+((c>>6)&0x3f);
1347 *dst++=128+(c&0x3f);
1351 if (*src==' ' || *src=='\t' || *src=='\n')
1353 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1354 if (src==end) return decoded;
1363 /*************************************************
1364 * Decode encoded-character string *
1365 *************************************************/
1368 Encoding definition:
1369 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1370 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1373 encoded points to an encoded string, returns decoded string
1374 filter points to the Sieve filter including its state
1380 static int string_decode(struct Sieve *filter, struct String *data)
1382 uschar *src,*dst,*end;
1384 src=data->character;
1386 end=data->character+data->length;
1392 strncmpic(src,US "${hex:",6)==0
1393 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1394 && (hex_decode(src+6,brace,(uschar*)0))>=0
1397 dst+=hex_decode(src+6,brace,dst);
1401 strncmpic(src,US "${unicode:",10)==0
1402 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1405 switch (unicode_decode(src+10,brace,(uschar*)0))
1409 filter->errmsg=CUS "unicode character out of range";
1419 dst+=unicode_decode(src+10,brace,dst);
1426 data->length=dst-data->character;
1433 /*************************************************
1434 * Parse an optional string *
1435 *************************************************/
1439 quoted-string = DQUOTE *CHAR DQUOTE
1440 ;; in general, \ CHAR inside a string maps to CHAR
1441 ;; so \" maps to " and \\ maps to \
1442 ;; note that newlines and other characters are all allowed
1445 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1446 *(multi-line-literal / multi-line-dotstuff)
1448 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1449 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1450 ;; A line containing only "." ends the multi-line.
1451 ;; Remove a leading '.' if followed by another '.'.
1452 string = quoted-string / multi-line
1455 filter points to the Sieve filter including its state
1456 id specifies identifier to match
1460 0 identifier not matched
1464 parse_string(struct Sieve *filter, struct String *data)
1469 data->character = NULL;
1471 if (*filter->pc=='"') /* quoted string */
1476 if (*filter->pc=='"') /* end of string */
1481 data->length = len_string_from_gstring(g, &data->character);
1483 data->character = US"\0";
1484 /* that way, there will be at least one character allocated */
1486 #ifdef ENCODED_CHARACTER
1487 if (filter->require_encoded_character
1488 && string_decode(filter,data)==-1)
1493 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1495 g = string_catn(g, filter->pc+1, 1);
1498 else /* regular character */
1501 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1503 if (*filter->pc=='\n')
1505 g = string_catn(g, US"\r", 1);
1509 g = string_catn(g, filter->pc, 1);
1513 filter->errmsg=CUS "missing end of string";
1516 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1519 /* skip optional white space followed by hashed comment or CRLF */
1520 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1521 if (*filter->pc=='#')
1523 if (parse_hashcomment(filter)==-1) return -1;
1526 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1528 else if (*filter->pc=='\n')
1540 filter->errmsg=CUS "syntax error";
1546 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1548 if (*filter->pc=='\n') /* end of line */
1551 g = string_catn(g, CUS "\r\n", 2);
1559 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1561 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1565 data->length = len_string_from_gstring(g, &data->character);
1567 data->character = US"\0";
1568 /* that way, there will be at least one character allocated */
1576 #ifdef ENCODED_CHARACTER
1577 if (filter->require_encoded_character
1578 && string_decode(filter,data)==-1)
1583 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1585 g = string_catn(g, CUS ".", 1);
1589 else /* regular character */
1591 g = string_catn(g, filter->pc, 1);
1595 filter->errmsg=CUS "missing end of multi line string";
1602 /*************************************************
1603 * Parse a specific identifier *
1604 *************************************************/
1608 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1611 filter points to the Sieve filter including its state
1612 id specifies identifier to match
1615 0 identifier not matched
1618 static int parse_identifier(struct Sieve *filter, const uschar *id)
1620 size_t idlen=Ustrlen(id);
1622 if (strncmpic(US filter->pc,US id,idlen)==0)
1624 uschar next=filter->pc[idlen];
1626 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1634 /*************************************************
1636 *************************************************/
1640 number = 1*DIGIT [QUANTIFIER]
1641 QUANTIFIER = "K" / "M" / "G"
1644 filter points to the Sieve filter including its state
1648 -1 no string list found
1651 static int parse_number(struct Sieve *filter, unsigned long *data)
1655 if (*filter->pc>='0' && *filter->pc<='9')
1660 d=Ustrtoul(filter->pc,&e,10);
1663 filter->errmsg=CUstrerror(ERANGE);
1668 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1669 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1670 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1671 if (d>(ULONG_MAX/u))
1673 filter->errmsg=CUstrerror(ERANGE);
1682 filter->errmsg=CUS "missing number";
1688 /*************************************************
1689 * Parse a string list *
1690 *************************************************/
1694 string-list = "[" string *("," string) "]" / string
1697 filter points to the Sieve filter including its state
1698 data returns string list
1701 -1 no string list found
1705 parse_stringlist(struct Sieve *filter, struct String **data)
1707 const uschar *orig=filter->pc;
1708 int dataCapacity = 0;
1710 struct String *d = NULL;
1713 if (*filter->pc=='[') /* string list */
1718 if (parse_white(filter)==-1) goto error;
1719 if (dataLength+1 >= dataCapacity) /* increase buffer */
1723 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1724 new = store_get(sizeof(struct String) * dataCapacity, GET_UNTAINTED);
1726 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1730 m=parse_string(filter,&d[dataLength]);
1733 if (dataLength==0) break;
1736 filter->errmsg=CUS "missing string";
1740 else if (m==-1) goto error;
1742 if (parse_white(filter)==-1) goto error;
1743 if (*filter->pc==',') ++filter->pc;
1746 if (*filter->pc==']')
1748 d[dataLength].character=(uschar*)0;
1749 d[dataLength].length=-1;
1756 filter->errmsg=CUS "missing closing bracket";
1760 else /* single string */
1762 if (!(d=store_get(sizeof(struct String)*2, GET_UNTAINTED)))
1765 m=parse_string(filter,&d[0]);
1776 d[1].character=(uschar*)0;
1783 filter->errmsg=CUS "missing string list";
1788 /*************************************************
1789 * Parse an optional address part specifier *
1790 *************************************************/
1794 address-part = ":localpart" / ":domain" / ":all"
1795 address-part =/ ":user" / ":detail"
1798 filter points to the Sieve filter including its state
1799 a returns address part specified
1802 0 no comparator found
1806 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1809 if (parse_identifier(filter,CUS ":user")==1)
1811 if (!filter->require_subaddress)
1813 filter->errmsg=CUS "missing previous require \"subaddress\";";
1819 else if (parse_identifier(filter,CUS ":detail")==1)
1821 if (!filter->require_subaddress)
1823 filter->errmsg=CUS "missing previous require \"subaddress\";";
1831 if (parse_identifier(filter,CUS ":localpart")==1)
1833 *a=ADDRPART_LOCALPART;
1836 else if (parse_identifier(filter,CUS ":domain")==1)
1841 else if (parse_identifier(filter,CUS ":all")==1)
1850 /*************************************************
1851 * Parse an optional comparator *
1852 *************************************************/
1856 comparator = ":comparator" <comparator-name: string>
1859 filter points to the Sieve filter including its state
1860 c returns comparator
1863 0 no comparator found
1864 -1 incomplete comparator found
1867 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1869 struct String comparator_name;
1871 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1872 if (parse_white(filter)==-1) return -1;
1873 switch (parse_string(filter,&comparator_name))
1878 filter->errmsg=CUS "missing comparator";
1885 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1890 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1892 *c=COMP_EN_ASCII_CASEMAP;
1895 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1897 *c=COMP_EN_ASCII_CASEMAP;
1900 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1902 *c=COMP_ASCII_NUMERIC;
1907 filter->errmsg=CUS "invalid comparator";
1916 /*************************************************
1917 * Parse an optional match type *
1918 *************************************************/
1922 match-type = ":is" / ":contains" / ":matches"
1925 filter points to the Sieve filter including its state
1926 m returns match type
1929 0 no match type found
1932 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1934 if (parse_identifier(filter,CUS ":is")==1)
1939 else if (parse_identifier(filter,CUS ":contains")==1)
1944 else if (parse_identifier(filter,CUS ":matches")==1)
1953 /*************************************************
1954 * Parse and interpret an optional test list *
1955 *************************************************/
1959 test-list = "(" test *("," test) ")"
1962 filter points to the Sieve filter including its state
1963 n total number of tests
1964 num_true number of passed tests
1965 exec Execute parsed statements
1968 0 no test list found
1969 -1 syntax or execution error
1972 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1974 if (parse_white(filter)==-1) return -1;
1975 if (*filter->pc=='(')
1984 switch (parse_test(filter,&cond,exec))
1987 case 0: filter->errmsg=CUS "missing test"; return -1;
1988 default: ++*n; if (cond) ++*num_true; break;
1990 if (parse_white(filter)==-1) return -1;
1991 if (*filter->pc==',') ++filter->pc;
1994 if (*filter->pc==')')
2001 filter->errmsg=CUS "missing closing paren";
2009 /*************************************************
2010 * Parse and interpret an optional test *
2011 *************************************************/
2015 filter points to the Sieve filter including its state
2016 cond returned condition status
2017 exec Execute parsed statements
2021 -1 syntax or execution error
2025 parse_test(struct Sieve *filter, int *cond, int exec)
2027 if (parse_white(filter)==-1) return -1;
2028 if (parse_identifier(filter,CUS "address"))
2031 address-test = "address" { [address-part] [comparator] [match-type] }
2032 <header-list: string-list> <key-list: string-list>
2034 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2037 enum AddressPart addressPart=ADDRPART_ALL;
2038 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2039 enum MatchType matchType=MATCH_IS;
2040 struct String *hdr,*key;
2046 if (parse_white(filter)==-1) return -1;
2047 if ((m=parse_addresspart(filter,&addressPart))!=0)
2049 if (m==-1) return -1;
2052 filter->errmsg=CUS "address part already specified";
2057 else if ((m=parse_comparator(filter,&comparator))!=0)
2059 if (m==-1) return -1;
2062 filter->errmsg=CUS "comparator already specified";
2067 else if ((m=parse_matchtype(filter,&matchType))!=0)
2069 if (m==-1) return -1;
2072 filter->errmsg=CUS "match type already specified";
2079 if (parse_white(filter)==-1) return -1;
2080 if ((m=parse_stringlist(filter,&hdr))!=1)
2082 if (m==0) filter->errmsg=CUS "header string list expected";
2085 if (parse_white(filter)==-1) return -1;
2086 if ((m=parse_stringlist(filter,&key))!=1)
2088 if (m==0) filter->errmsg=CUS "key string list expected";
2092 for (struct String * h = hdr; h->length!=-1 && !*cond; ++h)
2094 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2098 !eq_asciicase(h,&str_from,0)
2099 && !eq_asciicase(h,&str_to,0)
2100 && !eq_asciicase(h,&str_cc,0)
2101 && !eq_asciicase(h,&str_bcc,0)
2102 && !eq_asciicase(h,&str_sender,0)
2103 && !eq_asciicase(h,&str_resent_from,0)
2104 && !eq_asciicase(h,&str_resent_to,0)
2107 filter->errmsg=CUS "invalid header field";
2112 /* We are only interested in addresses below, so no MIME decoding */
2113 if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h)))))
2115 filter->errmsg=CUS "header string expansion failed";
2118 f.parse_allow_group = TRUE;
2119 while (*header_value && !*cond)
2122 int start, end, domain;
2126 end_addr = parse_find_address_end(header_value, FALSE);
2127 saveend = *end_addr;
2129 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2131 if (extracted_addr) switch (addressPart)
2133 case ADDRPART_ALL: part=extracted_addr; break;
2137 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2138 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2140 case ADDRPART_DETAIL: part=NULL; break;
2144 *end_addr = saveend;
2147 for (struct String * k = key; k->length !=- 1; ++k)
2149 struct String partStr = {.character = part, .length = Ustrlen(part)};
2153 *cond=compare(filter,k,&partStr,comparator,matchType);
2154 if (*cond==-1) return -1;
2159 if (saveend == 0) break;
2160 header_value = end_addr + 1;
2162 f.parse_allow_group = FALSE;
2163 f.parse_found_group = FALSE;
2168 else if (parse_identifier(filter,CUS "allof"))
2171 allof-test = "allof" <tests: test-list>
2176 switch (parse_testlist(filter,&n,&num_true,exec))
2179 case 0: filter->errmsg=CUS "missing test list"; return -1;
2180 default: *cond=(n==num_true); return 1;
2183 else if (parse_identifier(filter,CUS "anyof"))
2186 anyof-test = "anyof" <tests: test-list>
2191 switch (parse_testlist(filter,&n,&num_true,exec))
2194 case 0: filter->errmsg=CUS "missing test list"; return -1;
2195 default: *cond=(num_true>0); return 1;
2198 else if (parse_identifier(filter,CUS "exists"))
2201 exists-test = "exists" <header-names: string-list>
2207 if (parse_white(filter)==-1) return -1;
2208 if ((m=parse_stringlist(filter,&hdr))!=1)
2210 if (m==0) filter->errmsg=CUS "header string list expected";
2216 for (struct String * h = hdr; h->length != -1 && *cond; ++h)
2220 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2223 filter->errmsg=CUS "header string expansion failed";
2226 if (Ustrcmp(header_def,"false")==0) *cond=0;
2231 else if (parse_identifier(filter,CUS "false"))
2234 false-test = "false"
2240 else if (parse_identifier(filter,CUS "header"))
2243 header-test = "header" { [comparator] [match-type] }
2244 <header-names: string-list> <key-list: string-list>
2247 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2248 enum MatchType matchType=MATCH_IS;
2249 struct String *hdr,*key;
2255 if (parse_white(filter)==-1) return -1;
2256 if ((m=parse_comparator(filter,&comparator))!=0)
2258 if (m==-1) return -1;
2261 filter->errmsg=CUS "comparator already specified";
2266 else if ((m=parse_matchtype(filter,&matchType))!=0)
2268 if (m==-1) return -1;
2271 filter->errmsg=CUS "match type already specified";
2278 if (parse_white(filter)==-1) return -1;
2279 if ((m=parse_stringlist(filter,&hdr))!=1)
2281 if (m==0) filter->errmsg=CUS "header string list expected";
2284 if (parse_white(filter)==-1) return -1;
2285 if ((m=parse_stringlist(filter,&key))!=1)
2287 if (m==0) filter->errmsg=CUS "key string list expected";
2291 for (struct String * h = hdr; h->length != -1 && !*cond; ++h)
2295 filter->errmsg=CUS "invalid header field";
2300 struct String header_value;
2303 expand_header(&header_value,h);
2304 header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2305 if (!header_value.character || !header_def)
2307 filter->errmsg=CUS "header string expansion failed";
2310 for (struct String * k = key; k->length != -1; ++k)
2311 if (Ustrcmp(header_def,"true")==0)
2313 *cond=compare(filter,k,&header_value,comparator,matchType);
2314 if (*cond==-1) return -1;
2321 else if (parse_identifier(filter,CUS "not"))
2323 if (parse_white(filter)==-1) return -1;
2324 switch (parse_test(filter,cond,exec))
2327 case 0: filter->errmsg=CUS "missing test"; return -1;
2328 default: *cond=!*cond; return 1;
2331 else if (parse_identifier(filter,CUS "size"))
2334 relop = ":over" / ":under"
2335 size-test = "size" relop <limit: number>
2338 unsigned long limit;
2341 if (parse_white(filter)==-1) return -1;
2342 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2343 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2346 filter->errmsg=CUS "missing :over or :under";
2349 if (parse_white(filter)==-1) return -1;
2350 if (parse_number(filter,&limit)==-1) return -1;
2351 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2354 else if (parse_identifier(filter,CUS "true"))
2359 else if (parse_identifier(filter,CUS "envelope"))
2362 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2363 <envelope-part: string-list> <key-list: string-list>
2365 envelope-part is case insensitive "from" or "to"
2366 #ifdef ENVELOPE_AUTH
2367 envelope-part =/ "auth"
2371 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2372 enum AddressPart addressPart=ADDRPART_ALL;
2373 enum MatchType matchType=MATCH_IS;
2374 struct String *env,*key;
2378 if (!filter->require_envelope)
2380 filter->errmsg=CUS "missing previous require \"envelope\";";
2385 if (parse_white(filter)==-1) return -1;
2386 if ((m=parse_comparator(filter,&comparator))!=0)
2388 if (m==-1) return -1;
2391 filter->errmsg=CUS "comparator already specified";
2396 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2398 if (m==-1) return -1;
2401 filter->errmsg=CUS "address part already specified";
2406 else if ((m=parse_matchtype(filter,&matchType))!=0)
2408 if (m==-1) return -1;
2411 filter->errmsg=CUS "match type already specified";
2418 if (parse_white(filter)==-1) return -1;
2419 if ((m=parse_stringlist(filter,&env))!=1)
2421 if (m==0) filter->errmsg=CUS "envelope string list expected";
2424 if (parse_white(filter)==-1) return -1;
2425 if ((m=parse_stringlist(filter,&key))!=1)
2427 if (m==0) filter->errmsg=CUS "key string list expected";
2431 for (struct String * e = env; e->length != -1 && !*cond; ++e)
2433 const uschar *envelopeExpr=CUS 0;
2434 uschar *envelope=US 0;
2436 if (eq_asciicase(e,&str_from,0))
2438 switch (addressPart)
2440 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2444 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2445 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2447 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2451 else if (eq_asciicase(e,&str_to,0))
2453 switch (addressPart)
2455 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2457 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2458 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2460 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2461 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2464 #ifdef ENVELOPE_AUTH
2465 else if (eq_asciicase(e,&str_auth,0))
2467 switch (addressPart)
2469 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2473 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2474 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2476 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2483 filter->errmsg=CUS "invalid envelope string";
2486 if (exec && envelopeExpr)
2488 if (!(envelope=expand_string(US envelopeExpr)))
2490 filter->errmsg=CUS "header string expansion failed";
2493 for (struct String * k = key; k->length != -1; ++k)
2495 struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)};
2497 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2498 if (*cond==-1) return -1;
2506 else if (parse_identifier(filter,CUS "valid_notify_method"))
2509 valid_notify_method = "valid_notify_method"
2510 <notification-uris: string-list>
2513 struct String *uris;
2516 if (!filter->require_enotify)
2518 filter->errmsg=CUS "missing previous require \"enotify\";";
2521 if (parse_white(filter)==-1) return -1;
2522 if ((m=parse_stringlist(filter,&uris))!=1)
2524 if (m==0) filter->errmsg=CUS "URI string list expected";
2530 for (struct String * u = uris; u->length != -1 && *cond; ++u)
2532 string_item *recipient;
2533 struct String header,subject,body;
2537 header.character=(uschar*)0;
2539 subject.character=(uschar*)0;
2541 body.character=(uschar*)0;
2542 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2548 else if (parse_identifier(filter,CUS "notify_method_capability"))
2551 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2552 <notification-uri: string>
2553 <notification-capability: string>
2554 <key-list: string-list>
2560 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2561 enum MatchType matchType=MATCH_IS;
2562 struct String uri,capa,*keys;
2564 if (!filter->require_enotify)
2566 filter->errmsg=CUS "missing previous require \"enotify\";";
2571 if (parse_white(filter)==-1) return -1;
2572 if ((m=parse_comparator(filter,&comparator))!=0)
2574 if (m==-1) return -1;
2577 filter->errmsg=CUS "comparator already specified";
2582 else if ((m=parse_matchtype(filter,&matchType))!=0)
2584 if (m==-1) return -1;
2587 filter->errmsg=CUS "match type already specified";
2594 if ((m=parse_string(filter,&uri))!=1)
2596 if (m==0) filter->errmsg=CUS "missing notification URI string";
2599 if (parse_white(filter)==-1) return -1;
2600 if ((m=parse_string(filter,&capa))!=1)
2602 if (m==0) filter->errmsg=CUS "missing notification capability string";
2605 if (parse_white(filter)==-1) return -1;
2606 if ((m=parse_stringlist(filter,&keys))!=1)
2608 if (m==0) filter->errmsg=CUS "missing key string list";
2613 string_item *recipient;
2614 struct String header,subject,body;
2619 header.character=(uschar*)0;
2621 subject.character=(uschar*)0;
2623 body.character=(uschar*)0;
2624 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2625 if (eq_asciicase(&capa,&str_online,0)==1)
2626 for (struct String * k = keys; k->length != -1; ++k)
2628 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2629 if (*cond==-1) return -1;
2640 /*************************************************
2641 * Parse and interpret an optional block *
2642 *************************************************/
2646 filter points to the Sieve filter including its state
2647 exec Execute parsed statements
2648 generated where to hang newly-generated addresses
2650 Returns: 2 success by stop
2652 0 no block command found
2653 -1 syntax or execution error
2657 parse_block(struct Sieve *filter, int exec,
2658 address_item **generated)
2662 if (parse_white(filter)==-1) return -1;
2663 if (*filter->pc=='{')
2666 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2667 if (*filter->pc=='}')
2674 filter->errmsg=CUS "expecting command or closing brace";
2682 /*************************************************
2683 * Match a semicolon *
2684 *************************************************/
2688 filter points to the Sieve filter including its state
2694 static int parse_semicolon(struct Sieve *filter)
2696 if (parse_white(filter)==-1) return -1;
2697 if (*filter->pc==';')
2704 filter->errmsg=CUS "missing semicolon";
2710 /*************************************************
2711 * Parse and interpret a Sieve command *
2712 *************************************************/
2716 filter points to the Sieve filter including its state
2717 exec Execute parsed statements
2718 generated where to hang newly-generated addresses
2720 Returns: 2 success by stop
2722 -1 syntax or execution error
2725 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2729 if (parse_white(filter)==-1) return -1;
2730 if (parse_identifier(filter,CUS "if"))
2733 if-command = "if" test block *( "elsif" test block ) [ else block ]
2736 int cond,m,unsuccessful;
2739 if (parse_white(filter)==-1) return -1;
2740 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2743 filter->errmsg=CUS "missing test";
2746 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2747 (debug_selector & D_filter) != 0)
2749 if (exec) debug_printf_indent("if %s\n",cond?"true":"false");
2751 m=parse_block(filter,exec ? cond : 0, generated);
2752 if (m==-1 || m==2) return m;
2755 filter->errmsg=CUS "missing block";
2758 unsuccessful = !cond;
2759 for (;;) /* elsif test block */
2761 if (parse_white(filter)==-1) return -1;
2762 if (parse_identifier(filter,CUS "elsif"))
2764 if (parse_white(filter)==-1) return -1;
2765 m=parse_test(filter,&cond,exec && unsuccessful);
2766 if (m==-1 || m==2) return m;
2769 filter->errmsg=CUS "missing test";
2772 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2773 (debug_selector & D_filter) != 0)
2775 if (exec) debug_printf_indent("elsif %s\n",cond?"true":"false");
2777 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2778 if (m==-1 || m==2) return m;
2781 filter->errmsg=CUS "missing block";
2784 if (exec && unsuccessful && cond) unsuccessful = 0;
2789 if (parse_white(filter)==-1) return -1;
2790 if (parse_identifier(filter,CUS "else"))
2792 m=parse_block(filter,exec && unsuccessful, generated);
2793 if (m==-1 || m==2) return m;
2796 filter->errmsg=CUS "missing block";
2801 else if (parse_identifier(filter,CUS "stop"))
2804 stop-command = "stop" { stop-options } ";"
2808 if (parse_semicolon(filter)==-1) return -1;
2811 filter->pc+=Ustrlen(filter->pc);
2815 else if (parse_identifier(filter,CUS "keep"))
2818 keep-command = "keep" { keep-options } ";"
2822 if (parse_semicolon(filter)==-1) return -1;
2825 add_addr(generated,US"inbox",1,0,0,0);
2829 else if (parse_identifier(filter,CUS "discard"))
2832 discard-command = "discard" { discard-options } ";"
2836 if (parse_semicolon(filter)==-1) return -1;
2837 if (exec) filter->keep=0;
2839 else if (parse_identifier(filter,CUS "redirect"))
2842 redirect-command = "redirect" redirect-options "string" ";"
2844 redirect-options =) ":copy"
2847 struct String recipient;
2853 if (parse_white(filter)==-1) return -1;
2854 if (parse_identifier(filter,CUS ":copy")==1)
2856 if (!filter->require_copy)
2858 filter->errmsg=CUS "missing previous require \"copy\";";
2865 if (parse_white(filter)==-1) return -1;
2866 if ((m=parse_string(filter,&recipient))!=1)
2868 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2871 if (strchr(CCS recipient.character,'@')==(char*)0)
2873 filter->errmsg=CUS "unqualified recipient address";
2878 add_addr(generated,recipient.character,0,0,0,0);
2879 if (!copy) filter->keep = 0;
2881 if (parse_semicolon(filter)==-1) return -1;
2883 else if (parse_identifier(filter,CUS "fileinto"))
2886 fileinto-command = "fileinto" { fileinto-options } string ";"
2888 fileinto-options =) [ ":copy" ]
2891 struct String folder;
2894 unsigned long maxage, maxmessages, maxstorage;
2897 maxage = maxmessages = maxstorage = 0;
2898 if (!filter->require_fileinto)
2900 filter->errmsg=CUS "missing previous require \"fileinto\";";
2905 if (parse_white(filter)==-1) return -1;
2906 if (parse_identifier(filter,CUS ":copy")==1)
2908 if (!filter->require_copy)
2910 filter->errmsg=CUS "missing previous require \"copy\";";
2917 if (parse_white(filter)==-1) return -1;
2918 if ((m=parse_string(filter,&folder))!=1)
2920 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2923 m=0; s=folder.character;
2924 if (folder.length==0) m=1;
2925 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2928 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2933 filter->errmsg=CUS "invalid folder";
2938 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2939 if (!copy) filter->keep = 0;
2941 if (parse_semicolon(filter)==-1) return -1;
2944 else if (parse_identifier(filter,CUS "notify"))
2947 notify-command = "notify" { notify-options } <method: string> ";"
2948 notify-options = [":from" string]
2949 [":importance" <"1" / "2" / "3">]
2950 [":options" 1*(string-list / number)]
2956 struct String importance;
2957 struct String message;
2958 struct String method;
2959 struct Notification *already;
2960 string_item *recipient;
2961 struct String header;
2962 struct String subject;
2964 uschar *envelope_from;
2965 struct String auto_submitted_value;
2966 uschar *auto_submitted_def;
2968 if (!filter->require_enotify)
2970 filter->errmsg=CUS "missing previous require \"enotify\";";
2973 from.character=(uschar*)0;
2975 importance.character=(uschar*)0;
2976 importance.length=-1;
2977 message.character=(uschar*)0;
2981 header.character=(uschar*)0;
2983 subject.character=(uschar*)0;
2985 body.character=(uschar*)0;
2986 envelope_from = sender_address && sender_address[0]
2987 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2990 filter->errmsg=CUS "expansion failure for envelope from";
2995 if (parse_white(filter)==-1) return -1;
2996 if (parse_identifier(filter,CUS ":from")==1)
2998 if (parse_white(filter)==-1) return -1;
2999 if ((m=parse_string(filter,&from))!=1)
3001 if (m==0) filter->errmsg=CUS "from string expected";
3005 else if (parse_identifier(filter,CUS ":importance")==1)
3007 if (parse_white(filter)==-1) return -1;
3008 if ((m=parse_string(filter,&importance))!=1)
3010 if (m==0) filter->errmsg=CUS "importance string expected";
3013 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3015 filter->errmsg=CUS "invalid importance";
3019 else if (parse_identifier(filter,CUS ":options")==1)
3021 if (parse_white(filter)==-1) return -1;
3023 else if (parse_identifier(filter,CUS ":message")==1)
3025 if (parse_white(filter)==-1) return -1;
3026 if ((m=parse_string(filter,&message))!=1)
3028 if (m==0) filter->errmsg=CUS "message string expected";
3034 if (parse_white(filter)==-1) return -1;
3035 if ((m=parse_string(filter,&method))!=1)
3037 if (m==0) filter->errmsg=CUS "missing method string";
3040 if (parse_semicolon(filter)==-1) return -1;
3041 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3045 if (message.length==-1) message=subject;
3046 if (message.length==-1) expand_header(&message,&str_subject);
3047 expand_header(&auto_submitted_value,&str_auto_submitted);
3048 auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}");
3049 if (!auto_submitted_value.character || !auto_submitted_def)
3051 filter->errmsg=CUS "header string expansion failed";
3054 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3056 for (already=filter->notified; already; already=already->next)
3058 if (already->method.length==method.length
3059 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3060 && already->importance.length==importance.length
3061 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3062 && already->message.length==message.length
3063 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3067 /* New notification, process it */
3069 struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED);
3070 sent->method=method;
3071 sent->importance=importance;
3072 sent->message=message;
3073 sent->next=filter->notified;
3074 filter->notified=sent;
3075 #ifndef COMPILE_SYNTAX_CHECKER
3076 if (filter_test == FTEST_NONE)
3080 if ((pid = child_open_exim2(&fd, envelope_from, envelope_from,
3081 US"sieve-notify")) >= 1)
3083 FILE * f = fdopen(fd, "wb");
3085 fprintf(f,"From: %s\n", from.length == -1
3086 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
3088 for (string_item * p = recipient; p; p=p->next)
3089 fprintf(f, "To: %s\n",p->text);
3090 fprintf(f, "Auto-Submitted: auto-notified; %s\n", filter->enotify_mailto_owner);
3091 if (header.length > 0) fprintf(f, "%s", header.character);
3092 if (message.length==-1)
3094 message.character=US"Notification";
3095 message.length=Ustrlen(message.character);
3097 if (message.length != -1)
3098 fprintf(f, "Subject: %s\n", parse_quote_2047(message.character,
3099 message.length, US"utf-8", TRUE));
3101 if (body.length > 0) fprintf(f, "%s\n", body.character);
3104 (void)child_close(pid, 0);
3107 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3108 debug_printf_indent("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3112 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3113 debug_printf_indent("Repeated notification to `%s' ignored.\n",method.character);
3116 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3117 debug_printf_indent("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3122 else if (parse_identifier(filter,CUS "vacation"))
3125 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3126 vacation-options = [":days" number]
3129 [":addresses" string-list]
3136 struct String subject;
3138 struct String *addresses;
3140 string_item *aliases;
3141 struct String handle;
3142 struct String reason;
3144 if (!filter->require_vacation)
3146 filter->errmsg=CUS "missing previous require \"vacation\";";
3151 if (filter->vacation_ran)
3153 filter->errmsg=CUS "trying to execute vacation more than once";
3156 filter->vacation_ran=1;
3158 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3159 subject.character=(uschar*)0;
3161 from.character=(uschar*)0;
3163 addresses=(struct String*)0;
3166 handle.character=(uschar*)0;
3170 if (parse_white(filter)==-1) return -1;
3171 if (parse_identifier(filter,CUS ":days")==1)
3173 if (parse_white(filter)==-1) return -1;
3174 if (parse_number(filter,&days)==-1) return -1;
3175 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3176 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3178 else if (parse_identifier(filter,CUS ":subject")==1)
3180 if (parse_white(filter)==-1) return -1;
3181 if ((m=parse_string(filter,&subject))!=1)
3183 if (m==0) filter->errmsg=CUS "subject string expected";
3187 else if (parse_identifier(filter,CUS ":from")==1)
3189 if (parse_white(filter)==-1) return -1;
3190 if ((m=parse_string(filter,&from))!=1)
3192 if (m==0) filter->errmsg=CUS "from string expected";
3195 if (check_mail_address(filter,&from)!=1)
3198 else if (parse_identifier(filter,CUS ":addresses")==1)
3200 if (parse_white(filter)==-1) return -1;
3201 if ((m=parse_stringlist(filter,&addresses))!=1)
3203 if (m==0) filter->errmsg=CUS "addresses string list expected";
3206 for (struct String * a = addresses; a->length != -1; ++a)
3208 string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
3210 new->text = store_get(a->length+1, a->character);
3211 if (a->length) memcpy(new->text,a->character,a->length);
3212 new->text[a->length]='\0';
3217 else if (parse_identifier(filter,CUS ":mime")==1)
3219 else if (parse_identifier(filter,CUS ":handle")==1)
3221 if (parse_white(filter)==-1) return -1;
3222 if ((m=parse_string(filter,&from))!=1)
3224 if (m==0) filter->errmsg=CUS "handle string expected";
3230 if (parse_white(filter)==-1) return -1;
3231 if ((m=parse_string(filter,&reason))!=1)
3233 if (m==0) filter->errmsg=CUS "missing reason string";
3240 for (s = reason.character, end = reason.character + reason.length;
3241 s<end && (*s&0x80)==0; ) s++;
3244 filter->errmsg=CUS "MIME reason string contains 8bit text";
3248 if (parse_semicolon(filter)==-1) return -1;
3255 uschar hexdigest[33];
3258 if (filter_personal(aliases,TRUE))
3260 if (filter_test == FTEST_NONE)
3262 /* ensure oncelog directory exists; failure will be detected later */
3264 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3266 /* build oncelog filename */
3270 if (handle.length==-1)
3272 gstring * key = NULL;
3273 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3274 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3275 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3276 key = string_catn(key, reason.character, reason.length);
3277 md5_end(&base, key->s, key->ptr, digest);
3280 md5_end(&base, handle.character, handle.length, digest);
3282 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3284 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3285 debug_printf_indent("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3287 if (filter_test == FTEST_NONE)
3289 once = string_cat (NULL, filter->vacation_directory);
3290 once = string_catn(once, US"/", 1);
3291 once = string_catn(once, hexdigest, 33);
3293 /* process subject */
3295 if (subject.length==-1)
3297 uschar * subject_def;
3299 subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3300 if (subject_def && Ustrcmp(subject_def,"true")==0)
3302 gstring * g = string_catn(NULL, US"Auto: ", 6);
3304 expand_header(&subject,&str_subject);
3305 g = string_catn(g, subject.character, subject.length);
3306 subject.length = len_string_from_gstring(g, &subject.character);
3310 subject.character = US"Automated reply";
3311 subject.length = Ustrlen(subject.character);
3315 /* add address to list of generated addresses */
3317 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3318 setflag(addr, af_pfr);
3319 addr->prop.ignore_error = TRUE;
3320 addr->next = *generated;
3322 addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
3323 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3324 addr->reply->to = string_copy(sender_address);
3325 if (from.length==-1)
3326 addr->reply->from = expand_string(US"$local_part@$domain");
3328 addr->reply->from = from.character;
3329 /* deconst cast safe as we pass in a non-const item */
3330 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE);
3331 addr->reply->oncelog = string_from_gstring(once);
3332 addr->reply->once_repeat=days*86400;
3334 /* build body and MIME headers */
3338 uschar *mime_body,*reason_end;
3339 static const uschar nlnl[]="\r\n\r\n";
3343 mime_body = reason.character, reason_end = reason.character + reason.length;
3344 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3347 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3349 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3350 else mime_body=reason_end-1;
3351 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3355 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3357 addr->reply->headers = US"MIME-Version: 1.0\n"
3358 "Content-Type: text/plain;\n"
3359 "\tcharset=\"utf-8\"\n"
3360 "Content-Transfer-Encoding: quoted-printable";
3361 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3365 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3366 debug_printf_indent("Sieve: mail was not personal, vacation would ignore it\n");
3376 /*************************************************
3377 * Parse and interpret a sieve filter *
3378 *************************************************/
3382 filter points to the Sieve filter including its state
3383 exec Execute parsed statements
3384 generated where to hang newly-generated addresses
3387 -1 syntax or execution error
3391 parse_start(struct Sieve *filter, int exec, address_item **generated)
3393 filter->pc=filter->filter;
3396 filter->require_envelope=0;
3397 filter->require_fileinto=0;
3398 #ifdef ENCODED_CHARACTER
3399 filter->require_encoded_character=0;
3401 #ifdef ENVELOPE_AUTH
3402 filter->require_envelope_auth=0;
3405 filter->require_enotify=0;
3406 filter->notified=(struct Notification*)0;
3409 filter->require_subaddress=0;
3412 filter->require_vacation=0;
3413 filter->vacation_ran=0;
3415 filter->require_copy=0;
3416 filter->require_iascii_numeric=0;
3418 if (parse_white(filter)==-1) return -1;
3420 if (exec && filter->vacation_directory && filter_test == FTEST_NONE)
3423 struct dirent *oncelog;
3424 struct stat properties;
3427 /* clean up old vacation log databases */
3429 if ( !(oncelogdir = exim_opendir(filter->vacation_directory))
3432 filter->errmsg = CUS "unable to open vacation directory";
3440 while ((oncelog = readdir(oncelogdir)))
3441 if (strlen(oncelog->d_name)==32)
3443 uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name);
3444 if (Ustat(s,&properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now)
3447 closedir(oncelogdir);
3451 while (parse_identifier(filter,CUS "require"))
3454 require-command = "require" <capabilities: string-list>
3460 if (parse_white(filter)==-1) return -1;
3461 if ((m=parse_stringlist(filter,&cap))!=1)
3463 if (m==0) filter->errmsg=CUS "capability string list expected";
3466 for (struct String * check = cap; check->character; ++check)
3468 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3469 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3470 #ifdef ENCODED_CHARACTER
3471 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3473 #ifdef ENVELOPE_AUTH
3474 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3477 else if (eq_octet(check,&str_enotify,0))
3479 if (!filter->enotify_mailto_owner)
3481 filter->errmsg=CUS "enotify disabled";
3484 filter->require_enotify=1;
3488 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3491 else if (eq_octet(check,&str_vacation,0))
3493 if (filter_test == FTEST_NONE && !filter->vacation_directory)
3495 filter->errmsg=CUS "vacation disabled";
3498 filter->require_vacation=1;
3501 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3502 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3503 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3504 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3505 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3508 filter->errmsg=CUS "unknown capability";
3512 if (parse_semicolon(filter)==-1) return -1;
3514 if (parse_commands(filter,exec,generated)==-1) return -1;
3517 filter->errmsg=CUS "syntax error";
3524 /*************************************************
3525 * Interpret a sieve filter file *
3526 *************************************************/
3530 filter points to the entire file, read into store as a single string
3531 options controls whether various special things are allowed, and requests
3532 special actions (not currently used)
3533 vacation_directory where to store vacation "once" files
3534 enotify_mailto_owner owner of mailto notifications
3535 useraddress string expression for :user part of address
3536 subaddress string expression for :subaddress part of address
3537 generated where to hang newly-generated addresses
3538 error where to pass back an error text
3540 Returns: FF_DELIVERED success, a significant action was taken
3541 FF_NOTDELIVERED success, no significant action
3542 FF_DEFER defer requested
3543 FF_FAIL fail requested
3544 FF_FREEZE freeze requested
3545 FF_ERROR there was a problem
3549 sieve_interpret(const uschar * filter, int options,
3550 const uschar * vacation_directory, const uschar * enotify_mailto_owner,
3551 const uschar * useraddress, const uschar * subaddress,
3552 address_item ** generated, uschar ** error)
3558 DEBUG(D_route) debug_printf_indent("Sieve: start of processing\n");
3560 sieve.filter = filter;
3562 if (!vacation_directory)
3563 sieve.vacation_directory = NULL;
3564 else if (!(sieve.vacation_directory = expand_cstring(vacation_directory)))
3566 *error = string_sprintf("failed to expand \"%s\" "
3567 "(sieve_vacation_directory): %s", vacation_directory,
3568 expand_string_message);
3572 if (!enotify_mailto_owner)
3573 sieve.enotify_mailto_owner = NULL;
3574 else if (!(sieve.enotify_mailto_owner = expand_cstring(enotify_mailto_owner)))
3576 *error = string_sprintf("failed to expand \"%s\" "
3577 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3578 expand_string_message);
3582 sieve.useraddress = useraddress
3583 ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3584 sieve.subaddress = subaddress;
3586 #ifdef COMPILE_SYNTAX_CHECKER
3587 if (parse_start(&sieve, 0, generated) == 1)
3589 if (parse_start(&sieve, 1, generated) == 1)
3593 add_addr(generated, US"inbox", 1, 0, 0, 0);
3594 msg = US"Implicit keep";
3599 msg = US"No implicit keep";
3604 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3605 #ifdef COMPILE_SYNTAX_CHECKER
3609 add_addr(generated,US"inbox",1,0,0,0);
3614 #ifndef COMPILE_SYNTAX_CHECKER
3615 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3616 else debug_printf_indent("%s\n", msg);
3620 DEBUG(D_route) debug_printf_indent("Sieve: end of processing\n");