1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2016 - 2018
7 * See the file NOTICE for conditions of use and distribution.
10 /* This code was contributed by Michael Haardt. */
13 /* Sieve mail filter. */
27 /* Define this for RFC compliant \r\n end-of-line terminators. */
28 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 /* Define this for development of the Sieve extension "encoded-character". */
32 #define ENCODED_CHARACTER
34 /* Define this for development of the Sieve extension "envelope-auth". */
37 /* Define this for development of the Sieve extension "enotify". */
40 /* Define this for the Sieve extension "subaddress". */
43 /* Define this for the Sieve extension "vacation". */
47 #define VACATION_MIN_DAYS 1
48 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
49 #define VACATION_MAX_DAYS 31
51 /* Keep this at 75 to accept only RFC compliant MIME words. */
52 /* Increase it if you want to match headers from buggy MUAs. */
53 #define MIMEWORD_LENGTH 75
64 #ifdef ENCODED_CHARACTER
65 int require_encoded_character;
68 int require_envelope_auth;
72 struct Notification *notified;
74 uschar *enotify_mailto_owner;
76 int require_subaddress;
82 uschar *vacation_directory;
83 const uschar *subaddress;
84 const uschar *useraddress;
86 int require_iascii_numeric;
89 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
90 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
92 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
96 enum RelOp { LT, LE, EQ, GE, GT, NE };
106 struct String method;
107 struct String importance;
108 struct String message;
109 struct Notification *next;
112 /* This should be a complete list of supported extensions, so that an external
113 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
114 list of extensions and provide correct information to a client.
116 We'll emit the list in the order given here; keep it alphabetically sorted, so
117 that callers don't get surprised.
119 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
121 const uschar *exim_sieve_extension_list[] = {
122 CUS"comparator-i;ascii-numeric",
124 #ifdef ENCODED_CHARACTER
125 CUS"encoded-character",
144 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
145 static int parse_test(struct Sieve *filter, int *cond, int exec);
146 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
148 static uschar str_from_c[]="From";
149 static const struct String str_from={ str_from_c, 4 };
150 static uschar str_to_c[]="To";
151 static const struct String str_to={ str_to_c, 2 };
152 static uschar str_cc_c[]="Cc";
153 static const struct String str_cc={ str_cc_c, 2 };
154 static uschar str_bcc_c[]="Bcc";
155 static const struct String str_bcc={ str_bcc_c, 3 };
157 static uschar str_auth_c[]="auth";
158 static const struct String str_auth={ str_auth_c, 4 };
160 static uschar str_sender_c[]="Sender";
161 static const struct String str_sender={ str_sender_c, 6 };
162 static uschar str_resent_from_c[]="Resent-From";
163 static const struct String str_resent_from={ str_resent_from_c, 11 };
164 static uschar str_resent_to_c[]="Resent-To";
165 static const struct String str_resent_to={ str_resent_to_c, 9 };
166 static uschar str_fileinto_c[]="fileinto";
167 static const struct String str_fileinto={ str_fileinto_c, 8 };
168 static uschar str_envelope_c[]="envelope";
169 static const struct String str_envelope={ str_envelope_c, 8 };
170 #ifdef ENCODED_CHARACTER
171 static uschar str_encoded_character_c[]="encoded-character";
172 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
175 static uschar str_envelope_auth_c[]="envelope-auth";
176 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
179 static uschar str_enotify_c[]="enotify";
180 static const struct String str_enotify={ str_enotify_c, 7 };
181 static uschar str_online_c[]="online";
182 static const struct String str_online={ str_online_c, 6 };
183 static uschar str_maybe_c[]="maybe";
184 static const struct String str_maybe={ str_maybe_c, 5 };
185 static uschar str_auto_submitted_c[]="Auto-Submitted";
186 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
189 static uschar str_subaddress_c[]="subaddress";
190 static const struct String str_subaddress={ str_subaddress_c, 10 };
193 static uschar str_vacation_c[]="vacation";
194 static const struct String str_vacation={ str_vacation_c, 8 };
195 static uschar str_subject_c[]="Subject";
196 static const struct String str_subject={ str_subject_c, 7 };
198 static uschar str_copy_c[]="copy";
199 static const struct String str_copy={ str_copy_c, 4 };
200 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
201 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
202 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
203 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
204 static uschar str_ioctet_c[]="i;octet";
205 static const struct String str_ioctet={ str_ioctet_c, 7 };
206 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
207 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
208 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
209 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
210 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
211 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
212 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
213 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
214 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
215 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
218 /*************************************************
219 * Encode to quoted-printable *
220 *************************************************/
231 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
234 const uschar *start,*end;
239 /* Two passes: one to count output allocation size, second
240 to do the encoding */
242 for (pass=0; pass<=1; ++pass)
249 dst->character=store_get(dst->length+1); /* plus one for \0 */
252 for (start=src->character,end=start+src->length; start<end; ++start)
255 if (line>=73) /* line length limit */
261 *new++='='; /* line split */
266 if ( (ch>='!' && ch<='<')
267 || (ch>='>' && ch<='~')
268 || ( (ch=='\t' || ch==' ')
270 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
277 *new++=*start; /* copy char */
280 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
285 *new++='\n'; /* NL */
287 ++start; /* consume extra input char */
295 new += sprintf(CS new,"=%02X",ch);
301 *new='\0'; /* not included in length, but nice */
306 /*************************************************
307 * Check mail address for correct syntax *
308 *************************************************/
311 Check mail address for being syntactically correct.
314 filter points to the Sieve filter including its state
315 address String containing one address
318 1 Mail address is syntactically OK
322 int check_mail_address(struct Sieve *filter, const struct String *address)
324 int start, end, domain;
327 if (address->length>0)
329 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
333 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
334 address->character, error);
342 filter->errmsg=CUS "empty address";
348 /*************************************************
349 * Decode URI encoded string *
350 *************************************************/
354 str URI encoded string
357 0 Decoding successful
362 static int uri_decode(struct String *str)
366 if (str->length==0) return 0;
367 for (s=str->character,t=s,e=s+str->length; s<e; )
371 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
373 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
374 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
383 str->length=t-str->character;
388 /*************************************************
390 *************************************************/
395 mailtoURI = "mailto:" [ to ] [ headers ]
396 to = [ addr-spec *("%2C" addr-spec ) ]
397 headers = "?" header *( "&" header )
398 header = hname "=" hvalue
403 filter points to the Sieve filter including its state
404 uri URI, excluding scheme
409 1 URI is syntactically OK
415 parse_mailto_uri(struct Sieve *filter, const uschar *uri,
416 string_item **recipient, struct String *header, struct String *subject,
420 struct String to, hname;
421 struct String hvalue = {.character = NULL, .length = 0};
424 if (Ustrncmp(uri,"mailto:",7))
426 filter->errmsg=US "Unknown URI scheme";
431 if (*uri && *uri!='?')
435 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
438 gstring * g = string_catn(NULL, start, uri-start);
440 to.character = string_from_gstring(g);
442 if (uri_decode(&to)==-1)
444 filter->errmsg=US"Invalid URI encoding";
447 new=store_get(sizeof(string_item));
448 new->text=store_get(to.length+1);
449 if (to.length) memcpy(new->text,to.character,to.length);
450 new->text[to.length]='\0';
451 new->next=*recipient;
456 filter->errmsg=US"Missing addr-spec in URI";
459 if (*uri=='%') uri+=3;
468 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
471 gstring * g = string_catn(NULL, start, uri-start);
473 hname.character = string_from_gstring(g);
474 hname.length = g->ptr;
475 if (uri_decode(&hname)==-1)
477 filter->errmsg=US"Invalid URI encoding";
486 filter->errmsg=US"Missing equal after hname";
490 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
493 gstring * g = string_catn(NULL, start, uri-start);
495 hname.character = string_from_gstring(g);
496 hname.length = g->ptr;
497 if (uri_decode(&hvalue)==-1)
499 filter->errmsg=US"Invalid URI encoding";
503 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
505 new=store_get(sizeof(string_item));
506 new->text=store_get(hvalue.length+1);
507 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
508 new->text[hvalue.length]='\0';
509 new->next=*recipient;
512 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
514 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
518 static struct String ignore[]=
524 {US"auto-submitted",14}
526 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
529 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
534 if (header->length==-1) header->length = 0;
536 g = string_catn(NULL, header->character, header->length);
537 g = string_catn(g, hname.character, hname.length);
538 g = string_catn(g, CUS ": ", 2);
539 g = string_catn(g, hvalue.character, hvalue.length);
540 g = string_catn(g, CUS "\n", 1);
542 header->character = string_from_gstring(g);
543 header->length = g->ptr;
546 if (*uri=='&') ++uri;
552 filter->errmsg=US"Syntactically invalid URI";
560 /*************************************************
561 * Octet-wise string comparison *
562 *************************************************/
566 needle UTF-8 string to search ...
567 haystack ... inside the haystack
568 match_prefix 1 to compare if needle is a prefix of haystack
570 Returns: 0 needle not found in haystack
574 static int eq_octet(const struct String *needle,
575 const struct String *haystack, int match_prefix)
583 h=haystack->character;
587 if (*n&0x80) return 0;
588 if (*h&0x80) return 0;
590 if (*n!=*h) return 0;
596 return (match_prefix ? nl==0 : nl==0 && hl==0);
600 /*************************************************
601 * ASCII case-insensitive string comparison *
602 *************************************************/
606 needle UTF-8 string to search ...
607 haystack ... inside the haystack
608 match_prefix 1 to compare if needle is a prefix of haystack
610 Returns: 0 needle not found in haystack
614 static int eq_asciicase(const struct String *needle,
615 const struct String *haystack, int match_prefix)
624 h=haystack->character;
630 if (nc&0x80) return 0;
631 if (hc&0x80) return 0;
633 /* tolower depends on the locale and only ASCII case must be insensitive */
634 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
640 return (match_prefix ? nl==0 : nl==0 && hl==0);
644 /*************************************************
645 * Glob pattern search *
646 *************************************************/
650 needle pattern to search ...
651 haystack ... inside the haystack
652 ascii_caseless ignore ASCII case
653 match_octet match octets, not UTF-8 multi-octet characters
655 Returns: 0 needle not found in haystack
660 static int eq_glob(const struct String *needle,
661 const struct String *haystack, int ascii_caseless, int match_octet)
663 const uschar *n,*h,*nend,*hend;
667 h=haystack->character;
668 nend=n+needle->length;
669 hend=h+haystack->length;
679 const uschar *npart,*hpart;
681 /* Try to match a non-star part of the needle at the current */
682 /* position in the haystack. */
686 while (npart<nend && *npart!='*') switch (*npart)
690 if (hpart==hend) return 0;
695 /* Match one UTF8 encoded character */
696 if ((*hpart&0xc0)==0xc0)
699 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
710 if (npart==nend) return -1;
715 if (hpart==hend) return 0;
716 /* tolower depends on the locale, but we need ASCII */
720 (*hpart&0x80) || (*npart&0x80) ||
723 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
728 /* string match after a star failed, advance and try again */
742 /* at this point, a part was matched successfully */
743 if (may_advance && npart==nend && hpart<hend)
744 /* needle ends, but haystack does not: if there was a star before, advance and try again */
754 return (h==hend ? 1 : may_advance);
758 /*************************************************
759 * ASCII numeric comparison *
760 *************************************************/
764 a first numeric string
765 b second numeric string
766 relop relational operator
768 Returns: 0 not (a relop b)
772 static int eq_asciinumeric(const struct String *a,
773 const struct String *b, enum RelOp relop)
776 const uschar *as,*aend,*bs,*bend;
780 aend=a->character+a->length;
782 bend=b->character+b->length;
784 while (*as>='0' && *as<='9' && as<aend) ++as;
786 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
789 if (al && bl==0) cmp=-1;
790 else if (al==0 && bl==0) cmp=0;
791 else if (al==0 && bl) cmp=1;
795 if (cmp==0) cmp=memcmp(a->character,b->character,al);
799 case LT: return cmp<0;
800 case LE: return cmp<=0;
801 case EQ: return cmp==0;
802 case GE: return cmp>=0;
803 case GT: return cmp>0;
804 case NE: return cmp!=0;
811 /*************************************************
813 *************************************************/
817 filter points to the Sieve filter including its state
818 needle UTF-8 pattern or string to search ...
819 haystack ... inside the haystack
823 Returns: 0 needle not found in haystack
825 -1 comparator does not offer matchtype
828 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
829 enum Comparator co, enum MatchType mt)
833 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
834 (debug_selector & D_filter) != 0)
836 debug_printf("String comparison (match ");
839 case MATCH_IS: debug_printf(":is"); break;
840 case MATCH_CONTAINS: debug_printf(":contains"); break;
841 case MATCH_MATCHES: debug_printf(":matches"); break;
843 debug_printf(", comparison \"");
846 case COMP_OCTET: debug_printf("i;octet"); break;
847 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
848 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
850 debug_printf("\"):\n");
851 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
852 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
862 if (eq_octet(needle,haystack,0)) r=1;
865 case COMP_EN_ASCII_CASEMAP:
867 if (eq_asciicase(needle,haystack,0)) r=1;
870 case COMP_ASCII_NUMERIC:
872 if (!filter->require_iascii_numeric)
874 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
877 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
891 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
894 case COMP_EN_ASCII_CASEMAP:
896 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
901 filter->errmsg=CUS "comparator does not offer specified matchtype";
913 if ((r=eq_glob(needle,haystack,0,1))==-1)
915 filter->errmsg=CUS "syntactically invalid pattern";
920 case COMP_EN_ASCII_CASEMAP:
922 if ((r=eq_glob(needle,haystack,1,1))==-1)
924 filter->errmsg=CUS "syntactically invalid pattern";
931 filter->errmsg=CUS "comparator does not offer specified matchtype";
938 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
939 (debug_selector & D_filter) != 0)
940 debug_printf(" Result %s\n",r?"true":"false");
945 /*************************************************
946 * Check header field syntax *
947 *************************************************/
950 RFC 2822, section 3.6.8 says:
954 ftext = %d33-57 / ; Any character except
955 %d59-126 ; controls, SP, and
958 That forbids 8-bit header fields. This implementation accepts them, since
959 all of Exim is 8-bit clean, so it adds %d128-%d255.
962 header header field to quote for suitable use in Exim expansions
964 Returns: 0 string is not a valid header field
965 1 string is a value header field
968 static int is_header(const struct String *header)
978 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
989 /*************************************************
990 * Quote special characters string *
991 *************************************************/
995 header header field to quote for suitable use in Exim expansions
998 Returns: quoted string
1001 static const uschar *
1002 quote(const struct String *header)
1004 gstring * quoted = NULL;
1009 h=header->character;
1015 quoted = string_catn(quoted, CUS "\\0", 2);
1020 quoted = string_catn(quoted, CUS "\\", 1);
1022 quoted = string_catn(quoted, h, 1);
1027 quoted = string_catn(quoted, CUS "", 1);
1028 return string_from_gstring(quoted);
1032 /*************************************************
1033 * Add address to list of generated addresses *
1034 *************************************************/
1037 According to RFC 5228, duplicate delivery to the same address must
1038 not happen, so the list is first searched for the address.
1041 generated list of generated addresses
1042 addr new address to add
1043 file address denotes a file
1049 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1051 address_item *new_addr;
1053 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1054 if ( Ustrcmp(new_addr->address,addr) == 0
1056 || testflag(new_addr, af_pfr)
1057 || testflag(new_addr, af_file)
1061 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1062 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1067 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1068 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1070 new_addr = deliver_make_addr(addr,TRUE);
1073 setflag(new_addr, af_pfr);
1074 setflag(new_addr, af_file);
1077 new_addr->prop.errors_address = NULL;
1078 new_addr->next = *generated;
1079 *generated = new_addr;
1083 /*************************************************
1084 * Return decoded header field *
1085 *************************************************/
1088 Unfold the header field as described in RFC 2822 and remove all
1089 leading and trailing white space, then perform MIME decoding and
1090 translate the header field to UTF-8.
1093 value returned value of the field
1094 header name of the header field
1096 Returns: nothing The expanded string is empty
1097 in case there is no such header
1100 static void expand_header(struct String *value, const struct String *header)
1106 value->character=(uschar*)0;
1108 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1109 while (*r==' ' || *r=='\t') ++r;
1117 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1119 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1123 /*************************************************
1124 * Parse remaining hash comment *
1125 *************************************************/
1129 Comment up to terminating CRLF
1132 filter points to the Sieve filter including its state
1138 static int parse_hashcomment(struct Sieve *filter)
1144 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1146 if (*filter->pc=='\n')
1159 filter->errmsg=CUS "missing end of comment";
1164 /*************************************************
1165 * Parse remaining C-style comment *
1166 *************************************************/
1170 Everything up to star slash
1173 filter points to the Sieve filter including its state
1179 static int parse_comment(struct Sieve *filter)
1184 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1191 filter->errmsg=CUS "missing end of comment";
1196 /*************************************************
1197 * Parse optional white space *
1198 *************************************************/
1202 Spaces, tabs, CRLFs, hash comments or C-style comments
1205 filter points to the Sieve filter including its state
1211 static int parse_white(struct Sieve *filter)
1215 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1217 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1219 else if (*filter->pc=='\n')
1229 else if (*filter->pc=='#')
1231 if (parse_hashcomment(filter)==-1) return -1;
1233 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1235 if (parse_comment(filter)==-1) return -1;
1243 #ifdef ENCODED_CHARACTER
1244 /*************************************************
1245 * Decode hex-encoded-character string *
1246 *************************************************/
1249 Encoding definition:
1250 blank = SP / TAB / CRLF
1251 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1252 hex-pair = 1*2HEXDIG
1255 src points to a hex-pair-seq
1256 end points to its end
1257 dst points to the destination of the decoded octets,
1258 optionally to (uschar*)0 for checking only
1260 Returns: >=0 number of decoded octets
1264 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1268 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1273 for (x=0,d=0; d<2 && src<end && isxdigit(n=tolower(*src)); x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')),++d,++src);
1274 if (d==0) return -1;
1277 if (src==end) return decoded;
1278 if (*src==' ' || *src=='\t' || *src=='\n')
1279 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1288 /*************************************************
1289 * Decode unicode-encoded-character string *
1290 *************************************************/
1293 Encoding definition:
1294 blank = SP / TAB / CRLF
1295 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1296 unicode-hex = 1*HEXDIG
1298 It is an error for a script to use a hexadecimal value that isn't in
1299 either the range 0 to D7FF or the range E000 to 10FFFF.
1301 At this time, strings are already scanned, thus the CRLF is converted
1302 to the internally used \n (should RFC_EOL have been used).
1305 src points to a unicode-hex-seq
1306 end points to its end
1307 dst points to the destination of the decoded octets,
1308 optionally to (uschar*)0 for checking only
1310 Returns: >=0 number of decoded octets
1312 -2 semantic error (character range violation)
1315 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1319 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1326 for (hex_seq=src; src<end && *src=='0'; ++src);
1327 for (c=0,d=0; d<7 && src<end && isxdigit(n=tolower(*src)); c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')),++d,++src);
1328 if (src==hex_seq) return -1;
1329 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1335 else if (c>=0x80 && c<=0x7ff)
1340 *dst++=128+(c&0x3f);
1344 else if (c>=0x800 && c<=0xffff)
1349 *dst++=128+((c>>6)&0x3f);
1350 *dst++=128+(c&0x3f);
1354 else if (c>=0x10000 && c<=0x1fffff)
1359 *dst++=128+((c>>10)&0x3f);
1360 *dst++=128+((c>>6)&0x3f);
1361 *dst++=128+(c&0x3f);
1365 if (*src==' ' || *src=='\t' || *src=='\n')
1367 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1368 if (src==end) return decoded;
1377 /*************************************************
1378 * Decode encoded-character string *
1379 *************************************************/
1382 Encoding definition:
1383 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1384 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1387 encoded points to an encoded string, returns decoded string
1388 filter points to the Sieve filter including its state
1394 static int string_decode(struct Sieve *filter, struct String *data)
1396 uschar *src,*dst,*end;
1398 src=data->character;
1400 end=data->character+data->length;
1406 strncmpic(src,US "${hex:",6)==0
1407 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1408 && (hex_decode(src+6,brace,(uschar*)0))>=0
1411 dst+=hex_decode(src+6,brace,dst);
1415 strncmpic(src,US "${unicode:",10)==0
1416 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1419 switch (unicode_decode(src+10,brace,(uschar*)0))
1423 filter->errmsg=CUS "unicode character out of range";
1433 dst+=unicode_decode(src+10,brace,dst);
1440 data->length=dst-data->character;
1447 /*************************************************
1448 * Parse an optional string *
1449 *************************************************/
1453 quoted-string = DQUOTE *CHAR DQUOTE
1454 ;; in general, \ CHAR inside a string maps to CHAR
1455 ;; so \" maps to " and \\ maps to \
1456 ;; note that newlines and other characters are all allowed
1459 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1460 *(multi-line-literal / multi-line-dotstuff)
1462 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1463 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1464 ;; A line containing only "." ends the multi-line.
1465 ;; Remove a leading '.' if followed by another '.'.
1466 string = quoted-string / multi-line
1469 filter points to the Sieve filter including its state
1470 id specifies identifier to match
1474 0 identifier not matched
1478 parse_string(struct Sieve *filter, struct String *data)
1483 data->character = NULL;
1485 if (*filter->pc=='"') /* quoted string */
1490 if (*filter->pc=='"') /* end of string */
1496 data->character = string_from_gstring(g);
1497 data->length = g->ptr;
1500 data->character = US"\0";
1501 /* that way, there will be at least one character allocated */
1503 #ifdef ENCODED_CHARACTER
1504 if (filter->require_encoded_character
1505 && string_decode(filter,data)==-1)
1510 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1512 g = string_catn(g, filter->pc+1, 1);
1515 else /* regular character */
1518 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1520 if (*filter->pc=='\n')
1522 g = string_catn(g, US"\r", 1);
1526 g = string_catn(g, filter->pc, 1);
1530 filter->errmsg=CUS "missing end of string";
1533 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1536 /* skip optional white space followed by hashed comment or CRLF */
1537 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1538 if (*filter->pc=='#')
1540 if (parse_hashcomment(filter)==-1) return -1;
1543 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1545 else if (*filter->pc=='\n')
1557 filter->errmsg=CUS "syntax error";
1563 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1565 if (*filter->pc=='\n') /* end of line */
1568 g = string_catn(g, CUS "\r\n", 2);
1576 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1578 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1583 data->character = string_from_gstring(g);
1584 data->length = g->ptr;
1587 data->character = US"\0";
1588 /* that way, there will be at least one character allocated */
1596 #ifdef ENCODED_CHARACTER
1597 if (filter->require_encoded_character
1598 && string_decode(filter,data)==-1)
1603 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1605 g = string_catn(g, CUS ".", 1);
1609 else /* regular character */
1611 g = string_catn(g, filter->pc, 1);
1615 filter->errmsg=CUS "missing end of multi line string";
1622 /*************************************************
1623 * Parse a specific identifier *
1624 *************************************************/
1628 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1631 filter points to the Sieve filter including its state
1632 id specifies identifier to match
1635 0 identifier not matched
1638 static int parse_identifier(struct Sieve *filter, const uschar *id)
1640 size_t idlen=Ustrlen(id);
1642 if (strncmpic(US filter->pc,US id,idlen)==0)
1644 uschar next=filter->pc[idlen];
1646 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1654 /*************************************************
1656 *************************************************/
1660 number = 1*DIGIT [QUANTIFIER]
1661 QUANTIFIER = "K" / "M" / "G"
1664 filter points to the Sieve filter including its state
1668 -1 no string list found
1671 static int parse_number(struct Sieve *filter, unsigned long *data)
1675 if (*filter->pc>='0' && *filter->pc<='9')
1680 d=Ustrtoul(filter->pc,&e,10);
1683 filter->errmsg=CUstrerror(ERANGE);
1688 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1689 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1690 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1691 if (d>(ULONG_MAX/u))
1693 filter->errmsg=CUstrerror(ERANGE);
1702 filter->errmsg=CUS "missing number";
1708 /*************************************************
1709 * Parse a string list *
1710 *************************************************/
1714 string-list = "[" string *("," string) "]" / string
1717 filter points to the Sieve filter including its state
1718 data returns string list
1721 -1 no string list found
1725 parse_stringlist(struct Sieve *filter, struct String **data)
1727 const uschar *orig=filter->pc;
1728 int dataCapacity = 0;
1730 struct String *d = NULL;
1733 if (*filter->pc=='[') /* string list */
1738 if (parse_white(filter)==-1) goto error;
1739 if (dataLength+1 >= dataCapacity) /* increase buffer */
1743 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1744 new = store_get(sizeof(struct String) * dataCapacity);
1746 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1750 m=parse_string(filter,&d[dataLength]);
1753 if (dataLength==0) break;
1756 filter->errmsg=CUS "missing string";
1760 else if (m==-1) goto error;
1762 if (parse_white(filter)==-1) goto error;
1763 if (*filter->pc==',') ++filter->pc;
1766 if (*filter->pc==']')
1768 d[dataLength].character=(uschar*)0;
1769 d[dataLength].length=-1;
1776 filter->errmsg=CUS "missing closing bracket";
1780 else /* single string */
1782 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1786 m=parse_string(filter,&d[0]);
1798 d[1].character=(uschar*)0;
1805 filter->errmsg=CUS "missing string list";
1810 /*************************************************
1811 * Parse an optional address part specifier *
1812 *************************************************/
1816 address-part = ":localpart" / ":domain" / ":all"
1817 address-part =/ ":user" / ":detail"
1820 filter points to the Sieve filter including its state
1821 a returns address part specified
1824 0 no comparator found
1828 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1831 if (parse_identifier(filter,CUS ":user")==1)
1833 if (!filter->require_subaddress)
1835 filter->errmsg=CUS "missing previous require \"subaddress\";";
1841 else if (parse_identifier(filter,CUS ":detail")==1)
1843 if (!filter->require_subaddress)
1845 filter->errmsg=CUS "missing previous require \"subaddress\";";
1853 if (parse_identifier(filter,CUS ":localpart")==1)
1855 *a=ADDRPART_LOCALPART;
1858 else if (parse_identifier(filter,CUS ":domain")==1)
1863 else if (parse_identifier(filter,CUS ":all")==1)
1872 /*************************************************
1873 * Parse an optional comparator *
1874 *************************************************/
1878 comparator = ":comparator" <comparator-name: string>
1881 filter points to the Sieve filter including its state
1882 c returns comparator
1885 0 no comparator found
1886 -1 incomplete comparator found
1889 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1891 struct String comparator_name;
1893 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1894 if (parse_white(filter)==-1) return -1;
1895 switch (parse_string(filter,&comparator_name))
1900 filter->errmsg=CUS "missing comparator";
1907 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1912 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1914 *c=COMP_EN_ASCII_CASEMAP;
1917 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1919 *c=COMP_EN_ASCII_CASEMAP;
1922 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1924 *c=COMP_ASCII_NUMERIC;
1929 filter->errmsg=CUS "invalid comparator";
1938 /*************************************************
1939 * Parse an optional match type *
1940 *************************************************/
1944 match-type = ":is" / ":contains" / ":matches"
1947 filter points to the Sieve filter including its state
1948 m returns match type
1951 0 no match type found
1954 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1956 if (parse_identifier(filter,CUS ":is")==1)
1961 else if (parse_identifier(filter,CUS ":contains")==1)
1966 else if (parse_identifier(filter,CUS ":matches")==1)
1975 /*************************************************
1976 * Parse and interpret an optional test list *
1977 *************************************************/
1981 test-list = "(" test *("," test) ")"
1984 filter points to the Sieve filter including its state
1985 n total number of tests
1986 num_true number of passed tests
1987 exec Execute parsed statements
1990 0 no test list found
1991 -1 syntax or execution error
1994 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1996 if (parse_white(filter)==-1) return -1;
1997 if (*filter->pc=='(')
2006 switch (parse_test(filter,&cond,exec))
2009 case 0: filter->errmsg=CUS "missing test"; return -1;
2010 default: ++*n; if (cond) ++*num_true; break;
2012 if (parse_white(filter)==-1) return -1;
2013 if (*filter->pc==',') ++filter->pc;
2016 if (*filter->pc==')')
2023 filter->errmsg=CUS "missing closing paren";
2031 /*************************************************
2032 * Parse and interpret an optional test *
2033 *************************************************/
2037 filter points to the Sieve filter including its state
2038 cond returned condition status
2039 exec Execute parsed statements
2043 -1 syntax or execution error
2046 static int parse_test(struct Sieve *filter, int *cond, int exec)
2048 if (parse_white(filter)==-1) return -1;
2049 if (parse_identifier(filter,CUS "address"))
2052 address-test = "address" { [address-part] [comparator] [match-type] }
2053 <header-list: string-list> <key-list: string-list>
2055 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2058 enum AddressPart addressPart=ADDRPART_ALL;
2059 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2060 enum MatchType matchType=MATCH_IS;
2061 struct String *hdr,*h,*key,*k;
2067 if (parse_white(filter)==-1) return -1;
2068 if ((m=parse_addresspart(filter,&addressPart))!=0)
2070 if (m==-1) return -1;
2073 filter->errmsg=CUS "address part already specified";
2078 else if ((m=parse_comparator(filter,&comparator))!=0)
2080 if (m==-1) return -1;
2083 filter->errmsg=CUS "comparator already specified";
2088 else if ((m=parse_matchtype(filter,&matchType))!=0)
2090 if (m==-1) return -1;
2093 filter->errmsg=CUS "match type already specified";
2100 if (parse_white(filter)==-1) return -1;
2101 if ((m=parse_stringlist(filter,&hdr))!=1)
2103 if (m==0) filter->errmsg=CUS "header string list expected";
2106 if (parse_white(filter)==-1) return -1;
2107 if ((m=parse_stringlist(filter,&key))!=1)
2109 if (m==0) filter->errmsg=CUS "key string list expected";
2113 for (h=hdr; h->length!=-1 && !*cond; ++h)
2115 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2119 !eq_asciicase(h,&str_from,0)
2120 && !eq_asciicase(h,&str_to,0)
2121 && !eq_asciicase(h,&str_cc,0)
2122 && !eq_asciicase(h,&str_bcc,0)
2123 && !eq_asciicase(h,&str_sender,0)
2124 && !eq_asciicase(h,&str_resent_from,0)
2125 && !eq_asciicase(h,&str_resent_to,0)
2128 filter->errmsg=CUS "invalid header field";
2133 /* We are only interested in addresses below, so no MIME decoding */
2134 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2135 if (header_value == NULL)
2137 filter->errmsg=CUS "header string expansion failed";
2140 parse_allow_group = TRUE;
2141 while (*header_value && !*cond)
2144 int start, end, domain;
2148 end_addr = parse_find_address_end(header_value, FALSE);
2149 saveend = *end_addr;
2151 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2153 if (extracted_addr) switch (addressPart)
2155 case ADDRPART_ALL: part=extracted_addr; break;
2159 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2160 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2162 case ADDRPART_DETAIL: part=NULL; break;
2166 *end_addr = saveend;
2169 for (k=key; k->length!=-1; ++k)
2171 struct String partStr;
2173 partStr.character=part;
2174 partStr.length=Ustrlen(part);
2177 *cond=compare(filter,k,&partStr,comparator,matchType);
2178 if (*cond==-1) return -1;
2183 if (saveend == 0) break;
2184 header_value = end_addr + 1;
2186 parse_allow_group = FALSE;
2187 parse_found_group = FALSE;
2192 else if (parse_identifier(filter,CUS "allof"))
2195 allof-test = "allof" <tests: test-list>
2200 switch (parse_testlist(filter,&n,&num_true,exec))
2203 case 0: filter->errmsg=CUS "missing test list"; return -1;
2204 default: *cond=(n==num_true); return 1;
2207 else if (parse_identifier(filter,CUS "anyof"))
2210 anyof-test = "anyof" <tests: test-list>
2215 switch (parse_testlist(filter,&n,&num_true,exec))
2218 case 0: filter->errmsg=CUS "missing test list"; return -1;
2219 default: *cond=(num_true>0); return 1;
2222 else if (parse_identifier(filter,CUS "exists"))
2225 exists-test = "exists" <header-names: string-list>
2228 struct String *hdr,*h;
2231 if (parse_white(filter)==-1) return -1;
2232 if ((m=parse_stringlist(filter,&hdr))!=1)
2234 if (m==0) filter->errmsg=CUS "header string list expected";
2240 for (h=hdr; h->length!=-1 && *cond; ++h)
2244 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2245 if (header_def == NULL)
2247 filter->errmsg=CUS "header string expansion failed";
2250 if (Ustrcmp(header_def,"false")==0) *cond=0;
2255 else if (parse_identifier(filter,CUS "false"))
2258 false-test = "false"
2264 else if (parse_identifier(filter,CUS "header"))
2267 header-test = "header" { [comparator] [match-type] }
2268 <header-names: string-list> <key-list: string-list>
2271 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2272 enum MatchType matchType=MATCH_IS;
2273 struct String *hdr,*h,*key,*k;
2279 if (parse_white(filter)==-1) return -1;
2280 if ((m=parse_comparator(filter,&comparator))!=0)
2282 if (m==-1) return -1;
2285 filter->errmsg=CUS "comparator already specified";
2290 else if ((m=parse_matchtype(filter,&matchType))!=0)
2292 if (m==-1) return -1;
2295 filter->errmsg=CUS "match type already specified";
2302 if (parse_white(filter)==-1) return -1;
2303 if ((m=parse_stringlist(filter,&hdr))!=1)
2305 if (m==0) filter->errmsg=CUS "header string list expected";
2308 if (parse_white(filter)==-1) return -1;
2309 if ((m=parse_stringlist(filter,&key))!=1)
2311 if (m==0) filter->errmsg=CUS "key string list expected";
2315 for (h=hdr; h->length!=-1 && !*cond; ++h)
2319 filter->errmsg=CUS "invalid header field";
2324 struct String header_value;
2327 expand_header(&header_value,h);
2328 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2329 if (header_value.character == NULL || header_def == NULL)
2331 filter->errmsg=CUS "header string expansion failed";
2334 for (k=key; k->length!=-1; ++k)
2336 if (Ustrcmp(header_def,"true")==0)
2338 *cond=compare(filter,k,&header_value,comparator,matchType);
2339 if (*cond==-1) return -1;
2347 else if (parse_identifier(filter,CUS "not"))
2349 if (parse_white(filter)==-1) return -1;
2350 switch (parse_test(filter,cond,exec))
2353 case 0: filter->errmsg=CUS "missing test"; return -1;
2354 default: *cond=!*cond; return 1;
2357 else if (parse_identifier(filter,CUS "size"))
2360 relop = ":over" / ":under"
2361 size-test = "size" relop <limit: number>
2364 unsigned long limit;
2367 if (parse_white(filter)==-1) return -1;
2368 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2369 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2372 filter->errmsg=CUS "missing :over or :under";
2375 if (parse_white(filter)==-1) return -1;
2376 if (parse_number(filter,&limit)==-1) return -1;
2377 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2380 else if (parse_identifier(filter,CUS "true"))
2385 else if (parse_identifier(filter,CUS "envelope"))
2388 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2389 <envelope-part: string-list> <key-list: string-list>
2391 envelope-part is case insensitive "from" or "to"
2392 #ifdef ENVELOPE_AUTH
2393 envelope-part =/ "auth"
2397 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2398 enum AddressPart addressPart=ADDRPART_ALL;
2399 enum MatchType matchType=MATCH_IS;
2400 struct String *env,*e,*key,*k;
2404 if (!filter->require_envelope)
2406 filter->errmsg=CUS "missing previous require \"envelope\";";
2411 if (parse_white(filter)==-1) return -1;
2412 if ((m=parse_comparator(filter,&comparator))!=0)
2414 if (m==-1) return -1;
2417 filter->errmsg=CUS "comparator already specified";
2422 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2424 if (m==-1) return -1;
2427 filter->errmsg=CUS "address part already specified";
2432 else if ((m=parse_matchtype(filter,&matchType))!=0)
2434 if (m==-1) return -1;
2437 filter->errmsg=CUS "match type already specified";
2444 if (parse_white(filter)==-1) return -1;
2445 if ((m=parse_stringlist(filter,&env))!=1)
2447 if (m==0) filter->errmsg=CUS "envelope string list expected";
2450 if (parse_white(filter)==-1) return -1;
2451 if ((m=parse_stringlist(filter,&key))!=1)
2453 if (m==0) filter->errmsg=CUS "key string list expected";
2457 for (e=env; e->length!=-1 && !*cond; ++e)
2459 const uschar *envelopeExpr=CUS 0;
2460 uschar *envelope=US 0;
2462 if (eq_asciicase(e,&str_from,0))
2464 switch (addressPart)
2466 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2470 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2471 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2473 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2477 else if (eq_asciicase(e,&str_to,0))
2479 switch (addressPart)
2481 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2483 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2484 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2486 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2487 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2490 #ifdef ENVELOPE_AUTH
2491 else if (eq_asciicase(e,&str_auth,0))
2493 switch (addressPart)
2495 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2499 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2500 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2502 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2509 filter->errmsg=CUS "invalid envelope string";
2512 if (exec && envelopeExpr)
2514 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2516 filter->errmsg=CUS "header string expansion failed";
2519 for (k=key; k->length!=-1; ++k)
2521 struct String envelopeStr;
2523 envelopeStr.character=envelope;
2524 envelopeStr.length=Ustrlen(envelope);
2525 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2526 if (*cond==-1) return -1;
2534 else if (parse_identifier(filter,CUS "valid_notify_method"))
2537 valid_notify_method = "valid_notify_method"
2538 <notification-uris: string-list>
2541 struct String *uris,*u;
2544 if (!filter->require_enotify)
2546 filter->errmsg=CUS "missing previous require \"enotify\";";
2549 if (parse_white(filter)==-1) return -1;
2550 if ((m=parse_stringlist(filter,&uris))!=1)
2552 if (m==0) filter->errmsg=CUS "URI string list expected";
2558 for (u=uris; u->length!=-1 && *cond; ++u)
2560 string_item *recipient;
2561 struct String header,subject,body;
2565 header.character=(uschar*)0;
2567 subject.character=(uschar*)0;
2569 body.character=(uschar*)0;
2570 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2576 else if (parse_identifier(filter,CUS "notify_method_capability"))
2579 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2580 <notification-uri: string>
2581 <notification-capability: string>
2582 <key-list: string-list>
2588 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2589 enum MatchType matchType=MATCH_IS;
2590 struct String uri,capa,*keys,*k;
2592 if (!filter->require_enotify)
2594 filter->errmsg=CUS "missing previous require \"enotify\";";
2599 if (parse_white(filter)==-1) return -1;
2600 if ((m=parse_comparator(filter,&comparator))!=0)
2602 if (m==-1) return -1;
2605 filter->errmsg=CUS "comparator already specified";
2610 else if ((m=parse_matchtype(filter,&matchType))!=0)
2612 if (m==-1) return -1;
2615 filter->errmsg=CUS "match type already specified";
2622 if ((m=parse_string(filter,&uri))!=1)
2624 if (m==0) filter->errmsg=CUS "missing notification URI string";
2627 if (parse_white(filter)==-1) return -1;
2628 if ((m=parse_string(filter,&capa))!=1)
2630 if (m==0) filter->errmsg=CUS "missing notification capability string";
2633 if (parse_white(filter)==-1) return -1;
2634 if ((m=parse_stringlist(filter,&keys))!=1)
2636 if (m==0) filter->errmsg=CUS "missing key string list";
2641 string_item *recipient;
2642 struct String header,subject,body;
2647 header.character=(uschar*)0;
2649 subject.character=(uschar*)0;
2651 body.character=(uschar*)0;
2652 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2654 if (eq_asciicase(&capa,&str_online,0)==1)
2655 for (k=keys; k->length!=-1; ++k)
2657 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2658 if (*cond==-1) return -1;
2670 /*************************************************
2671 * Parse and interpret an optional block *
2672 *************************************************/
2676 filter points to the Sieve filter including its state
2677 exec Execute parsed statements
2678 generated where to hang newly-generated addresses
2680 Returns: 2 success by stop
2682 0 no block command found
2683 -1 syntax or execution error
2686 static int parse_block(struct Sieve *filter, int exec,
2687 address_item **generated)
2691 if (parse_white(filter)==-1) return -1;
2692 if (*filter->pc=='{')
2695 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2696 if (*filter->pc=='}')
2703 filter->errmsg=CUS "expecting command or closing brace";
2711 /*************************************************
2712 * Match a semicolon *
2713 *************************************************/
2717 filter points to the Sieve filter including its state
2723 static int parse_semicolon(struct Sieve *filter)
2725 if (parse_white(filter)==-1) return -1;
2726 if (*filter->pc==';')
2733 filter->errmsg=CUS "missing semicolon";
2739 /*************************************************
2740 * Parse and interpret a Sieve command *
2741 *************************************************/
2745 filter points to the Sieve filter including its state
2746 exec Execute parsed statements
2747 generated where to hang newly-generated addresses
2749 Returns: 2 success by stop
2751 -1 syntax or execution error
2754 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2758 if (parse_white(filter)==-1) return -1;
2759 if (parse_identifier(filter,CUS "if"))
2762 if-command = "if" test block *( "elsif" test block ) [ else block ]
2765 int cond,m,unsuccessful;
2768 if (parse_white(filter)==-1) return -1;
2769 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2772 filter->errmsg=CUS "missing test";
2775 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2776 (debug_selector & D_filter) != 0)
2778 if (exec) debug_printf("if %s\n",cond?"true":"false");
2780 m=parse_block(filter,exec ? cond : 0, generated);
2781 if (m==-1 || m==2) return m;
2784 filter->errmsg=CUS "missing block";
2787 unsuccessful = !cond;
2788 for (;;) /* elsif test block */
2790 if (parse_white(filter)==-1) return -1;
2791 if (parse_identifier(filter,CUS "elsif"))
2793 if (parse_white(filter)==-1) return -1;
2794 m=parse_test(filter,&cond,exec && unsuccessful);
2795 if (m==-1 || m==2) return m;
2798 filter->errmsg=CUS "missing test";
2801 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2802 (debug_selector & D_filter) != 0)
2804 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2806 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2807 if (m==-1 || m==2) return m;
2810 filter->errmsg=CUS "missing block";
2813 if (exec && unsuccessful && cond) unsuccessful = 0;
2818 if (parse_white(filter)==-1) return -1;
2819 if (parse_identifier(filter,CUS "else"))
2821 m=parse_block(filter,exec && unsuccessful, generated);
2822 if (m==-1 || m==2) return m;
2825 filter->errmsg=CUS "missing block";
2830 else if (parse_identifier(filter,CUS "stop"))
2833 stop-command = "stop" { stop-options } ";"
2837 if (parse_semicolon(filter)==-1) return -1;
2840 filter->pc+=Ustrlen(filter->pc);
2844 else if (parse_identifier(filter,CUS "keep"))
2847 keep-command = "keep" { keep-options } ";"
2851 if (parse_semicolon(filter)==-1) return -1;
2854 add_addr(generated,US"inbox",1,0,0,0);
2858 else if (parse_identifier(filter,CUS "discard"))
2861 discard-command = "discard" { discard-options } ";"
2865 if (parse_semicolon(filter)==-1) return -1;
2866 if (exec) filter->keep=0;
2868 else if (parse_identifier(filter,CUS "redirect"))
2871 redirect-command = "redirect" redirect-options "string" ";"
2873 redirect-options =) ":copy"
2876 struct String recipient;
2882 if (parse_white(filter)==-1) return -1;
2883 if (parse_identifier(filter,CUS ":copy")==1)
2885 if (!filter->require_copy)
2887 filter->errmsg=CUS "missing previous require \"copy\";";
2894 if (parse_white(filter)==-1) return -1;
2895 if ((m=parse_string(filter,&recipient))!=1)
2897 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2900 if (strchr(CCS recipient.character,'@')==(char*)0)
2902 filter->errmsg=CUS "unqualified recipient address";
2907 add_addr(generated,recipient.character,0,0,0,0);
2908 if (!copy) filter->keep = 0;
2910 if (parse_semicolon(filter)==-1) return -1;
2912 else if (parse_identifier(filter,CUS "fileinto"))
2915 fileinto-command = "fileinto" { fileinto-options } string ";"
2917 fileinto-options =) [ ":copy" ]
2920 struct String folder;
2923 unsigned long maxage, maxmessages, maxstorage;
2926 maxage = maxmessages = maxstorage = 0;
2927 if (!filter->require_fileinto)
2929 filter->errmsg=CUS "missing previous require \"fileinto\";";
2934 if (parse_white(filter)==-1) return -1;
2935 if (parse_identifier(filter,CUS ":copy")==1)
2937 if (!filter->require_copy)
2939 filter->errmsg=CUS "missing previous require \"copy\";";
2946 if (parse_white(filter)==-1) return -1;
2947 if ((m=parse_string(filter,&folder))!=1)
2949 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2952 m=0; s=folder.character;
2953 if (folder.length==0) m=1;
2954 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2957 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2962 filter->errmsg=CUS "invalid folder";
2967 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2968 if (!copy) filter->keep = 0;
2970 if (parse_semicolon(filter)==-1) return -1;
2973 else if (parse_identifier(filter,CUS "notify"))
2976 notify-command = "notify" { notify-options } <method: string> ";"
2977 notify-options = [":from" string]
2978 [":importance" <"1" / "2" / "3">]
2979 [":options" 1*(string-list / number)]
2985 struct String importance;
2986 struct String message;
2987 struct String method;
2988 struct Notification *already;
2989 string_item *recipient;
2990 struct String header;
2991 struct String subject;
2993 uschar *envelope_from;
2994 struct String auto_submitted_value;
2995 uschar *auto_submitted_def;
2997 if (!filter->require_enotify)
2999 filter->errmsg=CUS "missing previous require \"enotify\";";
3002 from.character=(uschar*)0;
3004 importance.character=(uschar*)0;
3005 importance.length=-1;
3006 message.character=(uschar*)0;
3010 header.character=(uschar*)0;
3012 subject.character=(uschar*)0;
3014 body.character=(uschar*)0;
3015 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3018 if (parse_white(filter)==-1) return -1;
3019 if (parse_identifier(filter,CUS ":from")==1)
3021 if (parse_white(filter)==-1) return -1;
3022 if ((m=parse_string(filter,&from))!=1)
3024 if (m==0) filter->errmsg=CUS "from string expected";
3028 else if (parse_identifier(filter,CUS ":importance")==1)
3030 if (parse_white(filter)==-1) return -1;
3031 if ((m=parse_string(filter,&importance))!=1)
3033 if (m==0) filter->errmsg=CUS "importance string expected";
3036 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3038 filter->errmsg=CUS "invalid importance";
3042 else if (parse_identifier(filter,CUS ":options")==1)
3044 if (parse_white(filter)==-1) return -1;
3046 else if (parse_identifier(filter,CUS ":message")==1)
3048 if (parse_white(filter)==-1) return -1;
3049 if ((m=parse_string(filter,&message))!=1)
3051 if (m==0) filter->errmsg=CUS "message string expected";
3057 if (parse_white(filter)==-1) return -1;
3058 if ((m=parse_string(filter,&method))!=1)
3060 if (m==0) filter->errmsg=CUS "missing method string";
3063 if (parse_semicolon(filter)==-1) return -1;
3064 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3068 if (message.length==-1) message=subject;
3069 if (message.length==-1) expand_header(&message,&str_subject);
3070 expand_header(&auto_submitted_value,&str_auto_submitted);
3071 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3072 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3074 filter->errmsg=CUS "header string expansion failed";
3077 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3079 for (already=filter->notified; already; already=already->next)
3081 if (already->method.length==method.length
3082 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3083 && already->importance.length==importance.length
3084 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3085 && already->message.length==message.length
3086 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3089 if (already==(struct Notification*)0)
3090 /* New notification, process it */
3092 struct Notification *sent;
3093 sent=store_get(sizeof(struct Notification));
3094 sent->method=method;
3095 sent->importance=importance;
3096 sent->message=message;
3097 sent->next=filter->notified;
3098 filter->notified=sent;
3099 #ifndef COMPILE_SYNTAX_CHECKER
3100 if (filter_test == FTEST_NONE)
3105 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3109 int buffer_capacity;
3111 f = fdopen(fd, "wb");
3112 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3113 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3114 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3115 if (header.length>0) fprintf(f,"%s",header.character);
3116 if (message.length==-1)
3118 message.character=US"Notification";
3119 message.length=Ustrlen(message.character);
3121 /* Allocation is larger than necessary, but enough even for split MIME words */
3122 buffer_capacity=32+4*message.length;
3123 buffer=store_get(buffer_capacity);
3124 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3126 if (body.length>0) fprintf(f,"%s\n",body.character);
3129 (void)child_close(pid, 0);
3132 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3134 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3140 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3142 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3148 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3150 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3157 else if (parse_identifier(filter,CUS "vacation"))
3160 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3161 vacation-options = [":days" number]
3164 [":addresses" string-list]
3171 struct String subject;
3173 struct String *addresses;
3175 string_item *aliases;
3176 struct String handle;
3177 struct String reason;
3179 if (!filter->require_vacation)
3181 filter->errmsg=CUS "missing previous require \"vacation\";";
3186 if (filter->vacation_ran)
3188 filter->errmsg=CUS "trying to execute vacation more than once";
3191 filter->vacation_ran=1;
3193 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3194 subject.character=(uschar*)0;
3196 from.character=(uschar*)0;
3198 addresses=(struct String*)0;
3201 handle.character=(uschar*)0;
3205 if (parse_white(filter)==-1) return -1;
3206 if (parse_identifier(filter,CUS ":days")==1)
3208 if (parse_white(filter)==-1) return -1;
3209 if (parse_number(filter,&days)==-1) return -1;
3210 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3211 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3213 else if (parse_identifier(filter,CUS ":subject")==1)
3215 if (parse_white(filter)==-1) return -1;
3216 if ((m=parse_string(filter,&subject))!=1)
3218 if (m==0) filter->errmsg=CUS "subject string expected";
3222 else if (parse_identifier(filter,CUS ":from")==1)
3224 if (parse_white(filter)==-1) return -1;
3225 if ((m=parse_string(filter,&from))!=1)
3227 if (m==0) filter->errmsg=CUS "from string expected";
3230 if (check_mail_address(filter,&from)!=1)
3233 else if (parse_identifier(filter,CUS ":addresses")==1)
3237 if (parse_white(filter)==-1) return -1;
3238 if ((m=parse_stringlist(filter,&addresses))!=1)
3240 if (m==0) filter->errmsg=CUS "addresses string list expected";
3243 for (a=addresses; a->length!=-1; ++a)
3247 new=store_get(sizeof(string_item));
3248 new->text=store_get(a->length+1);
3249 if (a->length) memcpy(new->text,a->character,a->length);
3250 new->text[a->length]='\0';
3255 else if (parse_identifier(filter,CUS ":mime")==1)
3257 else if (parse_identifier(filter,CUS ":handle")==1)
3259 if (parse_white(filter)==-1) return -1;
3260 if ((m=parse_string(filter,&from))!=1)
3262 if (m==0) filter->errmsg=CUS "handle string expected";
3268 if (parse_white(filter)==-1) return -1;
3269 if ((m=parse_string(filter,&reason))!=1)
3271 if (m==0) filter->errmsg=CUS "missing reason string";
3278 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3281 filter->errmsg=CUS "MIME reason string contains 8bit text";
3285 if (parse_semicolon(filter)==-1) return -1;
3291 int buffer_capacity;
3294 uschar hexdigest[33];
3298 if (filter_personal(aliases,TRUE))
3300 if (filter_test == FTEST_NONE)
3302 /* ensure oncelog directory exists; failure will be detected later */
3304 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3306 /* build oncelog filename */
3310 if (handle.length==-1)
3312 gstring * key = NULL;
3313 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3314 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3315 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3316 key = string_catn(key, reason.character, reason.length);
3317 md5_end(&base, key->s, key->ptr, digest);
3320 md5_end(&base, handle.character, handle.length, digest);
3322 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3324 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3325 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3327 if (filter_test == FTEST_NONE)
3329 once = string_cat (NULL, filter->vacation_directory);
3330 once = string_catn(once, US"/", 1);
3331 once = string_catn(once, hexdigest, 33);
3333 /* process subject */
3335 if (subject.length==-1)
3337 uschar *subject_def;
3339 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3340 if (Ustrcmp(subject_def,"true")==0)
3342 gstring * g = string_catn(NULL, US"Auto: ", 6);
3344 expand_header(&subject,&str_subject);
3345 g = string_catn(g, subject.character, subject.length);
3346 subject.character = string_from_gstring(g);
3347 subject.length = g->ptr;
3351 subject.character=US"Automated reply";
3352 subject.length=Ustrlen(subject.character);
3356 /* add address to list of generated addresses */
3358 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3359 setflag(addr, af_pfr);
3360 addr->prop.ignore_error = TRUE;
3361 addr->next = *generated;
3363 addr->reply = store_get(sizeof(reply_item));
3364 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3365 addr->reply->to = string_copy(sender_address);
3366 if (from.length==-1)
3367 addr->reply->from = expand_string(US"$local_part@$domain");
3369 addr->reply->from = from.character;
3370 /* Allocation is larger than necessary, but enough even for split MIME words */
3371 buffer_capacity=32+4*subject.length;
3372 buffer=store_get(buffer_capacity);
3373 /* deconst cast safe as we pass in a non-const item */
3374 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3375 addr->reply->oncelog = string_from_gstring(once);
3376 addr->reply->once_repeat=days*86400;
3378 /* build body and MIME headers */
3382 uschar *mime_body,*reason_end;
3383 static const uschar nlnl[]="\r\n\r\n";
3387 mime_body = reason.character, reason_end = reason.character + reason.length;
3388 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3392 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3394 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3395 else mime_body=reason_end-1;
3396 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3400 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3402 addr->reply->headers = US"MIME-Version: 1.0\n"
3403 "Content-Type: text/plain;\n"
3404 "\tcharset=\"utf-8\"\n"
3405 "Content-Transfer-Encoding: quoted-printable";
3406 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3410 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3411 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3421 /*************************************************
3422 * Parse and interpret a sieve filter *
3423 *************************************************/
3427 filter points to the Sieve filter including its state
3428 exec Execute parsed statements
3429 generated where to hang newly-generated addresses
3432 -1 syntax or execution error
3436 parse_start(struct Sieve *filter, int exec, address_item **generated)
3438 filter->pc=filter->filter;
3441 filter->require_envelope=0;
3442 filter->require_fileinto=0;
3443 #ifdef ENCODED_CHARACTER
3444 filter->require_encoded_character=0;
3446 #ifdef ENVELOPE_AUTH
3447 filter->require_envelope_auth=0;
3450 filter->require_enotify=0;
3451 filter->notified=(struct Notification*)0;
3454 filter->require_subaddress=0;
3457 filter->require_vacation=0;
3458 filter->vacation_ran=0;
3460 filter->require_copy=0;
3461 filter->require_iascii_numeric=0;
3463 if (parse_white(filter)==-1) return -1;
3465 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3468 struct dirent *oncelog;
3469 struct stat properties;
3472 /* clean up old vacation log databases */
3474 oncelogdir=opendir(CS filter->vacation_directory);
3476 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3478 filter->errmsg=CUS "unable to open vacation directory";
3482 if (oncelogdir != NULL)
3486 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3488 if (strlen(oncelog->d_name)==32)
3490 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3491 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3495 closedir(oncelogdir);
3499 while (parse_identifier(filter,CUS "require"))
3502 require-command = "require" <capabilities: string-list>
3505 struct String *cap,*check;
3508 if (parse_white(filter)==-1) return -1;
3509 if ((m=parse_stringlist(filter,&cap))!=1)
3511 if (m==0) filter->errmsg=CUS "capability string list expected";
3514 for (check=cap; check->character; ++check)
3516 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3517 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3518 #ifdef ENCODED_CHARACTER
3519 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3521 #ifdef ENVELOPE_AUTH
3522 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3525 else if (eq_octet(check,&str_enotify,0))
3527 if (filter->enotify_mailto_owner == NULL)
3529 filter->errmsg=CUS "enotify disabled";
3532 filter->require_enotify=1;
3536 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3539 else if (eq_octet(check,&str_vacation,0))
3541 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3543 filter->errmsg=CUS "vacation disabled";
3546 filter->require_vacation=1;
3549 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3550 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3551 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3552 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3553 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3556 filter->errmsg=CUS "unknown capability";
3560 if (parse_semicolon(filter)==-1) return -1;
3562 if (parse_commands(filter,exec,generated)==-1) return -1;
3565 filter->errmsg=CUS "syntax error";
3572 /*************************************************
3573 * Interpret a sieve filter file *
3574 *************************************************/
3578 filter points to the entire file, read into store as a single string
3579 options controls whether various special things are allowed, and requests
3580 special actions (not currently used)
3581 vacation_directory where to store vacation "once" files
3582 enotify_mailto_owner owner of mailto notifications
3583 useraddress string expression for :user part of address
3584 subaddress string expression for :subaddress part of address
3585 generated where to hang newly-generated addresses
3586 error where to pass back an error text
3588 Returns: FF_DELIVERED success, a significant action was taken
3589 FF_NOTDELIVERED success, no significant action
3590 FF_DEFER defer requested
3591 FF_FAIL fail requested
3592 FF_FREEZE freeze requested
3593 FF_ERROR there was a problem
3597 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3598 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3599 address_item **generated, uschar **error)
3605 options = options; /* Keep picky compilers happy */
3608 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3609 sieve.filter=filter;
3611 if (vacation_directory == NULL)
3612 sieve.vacation_directory = NULL;
3615 sieve.vacation_directory=expand_string(vacation_directory);
3616 if (sieve.vacation_directory == NULL)
3618 *error = string_sprintf("failed to expand \"%s\" "
3619 "(sieve_vacation_directory): %s", vacation_directory,
3620 expand_string_message);
3625 if (enotify_mailto_owner == NULL)
3626 sieve.enotify_mailto_owner = NULL;
3629 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3630 if (sieve.enotify_mailto_owner == NULL)
3632 *error = string_sprintf("failed to expand \"%s\" "
3633 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3634 expand_string_message);
3639 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3640 sieve.subaddress = subaddress;
3642 #ifdef COMPILE_SYNTAX_CHECKER
3643 if (parse_start(&sieve,0,generated)==1)
3645 if (parse_start(&sieve,1,generated)==1)
3650 add_addr(generated,US"inbox",1,0,0,0);
3651 msg = string_sprintf("Implicit keep");
3656 msg = string_sprintf("No implicit keep");
3662 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3663 #ifdef COMPILE_SYNTAX_CHECKER
3667 add_addr(generated,US"inbox",1,0,0,0);
3672 #ifndef COMPILE_SYNTAX_CHECKER
3673 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3674 else debug_printf("%s\n", msg);
3677 DEBUG(D_route) debug_printf("Sieve: end of processing\n");