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("String comparison (match ");
837 case MATCH_IS: debug_printf(":is"); break;
838 case MATCH_CONTAINS: debug_printf(":contains"); break;
839 case MATCH_MATCHES: debug_printf(":matches"); break;
841 debug_printf(", comparison \"");
844 case COMP_OCTET: debug_printf("i;octet"); break;
845 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
846 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
848 debug_printf("\"):\n");
849 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
850 debug_printf(" 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(" 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("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("%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
2656 static int parse_block(struct Sieve *filter, int exec,
2657 address_item **generated)
2661 if (parse_white(filter)==-1) return -1;
2662 if (*filter->pc=='{')
2665 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2666 if (*filter->pc=='}')
2673 filter->errmsg=CUS "expecting command or closing brace";
2681 /*************************************************
2682 * Match a semicolon *
2683 *************************************************/
2687 filter points to the Sieve filter including its state
2693 static int parse_semicolon(struct Sieve *filter)
2695 if (parse_white(filter)==-1) return -1;
2696 if (*filter->pc==';')
2703 filter->errmsg=CUS "missing semicolon";
2709 /*************************************************
2710 * Parse and interpret a Sieve command *
2711 *************************************************/
2715 filter points to the Sieve filter including its state
2716 exec Execute parsed statements
2717 generated where to hang newly-generated addresses
2719 Returns: 2 success by stop
2721 -1 syntax or execution error
2724 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2728 if (parse_white(filter)==-1) return -1;
2729 if (parse_identifier(filter,CUS "if"))
2732 if-command = "if" test block *( "elsif" test block ) [ else block ]
2735 int cond,m,unsuccessful;
2738 if (parse_white(filter)==-1) return -1;
2739 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2742 filter->errmsg=CUS "missing test";
2745 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2746 (debug_selector & D_filter) != 0)
2748 if (exec) debug_printf("if %s\n",cond?"true":"false");
2750 m=parse_block(filter,exec ? cond : 0, generated);
2751 if (m==-1 || m==2) return m;
2754 filter->errmsg=CUS "missing block";
2757 unsuccessful = !cond;
2758 for (;;) /* elsif test block */
2760 if (parse_white(filter)==-1) return -1;
2761 if (parse_identifier(filter,CUS "elsif"))
2763 if (parse_white(filter)==-1) return -1;
2764 m=parse_test(filter,&cond,exec && unsuccessful);
2765 if (m==-1 || m==2) return m;
2768 filter->errmsg=CUS "missing test";
2771 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2772 (debug_selector & D_filter) != 0)
2774 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2776 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2777 if (m==-1 || m==2) return m;
2780 filter->errmsg=CUS "missing block";
2783 if (exec && unsuccessful && cond) unsuccessful = 0;
2788 if (parse_white(filter)==-1) return -1;
2789 if (parse_identifier(filter,CUS "else"))
2791 m=parse_block(filter,exec && unsuccessful, generated);
2792 if (m==-1 || m==2) return m;
2795 filter->errmsg=CUS "missing block";
2800 else if (parse_identifier(filter,CUS "stop"))
2803 stop-command = "stop" { stop-options } ";"
2807 if (parse_semicolon(filter)==-1) return -1;
2810 filter->pc+=Ustrlen(filter->pc);
2814 else if (parse_identifier(filter,CUS "keep"))
2817 keep-command = "keep" { keep-options } ";"
2821 if (parse_semicolon(filter)==-1) return -1;
2824 add_addr(generated,US"inbox",1,0,0,0);
2828 else if (parse_identifier(filter,CUS "discard"))
2831 discard-command = "discard" { discard-options } ";"
2835 if (parse_semicolon(filter)==-1) return -1;
2836 if (exec) filter->keep=0;
2838 else if (parse_identifier(filter,CUS "redirect"))
2841 redirect-command = "redirect" redirect-options "string" ";"
2843 redirect-options =) ":copy"
2846 struct String recipient;
2852 if (parse_white(filter)==-1) return -1;
2853 if (parse_identifier(filter,CUS ":copy")==1)
2855 if (!filter->require_copy)
2857 filter->errmsg=CUS "missing previous require \"copy\";";
2864 if (parse_white(filter)==-1) return -1;
2865 if ((m=parse_string(filter,&recipient))!=1)
2867 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2870 if (strchr(CCS recipient.character,'@')==(char*)0)
2872 filter->errmsg=CUS "unqualified recipient address";
2877 add_addr(generated,recipient.character,0,0,0,0);
2878 if (!copy) filter->keep = 0;
2880 if (parse_semicolon(filter)==-1) return -1;
2882 else if (parse_identifier(filter,CUS "fileinto"))
2885 fileinto-command = "fileinto" { fileinto-options } string ";"
2887 fileinto-options =) [ ":copy" ]
2890 struct String folder;
2893 unsigned long maxage, maxmessages, maxstorage;
2896 maxage = maxmessages = maxstorage = 0;
2897 if (!filter->require_fileinto)
2899 filter->errmsg=CUS "missing previous require \"fileinto\";";
2904 if (parse_white(filter)==-1) return -1;
2905 if (parse_identifier(filter,CUS ":copy")==1)
2907 if (!filter->require_copy)
2909 filter->errmsg=CUS "missing previous require \"copy\";";
2916 if (parse_white(filter)==-1) return -1;
2917 if ((m=parse_string(filter,&folder))!=1)
2919 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2922 m=0; s=folder.character;
2923 if (folder.length==0) m=1;
2924 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2927 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2932 filter->errmsg=CUS "invalid folder";
2937 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2938 if (!copy) filter->keep = 0;
2940 if (parse_semicolon(filter)==-1) return -1;
2943 else if (parse_identifier(filter,CUS "notify"))
2946 notify-command = "notify" { notify-options } <method: string> ";"
2947 notify-options = [":from" string]
2948 [":importance" <"1" / "2" / "3">]
2949 [":options" 1*(string-list / number)]
2955 struct String importance;
2956 struct String message;
2957 struct String method;
2958 struct Notification *already;
2959 string_item *recipient;
2960 struct String header;
2961 struct String subject;
2963 uschar *envelope_from;
2964 struct String auto_submitted_value;
2965 uschar *auto_submitted_def;
2967 if (!filter->require_enotify)
2969 filter->errmsg=CUS "missing previous require \"enotify\";";
2972 from.character=(uschar*)0;
2974 importance.character=(uschar*)0;
2975 importance.length=-1;
2976 message.character=(uschar*)0;
2980 header.character=(uschar*)0;
2982 subject.character=(uschar*)0;
2984 body.character=(uschar*)0;
2985 envelope_from = sender_address && sender_address[0]
2986 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2989 filter->errmsg=CUS "expansion failure for envelope from";
2994 if (parse_white(filter)==-1) return -1;
2995 if (parse_identifier(filter,CUS ":from")==1)
2997 if (parse_white(filter)==-1) return -1;
2998 if ((m=parse_string(filter,&from))!=1)
3000 if (m==0) filter->errmsg=CUS "from string expected";
3004 else if (parse_identifier(filter,CUS ":importance")==1)
3006 if (parse_white(filter)==-1) return -1;
3007 if ((m=parse_string(filter,&importance))!=1)
3009 if (m==0) filter->errmsg=CUS "importance string expected";
3012 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3014 filter->errmsg=CUS "invalid importance";
3018 else if (parse_identifier(filter,CUS ":options")==1)
3020 if (parse_white(filter)==-1) return -1;
3022 else if (parse_identifier(filter,CUS ":message")==1)
3024 if (parse_white(filter)==-1) return -1;
3025 if ((m=parse_string(filter,&message))!=1)
3027 if (m==0) filter->errmsg=CUS "message string expected";
3033 if (parse_white(filter)==-1) return -1;
3034 if ((m=parse_string(filter,&method))!=1)
3036 if (m==0) filter->errmsg=CUS "missing method string";
3039 if (parse_semicolon(filter)==-1) return -1;
3040 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3044 if (message.length==-1) message=subject;
3045 if (message.length==-1) expand_header(&message,&str_subject);
3046 expand_header(&auto_submitted_value,&str_auto_submitted);
3047 auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}");
3048 if (!auto_submitted_value.character || !auto_submitted_def)
3050 filter->errmsg=CUS "header string expansion failed";
3053 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3055 for (already=filter->notified; already; already=already->next)
3057 if (already->method.length==method.length
3058 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3059 && already->importance.length==importance.length
3060 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3061 && already->message.length==message.length
3062 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3066 /* New notification, process it */
3068 struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED);
3069 sent->method=method;
3070 sent->importance=importance;
3071 sent->message=message;
3072 sent->next=filter->notified;
3073 filter->notified=sent;
3074 #ifndef COMPILE_SYNTAX_CHECKER
3075 if (filter_test == FTEST_NONE)
3079 if ((pid = child_open_exim2(&fd, envelope_from, envelope_from,
3080 US"sieve-notify")) >= 1)
3082 FILE * f = fdopen(fd, "wb");
3084 fprintf(f,"From: %s\n", from.length == -1
3085 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain")
3087 for (string_item * p = recipient; p; p=p->next)
3088 fprintf(f, "To: %s\n",p->text);
3089 fprintf(f, "Auto-Submitted: auto-notified; %s\n", filter->enotify_mailto_owner);
3090 if (header.length > 0) fprintf(f, "%s", header.character);
3091 if (message.length==-1)
3093 message.character=US"Notification";
3094 message.length=Ustrlen(message.character);
3096 if (message.length != -1)
3097 fprintf(f, "Subject: %s\n", parse_quote_2047(message.character,
3098 message.length, US"utf-8", TRUE));
3100 if (body.length > 0) fprintf(f, "%s\n", body.character);
3103 (void)child_close(pid, 0);
3106 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3107 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3111 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3112 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3115 if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter)
3116 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3121 else if (parse_identifier(filter,CUS "vacation"))
3124 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3125 vacation-options = [":days" number]
3128 [":addresses" string-list]
3135 struct String subject;
3137 struct String *addresses;
3139 string_item *aliases;
3140 struct String handle;
3141 struct String reason;
3143 if (!filter->require_vacation)
3145 filter->errmsg=CUS "missing previous require \"vacation\";";
3150 if (filter->vacation_ran)
3152 filter->errmsg=CUS "trying to execute vacation more than once";
3155 filter->vacation_ran=1;
3157 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3158 subject.character=(uschar*)0;
3160 from.character=(uschar*)0;
3162 addresses=(struct String*)0;
3165 handle.character=(uschar*)0;
3169 if (parse_white(filter)==-1) return -1;
3170 if (parse_identifier(filter,CUS ":days")==1)
3172 if (parse_white(filter)==-1) return -1;
3173 if (parse_number(filter,&days)==-1) return -1;
3174 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3175 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3177 else if (parse_identifier(filter,CUS ":subject")==1)
3179 if (parse_white(filter)==-1) return -1;
3180 if ((m=parse_string(filter,&subject))!=1)
3182 if (m==0) filter->errmsg=CUS "subject string expected";
3186 else if (parse_identifier(filter,CUS ":from")==1)
3188 if (parse_white(filter)==-1) return -1;
3189 if ((m=parse_string(filter,&from))!=1)
3191 if (m==0) filter->errmsg=CUS "from string expected";
3194 if (check_mail_address(filter,&from)!=1)
3197 else if (parse_identifier(filter,CUS ":addresses")==1)
3199 if (parse_white(filter)==-1) return -1;
3200 if ((m=parse_stringlist(filter,&addresses))!=1)
3202 if (m==0) filter->errmsg=CUS "addresses string list expected";
3205 for (struct String * a = addresses; a->length != -1; ++a)
3207 string_item * new = store_get(sizeof(string_item), GET_UNTAINTED);
3209 new->text = store_get(a->length+1, a->character);
3210 if (a->length) memcpy(new->text,a->character,a->length);
3211 new->text[a->length]='\0';
3216 else if (parse_identifier(filter,CUS ":mime")==1)
3218 else if (parse_identifier(filter,CUS ":handle")==1)
3220 if (parse_white(filter)==-1) return -1;
3221 if ((m=parse_string(filter,&from))!=1)
3223 if (m==0) filter->errmsg=CUS "handle string expected";
3229 if (parse_white(filter)==-1) return -1;
3230 if ((m=parse_string(filter,&reason))!=1)
3232 if (m==0) filter->errmsg=CUS "missing reason string";
3239 for (s = reason.character, end = reason.character + reason.length;
3240 s<end && (*s&0x80)==0; ) s++;
3243 filter->errmsg=CUS "MIME reason string contains 8bit text";
3247 if (parse_semicolon(filter)==-1) return -1;
3254 uschar hexdigest[33];
3257 if (filter_personal(aliases,TRUE))
3259 if (filter_test == FTEST_NONE)
3261 /* ensure oncelog directory exists; failure will be detected later */
3263 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3265 /* build oncelog filename */
3269 if (handle.length==-1)
3271 gstring * key = NULL;
3272 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3273 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3274 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3275 key = string_catn(key, reason.character, reason.length);
3276 md5_end(&base, key->s, key->ptr, digest);
3279 md5_end(&base, handle.character, handle.length, digest);
3281 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3283 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3284 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3286 if (filter_test == FTEST_NONE)
3288 once = string_cat (NULL, filter->vacation_directory);
3289 once = string_catn(once, US"/", 1);
3290 once = string_catn(once, hexdigest, 33);
3292 /* process subject */
3294 if (subject.length==-1)
3296 uschar * subject_def;
3298 subject_def = expand_string(US"${if def:header_subject {true}{false}}");
3299 if (subject_def && Ustrcmp(subject_def,"true")==0)
3301 gstring * g = string_catn(NULL, US"Auto: ", 6);
3303 expand_header(&subject,&str_subject);
3304 g = string_catn(g, subject.character, subject.length);
3305 subject.length = len_string_from_gstring(g, &subject.character);
3309 subject.character = US"Automated reply";
3310 subject.length = Ustrlen(subject.character);
3314 /* add address to list of generated addresses */
3316 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3317 setflag(addr, af_pfr);
3318 addr->prop.ignore_error = TRUE;
3319 addr->next = *generated;
3321 addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
3322 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3323 addr->reply->to = string_copy(sender_address);
3324 if (from.length==-1)
3325 addr->reply->from = expand_string(US"$local_part@$domain");
3327 addr->reply->from = from.character;
3328 /* deconst cast safe as we pass in a non-const item */
3329 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE);
3330 addr->reply->oncelog = string_from_gstring(once);
3331 addr->reply->once_repeat=days*86400;
3333 /* build body and MIME headers */
3337 uschar *mime_body,*reason_end;
3338 static const uschar nlnl[]="\r\n\r\n";
3342 mime_body = reason.character, reason_end = reason.character + reason.length;
3343 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3346 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3348 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3349 else mime_body=reason_end-1;
3350 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3354 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3356 addr->reply->headers = US"MIME-Version: 1.0\n"
3357 "Content-Type: text/plain;\n"
3358 "\tcharset=\"utf-8\"\n"
3359 "Content-Transfer-Encoding: quoted-printable";
3360 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3364 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3365 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3375 /*************************************************
3376 * Parse and interpret a sieve filter *
3377 *************************************************/
3381 filter points to the Sieve filter including its state
3382 exec Execute parsed statements
3383 generated where to hang newly-generated addresses
3386 -1 syntax or execution error
3390 parse_start(struct Sieve *filter, int exec, address_item **generated)
3392 filter->pc=filter->filter;
3395 filter->require_envelope=0;
3396 filter->require_fileinto=0;
3397 #ifdef ENCODED_CHARACTER
3398 filter->require_encoded_character=0;
3400 #ifdef ENVELOPE_AUTH
3401 filter->require_envelope_auth=0;
3404 filter->require_enotify=0;
3405 filter->notified=(struct Notification*)0;
3408 filter->require_subaddress=0;
3411 filter->require_vacation=0;
3412 filter->vacation_ran=0;
3414 filter->require_copy=0;
3415 filter->require_iascii_numeric=0;
3417 if (parse_white(filter)==-1) return -1;
3419 if (exec && filter->vacation_directory && filter_test == FTEST_NONE)
3422 struct dirent *oncelog;
3423 struct stat properties;
3426 /* clean up old vacation log databases */
3428 if ( !(oncelogdir = exim_opendir(filter->vacation_directory))
3431 filter->errmsg = CUS "unable to open vacation directory";
3439 while ((oncelog = readdir(oncelogdir)))
3440 if (strlen(oncelog->d_name)==32)
3442 uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name);
3443 if (Ustat(s,&properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now)
3446 closedir(oncelogdir);
3450 while (parse_identifier(filter,CUS "require"))
3453 require-command = "require" <capabilities: string-list>
3459 if (parse_white(filter)==-1) return -1;
3460 if ((m=parse_stringlist(filter,&cap))!=1)
3462 if (m==0) filter->errmsg=CUS "capability string list expected";
3465 for (struct String * check = cap; check->character; ++check)
3467 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3468 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3469 #ifdef ENCODED_CHARACTER
3470 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3472 #ifdef ENVELOPE_AUTH
3473 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3476 else if (eq_octet(check,&str_enotify,0))
3478 if (!filter->enotify_mailto_owner)
3480 filter->errmsg=CUS "enotify disabled";
3483 filter->require_enotify=1;
3487 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3490 else if (eq_octet(check,&str_vacation,0))
3492 if (filter_test == FTEST_NONE && !filter->vacation_directory)
3494 filter->errmsg=CUS "vacation disabled";
3497 filter->require_vacation=1;
3500 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3501 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3502 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3503 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3504 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3507 filter->errmsg=CUS "unknown capability";
3511 if (parse_semicolon(filter)==-1) return -1;
3513 if (parse_commands(filter,exec,generated)==-1) return -1;
3516 filter->errmsg=CUS "syntax error";
3523 /*************************************************
3524 * Interpret a sieve filter file *
3525 *************************************************/
3529 filter points to the entire file, read into store as a single string
3530 options controls whether various special things are allowed, and requests
3531 special actions (not currently used)
3532 vacation_directory where to store vacation "once" files
3533 enotify_mailto_owner owner of mailto notifications
3534 useraddress string expression for :user part of address
3535 subaddress string expression for :subaddress part of address
3536 generated where to hang newly-generated addresses
3537 error where to pass back an error text
3539 Returns: FF_DELIVERED success, a significant action was taken
3540 FF_NOTDELIVERED success, no significant action
3541 FF_DEFER defer requested
3542 FF_FAIL fail requested
3543 FF_FREEZE freeze requested
3544 FF_ERROR there was a problem
3548 sieve_interpret(const uschar * filter, int options,
3549 const uschar * vacation_directory, const uschar * enotify_mailto_owner,
3550 const uschar * useraddress, const uschar * subaddress,
3551 address_item ** generated, uschar ** error)
3557 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3558 sieve.filter = filter;
3560 if (!vacation_directory)
3561 sieve.vacation_directory = NULL;
3562 else if (!(sieve.vacation_directory = expand_cstring(vacation_directory)))
3564 *error = string_sprintf("failed to expand \"%s\" "
3565 "(sieve_vacation_directory): %s", vacation_directory,
3566 expand_string_message);
3570 if (!enotify_mailto_owner)
3571 sieve.enotify_mailto_owner = NULL;
3572 else if (!(sieve.enotify_mailto_owner = expand_cstring(enotify_mailto_owner)))
3574 *error = string_sprintf("failed to expand \"%s\" "
3575 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3576 expand_string_message);
3580 sieve.useraddress = useraddress
3581 ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
3582 sieve.subaddress = subaddress;
3584 #ifdef COMPILE_SYNTAX_CHECKER
3585 if (parse_start(&sieve, 0, generated) == 1)
3587 if (parse_start(&sieve, 1, generated) == 1)
3591 add_addr(generated, US"inbox", 1, 0, 0, 0);
3592 msg = US"Implicit keep";
3597 msg = US"No implicit keep";
3602 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3603 #ifdef COMPILE_SYNTAX_CHECKER
3607 add_addr(generated,US"inbox",1,0,0,0);
3612 #ifndef COMPILE_SYNTAX_CHECKER
3613 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3614 else debug_printf("%s\n", msg);
3617 DEBUG(D_route) debug_printf("Sieve: end of processing\n");