1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2017
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 };
156 static uschar str_auth_c[]="auth";
157 static const struct String str_auth={ str_auth_c, 4 };
158 static uschar str_sender_c[]="Sender";
159 static const struct String str_sender={ str_sender_c, 6 };
160 static uschar str_resent_from_c[]="Resent-From";
161 static const struct String str_resent_from={ str_resent_from_c, 11 };
162 static uschar str_resent_to_c[]="Resent-To";
163 static const struct String str_resent_to={ str_resent_to_c, 9 };
164 static uschar str_fileinto_c[]="fileinto";
165 static const struct String str_fileinto={ str_fileinto_c, 8 };
166 static uschar str_envelope_c[]="envelope";
167 static const struct String str_envelope={ str_envelope_c, 8 };
168 #ifdef ENCODED_CHARACTER
169 static uschar str_encoded_character_c[]="encoded-character";
170 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
173 static uschar str_envelope_auth_c[]="envelope-auth";
174 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
177 static uschar str_enotify_c[]="enotify";
178 static const struct String str_enotify={ str_enotify_c, 7 };
179 static uschar str_online_c[]="online";
180 static const struct String str_online={ str_online_c, 6 };
181 static uschar str_maybe_c[]="maybe";
182 static const struct String str_maybe={ str_maybe_c, 5 };
183 static uschar str_auto_submitted_c[]="Auto-Submitted";
184 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
187 static uschar str_subaddress_c[]="subaddress";
188 static const struct String str_subaddress={ str_subaddress_c, 10 };
191 static uschar str_vacation_c[]="vacation";
192 static const struct String str_vacation={ str_vacation_c, 8 };
193 static uschar str_subject_c[]="Subject";
194 static const struct String str_subject={ str_subject_c, 7 };
196 static uschar str_copy_c[]="copy";
197 static const struct String str_copy={ str_copy_c, 4 };
198 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
199 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
200 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
201 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
202 static uschar str_ioctet_c[]="i;octet";
203 static const struct String str_ioctet={ str_ioctet_c, 7 };
204 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
205 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
206 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
207 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
208 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
209 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
210 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
211 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
212 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
213 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
216 /*************************************************
217 * Encode to quoted-printable *
218 *************************************************/
229 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
232 const uschar *start,*end;
237 /* Two passes: one to count output allocation size, second
238 to do the encoding */
240 for (pass=0; pass<=1; ++pass)
247 dst->character=store_get(dst->length+1); /* plus one for \0 */
250 for (start=src->character,end=start+src->length; start<end; ++start)
253 if (line>=73) /* line length limit */
259 *new++='='; /* line split */
264 if ( (ch>='!' && ch<='<')
265 || (ch>='>' && ch<='~')
266 || ( (ch=='\t' || ch==' ')
268 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
275 *new++=*start; /* copy char */
278 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
283 *new++='\n'; /* NL */
285 ++start; /* consume extra input char */
293 new += sprintf(CS new,"=%02X",ch);
299 *new='\0'; /* not included in length, but nice */
304 /*************************************************
305 * Check mail address for correct syntax *
306 *************************************************/
309 Check mail address for being syntactically correct.
312 filter points to the Sieve filter including its state
313 address String containing one address
316 1 Mail address is syntactically OK
320 int check_mail_address(struct Sieve *filter, const struct String *address)
322 int start, end, domain;
325 if (address->length>0)
327 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
331 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
332 address->character, error);
340 filter->errmsg=CUS "empty address";
346 /*************************************************
347 * Decode URI encoded string *
348 *************************************************/
352 str URI encoded string
355 0 Decoding successful
360 static int uri_decode(struct String *str)
364 if (str->length==0) return 0;
365 for (s=str->character,t=s,e=s+str->length; s<e; )
369 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
371 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
372 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
381 str->length=t-str->character;
386 /*************************************************
388 *************************************************/
393 mailtoURI = "mailto:" [ to ] [ headers ]
394 to = [ addr-spec *("%2C" addr-spec ) ]
395 headers = "?" header *( "&" header )
396 header = hname "=" hvalue
401 filter points to the Sieve filter including its state
402 uri URI, excluding scheme
407 1 URI is syntactically OK
413 parse_mailto_uri(struct Sieve *filter, const uschar *uri,
414 string_item **recipient, struct String *header, struct String *subject,
418 struct String to, hname;
419 struct String hvalue = {.character = NULL, .length = 0};
422 if (Ustrncmp(uri,"mailto:",7))
424 filter->errmsg=US "Unknown URI scheme";
429 if (*uri && *uri!='?')
433 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
436 gstring * g = string_catn(NULL, start, uri-start);
438 to.character = string_from_gstring(g);
440 if (uri_decode(&to)==-1)
442 filter->errmsg=US"Invalid URI encoding";
445 new=store_get(sizeof(string_item));
446 new->text=store_get(to.length+1);
447 if (to.length) memcpy(new->text,to.character,to.length);
448 new->text[to.length]='\0';
449 new->next=*recipient;
454 filter->errmsg=US"Missing addr-spec in URI";
457 if (*uri=='%') uri+=3;
466 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
469 gstring * g = string_catn(NULL, start, uri-start);
471 hname.character = string_from_gstring(g);
472 hname.length = g->ptr;
473 if (uri_decode(&hname)==-1)
475 filter->errmsg=US"Invalid URI encoding";
484 filter->errmsg=US"Missing equal after hname";
488 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
491 gstring * g = string_catn(NULL, start, uri-start);
493 hname.character = string_from_gstring(g);
494 hname.length = g->ptr;
495 if (uri_decode(&hvalue)==-1)
497 filter->errmsg=US"Invalid URI encoding";
501 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
503 new=store_get(sizeof(string_item));
504 new->text=store_get(hvalue.length+1);
505 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
506 new->text[hvalue.length]='\0';
507 new->next=*recipient;
510 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
512 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
516 static struct String ignore[]=
522 {US"auto-submitted",14}
524 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
527 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
532 if (header->length==-1) header->length = 0;
534 g = string_catn(NULL, header->character, header->length);
535 g = string_catn(g, hname.character, hname.length);
536 g = string_catn(g, CUS ": ", 2);
537 g = string_catn(g, hvalue.character, hvalue.length);
538 g = string_catn(g, CUS "\n", 1);
540 header->character = string_from_gstring(g);
541 header->length = g->ptr;
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);
860 if (eq_octet(needle,haystack,0)) r=1;
863 case COMP_EN_ASCII_CASEMAP:
865 if (eq_asciicase(needle,haystack,0)) r=1;
868 case COMP_ASCII_NUMERIC:
870 if (!filter->require_iascii_numeric)
872 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
875 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
889 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
892 case COMP_EN_ASCII_CASEMAP:
894 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
899 filter->errmsg=CUS "comparator does not offer specified matchtype";
911 if ((r=eq_glob(needle,haystack,0,1))==-1)
913 filter->errmsg=CUS "syntactically invalid pattern";
918 case COMP_EN_ASCII_CASEMAP:
920 if ((r=eq_glob(needle,haystack,1,1))==-1)
922 filter->errmsg=CUS "syntactically invalid pattern";
929 filter->errmsg=CUS "comparator does not offer specified matchtype";
936 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
937 (debug_selector & D_filter) != 0)
938 debug_printf(" Result %s\n",r?"true":"false");
943 /*************************************************
944 * Check header field syntax *
945 *************************************************/
948 RFC 2822, section 3.6.8 says:
952 ftext = %d33-57 / ; Any character except
953 %d59-126 ; controls, SP, and
956 That forbids 8-bit header fields. This implementation accepts them, since
957 all of Exim is 8-bit clean, so it adds %d128-%d255.
960 header header field to quote for suitable use in Exim expansions
962 Returns: 0 string is not a valid header field
963 1 string is a value header field
966 static int is_header(const struct String *header)
976 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
987 /*************************************************
988 * Quote special characters string *
989 *************************************************/
993 header header field to quote for suitable use in Exim expansions
996 Returns: quoted string
999 static const uschar *
1000 quote(const struct String *header)
1002 gstring * quoted = NULL;
1007 h=header->character;
1013 quoted = string_catn(quoted, CUS "\\0", 2);
1018 quoted = string_catn(quoted, CUS "\\", 1);
1020 quoted = string_catn(quoted, h, 1);
1025 quoted = string_catn(quoted, CUS "", 1);
1026 return string_from_gstring(quoted);
1030 /*************************************************
1031 * Add address to list of generated addresses *
1032 *************************************************/
1035 According to RFC 5228, duplicate delivery to the same address must
1036 not happen, so the list is first searched for the address.
1039 generated list of generated addresses
1040 addr new address to add
1041 file address denotes a file
1047 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1049 address_item *new_addr;
1051 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1052 if ( Ustrcmp(new_addr->address,addr) == 0
1054 || testflag(new_addr, af_pfr)
1055 || testflag(new_addr, af_file)
1059 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1060 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1065 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1066 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1068 new_addr = deliver_make_addr(addr,TRUE);
1071 setflag(new_addr, af_pfr);
1072 setflag(new_addr, af_file);
1075 new_addr->prop.errors_address = NULL;
1076 new_addr->next = *generated;
1077 *generated = new_addr;
1081 /*************************************************
1082 * Return decoded header field *
1083 *************************************************/
1086 Unfold the header field as described in RFC 2822 and remove all
1087 leading and trailing white space, then perform MIME decoding and
1088 translate the header field to UTF-8.
1091 value returned value of the field
1092 header name of the header field
1094 Returns: nothing The expanded string is empty
1095 in case there is no such header
1098 static void expand_header(struct String *value, const struct String *header)
1104 value->character=(uschar*)0;
1106 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1107 while (*r==' ' || *r=='\t') ++r;
1115 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1117 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1121 /*************************************************
1122 * Parse remaining hash comment *
1123 *************************************************/
1127 Comment up to terminating CRLF
1130 filter points to the Sieve filter including its state
1136 static int parse_hashcomment(struct Sieve *filter)
1142 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1144 if (*filter->pc=='\n')
1157 filter->errmsg=CUS "missing end of comment";
1162 /*************************************************
1163 * Parse remaining C-style comment *
1164 *************************************************/
1168 Everything up to star slash
1171 filter points to the Sieve filter including its state
1177 static int parse_comment(struct Sieve *filter)
1182 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1189 filter->errmsg=CUS "missing end of comment";
1194 /*************************************************
1195 * Parse optional white space *
1196 *************************************************/
1200 Spaces, tabs, CRLFs, hash comments or C-style comments
1203 filter points to the Sieve filter including its state
1209 static int parse_white(struct Sieve *filter)
1213 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1215 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1217 else if (*filter->pc=='\n')
1227 else if (*filter->pc=='#')
1229 if (parse_hashcomment(filter)==-1) return -1;
1231 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1233 if (parse_comment(filter)==-1) return -1;
1241 #ifdef ENCODED_CHARACTER
1242 /*************************************************
1243 * Decode hex-encoded-character string *
1244 *************************************************/
1247 Encoding definition:
1248 blank = SP / TAB / CRLF
1249 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1250 hex-pair = 1*2HEXDIG
1253 src points to a hex-pair-seq
1254 end points to its end
1255 dst points to the destination of the decoded octets,
1256 optionally to (uschar*)0 for checking only
1258 Returns: >=0 number of decoded octets
1262 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1266 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1271 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);
1272 if (d==0) return -1;
1275 if (src==end) return decoded;
1276 if (*src==' ' || *src=='\t' || *src=='\n')
1277 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1286 /*************************************************
1287 * Decode unicode-encoded-character string *
1288 *************************************************/
1291 Encoding definition:
1292 blank = SP / TAB / CRLF
1293 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1294 unicode-hex = 1*HEXDIG
1296 It is an error for a script to use a hexadecimal value that isn't in
1297 either the range 0 to D7FF or the range E000 to 10FFFF.
1299 At this time, strings are already scanned, thus the CRLF is converted
1300 to the internally used \n (should RFC_EOL have been used).
1303 src points to a unicode-hex-seq
1304 end points to its end
1305 dst points to the destination of the decoded octets,
1306 optionally to (uschar*)0 for checking only
1308 Returns: >=0 number of decoded octets
1310 -2 semantic error (character range violation)
1313 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1317 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1324 for (hex_seq=src; src<end && *src=='0'; ++src);
1325 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);
1326 if (src==hex_seq) return -1;
1327 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1333 else if (c>=0x80 && c<=0x7ff)
1338 *dst++=128+(c&0x3f);
1342 else if (c>=0x800 && c<=0xffff)
1347 *dst++=128+((c>>6)&0x3f);
1348 *dst++=128+(c&0x3f);
1352 else if (c>=0x10000 && c<=0x1fffff)
1357 *dst++=128+((c>>10)&0x3f);
1358 *dst++=128+((c>>6)&0x3f);
1359 *dst++=128+(c&0x3f);
1363 if (*src==' ' || *src=='\t' || *src=='\n')
1365 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1366 if (src==end) return decoded;
1375 /*************************************************
1376 * Decode encoded-character string *
1377 *************************************************/
1380 Encoding definition:
1381 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1382 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1385 encoded points to an encoded string, returns decoded string
1386 filter points to the Sieve filter including its state
1392 static int string_decode(struct Sieve *filter, struct String *data)
1394 uschar *src,*dst,*end;
1396 src=data->character;
1398 end=data->character+data->length;
1404 strncmpic(src,US "${hex:",6)==0
1405 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1406 && (hex_decode(src+6,brace,(uschar*)0))>=0
1409 dst+=hex_decode(src+6,brace,dst);
1413 strncmpic(src,US "${unicode:",10)==0
1414 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1417 switch (unicode_decode(src+10,brace,(uschar*)0))
1421 filter->errmsg=CUS "unicode character out of range";
1431 dst+=unicode_decode(src+10,brace,dst);
1438 data->length=dst-data->character;
1445 /*************************************************
1446 * Parse an optional string *
1447 *************************************************/
1451 quoted-string = DQUOTE *CHAR DQUOTE
1452 ;; in general, \ CHAR inside a string maps to CHAR
1453 ;; so \" maps to " and \\ maps to \
1454 ;; note that newlines and other characters are all allowed
1457 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1458 *(multi-line-literal / multi-line-dotstuff)
1460 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1461 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1462 ;; A line containing only "." ends the multi-line.
1463 ;; Remove a leading '.' if followed by another '.'.
1464 string = quoted-string / multi-line
1467 filter points to the Sieve filter including its state
1468 id specifies identifier to match
1472 0 identifier not matched
1476 parse_string(struct Sieve *filter, struct String *data)
1481 data->character = NULL;
1483 if (*filter->pc=='"') /* quoted string */
1488 if (*filter->pc=='"') /* end of string */
1494 data->character = string_from_gstring(g);
1495 data->length = g->ptr;
1498 data->character = US"\0";
1499 /* that way, there will be at least one character allocated */
1501 #ifdef ENCODED_CHARACTER
1502 if (filter->require_encoded_character
1503 && string_decode(filter,data)==-1)
1508 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1510 g = string_catn(g, filter->pc+1, 1);
1513 else /* regular character */
1516 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1518 if (*filter->pc=='\n')
1520 g = string_catn(g, US"\r", 1);
1524 g = string_catn(g, filter->pc, 1);
1528 filter->errmsg=CUS "missing end of string";
1531 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1534 /* skip optional white space followed by hashed comment or CRLF */
1535 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1536 if (*filter->pc=='#')
1538 if (parse_hashcomment(filter)==-1) return -1;
1541 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1543 else if (*filter->pc=='\n')
1555 filter->errmsg=CUS "syntax error";
1561 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1563 if (*filter->pc=='\n') /* end of line */
1566 g = string_catn(g, CUS "\r\n", 2);
1574 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1576 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1581 data->character = string_from_gstring(g);
1582 data->length = g->ptr;
1585 data->character = US"\0";
1586 /* that way, there will be at least one character allocated */
1594 #ifdef ENCODED_CHARACTER
1595 if (filter->require_encoded_character
1596 && string_decode(filter,data)==-1)
1601 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1603 g = string_catn(g, CUS ".", 1);
1607 else /* regular character */
1609 g = string_catn(g, filter->pc, 1);
1613 filter->errmsg=CUS "missing end of multi line string";
1620 /*************************************************
1621 * Parse a specific identifier *
1622 *************************************************/
1626 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1629 filter points to the Sieve filter including its state
1630 id specifies identifier to match
1633 0 identifier not matched
1636 static int parse_identifier(struct Sieve *filter, const uschar *id)
1638 size_t idlen=Ustrlen(id);
1640 if (strncmpic(US filter->pc,US id,idlen)==0)
1642 uschar next=filter->pc[idlen];
1644 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1652 /*************************************************
1654 *************************************************/
1658 number = 1*DIGIT [QUANTIFIER]
1659 QUANTIFIER = "K" / "M" / "G"
1662 filter points to the Sieve filter including its state
1666 -1 no string list found
1669 static int parse_number(struct Sieve *filter, unsigned long *data)
1673 if (*filter->pc>='0' && *filter->pc<='9')
1678 d=Ustrtoul(filter->pc,&e,10);
1681 filter->errmsg=CUstrerror(ERANGE);
1686 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1687 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1688 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1689 if (d>(ULONG_MAX/u))
1691 filter->errmsg=CUstrerror(ERANGE);
1700 filter->errmsg=CUS "missing number";
1706 /*************************************************
1707 * Parse a string list *
1708 *************************************************/
1712 string-list = "[" string *("," string) "]" / string
1715 filter points to the Sieve filter including its state
1716 data returns string list
1719 -1 no string list found
1723 parse_stringlist(struct Sieve *filter, struct String **data)
1725 const uschar *orig=filter->pc;
1726 int dataCapacity = 0;
1728 struct String *d = NULL;
1731 if (*filter->pc=='[') /* string list */
1736 if (parse_white(filter)==-1) goto error;
1737 if (dataLength+1 >= dataCapacity) /* increase buffer */
1741 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1742 new = store_get(sizeof(struct String) * dataCapacity);
1744 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1748 m=parse_string(filter,&d[dataLength]);
1751 if (dataLength==0) break;
1754 filter->errmsg=CUS "missing string";
1758 else if (m==-1) goto error;
1760 if (parse_white(filter)==-1) goto error;
1761 if (*filter->pc==',') ++filter->pc;
1764 if (*filter->pc==']')
1766 d[dataLength].character=(uschar*)0;
1767 d[dataLength].length=-1;
1774 filter->errmsg=CUS "missing closing bracket";
1778 else /* single string */
1780 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1784 m=parse_string(filter,&d[0]);
1796 d[1].character=(uschar*)0;
1803 filter->errmsg=CUS "missing string list";
1808 /*************************************************
1809 * Parse an optional address part specifier *
1810 *************************************************/
1814 address-part = ":localpart" / ":domain" / ":all"
1815 address-part =/ ":user" / ":detail"
1818 filter points to the Sieve filter including its state
1819 a returns address part specified
1822 0 no comparator found
1826 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1829 if (parse_identifier(filter,CUS ":user")==1)
1831 if (!filter->require_subaddress)
1833 filter->errmsg=CUS "missing previous require \"subaddress\";";
1839 else if (parse_identifier(filter,CUS ":detail")==1)
1841 if (!filter->require_subaddress)
1843 filter->errmsg=CUS "missing previous require \"subaddress\";";
1851 if (parse_identifier(filter,CUS ":localpart")==1)
1853 *a=ADDRPART_LOCALPART;
1856 else if (parse_identifier(filter,CUS ":domain")==1)
1861 else if (parse_identifier(filter,CUS ":all")==1)
1870 /*************************************************
1871 * Parse an optional comparator *
1872 *************************************************/
1876 comparator = ":comparator" <comparator-name: string>
1879 filter points to the Sieve filter including its state
1880 c returns comparator
1883 0 no comparator found
1884 -1 incomplete comparator found
1887 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1889 struct String comparator_name;
1891 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1892 if (parse_white(filter)==-1) return -1;
1893 switch (parse_string(filter,&comparator_name))
1898 filter->errmsg=CUS "missing comparator";
1905 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1910 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1912 *c=COMP_EN_ASCII_CASEMAP;
1915 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1917 *c=COMP_EN_ASCII_CASEMAP;
1920 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1922 *c=COMP_ASCII_NUMERIC;
1927 filter->errmsg=CUS "invalid comparator";
1936 /*************************************************
1937 * Parse an optional match type *
1938 *************************************************/
1942 match-type = ":is" / ":contains" / ":matches"
1945 filter points to the Sieve filter including its state
1946 m returns match type
1949 0 no match type found
1952 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1954 if (parse_identifier(filter,CUS ":is")==1)
1959 else if (parse_identifier(filter,CUS ":contains")==1)
1964 else if (parse_identifier(filter,CUS ":matches")==1)
1973 /*************************************************
1974 * Parse and interpret an optional test list *
1975 *************************************************/
1979 test-list = "(" test *("," test) ")"
1982 filter points to the Sieve filter including its state
1983 n total number of tests
1984 num_true number of passed tests
1985 exec Execute parsed statements
1988 0 no test list found
1989 -1 syntax or execution error
1992 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1994 if (parse_white(filter)==-1) return -1;
1995 if (*filter->pc=='(')
2004 switch (parse_test(filter,&cond,exec))
2007 case 0: filter->errmsg=CUS "missing test"; return -1;
2008 default: ++*n; if (cond) ++*num_true; break;
2010 if (parse_white(filter)==-1) return -1;
2011 if (*filter->pc==',') ++filter->pc;
2014 if (*filter->pc==')')
2021 filter->errmsg=CUS "missing closing paren";
2029 /*************************************************
2030 * Parse and interpret an optional test *
2031 *************************************************/
2035 filter points to the Sieve filter including its state
2036 cond returned condition status
2037 exec Execute parsed statements
2041 -1 syntax or execution error
2044 static int parse_test(struct Sieve *filter, int *cond, int exec)
2046 if (parse_white(filter)==-1) return -1;
2047 if (parse_identifier(filter,CUS "address"))
2050 address-test = "address" { [address-part] [comparator] [match-type] }
2051 <header-list: string-list> <key-list: string-list>
2053 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2056 enum AddressPart addressPart=ADDRPART_ALL;
2057 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2058 enum MatchType matchType=MATCH_IS;
2059 struct String *hdr,*h,*key,*k;
2065 if (parse_white(filter)==-1) return -1;
2066 if ((m=parse_addresspart(filter,&addressPart))!=0)
2068 if (m==-1) return -1;
2071 filter->errmsg=CUS "address part already specified";
2076 else if ((m=parse_comparator(filter,&comparator))!=0)
2078 if (m==-1) return -1;
2081 filter->errmsg=CUS "comparator already specified";
2086 else if ((m=parse_matchtype(filter,&matchType))!=0)
2088 if (m==-1) return -1;
2091 filter->errmsg=CUS "match type already specified";
2098 if (parse_white(filter)==-1) return -1;
2099 if ((m=parse_stringlist(filter,&hdr))!=1)
2101 if (m==0) filter->errmsg=CUS "header string list expected";
2104 if (parse_white(filter)==-1) return -1;
2105 if ((m=parse_stringlist(filter,&key))!=1)
2107 if (m==0) filter->errmsg=CUS "key string list expected";
2111 for (h=hdr; h->length!=-1 && !*cond; ++h)
2113 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2117 !eq_asciicase(h,&str_from,0)
2118 && !eq_asciicase(h,&str_to,0)
2119 && !eq_asciicase(h,&str_cc,0)
2120 && !eq_asciicase(h,&str_bcc,0)
2121 && !eq_asciicase(h,&str_sender,0)
2122 && !eq_asciicase(h,&str_resent_from,0)
2123 && !eq_asciicase(h,&str_resent_to,0)
2126 filter->errmsg=CUS "invalid header field";
2131 /* We are only interested in addresses below, so no MIME decoding */
2132 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2133 if (header_value == NULL)
2135 filter->errmsg=CUS "header string expansion failed";
2138 parse_allow_group = TRUE;
2139 while (*header_value && !*cond)
2142 int start, end, domain;
2146 end_addr = parse_find_address_end(header_value, FALSE);
2147 saveend = *end_addr;
2149 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2151 if (extracted_addr) switch (addressPart)
2153 case ADDRPART_ALL: part=extracted_addr; break;
2157 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2158 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2160 case ADDRPART_DETAIL: part=NULL; break;
2164 *end_addr = saveend;
2167 for (k=key; k->length!=-1; ++k)
2169 struct String partStr;
2171 partStr.character=part;
2172 partStr.length=Ustrlen(part);
2175 *cond=compare(filter,k,&partStr,comparator,matchType);
2176 if (*cond==-1) return -1;
2181 if (saveend == 0) break;
2182 header_value = end_addr + 1;
2184 parse_allow_group = FALSE;
2185 parse_found_group = FALSE;
2190 else if (parse_identifier(filter,CUS "allof"))
2193 allof-test = "allof" <tests: test-list>
2198 switch (parse_testlist(filter,&n,&num_true,exec))
2201 case 0: filter->errmsg=CUS "missing test list"; return -1;
2202 default: *cond=(n==num_true); return 1;
2205 else if (parse_identifier(filter,CUS "anyof"))
2208 anyof-test = "anyof" <tests: test-list>
2213 switch (parse_testlist(filter,&n,&num_true,exec))
2216 case 0: filter->errmsg=CUS "missing test list"; return -1;
2217 default: *cond=(num_true>0); return 1;
2220 else if (parse_identifier(filter,CUS "exists"))
2223 exists-test = "exists" <header-names: string-list>
2226 struct String *hdr,*h;
2229 if (parse_white(filter)==-1) return -1;
2230 if ((m=parse_stringlist(filter,&hdr))!=1)
2232 if (m==0) filter->errmsg=CUS "header string list expected";
2238 for (h=hdr; h->length!=-1 && *cond; ++h)
2242 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2243 if (header_def == NULL)
2245 filter->errmsg=CUS "header string expansion failed";
2248 if (Ustrcmp(header_def,"false")==0) *cond=0;
2253 else if (parse_identifier(filter,CUS "false"))
2256 false-test = "false"
2262 else if (parse_identifier(filter,CUS "header"))
2265 header-test = "header" { [comparator] [match-type] }
2266 <header-names: string-list> <key-list: string-list>
2269 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2270 enum MatchType matchType=MATCH_IS;
2271 struct String *hdr,*h,*key,*k;
2277 if (parse_white(filter)==-1) return -1;
2278 if ((m=parse_comparator(filter,&comparator))!=0)
2280 if (m==-1) return -1;
2283 filter->errmsg=CUS "comparator already specified";
2288 else if ((m=parse_matchtype(filter,&matchType))!=0)
2290 if (m==-1) return -1;
2293 filter->errmsg=CUS "match type already specified";
2300 if (parse_white(filter)==-1) return -1;
2301 if ((m=parse_stringlist(filter,&hdr))!=1)
2303 if (m==0) filter->errmsg=CUS "header string list expected";
2306 if (parse_white(filter)==-1) return -1;
2307 if ((m=parse_stringlist(filter,&key))!=1)
2309 if (m==0) filter->errmsg=CUS "key string list expected";
2313 for (h=hdr; h->length!=-1 && !*cond; ++h)
2317 filter->errmsg=CUS "invalid header field";
2322 struct String header_value;
2325 expand_header(&header_value,h);
2326 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2327 if (header_value.character == NULL || header_def == NULL)
2329 filter->errmsg=CUS "header string expansion failed";
2332 for (k=key; k->length!=-1; ++k)
2334 if (Ustrcmp(header_def,"true")==0)
2336 *cond=compare(filter,k,&header_value,comparator,matchType);
2337 if (*cond==-1) return -1;
2345 else if (parse_identifier(filter,CUS "not"))
2347 if (parse_white(filter)==-1) return -1;
2348 switch (parse_test(filter,cond,exec))
2351 case 0: filter->errmsg=CUS "missing test"; return -1;
2352 default: *cond=!*cond; return 1;
2355 else if (parse_identifier(filter,CUS "size"))
2358 relop = ":over" / ":under"
2359 size-test = "size" relop <limit: number>
2362 unsigned long limit;
2365 if (parse_white(filter)==-1) return -1;
2366 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2367 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2370 filter->errmsg=CUS "missing :over or :under";
2373 if (parse_white(filter)==-1) return -1;
2374 if (parse_number(filter,&limit)==-1) return -1;
2375 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2378 else if (parse_identifier(filter,CUS "true"))
2383 else if (parse_identifier(filter,CUS "envelope"))
2386 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2387 <envelope-part: string-list> <key-list: string-list>
2389 envelope-part is case insensitive "from" or "to"
2390 #ifdef ENVELOPE_AUTH
2391 envelope-part =/ "auth"
2395 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2396 enum AddressPart addressPart=ADDRPART_ALL;
2397 enum MatchType matchType=MATCH_IS;
2398 struct String *env,*e,*key,*k;
2402 if (!filter->require_envelope)
2404 filter->errmsg=CUS "missing previous require \"envelope\";";
2409 if (parse_white(filter)==-1) return -1;
2410 if ((m=parse_comparator(filter,&comparator))!=0)
2412 if (m==-1) return -1;
2415 filter->errmsg=CUS "comparator already specified";
2420 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2422 if (m==-1) return -1;
2425 filter->errmsg=CUS "address part already specified";
2430 else if ((m=parse_matchtype(filter,&matchType))!=0)
2432 if (m==-1) return -1;
2435 filter->errmsg=CUS "match type already specified";
2442 if (parse_white(filter)==-1) return -1;
2443 if ((m=parse_stringlist(filter,&env))!=1)
2445 if (m==0) filter->errmsg=CUS "envelope string list expected";
2448 if (parse_white(filter)==-1) return -1;
2449 if ((m=parse_stringlist(filter,&key))!=1)
2451 if (m==0) filter->errmsg=CUS "key string list expected";
2455 for (e=env; e->length!=-1 && !*cond; ++e)
2457 const uschar *envelopeExpr=CUS 0;
2458 uschar *envelope=US 0;
2460 if (eq_asciicase(e,&str_from,0))
2462 switch (addressPart)
2464 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2468 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2469 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2471 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2475 else if (eq_asciicase(e,&str_to,0))
2477 switch (addressPart)
2479 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2481 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2482 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2484 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2485 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2488 #ifdef ENVELOPE_AUTH
2489 else if (eq_asciicase(e,&str_auth,0))
2491 switch (addressPart)
2493 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2497 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2498 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2500 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2507 filter->errmsg=CUS "invalid envelope string";
2510 if (exec && envelopeExpr)
2512 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2514 filter->errmsg=CUS "header string expansion failed";
2517 for (k=key; k->length!=-1; ++k)
2519 struct String envelopeStr;
2521 envelopeStr.character=envelope;
2522 envelopeStr.length=Ustrlen(envelope);
2523 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2524 if (*cond==-1) return -1;
2532 else if (parse_identifier(filter,CUS "valid_notify_method"))
2535 valid_notify_method = "valid_notify_method"
2536 <notification-uris: string-list>
2539 struct String *uris,*u;
2542 if (!filter->require_enotify)
2544 filter->errmsg=CUS "missing previous require \"enotify\";";
2547 if (parse_white(filter)==-1) return -1;
2548 if ((m=parse_stringlist(filter,&uris))!=1)
2550 if (m==0) filter->errmsg=CUS "URI string list expected";
2556 for (u=uris; u->length!=-1 && *cond; ++u)
2558 string_item *recipient;
2559 struct String header,subject,body;
2563 header.character=(uschar*)0;
2565 subject.character=(uschar*)0;
2567 body.character=(uschar*)0;
2568 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2574 else if (parse_identifier(filter,CUS "notify_method_capability"))
2577 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2578 <notification-uri: string>
2579 <notification-capability: string>
2580 <key-list: string-list>
2586 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2587 enum MatchType matchType=MATCH_IS;
2588 struct String uri,capa,*keys,*k;
2590 if (!filter->require_enotify)
2592 filter->errmsg=CUS "missing previous require \"enotify\";";
2597 if (parse_white(filter)==-1) return -1;
2598 if ((m=parse_comparator(filter,&comparator))!=0)
2600 if (m==-1) return -1;
2603 filter->errmsg=CUS "comparator already specified";
2608 else if ((m=parse_matchtype(filter,&matchType))!=0)
2610 if (m==-1) return -1;
2613 filter->errmsg=CUS "match type already specified";
2620 if ((m=parse_string(filter,&uri))!=1)
2622 if (m==0) filter->errmsg=CUS "missing notification URI string";
2625 if (parse_white(filter)==-1) return -1;
2626 if ((m=parse_string(filter,&capa))!=1)
2628 if (m==0) filter->errmsg=CUS "missing notification capability string";
2631 if (parse_white(filter)==-1) return -1;
2632 if ((m=parse_stringlist(filter,&keys))!=1)
2634 if (m==0) filter->errmsg=CUS "missing key string list";
2639 string_item *recipient;
2640 struct String header,subject,body;
2645 header.character=(uschar*)0;
2647 subject.character=(uschar*)0;
2649 body.character=(uschar*)0;
2650 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2652 if (eq_asciicase(&capa,&str_online,0)==1)
2653 for (k=keys; k->length!=-1; ++k)
2655 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2656 if (*cond==-1) return -1;
2668 /*************************************************
2669 * Parse and interpret an optional block *
2670 *************************************************/
2674 filter points to the Sieve filter including its state
2675 exec Execute parsed statements
2676 generated where to hang newly-generated addresses
2678 Returns: 2 success by stop
2680 0 no block command found
2681 -1 syntax or execution error
2684 static int parse_block(struct Sieve *filter, int exec,
2685 address_item **generated)
2689 if (parse_white(filter)==-1) return -1;
2690 if (*filter->pc=='{')
2693 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2694 if (*filter->pc=='}')
2701 filter->errmsg=CUS "expecting command or closing brace";
2709 /*************************************************
2710 * Match a semicolon *
2711 *************************************************/
2715 filter points to the Sieve filter including its state
2721 static int parse_semicolon(struct Sieve *filter)
2723 if (parse_white(filter)==-1) return -1;
2724 if (*filter->pc==';')
2731 filter->errmsg=CUS "missing semicolon";
2737 /*************************************************
2738 * Parse and interpret a Sieve command *
2739 *************************************************/
2743 filter points to the Sieve filter including its state
2744 exec Execute parsed statements
2745 generated where to hang newly-generated addresses
2747 Returns: 2 success by stop
2749 -1 syntax or execution error
2752 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2756 if (parse_white(filter)==-1) return -1;
2757 if (parse_identifier(filter,CUS "if"))
2760 if-command = "if" test block *( "elsif" test block ) [ else block ]
2763 int cond,m,unsuccessful;
2766 if (parse_white(filter)==-1) return -1;
2767 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2770 filter->errmsg=CUS "missing test";
2773 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2774 (debug_selector & D_filter) != 0)
2776 if (exec) debug_printf("if %s\n",cond?"true":"false");
2778 m=parse_block(filter,exec ? cond : 0, generated);
2779 if (m==-1 || m==2) return m;
2782 filter->errmsg=CUS "missing block";
2785 unsuccessful = !cond;
2786 for (;;) /* elsif test block */
2788 if (parse_white(filter)==-1) return -1;
2789 if (parse_identifier(filter,CUS "elsif"))
2791 if (parse_white(filter)==-1) return -1;
2792 m=parse_test(filter,&cond,exec && unsuccessful);
2793 if (m==-1 || m==2) return m;
2796 filter->errmsg=CUS "missing test";
2799 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2800 (debug_selector & D_filter) != 0)
2802 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2804 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2805 if (m==-1 || m==2) return m;
2808 filter->errmsg=CUS "missing block";
2811 if (exec && unsuccessful && cond) unsuccessful = 0;
2816 if (parse_white(filter)==-1) return -1;
2817 if (parse_identifier(filter,CUS "else"))
2819 m=parse_block(filter,exec && unsuccessful, generated);
2820 if (m==-1 || m==2) return m;
2823 filter->errmsg=CUS "missing block";
2828 else if (parse_identifier(filter,CUS "stop"))
2831 stop-command = "stop" { stop-options } ";"
2835 if (parse_semicolon(filter)==-1) return -1;
2838 filter->pc+=Ustrlen(filter->pc);
2842 else if (parse_identifier(filter,CUS "keep"))
2845 keep-command = "keep" { keep-options } ";"
2849 if (parse_semicolon(filter)==-1) return -1;
2852 add_addr(generated,US"inbox",1,0,0,0);
2856 else if (parse_identifier(filter,CUS "discard"))
2859 discard-command = "discard" { discard-options } ";"
2863 if (parse_semicolon(filter)==-1) return -1;
2864 if (exec) filter->keep=0;
2866 else if (parse_identifier(filter,CUS "redirect"))
2869 redirect-command = "redirect" redirect-options "string" ";"
2871 redirect-options =) ":copy"
2874 struct String recipient;
2880 if (parse_white(filter)==-1) return -1;
2881 if (parse_identifier(filter,CUS ":copy")==1)
2883 if (!filter->require_copy)
2885 filter->errmsg=CUS "missing previous require \"copy\";";
2892 if (parse_white(filter)==-1) return -1;
2893 if ((m=parse_string(filter,&recipient))!=1)
2895 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2898 if (strchr(CCS recipient.character,'@')==(char*)0)
2900 filter->errmsg=CUS "unqualified recipient address";
2905 add_addr(generated,recipient.character,0,0,0,0);
2906 if (!copy) filter->keep = 0;
2908 if (parse_semicolon(filter)==-1) return -1;
2910 else if (parse_identifier(filter,CUS "fileinto"))
2913 fileinto-command = "fileinto" { fileinto-options } string ";"
2915 fileinto-options =) [ ":copy" ]
2918 struct String folder;
2921 unsigned long maxage, maxmessages, maxstorage;
2924 maxage = maxmessages = maxstorage = 0;
2925 if (!filter->require_fileinto)
2927 filter->errmsg=CUS "missing previous require \"fileinto\";";
2932 if (parse_white(filter)==-1) return -1;
2933 if (parse_identifier(filter,CUS ":copy")==1)
2935 if (!filter->require_copy)
2937 filter->errmsg=CUS "missing previous require \"copy\";";
2944 if (parse_white(filter)==-1) return -1;
2945 if ((m=parse_string(filter,&folder))!=1)
2947 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2950 m=0; s=folder.character;
2951 if (folder.length==0) m=1;
2952 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2955 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2960 filter->errmsg=CUS "invalid folder";
2965 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2966 if (!copy) filter->keep = 0;
2968 if (parse_semicolon(filter)==-1) return -1;
2971 else if (parse_identifier(filter,CUS "notify"))
2974 notify-command = "notify" { notify-options } <method: string> ";"
2975 notify-options = [":from" string]
2976 [":importance" <"1" / "2" / "3">]
2977 [":options" 1*(string-list / number)]
2983 struct String importance;
2984 struct String message;
2985 struct String method;
2986 struct Notification *already;
2987 string_item *recipient;
2988 struct String header;
2989 struct String subject;
2991 uschar *envelope_from;
2992 struct String auto_submitted_value;
2993 uschar *auto_submitted_def;
2995 if (!filter->require_enotify)
2997 filter->errmsg=CUS "missing previous require \"enotify\";";
3000 from.character=(uschar*)0;
3002 importance.character=(uschar*)0;
3003 importance.length=-1;
3004 message.character=(uschar*)0;
3008 header.character=(uschar*)0;
3010 subject.character=(uschar*)0;
3012 body.character=(uschar*)0;
3013 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3016 if (parse_white(filter)==-1) return -1;
3017 if (parse_identifier(filter,CUS ":from")==1)
3019 if (parse_white(filter)==-1) return -1;
3020 if ((m=parse_string(filter,&from))!=1)
3022 if (m==0) filter->errmsg=CUS "from string expected";
3026 else if (parse_identifier(filter,CUS ":importance")==1)
3028 if (parse_white(filter)==-1) return -1;
3029 if ((m=parse_string(filter,&importance))!=1)
3031 if (m==0) filter->errmsg=CUS "importance string expected";
3034 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3036 filter->errmsg=CUS "invalid importance";
3040 else if (parse_identifier(filter,CUS ":options")==1)
3042 if (parse_white(filter)==-1) return -1;
3044 else if (parse_identifier(filter,CUS ":message")==1)
3046 if (parse_white(filter)==-1) return -1;
3047 if ((m=parse_string(filter,&message))!=1)
3049 if (m==0) filter->errmsg=CUS "message string expected";
3055 if (parse_white(filter)==-1) return -1;
3056 if ((m=parse_string(filter,&method))!=1)
3058 if (m==0) filter->errmsg=CUS "missing method string";
3061 if (parse_semicolon(filter)==-1) return -1;
3062 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3066 if (message.length==-1) message=subject;
3067 if (message.length==-1) expand_header(&message,&str_subject);
3068 expand_header(&auto_submitted_value,&str_auto_submitted);
3069 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3070 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3072 filter->errmsg=CUS "header string expansion failed";
3075 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3077 for (already=filter->notified; already; already=already->next)
3079 if (already->method.length==method.length
3080 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3081 && already->importance.length==importance.length
3082 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3083 && already->message.length==message.length
3084 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3087 if (already==(struct Notification*)0)
3088 /* New notification, process it */
3090 struct Notification *sent;
3091 sent=store_get(sizeof(struct Notification));
3092 sent->method=method;
3093 sent->importance=importance;
3094 sent->message=message;
3095 sent->next=filter->notified;
3096 filter->notified=sent;
3097 #ifndef COMPILE_SYNTAX_CHECKER
3098 if (filter_test == FTEST_NONE)
3103 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3107 int buffer_capacity;
3109 f = fdopen(fd, "wb");
3110 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3111 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3112 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3113 if (header.length>0) fprintf(f,"%s",header.character);
3114 if (message.length==-1)
3116 message.character=US"Notification";
3117 message.length=Ustrlen(message.character);
3119 /* Allocation is larger than necessary, but enough even for split MIME words */
3120 buffer_capacity=32+4*message.length;
3121 buffer=store_get(buffer_capacity);
3122 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3124 if (body.length>0) fprintf(f,"%s\n",body.character);
3127 (void)child_close(pid, 0);
3130 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3132 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3138 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3140 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3146 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3148 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3155 else if (parse_identifier(filter,CUS "vacation"))
3158 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3159 vacation-options = [":days" number]
3162 [":addresses" string-list]
3169 struct String subject;
3171 struct String *addresses;
3173 string_item *aliases;
3174 struct String handle;
3175 struct String reason;
3177 if (!filter->require_vacation)
3179 filter->errmsg=CUS "missing previous require \"vacation\";";
3184 if (filter->vacation_ran)
3186 filter->errmsg=CUS "trying to execute vacation more than once";
3189 filter->vacation_ran=1;
3191 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3192 subject.character=(uschar*)0;
3194 from.character=(uschar*)0;
3196 addresses=(struct String*)0;
3199 handle.character=(uschar*)0;
3203 if (parse_white(filter)==-1) return -1;
3204 if (parse_identifier(filter,CUS ":days")==1)
3206 if (parse_white(filter)==-1) return -1;
3207 if (parse_number(filter,&days)==-1) return -1;
3208 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3209 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3211 else if (parse_identifier(filter,CUS ":subject")==1)
3213 if (parse_white(filter)==-1) return -1;
3214 if ((m=parse_string(filter,&subject))!=1)
3216 if (m==0) filter->errmsg=CUS "subject string expected";
3220 else if (parse_identifier(filter,CUS ":from")==1)
3222 if (parse_white(filter)==-1) return -1;
3223 if ((m=parse_string(filter,&from))!=1)
3225 if (m==0) filter->errmsg=CUS "from string expected";
3228 if (check_mail_address(filter,&from)!=1)
3231 else if (parse_identifier(filter,CUS ":addresses")==1)
3235 if (parse_white(filter)==-1) return -1;
3236 if ((m=parse_stringlist(filter,&addresses))!=1)
3238 if (m==0) filter->errmsg=CUS "addresses string list expected";
3241 for (a=addresses; a->length!=-1; ++a)
3245 new=store_get(sizeof(string_item));
3246 new->text=store_get(a->length+1);
3247 if (a->length) memcpy(new->text,a->character,a->length);
3248 new->text[a->length]='\0';
3253 else if (parse_identifier(filter,CUS ":mime")==1)
3255 else if (parse_identifier(filter,CUS ":handle")==1)
3257 if (parse_white(filter)==-1) return -1;
3258 if ((m=parse_string(filter,&from))!=1)
3260 if (m==0) filter->errmsg=CUS "handle string expected";
3266 if (parse_white(filter)==-1) return -1;
3267 if ((m=parse_string(filter,&reason))!=1)
3269 if (m==0) filter->errmsg=CUS "missing reason string";
3276 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3279 filter->errmsg=CUS "MIME reason string contains 8bit text";
3283 if (parse_semicolon(filter)==-1) return -1;
3289 int buffer_capacity;
3292 uschar hexdigest[33];
3296 if (filter_personal(aliases,TRUE))
3298 if (filter_test == FTEST_NONE)
3300 /* ensure oncelog directory exists; failure will be detected later */
3302 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3304 /* build oncelog filename */
3308 if (handle.length==-1)
3310 gstring * key = NULL;
3311 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3312 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3313 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3314 key = string_catn(key, reason.character, reason.length);
3315 md5_end(&base, key->s, key->ptr, digest);
3318 md5_end(&base, handle.character, handle.length, digest);
3320 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3322 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3323 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3325 if (filter_test == FTEST_NONE)
3327 once = string_cat (NULL, filter->vacation_directory);
3328 once = string_catn(once, US"/", 1);
3329 once = string_catn(once, hexdigest, 33);
3331 /* process subject */
3333 if (subject.length==-1)
3335 uschar *subject_def;
3337 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3338 if (Ustrcmp(subject_def,"true")==0)
3340 gstring * g = string_catn(NULL, US"Auto: ", 6);
3342 expand_header(&subject,&str_subject);
3343 g = string_catn(g, subject.character, subject.length);
3344 subject.character = string_from_gstring(g);
3345 subject.length = g->ptr;
3349 subject.character=US"Automated reply";
3350 subject.length=Ustrlen(subject.character);
3354 /* add address to list of generated addresses */
3356 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3357 setflag(addr, af_pfr);
3358 addr->prop.ignore_error = TRUE;
3359 addr->next = *generated;
3361 addr->reply = store_get(sizeof(reply_item));
3362 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3363 addr->reply->to = string_copy(sender_address);
3364 if (from.length==-1)
3365 addr->reply->from = expand_string(US"$local_part@$domain");
3367 addr->reply->from = from.character;
3368 /* Allocation is larger than necessary, but enough even for split MIME words */
3369 buffer_capacity=32+4*subject.length;
3370 buffer=store_get(buffer_capacity);
3371 /* deconst cast safe as we pass in a non-const item */
3372 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3373 addr->reply->oncelog = string_from_gstring(once);
3374 addr->reply->once_repeat=days*86400;
3376 /* build body and MIME headers */
3380 uschar *mime_body,*reason_end;
3381 static const uschar nlnl[]="\r\n\r\n";
3385 mime_body = reason.character, reason_end = reason.character + reason.length;
3386 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
3390 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3392 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3393 else mime_body=reason_end-1;
3394 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3398 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3400 addr->reply->headers = US"MIME-Version: 1.0\n"
3401 "Content-Type: text/plain;\n"
3402 "\tcharset=\"utf-8\"\n"
3403 "Content-Transfer-Encoding: quoted-printable";
3404 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3408 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3409 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3419 /*************************************************
3420 * Parse and interpret a sieve filter *
3421 *************************************************/
3425 filter points to the Sieve filter including its state
3426 exec Execute parsed statements
3427 generated where to hang newly-generated addresses
3430 -1 syntax or execution error
3434 parse_start(struct Sieve *filter, int exec, address_item **generated)
3436 filter->pc=filter->filter;
3439 filter->require_envelope=0;
3440 filter->require_fileinto=0;
3441 #ifdef ENCODED_CHARACTER
3442 filter->require_encoded_character=0;
3444 #ifdef ENVELOPE_AUTH
3445 filter->require_envelope_auth=0;
3448 filter->require_enotify=0;
3449 filter->notified=(struct Notification*)0;
3452 filter->require_subaddress=0;
3455 filter->require_vacation=0;
3456 filter->vacation_ran=0;
3458 filter->require_copy=0;
3459 filter->require_iascii_numeric=0;
3461 if (parse_white(filter)==-1) return -1;
3463 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3466 struct dirent *oncelog;
3467 struct stat properties;
3470 /* clean up old vacation log databases */
3472 oncelogdir=opendir(CS filter->vacation_directory);
3474 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3476 filter->errmsg=CUS "unable to open vacation directory";
3480 if (oncelogdir != NULL)
3484 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3486 if (strlen(oncelog->d_name)==32)
3488 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3489 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3493 closedir(oncelogdir);
3497 while (parse_identifier(filter,CUS "require"))
3500 require-command = "require" <capabilities: string-list>
3503 struct String *cap,*check;
3506 if (parse_white(filter)==-1) return -1;
3507 if ((m=parse_stringlist(filter,&cap))!=1)
3509 if (m==0) filter->errmsg=CUS "capability string list expected";
3512 for (check=cap; check->character; ++check)
3514 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3515 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3516 #ifdef ENCODED_CHARACTER
3517 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3519 #ifdef ENVELOPE_AUTH
3520 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3523 else if (eq_octet(check,&str_enotify,0))
3525 if (filter->enotify_mailto_owner == NULL)
3527 filter->errmsg=CUS "enotify disabled";
3530 filter->require_enotify=1;
3534 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3537 else if (eq_octet(check,&str_vacation,0))
3539 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3541 filter->errmsg=CUS "vacation disabled";
3544 filter->require_vacation=1;
3547 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3548 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3549 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3550 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3551 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3554 filter->errmsg=CUS "unknown capability";
3558 if (parse_semicolon(filter)==-1) return -1;
3560 if (parse_commands(filter,exec,generated)==-1) return -1;
3563 filter->errmsg=CUS "syntax error";
3570 /*************************************************
3571 * Interpret a sieve filter file *
3572 *************************************************/
3576 filter points to the entire file, read into store as a single string
3577 options controls whether various special things are allowed, and requests
3578 special actions (not currently used)
3579 vacation_directory where to store vacation "once" files
3580 enotify_mailto_owner owner of mailto notifications
3581 useraddress string expression for :user part of address
3582 subaddress string expression for :subaddress part of address
3583 generated where to hang newly-generated addresses
3584 error where to pass back an error text
3586 Returns: FF_DELIVERED success, a significant action was taken
3587 FF_NOTDELIVERED success, no significant action
3588 FF_DEFER defer requested
3589 FF_FAIL fail requested
3590 FF_FREEZE freeze requested
3591 FF_ERROR there was a problem
3595 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3596 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3597 address_item **generated, uschar **error)
3603 options = options; /* Keep picky compilers happy */
3606 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3607 sieve.filter=filter;
3609 if (vacation_directory == NULL)
3610 sieve.vacation_directory = NULL;
3613 sieve.vacation_directory=expand_string(vacation_directory);
3614 if (sieve.vacation_directory == NULL)
3616 *error = string_sprintf("failed to expand \"%s\" "
3617 "(sieve_vacation_directory): %s", vacation_directory,
3618 expand_string_message);
3623 if (enotify_mailto_owner == NULL)
3624 sieve.enotify_mailto_owner = NULL;
3627 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3628 if (sieve.enotify_mailto_owner == NULL)
3630 *error = string_sprintf("failed to expand \"%s\" "
3631 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3632 expand_string_message);
3637 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3638 sieve.subaddress = subaddress;
3640 #ifdef COMPILE_SYNTAX_CHECKER
3641 if (parse_start(&sieve,0,generated)==1)
3643 if (parse_start(&sieve,1,generated)==1)
3648 add_addr(generated,US"inbox",1,0,0,0);
3649 msg = string_sprintf("Implicit keep");
3654 msg = string_sprintf("No implicit keep");
3660 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3661 #ifdef COMPILE_SYNTAX_CHECKER
3665 add_addr(generated,US"inbox",1,0,0,0);
3670 #ifndef COMPILE_SYNTAX_CHECKER
3671 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3672 else debug_printf("%s\n", msg);
3675 DEBUG(D_route) debug_printf("Sieve: end of processing\n");