1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2016
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);
300 *new='\0'; /* not included in length, but nice */
305 /*************************************************
306 * Check mail address for correct syntax *
307 *************************************************/
310 Check mail address for being syntactically correct.
313 filter points to the Sieve filter including its state
314 address String containing one address
317 1 Mail address is syntactically OK
321 int check_mail_address(struct Sieve *filter, const struct String *address)
323 int start, end, domain;
326 if (address->length>0)
328 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
332 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
333 address->character, error);
341 filter->errmsg=CUS "empty address";
347 /*************************************************
348 * Decode URI encoded string *
349 *************************************************/
353 str URI encoded string
356 0 Decoding successful
361 static int uri_decode(struct String *str)
365 if (str->length==0) return 0;
366 for (s=str->character,t=s,e=s+str->length; s<e; )
370 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
372 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
373 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
382 str->length=t-str->character;
387 /*************************************************
389 *************************************************/
394 mailtoURI = "mailto:" [ to ] [ headers ]
395 to = [ addr-spec *("%2C" addr-spec ) ]
396 headers = "?" header *( "&" header )
397 header = hname "=" hvalue
402 filter points to the Sieve filter including its state
403 uri URI, excluding scheme
408 1 URI is syntactically OK
413 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
416 struct String to, hname;
417 struct String hvalue = {NULL, 0};
421 if (Ustrncmp(uri,"mailto:",7))
423 filter->errmsg=US "Unknown URI scheme";
428 if (*uri && *uri!='?')
432 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
438 to.character=string_catn(to.character, &capacity, &to.length, start, uri-start);
439 to.character[to.length]='\0';
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);
470 hname.character= NULL;
472 hname.character = string_catn(hname.character, &capacity, &hname.length, start, uri-start);
473 hname.character[hname.length]='\0';
474 if (uri_decode(&hname)==-1)
476 filter->errmsg=US"Invalid URI encoding";
485 filter->errmsg=US"Missing equal after hname";
489 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
493 hvalue.character= NULL;
495 hvalue.character=string_catn(hvalue.character,&capacity,&hvalue.length,start,uri-start);
496 hvalue.character[hvalue.length]='\0';
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);
532 if (header->length==-1) header->length=0;
533 capacity=header->length;
534 header->character=string_catn(header->character,&capacity,&header->length,hname.character,hname.length);
535 header->character=string_catn(header->character,&capacity,&header->length,CUS ": ",2);
536 header->character=string_catn(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
537 header->character=string_catn(header->character,&capacity,&header->length,CUS "\n",1);
538 header->character[header->length]='\0';
541 if (*uri=='&') ++uri;
547 filter->errmsg=US"Syntactically invalid URI";
555 /*************************************************
556 * Octet-wise string comparison *
557 *************************************************/
561 needle UTF-8 string to search ...
562 haystack ... inside the haystack
563 match_prefix 1 to compare if needle is a prefix of haystack
565 Returns: 0 needle not found in haystack
569 static int eq_octet(const struct String *needle,
570 const struct String *haystack, int match_prefix)
578 h=haystack->character;
582 if (*n&0x80) return 0;
583 if (*h&0x80) return 0;
585 if (*n!=*h) return 0;
591 return (match_prefix ? nl==0 : nl==0 && hl==0);
595 /*************************************************
596 * ASCII case-insensitive string comparison *
597 *************************************************/
601 needle UTF-8 string to search ...
602 haystack ... inside the haystack
603 match_prefix 1 to compare if needle is a prefix of haystack
605 Returns: 0 needle not found in haystack
609 static int eq_asciicase(const struct String *needle,
610 const struct String *haystack, int match_prefix)
619 h=haystack->character;
625 if (nc&0x80) return 0;
626 if (hc&0x80) return 0;
628 /* tolower depends on the locale and only ASCII case must be insensitive */
629 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
635 return (match_prefix ? nl==0 : nl==0 && hl==0);
639 /*************************************************
640 * Glob pattern search *
641 *************************************************/
645 needle pattern to search ...
646 haystack ... inside the haystack
647 ascii_caseless ignore ASCII case
648 match_octet match octets, not UTF-8 multi-octet characters
650 Returns: 0 needle not found in haystack
655 static int eq_glob(const struct String *needle,
656 const struct String *haystack, int ascii_caseless, int match_octet)
658 const uschar *n,*h,*nend,*hend;
662 h=haystack->character;
663 nend=n+needle->length;
664 hend=h+haystack->length;
674 const uschar *npart,*hpart;
676 /* Try to match a non-star part of the needle at the current */
677 /* position in the haystack. */
681 while (npart<nend && *npart!='*') switch (*npart)
685 if (hpart==hend) return 0;
690 /* Match one UTF8 encoded character */
691 if ((*hpart&0xc0)==0xc0)
694 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
705 if (npart==nend) return -1;
710 if (hpart==hend) return 0;
711 /* tolower depends on the locale, but we need ASCII */
715 (*hpart&0x80) || (*npart&0x80) ||
718 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
723 /* string match after a star failed, advance and try again */
737 /* at this point, a part was matched successfully */
738 if (may_advance && npart==nend && hpart<hend)
739 /* needle ends, but haystack does not: if there was a star before, advance and try again */
749 return (h==hend ? 1 : may_advance);
753 /*************************************************
754 * ASCII numeric comparison *
755 *************************************************/
759 a first numeric string
760 b second numeric string
761 relop relational operator
763 Returns: 0 not (a relop b)
767 static int eq_asciinumeric(const struct String *a,
768 const struct String *b, enum RelOp relop)
771 const uschar *as,*aend,*bs,*bend;
775 aend=a->character+a->length;
777 bend=b->character+b->length;
779 while (*as>='0' && *as<='9' && as<aend) ++as;
781 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
784 if (al && bl==0) cmp=-1;
785 else if (al==0 && bl==0) cmp=0;
786 else if (al==0 && bl) cmp=1;
790 if (cmp==0) cmp=memcmp(a->character,b->character,al);
794 case LT: return cmp<0;
795 case LE: return cmp<=0;
796 case EQ: return cmp==0;
797 case GE: return cmp>=0;
798 case GT: return cmp>0;
799 case NE: return cmp!=0;
806 /*************************************************
808 *************************************************/
812 filter points to the Sieve filter including its state
813 needle UTF-8 pattern or string to search ...
814 haystack ... inside the haystack
818 Returns: 0 needle not found in haystack
820 -1 comparator does not offer matchtype
823 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
824 enum Comparator co, enum MatchType mt)
828 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
829 (debug_selector & D_filter) != 0)
831 debug_printf("String comparison (match ");
834 case MATCH_IS: debug_printf(":is"); break;
835 case MATCH_CONTAINS: debug_printf(":contains"); break;
836 case MATCH_MATCHES: debug_printf(":matches"); break;
838 debug_printf(", comparison \"");
841 case COMP_OCTET: debug_printf("i;octet"); break;
842 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
843 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
845 debug_printf("\"):\n");
846 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
847 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
857 if (eq_octet(needle,haystack,0)) r=1;
860 case COMP_EN_ASCII_CASEMAP:
862 if (eq_asciicase(needle,haystack,0)) r=1;
865 case COMP_ASCII_NUMERIC:
867 if (!filter->require_iascii_numeric)
869 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
872 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
886 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
889 case COMP_EN_ASCII_CASEMAP:
891 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
896 filter->errmsg=CUS "comparator does not offer specified matchtype";
908 if ((r=eq_glob(needle,haystack,0,1))==-1)
910 filter->errmsg=CUS "syntactically invalid pattern";
915 case COMP_EN_ASCII_CASEMAP:
917 if ((r=eq_glob(needle,haystack,1,1))==-1)
919 filter->errmsg=CUS "syntactically invalid pattern";
926 filter->errmsg=CUS "comparator does not offer specified matchtype";
933 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
934 (debug_selector & D_filter) != 0)
935 debug_printf(" Result %s\n",r?"true":"false");
940 /*************************************************
941 * Check header field syntax *
942 *************************************************/
945 RFC 2822, section 3.6.8 says:
949 ftext = %d33-57 / ; Any character except
950 %d59-126 ; controls, SP, and
953 That forbids 8-bit header fields. This implementation accepts them, since
954 all of Exim is 8-bit clean, so it adds %d128-%d255.
957 header header field to quote for suitable use in Exim expansions
959 Returns: 0 string is not a valid header field
960 1 string is a value header field
963 static int is_header(const struct String *header)
973 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
984 /*************************************************
985 * Quote special characters string *
986 *************************************************/
990 header header field to quote for suitable use in Exim expansions
993 Returns: quoted string
996 static const uschar *quote(const struct String *header)
1004 h=header->character;
1011 quoted=string_catn(quoted,&size,&ptr,CUS "\\0",2);
1018 quoted=string_catn(quoted,&size,&ptr,CUS "\\",1);
1022 quoted=string_catn(quoted,&size,&ptr,h,1);
1028 quoted=string_catn(quoted,&size,&ptr,CUS "",1);
1033 /*************************************************
1034 * Add address to list of generated addresses *
1035 *************************************************/
1038 According to RFC 5228, duplicate delivery to the same address must
1039 not happen, so the list is first searched for the address.
1042 generated list of generated addresses
1043 addr new address to add
1044 file address denotes a file
1049 static void 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)
1055 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1057 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1059 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)
1067 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1069 new_addr=deliver_make_addr(addr,TRUE);
1072 setflag(new_addr, af_pfr|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
1475 static int parse_string(struct Sieve *filter, struct String *data)
1480 data->character=(uschar*)0;
1481 if (*filter->pc=='"') /* quoted string */
1486 if (*filter->pc=='"') /* end of string */
1488 int foo=data->length;
1491 /* that way, there will be at least one character allocated */
1492 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1493 #ifdef ENCODED_CHARACTER
1494 if (filter->require_encoded_character
1495 && string_decode(filter,data)==-1)
1500 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1502 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1505 else /* regular character */
1508 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1510 if (*filter->pc=='\n')
1512 data->character=string_catn(data->character,&dataCapacity,&data->length,US"\r",1);
1516 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1520 filter->errmsg=CUS "missing end of string";
1523 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1526 /* skip optional white space followed by hashed comment or CRLF */
1527 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1528 if (*filter->pc=='#')
1530 if (parse_hashcomment(filter)==-1) return -1;
1533 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1535 else if (*filter->pc=='\n')
1547 filter->errmsg=CUS "syntax error";
1553 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1555 if (*filter->pc=='\n') /* end of line */
1558 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1566 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1568 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1571 int foo=data->length;
1573 /* that way, there will be at least one character allocated */
1574 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1581 #ifdef ENCODED_CHARACTER
1582 if (filter->require_encoded_character
1583 && string_decode(filter,data)==-1)
1588 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1590 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS ".",1);
1594 else /* regular character */
1596 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1600 filter->errmsg=CUS "missing end of multi line string";
1607 /*************************************************
1608 * Parse a specific identifier *
1609 *************************************************/
1613 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1616 filter points to the Sieve filter including its state
1617 id specifies identifier to match
1620 0 identifier not matched
1623 static int parse_identifier(struct Sieve *filter, const uschar *id)
1625 size_t idlen=Ustrlen(id);
1627 if (strncmpic(US filter->pc,US id,idlen)==0)
1629 uschar next=filter->pc[idlen];
1631 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1639 /*************************************************
1641 *************************************************/
1645 number = 1*DIGIT [QUANTIFIER]
1646 QUANTIFIER = "K" / "M" / "G"
1649 filter points to the Sieve filter including its state
1653 -1 no string list found
1656 static int parse_number(struct Sieve *filter, unsigned long *data)
1660 if (*filter->pc>='0' && *filter->pc<='9')
1665 d=Ustrtoul(filter->pc,&e,10);
1668 filter->errmsg=CUstrerror(ERANGE);
1673 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1674 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1675 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1676 if (d>(ULONG_MAX/u))
1678 filter->errmsg=CUstrerror(ERANGE);
1687 filter->errmsg=CUS "missing number";
1693 /*************************************************
1694 * Parse a string list *
1695 *************************************************/
1699 string-list = "[" string *("," string) "]" / string
1702 filter points to the Sieve filter including its state
1703 data returns string list
1706 -1 no string list found
1710 parse_stringlist(struct Sieve *filter, struct String **data)
1712 const uschar *orig=filter->pc;
1713 int dataCapacity = 0;
1715 struct String *d = NULL;
1718 if (*filter->pc=='[') /* string list */
1723 if (parse_white(filter)==-1) goto error;
1724 if (dataLength+1 >= dataCapacity) /* increase buffer */
1727 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1729 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1730 new = store_get(sizeof(struct String) * dataCapacity);
1732 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1736 m=parse_string(filter,&d[dataLength]);
1739 if (dataLength==0) break;
1742 filter->errmsg=CUS "missing string";
1746 else if (m==-1) goto error;
1748 if (parse_white(filter)==-1) goto error;
1749 if (*filter->pc==',') ++filter->pc;
1752 if (*filter->pc==']')
1754 d[dataLength].character=(uschar*)0;
1755 d[dataLength].length=-1;
1762 filter->errmsg=CUS "missing closing bracket";
1766 else /* single string */
1768 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1772 m=parse_string(filter,&d[0]);
1784 d[1].character=(uschar*)0;
1791 filter->errmsg=CUS "missing string list";
1796 /*************************************************
1797 * Parse an optional address part specifier *
1798 *************************************************/
1802 address-part = ":localpart" / ":domain" / ":all"
1803 address-part =/ ":user" / ":detail"
1806 filter points to the Sieve filter including its state
1807 a returns address part specified
1810 0 no comparator found
1814 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1817 if (parse_identifier(filter,CUS ":user")==1)
1819 if (!filter->require_subaddress)
1821 filter->errmsg=CUS "missing previous require \"subaddress\";";
1827 else if (parse_identifier(filter,CUS ":detail")==1)
1829 if (!filter->require_subaddress)
1831 filter->errmsg=CUS "missing previous require \"subaddress\";";
1839 if (parse_identifier(filter,CUS ":localpart")==1)
1841 *a=ADDRPART_LOCALPART;
1844 else if (parse_identifier(filter,CUS ":domain")==1)
1849 else if (parse_identifier(filter,CUS ":all")==1)
1858 /*************************************************
1859 * Parse an optional comparator *
1860 *************************************************/
1864 comparator = ":comparator" <comparator-name: string>
1867 filter points to the Sieve filter including its state
1868 c returns comparator
1871 0 no comparator found
1872 -1 incomplete comparator found
1875 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1877 struct String comparator_name;
1879 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1880 if (parse_white(filter)==-1) return -1;
1881 switch (parse_string(filter,&comparator_name))
1886 filter->errmsg=CUS "missing comparator";
1893 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1898 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1900 *c=COMP_EN_ASCII_CASEMAP;
1903 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1905 *c=COMP_EN_ASCII_CASEMAP;
1908 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1910 *c=COMP_ASCII_NUMERIC;
1915 filter->errmsg=CUS "invalid comparator";
1924 /*************************************************
1925 * Parse an optional match type *
1926 *************************************************/
1930 match-type = ":is" / ":contains" / ":matches"
1933 filter points to the Sieve filter including its state
1934 m returns match type
1937 0 no match type found
1940 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1942 if (parse_identifier(filter,CUS ":is")==1)
1947 else if (parse_identifier(filter,CUS ":contains")==1)
1952 else if (parse_identifier(filter,CUS ":matches")==1)
1961 /*************************************************
1962 * Parse and interpret an optional test list *
1963 *************************************************/
1967 test-list = "(" test *("," test) ")"
1970 filter points to the Sieve filter including its state
1971 n total number of tests
1972 num_true number of passed tests
1973 exec Execute parsed statements
1976 0 no test list found
1977 -1 syntax or execution error
1980 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1982 if (parse_white(filter)==-1) return -1;
1983 if (*filter->pc=='(')
1992 switch (parse_test(filter,&cond,exec))
1995 case 0: filter->errmsg=CUS "missing test"; return -1;
1996 default: ++*n; if (cond) ++*num_true; break;
1998 if (parse_white(filter)==-1) return -1;
1999 if (*filter->pc==',') ++filter->pc;
2002 if (*filter->pc==')')
2009 filter->errmsg=CUS "missing closing paren";
2017 /*************************************************
2018 * Parse and interpret an optional test *
2019 *************************************************/
2023 filter points to the Sieve filter including its state
2024 cond returned condition status
2025 exec Execute parsed statements
2029 -1 syntax or execution error
2032 static int parse_test(struct Sieve *filter, int *cond, int exec)
2034 if (parse_white(filter)==-1) return -1;
2035 if (parse_identifier(filter,CUS "address"))
2038 address-test = "address" { [address-part] [comparator] [match-type] }
2039 <header-list: string-list> <key-list: string-list>
2041 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2044 enum AddressPart addressPart=ADDRPART_ALL;
2045 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2046 enum MatchType matchType=MATCH_IS;
2047 struct String *hdr,*h,*key,*k;
2053 if (parse_white(filter)==-1) return -1;
2054 if ((m=parse_addresspart(filter,&addressPart))!=0)
2056 if (m==-1) return -1;
2059 filter->errmsg=CUS "address part already specified";
2064 else if ((m=parse_comparator(filter,&comparator))!=0)
2066 if (m==-1) return -1;
2069 filter->errmsg=CUS "comparator already specified";
2074 else if ((m=parse_matchtype(filter,&matchType))!=0)
2076 if (m==-1) return -1;
2079 filter->errmsg=CUS "match type already specified";
2086 if (parse_white(filter)==-1) return -1;
2087 if ((m=parse_stringlist(filter,&hdr))!=1)
2089 if (m==0) filter->errmsg=CUS "header string list expected";
2092 if (parse_white(filter)==-1) return -1;
2093 if ((m=parse_stringlist(filter,&key))!=1)
2095 if (m==0) filter->errmsg=CUS "key string list expected";
2099 for (h=hdr; h->length!=-1 && !*cond; ++h)
2101 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2105 !eq_asciicase(h,&str_from,0)
2106 && !eq_asciicase(h,&str_to,0)
2107 && !eq_asciicase(h,&str_cc,0)
2108 && !eq_asciicase(h,&str_bcc,0)
2109 && !eq_asciicase(h,&str_sender,0)
2110 && !eq_asciicase(h,&str_resent_from,0)
2111 && !eq_asciicase(h,&str_resent_to,0)
2114 filter->errmsg=CUS "invalid header field";
2119 /* We are only interested in addresses below, so no MIME decoding */
2120 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2121 if (header_value == NULL)
2123 filter->errmsg=CUS "header string expansion failed";
2126 parse_allow_group = TRUE;
2127 while (*header_value && !*cond)
2130 int start, end, domain;
2134 end_addr = parse_find_address_end(header_value, FALSE);
2135 saveend = *end_addr;
2137 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2139 if (extracted_addr) switch (addressPart)
2141 case ADDRPART_ALL: part=extracted_addr; break;
2145 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2146 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2148 case ADDRPART_DETAIL: part=NULL; break;
2152 *end_addr = saveend;
2155 for (k=key; k->length!=-1; ++k)
2157 struct String partStr;
2159 partStr.character=part;
2160 partStr.length=Ustrlen(part);
2163 *cond=compare(filter,k,&partStr,comparator,matchType);
2164 if (*cond==-1) return -1;
2169 if (saveend == 0) break;
2170 header_value = end_addr + 1;
2172 parse_allow_group = FALSE;
2173 parse_found_group = FALSE;
2178 else if (parse_identifier(filter,CUS "allof"))
2181 allof-test = "allof" <tests: test-list>
2186 switch (parse_testlist(filter,&n,&num_true,exec))
2189 case 0: filter->errmsg=CUS "missing test list"; return -1;
2190 default: *cond=(n==num_true); return 1;
2193 else if (parse_identifier(filter,CUS "anyof"))
2196 anyof-test = "anyof" <tests: test-list>
2201 switch (parse_testlist(filter,&n,&num_true,exec))
2204 case 0: filter->errmsg=CUS "missing test list"; return -1;
2205 default: *cond=(num_true>0); return 1;
2208 else if (parse_identifier(filter,CUS "exists"))
2211 exists-test = "exists" <header-names: string-list>
2214 struct String *hdr,*h;
2217 if (parse_white(filter)==-1) return -1;
2218 if ((m=parse_stringlist(filter,&hdr))!=1)
2220 if (m==0) filter->errmsg=CUS "header string list expected";
2226 for (h=hdr; h->length!=-1 && *cond; ++h)
2230 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2231 if (header_def == NULL)
2233 filter->errmsg=CUS "header string expansion failed";
2236 if (Ustrcmp(header_def,"false")==0) *cond=0;
2241 else if (parse_identifier(filter,CUS "false"))
2244 false-test = "false"
2250 else if (parse_identifier(filter,CUS "header"))
2253 header-test = "header" { [comparator] [match-type] }
2254 <header-names: string-list> <key-list: string-list>
2257 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2258 enum MatchType matchType=MATCH_IS;
2259 struct String *hdr,*h,*key,*k;
2265 if (parse_white(filter)==-1) return -1;
2266 if ((m=parse_comparator(filter,&comparator))!=0)
2268 if (m==-1) return -1;
2271 filter->errmsg=CUS "comparator already specified";
2276 else if ((m=parse_matchtype(filter,&matchType))!=0)
2278 if (m==-1) return -1;
2281 filter->errmsg=CUS "match type already specified";
2288 if (parse_white(filter)==-1) return -1;
2289 if ((m=parse_stringlist(filter,&hdr))!=1)
2291 if (m==0) filter->errmsg=CUS "header string list expected";
2294 if (parse_white(filter)==-1) return -1;
2295 if ((m=parse_stringlist(filter,&key))!=1)
2297 if (m==0) filter->errmsg=CUS "key string list expected";
2301 for (h=hdr; h->length!=-1 && !*cond; ++h)
2305 filter->errmsg=CUS "invalid header field";
2310 struct String header_value;
2313 expand_header(&header_value,h);
2314 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2315 if (header_value.character == NULL || header_def == NULL)
2317 filter->errmsg=CUS "header string expansion failed";
2320 for (k=key; k->length!=-1; ++k)
2322 if (Ustrcmp(header_def,"true")==0)
2324 *cond=compare(filter,k,&header_value,comparator,matchType);
2325 if (*cond==-1) return -1;
2333 else if (parse_identifier(filter,CUS "not"))
2335 if (parse_white(filter)==-1) return -1;
2336 switch (parse_test(filter,cond,exec))
2339 case 0: filter->errmsg=CUS "missing test"; return -1;
2340 default: *cond=!*cond; return 1;
2343 else if (parse_identifier(filter,CUS "size"))
2346 relop = ":over" / ":under"
2347 size-test = "size" relop <limit: number>
2350 unsigned long limit;
2353 if (parse_white(filter)==-1) return -1;
2354 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2355 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2358 filter->errmsg=CUS "missing :over or :under";
2361 if (parse_white(filter)==-1) return -1;
2362 if (parse_number(filter,&limit)==-1) return -1;
2363 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2366 else if (parse_identifier(filter,CUS "true"))
2371 else if (parse_identifier(filter,CUS "envelope"))
2374 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2375 <envelope-part: string-list> <key-list: string-list>
2377 envelope-part is case insensitive "from" or "to"
2378 #ifdef ENVELOPE_AUTH
2379 envelope-part =/ "auth"
2383 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2384 enum AddressPart addressPart=ADDRPART_ALL;
2385 enum MatchType matchType=MATCH_IS;
2386 struct String *env,*e,*key,*k;
2390 if (!filter->require_envelope)
2392 filter->errmsg=CUS "missing previous require \"envelope\";";
2397 if (parse_white(filter)==-1) return -1;
2398 if ((m=parse_comparator(filter,&comparator))!=0)
2400 if (m==-1) return -1;
2403 filter->errmsg=CUS "comparator already specified";
2408 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2410 if (m==-1) return -1;
2413 filter->errmsg=CUS "address part already specified";
2418 else if ((m=parse_matchtype(filter,&matchType))!=0)
2420 if (m==-1) return -1;
2423 filter->errmsg=CUS "match type already specified";
2430 if (parse_white(filter)==-1) return -1;
2431 if ((m=parse_stringlist(filter,&env))!=1)
2433 if (m==0) filter->errmsg=CUS "envelope string list expected";
2436 if (parse_white(filter)==-1) return -1;
2437 if ((m=parse_stringlist(filter,&key))!=1)
2439 if (m==0) filter->errmsg=CUS "key string list expected";
2443 for (e=env; e->length!=-1 && !*cond; ++e)
2445 const uschar *envelopeExpr=CUS 0;
2446 uschar *envelope=US 0;
2448 if (eq_asciicase(e,&str_from,0))
2450 switch (addressPart)
2452 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2456 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2457 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2459 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2463 else if (eq_asciicase(e,&str_to,0))
2465 switch (addressPart)
2467 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2469 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2470 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2472 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2473 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2476 #ifdef ENVELOPE_AUTH
2477 else if (eq_asciicase(e,&str_auth,0))
2479 switch (addressPart)
2481 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2485 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2486 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2488 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2495 filter->errmsg=CUS "invalid envelope string";
2498 if (exec && envelopeExpr)
2500 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2502 filter->errmsg=CUS "header string expansion failed";
2505 for (k=key; k->length!=-1; ++k)
2507 struct String envelopeStr;
2509 envelopeStr.character=envelope;
2510 envelopeStr.length=Ustrlen(envelope);
2511 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2512 if (*cond==-1) return -1;
2520 else if (parse_identifier(filter,CUS "valid_notify_method"))
2523 valid_notify_method = "valid_notify_method"
2524 <notification-uris: string-list>
2527 struct String *uris,*u;
2530 if (!filter->require_enotify)
2532 filter->errmsg=CUS "missing previous require \"enotify\";";
2535 if (parse_white(filter)==-1) return -1;
2536 if ((m=parse_stringlist(filter,&uris))!=1)
2538 if (m==0) filter->errmsg=CUS "URI string list expected";
2544 for (u=uris; u->length!=-1 && *cond; ++u)
2546 string_item *recipient;
2547 struct String header,subject,body;
2551 header.character=(uschar*)0;
2553 subject.character=(uschar*)0;
2555 body.character=(uschar*)0;
2556 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2562 else if (parse_identifier(filter,CUS "notify_method_capability"))
2565 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2566 <notification-uri: string>
2567 <notification-capability: string>
2568 <key-list: string-list>
2574 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2575 enum MatchType matchType=MATCH_IS;
2576 struct String uri,capa,*keys,*k;
2578 if (!filter->require_enotify)
2580 filter->errmsg=CUS "missing previous require \"enotify\";";
2585 if (parse_white(filter)==-1) return -1;
2586 if ((m=parse_comparator(filter,&comparator))!=0)
2588 if (m==-1) return -1;
2591 filter->errmsg=CUS "comparator already specified";
2596 else if ((m=parse_matchtype(filter,&matchType))!=0)
2598 if (m==-1) return -1;
2601 filter->errmsg=CUS "match type already specified";
2608 if ((m=parse_string(filter,&uri))!=1)
2610 if (m==0) filter->errmsg=CUS "missing notification URI string";
2613 if (parse_white(filter)==-1) return -1;
2614 if ((m=parse_string(filter,&capa))!=1)
2616 if (m==0) filter->errmsg=CUS "missing notification capability string";
2619 if (parse_white(filter)==-1) return -1;
2620 if ((m=parse_stringlist(filter,&keys))!=1)
2622 if (m==0) filter->errmsg=CUS "missing key string list";
2627 string_item *recipient;
2628 struct String header,subject,body;
2633 header.character=(uschar*)0;
2635 subject.character=(uschar*)0;
2637 body.character=(uschar*)0;
2638 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2640 if (eq_asciicase(&capa,&str_online,0)==1)
2641 for (k=keys; k->length!=-1; ++k)
2643 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2644 if (*cond==-1) return -1;
2656 /*************************************************
2657 * Parse and interpret an optional block *
2658 *************************************************/
2662 filter points to the Sieve filter including its state
2663 exec Execute parsed statements
2664 generated where to hang newly-generated addresses
2666 Returns: 2 success by stop
2668 0 no block command found
2669 -1 syntax or execution error
2672 static int parse_block(struct Sieve *filter, int exec,
2673 address_item **generated)
2677 if (parse_white(filter)==-1) return -1;
2678 if (*filter->pc=='{')
2681 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2682 if (*filter->pc=='}')
2689 filter->errmsg=CUS "expecting command or closing brace";
2697 /*************************************************
2698 * Match a semicolon *
2699 *************************************************/
2703 filter points to the Sieve filter including its state
2709 static int parse_semicolon(struct Sieve *filter)
2711 if (parse_white(filter)==-1) return -1;
2712 if (*filter->pc==';')
2719 filter->errmsg=CUS "missing semicolon";
2725 /*************************************************
2726 * Parse and interpret a Sieve command *
2727 *************************************************/
2731 filter points to the Sieve filter including its state
2732 exec Execute parsed statements
2733 generated where to hang newly-generated addresses
2735 Returns: 2 success by stop
2737 -1 syntax or execution error
2740 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2744 if (parse_white(filter)==-1) return -1;
2745 if (parse_identifier(filter,CUS "if"))
2748 if-command = "if" test block *( "elsif" test block ) [ else block ]
2751 int cond,m,unsuccessful;
2754 if (parse_white(filter)==-1) return -1;
2755 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2758 filter->errmsg=CUS "missing test";
2761 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2762 (debug_selector & D_filter) != 0)
2764 if (exec) debug_printf("if %s\n",cond?"true":"false");
2766 m=parse_block(filter,exec ? cond : 0, generated);
2767 if (m==-1 || m==2) return m;
2770 filter->errmsg=CUS "missing block";
2773 unsuccessful = !cond;
2774 for (;;) /* elsif test block */
2776 if (parse_white(filter)==-1) return -1;
2777 if (parse_identifier(filter,CUS "elsif"))
2779 if (parse_white(filter)==-1) return -1;
2780 m=parse_test(filter,&cond,exec && unsuccessful);
2781 if (m==-1 || m==2) return m;
2784 filter->errmsg=CUS "missing test";
2787 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2788 (debug_selector & D_filter) != 0)
2790 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2792 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2793 if (m==-1 || m==2) return m;
2796 filter->errmsg=CUS "missing block";
2799 if (exec && unsuccessful && cond) unsuccessful = 0;
2804 if (parse_white(filter)==-1) return -1;
2805 if (parse_identifier(filter,CUS "else"))
2807 m=parse_block(filter,exec && unsuccessful, generated);
2808 if (m==-1 || m==2) return m;
2811 filter->errmsg=CUS "missing block";
2816 else if (parse_identifier(filter,CUS "stop"))
2819 stop-command = "stop" { stop-options } ";"
2823 if (parse_semicolon(filter)==-1) return -1;
2826 filter->pc+=Ustrlen(filter->pc);
2830 else if (parse_identifier(filter,CUS "keep"))
2833 keep-command = "keep" { keep-options } ";"
2837 if (parse_semicolon(filter)==-1) return -1;
2840 add_addr(generated,US"inbox",1,0,0,0);
2844 else if (parse_identifier(filter,CUS "discard"))
2847 discard-command = "discard" { discard-options } ";"
2851 if (parse_semicolon(filter)==-1) return -1;
2852 if (exec) filter->keep=0;
2854 else if (parse_identifier(filter,CUS "redirect"))
2857 redirect-command = "redirect" redirect-options "string" ";"
2859 redirect-options =) ":copy"
2862 struct String recipient;
2868 if (parse_white(filter)==-1) return -1;
2869 if (parse_identifier(filter,CUS ":copy")==1)
2871 if (!filter->require_copy)
2873 filter->errmsg=CUS "missing previous require \"copy\";";
2880 if (parse_white(filter)==-1) return -1;
2881 if ((m=parse_string(filter,&recipient))!=1)
2883 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2886 if (strchr(CCS recipient.character,'@')==(char*)0)
2888 filter->errmsg=CUS "unqualified recipient address";
2893 add_addr(generated,recipient.character,0,0,0,0);
2894 if (!copy) filter->keep = 0;
2896 if (parse_semicolon(filter)==-1) return -1;
2898 else if (parse_identifier(filter,CUS "fileinto"))
2901 fileinto-command = "fileinto" { fileinto-options } string ";"
2903 fileinto-options =) [ ":copy" ]
2906 struct String folder;
2909 unsigned long maxage, maxmessages, maxstorage;
2912 maxage = maxmessages = maxstorage = 0;
2913 if (!filter->require_fileinto)
2915 filter->errmsg=CUS "missing previous require \"fileinto\";";
2920 if (parse_white(filter)==-1) return -1;
2921 if (parse_identifier(filter,CUS ":copy")==1)
2923 if (!filter->require_copy)
2925 filter->errmsg=CUS "missing previous require \"copy\";";
2932 if (parse_white(filter)==-1) return -1;
2933 if ((m=parse_string(filter,&folder))!=1)
2935 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2938 m=0; s=folder.character;
2939 if (folder.length==0) m=1;
2940 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2943 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2948 filter->errmsg=CUS "invalid folder";
2953 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2954 if (!copy) filter->keep = 0;
2956 if (parse_semicolon(filter)==-1) return -1;
2959 else if (parse_identifier(filter,CUS "notify"))
2962 notify-command = "notify" { notify-options } <method: string> ";"
2963 notify-options = [":from" string]
2964 [":importance" <"1" / "2" / "3">]
2965 [":options" 1*(string-list / number)]
2971 struct String importance;
2972 struct String message;
2973 struct String method;
2974 struct Notification *already;
2975 string_item *recipient;
2976 struct String header;
2977 struct String subject;
2979 uschar *envelope_from;
2980 struct String auto_submitted_value;
2981 uschar *auto_submitted_def;
2983 if (!filter->require_enotify)
2985 filter->errmsg=CUS "missing previous require \"enotify\";";
2988 from.character=(uschar*)0;
2990 importance.character=(uschar*)0;
2991 importance.length=-1;
2992 message.character=(uschar*)0;
2996 header.character=(uschar*)0;
2998 subject.character=(uschar*)0;
3000 body.character=(uschar*)0;
3001 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3004 if (parse_white(filter)==-1) return -1;
3005 if (parse_identifier(filter,CUS ":from")==1)
3007 if (parse_white(filter)==-1) return -1;
3008 if ((m=parse_string(filter,&from))!=1)
3010 if (m==0) filter->errmsg=CUS "from string expected";
3014 else if (parse_identifier(filter,CUS ":importance")==1)
3016 if (parse_white(filter)==-1) return -1;
3017 if ((m=parse_string(filter,&importance))!=1)
3019 if (m==0) filter->errmsg=CUS "importance string expected";
3022 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3024 filter->errmsg=CUS "invalid importance";
3028 else if (parse_identifier(filter,CUS ":options")==1)
3030 if (parse_white(filter)==-1) return -1;
3032 else if (parse_identifier(filter,CUS ":message")==1)
3034 if (parse_white(filter)==-1) return -1;
3035 if ((m=parse_string(filter,&message))!=1)
3037 if (m==0) filter->errmsg=CUS "message string expected";
3043 if (parse_white(filter)==-1) return -1;
3044 if ((m=parse_string(filter,&method))!=1)
3046 if (m==0) filter->errmsg=CUS "missing method string";
3049 if (parse_semicolon(filter)==-1) return -1;
3050 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3054 if (message.length==-1) message=subject;
3055 if (message.length==-1) expand_header(&message,&str_subject);
3056 expand_header(&auto_submitted_value,&str_auto_submitted);
3057 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3058 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3060 filter->errmsg=CUS "header string expansion failed";
3063 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3065 for (already=filter->notified; already; already=already->next)
3067 if (already->method.length==method.length
3068 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3069 && already->importance.length==importance.length
3070 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3071 && already->message.length==message.length
3072 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3075 if (already==(struct Notification*)0)
3076 /* New notification, process it */
3078 struct Notification *sent;
3079 sent=store_get(sizeof(struct Notification));
3080 sent->method=method;
3081 sent->importance=importance;
3082 sent->message=message;
3083 sent->next=filter->notified;
3084 filter->notified=sent;
3085 #ifndef COMPILE_SYNTAX_CHECKER
3086 if (filter_test == FTEST_NONE)
3091 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3095 int buffer_capacity;
3097 f = fdopen(fd, "wb");
3098 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3099 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3100 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3101 if (header.length>0) fprintf(f,"%s",header.character);
3102 if (message.length==-1)
3104 message.character=US"Notification";
3105 message.length=Ustrlen(message.character);
3107 /* Allocation is larger than necessary, but enough even for split MIME words */
3108 buffer_capacity=32+4*message.length;
3109 buffer=store_get(buffer_capacity);
3110 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3112 if (body.length>0) fprintf(f,"%s\n",body.character);
3115 (void)child_close(pid, 0);
3118 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3120 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3126 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3128 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3134 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3136 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3143 else if (parse_identifier(filter,CUS "vacation"))
3146 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3147 vacation-options = [":days" number]
3150 [":addresses" string-list]
3157 struct String subject;
3159 struct String *addresses;
3161 string_item *aliases;
3162 struct String handle;
3163 struct String reason;
3165 if (!filter->require_vacation)
3167 filter->errmsg=CUS "missing previous require \"vacation\";";
3172 if (filter->vacation_ran)
3174 filter->errmsg=CUS "trying to execute vacation more than once";
3177 filter->vacation_ran=1;
3179 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3180 subject.character=(uschar*)0;
3182 from.character=(uschar*)0;
3184 addresses=(struct String*)0;
3187 handle.character=(uschar*)0;
3191 if (parse_white(filter)==-1) return -1;
3192 if (parse_identifier(filter,CUS ":days")==1)
3194 if (parse_white(filter)==-1) return -1;
3195 if (parse_number(filter,&days)==-1) return -1;
3196 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3197 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3199 else if (parse_identifier(filter,CUS ":subject")==1)
3201 if (parse_white(filter)==-1) return -1;
3202 if ((m=parse_string(filter,&subject))!=1)
3204 if (m==0) filter->errmsg=CUS "subject string expected";
3208 else if (parse_identifier(filter,CUS ":from")==1)
3210 if (parse_white(filter)==-1) return -1;
3211 if ((m=parse_string(filter,&from))!=1)
3213 if (m==0) filter->errmsg=CUS "from string expected";
3216 if (check_mail_address(filter,&from)!=1)
3219 else if (parse_identifier(filter,CUS ":addresses")==1)
3223 if (parse_white(filter)==-1) return -1;
3224 if ((m=parse_stringlist(filter,&addresses))!=1)
3226 if (m==0) filter->errmsg=CUS "addresses string list expected";
3229 for (a=addresses; a->length!=-1; ++a)
3233 new=store_get(sizeof(string_item));
3234 new->text=store_get(a->length+1);
3235 if (a->length) memcpy(new->text,a->character,a->length);
3236 new->text[a->length]='\0';
3241 else if (parse_identifier(filter,CUS ":mime")==1)
3243 else if (parse_identifier(filter,CUS ":handle")==1)
3245 if (parse_white(filter)==-1) return -1;
3246 if ((m=parse_string(filter,&from))!=1)
3248 if (m==0) filter->errmsg=CUS "handle string expected";
3254 if (parse_white(filter)==-1) return -1;
3255 if ((m=parse_string(filter,&reason))!=1)
3257 if (m==0) filter->errmsg=CUS "missing reason string";
3264 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3267 filter->errmsg=CUS "MIME reason string contains 8bit text";
3271 if (parse_semicolon(filter)==-1) return -1;
3278 int buffer_capacity;
3282 uschar hexdigest[33];
3286 if (filter_personal(aliases,TRUE))
3288 if (filter_test == FTEST_NONE)
3290 /* ensure oncelog directory exists; failure will be detected later */
3292 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3294 /* build oncelog filename */
3296 key.character=(uschar*)0;
3299 if (handle.length==-1)
3301 if (subject.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,subject.character,subject.length);
3302 if (from.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,from.character,from.length);
3303 key.character=string_catn(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3304 key.character=string_catn(key.character,&capacity,&key.length,reason.character,reason.length);
3309 md5_end(&base, key.character, key.length, digest);
3310 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3311 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3313 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3315 if (filter_test == FTEST_NONE)
3317 capacity=Ustrlen(filter->vacation_directory);
3319 once=string_catn(filter->vacation_directory,&capacity,&start,US"/",1);
3320 once=string_catn(once,&capacity,&start,hexdigest,33);
3323 /* process subject */
3325 if (subject.length==-1)
3327 uschar *subject_def;
3329 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3330 if (Ustrcmp(subject_def,"true")==0)
3332 expand_header(&subject,&str_subject);
3335 subject.character=string_catn(US"Auto: ",&capacity,&start,subject.character,subject.length);
3336 subject.length=start;
3340 subject.character=US"Automated reply";
3341 subject.length=Ustrlen(subject.character);
3345 /* add address to list of generated addresses */
3347 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3348 setflag(addr, af_pfr);
3349 setflag(addr, af_ignore_error);
3350 addr->next = *generated;
3352 addr->reply = store_get(sizeof(reply_item));
3353 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3354 addr->reply->to = string_copy(sender_address);
3355 if (from.length==-1)
3356 addr->reply->from = expand_string(US"$local_part@$domain");
3358 addr->reply->from = from.character;
3359 /* Allocation is larger than necessary, but enough even for split MIME words */
3360 buffer_capacity=32+4*subject.length;
3361 buffer=store_get(buffer_capacity);
3362 /* deconst cast safe as we pass in a non-const item */
3363 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3364 addr->reply->oncelog=once;
3365 addr->reply->once_repeat=days*86400;
3367 /* build body and MIME headers */
3371 uschar *mime_body,*reason_end;
3372 static const uschar nlnl[]="\r\n\r\n";
3376 mime_body=reason.character,reason_end=reason.character+reason.length;
3377 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3382 addr->reply->headers = string_catn(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3383 addr->reply->headers[start] = '\0';
3386 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3387 else mime_body=reason_end-1;
3388 addr->reply->text = string_catn(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3389 addr->reply->text[start] = '\0';
3393 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3396 start = reason.length;
3397 addr->reply->headers = US"MIME-Version: 1.0\n"
3398 "Content-Type: text/plain;\n"
3399 "\tcharset=\"utf-8\"\n"
3400 "Content-Transfer-Encoding: quoted-printable";
3401 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3405 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3407 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3418 /*************************************************
3419 * Parse and interpret a sieve filter *
3420 *************************************************/
3424 filter points to the Sieve filter including its state
3425 exec Execute parsed statements
3426 generated where to hang newly-generated addresses
3429 -1 syntax or execution error
3433 parse_start(struct Sieve *filter, int exec, address_item **generated)
3435 filter->pc=filter->filter;
3438 filter->require_envelope=0;
3439 filter->require_fileinto=0;
3440 #ifdef ENCODED_CHARACTER
3441 filter->require_encoded_character=0;
3443 #ifdef ENVELOPE_AUTH
3444 filter->require_envelope_auth=0;
3447 filter->require_enotify=0;
3448 filter->notified=(struct Notification*)0;
3451 filter->require_subaddress=0;
3454 filter->require_vacation=0;
3455 filter->vacation_ran=0;
3457 filter->require_copy=0;
3458 filter->require_iascii_numeric=0;
3460 if (parse_white(filter)==-1) return -1;
3462 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3465 struct dirent *oncelog;
3466 struct stat properties;
3469 /* clean up old vacation log databases */
3471 oncelogdir=opendir(CS filter->vacation_directory);
3473 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3475 filter->errmsg=CUS "unable to open vacation directory";
3479 if (oncelogdir != NULL)
3483 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3485 if (strlen(oncelog->d_name)==32)
3487 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3488 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3492 closedir(oncelogdir);
3496 while (parse_identifier(filter,CUS "require"))
3499 require-command = "require" <capabilities: string-list>
3502 struct String *cap,*check;
3505 if (parse_white(filter)==-1) return -1;
3506 if ((m=parse_stringlist(filter,&cap))!=1)
3508 if (m==0) filter->errmsg=CUS "capability string list expected";
3511 for (check=cap; check->character; ++check)
3513 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3514 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3515 #ifdef ENCODED_CHARACTER
3516 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3518 #ifdef ENVELOPE_AUTH
3519 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3522 else if (eq_octet(check,&str_enotify,0))
3524 if (filter->enotify_mailto_owner == NULL)
3526 filter->errmsg=CUS "enotify disabled";
3529 filter->require_enotify=1;
3533 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3536 else if (eq_octet(check,&str_vacation,0))
3538 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3540 filter->errmsg=CUS "vacation disabled";
3543 filter->require_vacation=1;
3546 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3547 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3548 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3549 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3550 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3553 filter->errmsg=CUS "unknown capability";
3557 if (parse_semicolon(filter)==-1) return -1;
3559 if (parse_commands(filter,exec,generated)==-1) return -1;
3562 filter->errmsg=CUS "syntax error";
3569 /*************************************************
3570 * Interpret a sieve filter file *
3571 *************************************************/
3575 filter points to the entire file, read into store as a single string
3576 options controls whether various special things are allowed, and requests
3577 special actions (not currently used)
3578 vacation_directory where to store vacation "once" files
3579 enotify_mailto_owner owner of mailto notifications
3580 useraddress string expression for :user part of address
3581 subaddress string expression for :subaddress part of address
3582 generated where to hang newly-generated addresses
3583 error where to pass back an error text
3585 Returns: FF_DELIVERED success, a significant action was taken
3586 FF_NOTDELIVERED success, no significant action
3587 FF_DEFER defer requested
3588 FF_FAIL fail requested
3589 FF_FREEZE freeze requested
3590 FF_ERROR there was a problem
3594 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3595 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3596 address_item **generated, uschar **error)
3602 options = options; /* Keep picky compilers happy */
3605 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3606 sieve.filter=filter;
3608 if (vacation_directory == NULL)
3609 sieve.vacation_directory = NULL;
3612 sieve.vacation_directory=expand_string(vacation_directory);
3613 if (sieve.vacation_directory == NULL)
3615 *error = string_sprintf("failed to expand \"%s\" "
3616 "(sieve_vacation_directory): %s", vacation_directory,
3617 expand_string_message);
3622 if (enotify_mailto_owner == NULL)
3623 sieve.enotify_mailto_owner = NULL;
3626 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3627 if (sieve.enotify_mailto_owner == NULL)
3629 *error = string_sprintf("failed to expand \"%s\" "
3630 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3631 expand_string_message);
3636 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3637 sieve.subaddress = subaddress;
3639 #ifdef COMPILE_SYNTAX_CHECKER
3640 if (parse_start(&sieve,0,generated)==1)
3642 if (parse_start(&sieve,1,generated)==1)
3647 add_addr(generated,US"inbox",1,0,0,0);
3648 msg = string_sprintf("Implicit keep");
3653 msg = string_sprintf("No implicit keep");
3659 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3660 #ifdef COMPILE_SYNTAX_CHECKER
3664 add_addr(generated,US"inbox",1,0,0,0);
3669 #ifndef COMPILE_SYNTAX_CHECKER
3670 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3671 else debug_printf("%s\n", msg);
3674 DEBUG(D_route) debug_printf("Sieve: end of processing\n");