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,hvalue;
420 if (Ustrncmp(uri,"mailto:",7))
422 filter->errmsg=US "Unknown URI scheme";
426 if (*uri && *uri!='?')
430 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
436 to.character=string_catn(to.character, &capacity, &to.length, start, uri-start);
437 to.character[to.length]='\0';
438 if (uri_decode(&to)==-1)
440 filter->errmsg=US"Invalid URI encoding";
443 new=store_get(sizeof(string_item));
444 new->text=store_get(to.length+1);
445 if (to.length) memcpy(new->text,to.character,to.length);
446 new->text[to.length]='\0';
447 new->next=*recipient;
452 filter->errmsg=US"Missing addr-spec in URI";
455 if (*uri=='%') uri+=3;
464 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
468 hname.character= NULL;
470 hname.character = string_catn(hname.character, &capacity, &hname.length, start, uri-start);
471 hname.character[hname.length]='\0';
472 if (uri_decode(&hname)==-1)
474 filter->errmsg=US"Invalid URI encoding";
483 filter->errmsg=US"Missing equal after hname";
487 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
491 hvalue.character= NULL;
493 hvalue.character=string_catn(hvalue.character,&capacity,&hvalue.length,start,uri-start);
494 hvalue.character[hvalue.length]='\0';
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);
530 if (header->length==-1) header->length=0;
531 capacity=header->length;
532 header->character=string_catn(header->character,&capacity,&header->length,hname.character,hname.length);
533 header->character=string_catn(header->character,&capacity,&header->length,CUS ": ",2);
534 header->character=string_catn(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
535 header->character=string_catn(header->character,&capacity,&header->length,CUS "\n",1);
536 header->character[header->length]='\0';
539 if (*uri=='&') ++uri;
545 filter->errmsg=US"Syntactically invalid URI";
553 /*************************************************
554 * Octet-wise string comparison *
555 *************************************************/
559 needle UTF-8 string to search ...
560 haystack ... inside the haystack
561 match_prefix 1 to compare if needle is a prefix of haystack
563 Returns: 0 needle not found in haystack
567 static int eq_octet(const struct String *needle,
568 const struct String *haystack, int match_prefix)
576 h=haystack->character;
580 if (*n&0x80) return 0;
581 if (*h&0x80) return 0;
583 if (*n!=*h) return 0;
589 return (match_prefix ? nl==0 : nl==0 && hl==0);
593 /*************************************************
594 * ASCII case-insensitive string comparison *
595 *************************************************/
599 needle UTF-8 string to search ...
600 haystack ... inside the haystack
601 match_prefix 1 to compare if needle is a prefix of haystack
603 Returns: 0 needle not found in haystack
607 static int eq_asciicase(const struct String *needle,
608 const struct String *haystack, int match_prefix)
617 h=haystack->character;
623 if (nc&0x80) return 0;
624 if (hc&0x80) return 0;
626 /* tolower depends on the locale and only ASCII case must be insensitive */
627 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
633 return (match_prefix ? nl==0 : nl==0 && hl==0);
637 /*************************************************
638 * Glob pattern search *
639 *************************************************/
643 needle pattern to search ...
644 haystack ... inside the haystack
645 ascii_caseless ignore ASCII case
646 match_octet match octets, not UTF-8 multi-octet characters
648 Returns: 0 needle not found in haystack
653 static int eq_glob(const struct String *needle,
654 const struct String *haystack, int ascii_caseless, int match_octet)
656 const uschar *n,*h,*nend,*hend;
660 h=haystack->character;
661 nend=n+needle->length;
662 hend=h+haystack->length;
672 const uschar *npart,*hpart;
674 /* Try to match a non-star part of the needle at the current */
675 /* position in the haystack. */
679 while (npart<nend && *npart!='*') switch (*npart)
683 if (hpart==hend) return 0;
688 /* Match one UTF8 encoded character */
689 if ((*hpart&0xc0)==0xc0)
692 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
703 if (npart==nend) return -1;
708 if (hpart==hend) return 0;
709 /* tolower depends on the locale, but we need ASCII */
713 (*hpart&0x80) || (*npart&0x80) ||
716 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
721 /* string match after a star failed, advance and try again */
735 /* at this point, a part was matched successfully */
736 if (may_advance && npart==nend && hpart<hend)
737 /* needle ends, but haystack does not: if there was a star before, advance and try again */
747 return (h==hend ? 1 : may_advance);
751 /*************************************************
752 * ASCII numeric comparison *
753 *************************************************/
757 a first numeric string
758 b second numeric string
759 relop relational operator
761 Returns: 0 not (a relop b)
765 static int eq_asciinumeric(const struct String *a,
766 const struct String *b, enum RelOp relop)
769 const uschar *as,*aend,*bs,*bend;
773 aend=a->character+a->length;
775 bend=b->character+b->length;
777 while (*as>='0' && *as<='9' && as<aend) ++as;
779 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
782 if (al && bl==0) cmp=-1;
783 else if (al==0 && bl==0) cmp=0;
784 else if (al==0 && bl) cmp=1;
788 if (cmp==0) cmp=memcmp(a->character,b->character,al);
792 case LT: return cmp<0;
793 case LE: return cmp<=0;
794 case EQ: return cmp==0;
795 case GE: return cmp>=0;
796 case GT: return cmp>0;
797 case NE: return cmp!=0;
804 /*************************************************
806 *************************************************/
810 filter points to the Sieve filter including its state
811 needle UTF-8 pattern or string to search ...
812 haystack ... inside the haystack
816 Returns: 0 needle not found in haystack
818 -1 comparator does not offer matchtype
821 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
822 enum Comparator co, enum MatchType mt)
826 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
827 (debug_selector & D_filter) != 0)
829 debug_printf("String comparison (match ");
832 case MATCH_IS: debug_printf(":is"); break;
833 case MATCH_CONTAINS: debug_printf(":contains"); break;
834 case MATCH_MATCHES: debug_printf(":matches"); break;
836 debug_printf(", comparison \"");
839 case COMP_OCTET: debug_printf("i;octet"); break;
840 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
841 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
843 debug_printf("\"):\n");
844 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
845 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
855 if (eq_octet(needle,haystack,0)) r=1;
858 case COMP_EN_ASCII_CASEMAP:
860 if (eq_asciicase(needle,haystack,0)) r=1;
863 case COMP_ASCII_NUMERIC:
865 if (!filter->require_iascii_numeric)
867 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
870 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
884 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
887 case COMP_EN_ASCII_CASEMAP:
889 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
894 filter->errmsg=CUS "comparator does not offer specified matchtype";
906 if ((r=eq_glob(needle,haystack,0,1))==-1)
908 filter->errmsg=CUS "syntactically invalid pattern";
913 case COMP_EN_ASCII_CASEMAP:
915 if ((r=eq_glob(needle,haystack,1,1))==-1)
917 filter->errmsg=CUS "syntactically invalid pattern";
924 filter->errmsg=CUS "comparator does not offer specified matchtype";
931 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
932 (debug_selector & D_filter) != 0)
933 debug_printf(" Result %s\n",r?"true":"false");
938 /*************************************************
939 * Check header field syntax *
940 *************************************************/
943 RFC 2822, section 3.6.8 says:
947 ftext = %d33-57 / ; Any character except
948 %d59-126 ; controls, SP, and
951 That forbids 8-bit header fields. This implementation accepts them, since
952 all of Exim is 8-bit clean, so it adds %d128-%d255.
955 header header field to quote for suitable use in Exim expansions
957 Returns: 0 string is not a valid header field
958 1 string is a value header field
961 static int is_header(const struct String *header)
971 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
982 /*************************************************
983 * Quote special characters string *
984 *************************************************/
988 header header field to quote for suitable use in Exim expansions
991 Returns: quoted string
994 static const uschar *quote(const struct String *header)
1002 h=header->character;
1009 quoted=string_catn(quoted,&size,&ptr,CUS "\\0",2);
1016 quoted=string_catn(quoted,&size,&ptr,CUS "\\",1);
1020 quoted=string_catn(quoted,&size,&ptr,h,1);
1026 quoted=string_catn(quoted,&size,&ptr,CUS "",1);
1031 /*************************************************
1032 * Add address to list of generated addresses *
1033 *************************************************/
1036 According to RFC 5228, duplicate delivery to the same address must
1037 not happen, so the list is first searched for the address.
1040 generated list of generated addresses
1041 addr new address to add
1042 file address denotes a file
1047 static void 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)
1053 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1055 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1057 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1063 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1065 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1067 new_addr=deliver_make_addr(addr,TRUE);
1070 setflag(new_addr, af_pfr|af_file);
1073 new_addr->prop.errors_address = NULL;
1074 new_addr->next = *generated;
1075 *generated = new_addr;
1079 /*************************************************
1080 * Return decoded header field *
1081 *************************************************/
1084 Unfold the header field as described in RFC 2822 and remove all
1085 leading and trailing white space, then perform MIME decoding and
1086 translate the header field to UTF-8.
1089 value returned value of the field
1090 header name of the header field
1092 Returns: nothing The expanded string is empty
1093 in case there is no such header
1096 static void expand_header(struct String *value, const struct String *header)
1102 value->character=(uschar*)0;
1104 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1105 while (*r==' ' || *r=='\t') ++r;
1113 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1115 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1119 /*************************************************
1120 * Parse remaining hash comment *
1121 *************************************************/
1125 Comment up to terminating CRLF
1128 filter points to the Sieve filter including its state
1134 static int parse_hashcomment(struct Sieve *filter)
1140 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1142 if (*filter->pc=='\n')
1155 filter->errmsg=CUS "missing end of comment";
1160 /*************************************************
1161 * Parse remaining C-style comment *
1162 *************************************************/
1166 Everything up to star slash
1169 filter points to the Sieve filter including its state
1175 static int parse_comment(struct Sieve *filter)
1180 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1187 filter->errmsg=CUS "missing end of comment";
1192 /*************************************************
1193 * Parse optional white space *
1194 *************************************************/
1198 Spaces, tabs, CRLFs, hash comments or C-style comments
1201 filter points to the Sieve filter including its state
1207 static int parse_white(struct Sieve *filter)
1211 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1213 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1215 else if (*filter->pc=='\n')
1225 else if (*filter->pc=='#')
1227 if (parse_hashcomment(filter)==-1) return -1;
1229 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1231 if (parse_comment(filter)==-1) return -1;
1239 #ifdef ENCODED_CHARACTER
1240 /*************************************************
1241 * Decode hex-encoded-character string *
1242 *************************************************/
1245 Encoding definition:
1246 blank = SP / TAB / CRLF
1247 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1248 hex-pair = 1*2HEXDIG
1251 src points to a hex-pair-seq
1252 end points to its end
1253 dst points to the destination of the decoded octets,
1254 optionally to (uschar*)0 for checking only
1256 Returns: >=0 number of decoded octets
1260 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1264 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1269 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);
1270 if (d==0) return -1;
1273 if (src==end) return decoded;
1274 if (*src==' ' || *src=='\t' || *src=='\n')
1275 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1284 /*************************************************
1285 * Decode unicode-encoded-character string *
1286 *************************************************/
1289 Encoding definition:
1290 blank = SP / TAB / CRLF
1291 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1292 unicode-hex = 1*HEXDIG
1294 It is an error for a script to use a hexadecimal value that isn't in
1295 either the range 0 to D7FF or the range E000 to 10FFFF.
1297 At this time, strings are already scanned, thus the CRLF is converted
1298 to the internally used \n (should RFC_EOL have been used).
1301 src points to a unicode-hex-seq
1302 end points to its end
1303 dst points to the destination of the decoded octets,
1304 optionally to (uschar*)0 for checking only
1306 Returns: >=0 number of decoded octets
1308 -2 semantic error (character range violation)
1311 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1315 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1322 for (hex_seq=src; src<end && *src=='0'; ++src);
1323 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);
1324 if (src==hex_seq) return -1;
1325 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1331 else if (c>=0x80 && c<=0x7ff)
1336 *dst++=128+(c&0x3f);
1340 else if (c>=0x800 && c<=0xffff)
1345 *dst++=128+((c>>6)&0x3f);
1346 *dst++=128+(c&0x3f);
1350 else if (c>=0x10000 && c<=0x1fffff)
1355 *dst++=128+((c>>10)&0x3f);
1356 *dst++=128+((c>>6)&0x3f);
1357 *dst++=128+(c&0x3f);
1361 if (*src==' ' || *src=='\t' || *src=='\n')
1363 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1364 if (src==end) return decoded;
1373 /*************************************************
1374 * Decode encoded-character string *
1375 *************************************************/
1378 Encoding definition:
1379 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1380 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1383 encoded points to an encoded string, returns decoded string
1384 filter points to the Sieve filter including its state
1390 static int string_decode(struct Sieve *filter, struct String *data)
1392 uschar *src,*dst,*end;
1394 src=data->character;
1396 end=data->character+data->length;
1402 strncmpic(src,US "${hex:",6)==0
1403 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1404 && (hex_decode(src+6,brace,(uschar*)0))>=0
1407 dst+=hex_decode(src+6,brace,dst);
1411 strncmpic(src,US "${unicode:",10)==0
1412 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1415 switch (unicode_decode(src+10,brace,(uschar*)0))
1419 filter->errmsg=CUS "unicode character out of range";
1429 dst+=unicode_decode(src+10,brace,dst);
1436 data->length=dst-data->character;
1443 /*************************************************
1444 * Parse an optional string *
1445 *************************************************/
1449 quoted-string = DQUOTE *CHAR DQUOTE
1450 ;; in general, \ CHAR inside a string maps to CHAR
1451 ;; so \" maps to " and \\ maps to \
1452 ;; note that newlines and other characters are all allowed
1455 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1456 *(multi-line-literal / multi-line-dotstuff)
1458 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1459 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1460 ;; A line containing only "." ends the multi-line.
1461 ;; Remove a leading '.' if followed by another '.'.
1462 string = quoted-string / multi-line
1465 filter points to the Sieve filter including its state
1466 id specifies identifier to match
1470 0 identifier not matched
1473 static int parse_string(struct Sieve *filter, struct String *data)
1478 data->character=(uschar*)0;
1479 if (*filter->pc=='"') /* quoted string */
1484 if (*filter->pc=='"') /* end of string */
1486 int foo=data->length;
1489 /* that way, there will be at least one character allocated */
1490 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1491 #ifdef ENCODED_CHARACTER
1492 if (filter->require_encoded_character
1493 && string_decode(filter,data)==-1)
1498 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1500 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1503 else /* regular character */
1506 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1508 if (*filter->pc=='\n')
1510 data->character=string_catn(data->character,&dataCapacity,&data->length,US"\r",1);
1514 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1518 filter->errmsg=CUS "missing end of string";
1521 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1524 /* skip optional white space followed by hashed comment or CRLF */
1525 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1526 if (*filter->pc=='#')
1528 if (parse_hashcomment(filter)==-1) return -1;
1531 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1533 else if (*filter->pc=='\n')
1545 filter->errmsg=CUS "syntax error";
1551 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1553 if (*filter->pc=='\n') /* end of line */
1556 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1564 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1566 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1569 int foo=data->length;
1571 /* that way, there will be at least one character allocated */
1572 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1579 #ifdef ENCODED_CHARACTER
1580 if (filter->require_encoded_character
1581 && string_decode(filter,data)==-1)
1586 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1588 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS ".",1);
1592 else /* regular character */
1594 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1598 filter->errmsg=CUS "missing end of multi line string";
1605 /*************************************************
1606 * Parse a specific identifier *
1607 *************************************************/
1611 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1614 filter points to the Sieve filter including its state
1615 id specifies identifier to match
1618 0 identifier not matched
1621 static int parse_identifier(struct Sieve *filter, const uschar *id)
1623 size_t idlen=Ustrlen(id);
1625 if (strncmpic(US filter->pc,US id,idlen)==0)
1627 uschar next=filter->pc[idlen];
1629 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1637 /*************************************************
1639 *************************************************/
1643 number = 1*DIGIT [QUANTIFIER]
1644 QUANTIFIER = "K" / "M" / "G"
1647 filter points to the Sieve filter including its state
1651 -1 no string list found
1654 static int parse_number(struct Sieve *filter, unsigned long *data)
1658 if (*filter->pc>='0' && *filter->pc<='9')
1663 d=Ustrtoul(filter->pc,&e,10);
1666 filter->errmsg=CUstrerror(ERANGE);
1671 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1672 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1673 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1674 if (d>(ULONG_MAX/u))
1676 filter->errmsg=CUstrerror(ERANGE);
1685 filter->errmsg=CUS "missing number";
1691 /*************************************************
1692 * Parse a string list *
1693 *************************************************/
1697 string-list = "[" string *("," string) "]" / string
1700 filter points to the Sieve filter including its state
1701 data returns string list
1704 -1 no string list found
1708 parse_stringlist(struct Sieve *filter, struct String **data)
1710 const uschar *orig=filter->pc;
1711 int dataCapacity = 0;
1713 struct String *d = NULL;
1716 if (*filter->pc=='[') /* string list */
1721 if (parse_white(filter)==-1) goto error;
1722 if (dataLength+1 >= dataCapacity) /* increase buffer */
1725 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1727 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1728 new = store_get(sizeof(struct String) * dataCapacity);
1730 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1734 m=parse_string(filter,&d[dataLength]);
1737 if (dataLength==0) break;
1740 filter->errmsg=CUS "missing string";
1744 else if (m==-1) goto error;
1746 if (parse_white(filter)==-1) goto error;
1747 if (*filter->pc==',') ++filter->pc;
1750 if (*filter->pc==']')
1752 d[dataLength].character=(uschar*)0;
1753 d[dataLength].length=-1;
1760 filter->errmsg=CUS "missing closing bracket";
1764 else /* single string */
1766 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1770 m=parse_string(filter,&d[0]);
1782 d[1].character=(uschar*)0;
1789 filter->errmsg=CUS "missing string list";
1794 /*************************************************
1795 * Parse an optional address part specifier *
1796 *************************************************/
1800 address-part = ":localpart" / ":domain" / ":all"
1801 address-part =/ ":user" / ":detail"
1804 filter points to the Sieve filter including its state
1805 a returns address part specified
1808 0 no comparator found
1812 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1815 if (parse_identifier(filter,CUS ":user")==1)
1817 if (!filter->require_subaddress)
1819 filter->errmsg=CUS "missing previous require \"subaddress\";";
1825 else if (parse_identifier(filter,CUS ":detail")==1)
1827 if (!filter->require_subaddress)
1829 filter->errmsg=CUS "missing previous require \"subaddress\";";
1837 if (parse_identifier(filter,CUS ":localpart")==1)
1839 *a=ADDRPART_LOCALPART;
1842 else if (parse_identifier(filter,CUS ":domain")==1)
1847 else if (parse_identifier(filter,CUS ":all")==1)
1856 /*************************************************
1857 * Parse an optional comparator *
1858 *************************************************/
1862 comparator = ":comparator" <comparator-name: string>
1865 filter points to the Sieve filter including its state
1866 c returns comparator
1869 0 no comparator found
1870 -1 incomplete comparator found
1873 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1875 struct String comparator_name;
1877 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1878 if (parse_white(filter)==-1) return -1;
1879 switch (parse_string(filter,&comparator_name))
1884 filter->errmsg=CUS "missing comparator";
1891 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1896 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1898 *c=COMP_EN_ASCII_CASEMAP;
1901 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1903 *c=COMP_EN_ASCII_CASEMAP;
1906 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1908 *c=COMP_ASCII_NUMERIC;
1913 filter->errmsg=CUS "invalid comparator";
1922 /*************************************************
1923 * Parse an optional match type *
1924 *************************************************/
1928 match-type = ":is" / ":contains" / ":matches"
1931 filter points to the Sieve filter including its state
1932 m returns match type
1935 0 no match type found
1938 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1940 if (parse_identifier(filter,CUS ":is")==1)
1945 else if (parse_identifier(filter,CUS ":contains")==1)
1950 else if (parse_identifier(filter,CUS ":matches")==1)
1959 /*************************************************
1960 * Parse and interpret an optional test list *
1961 *************************************************/
1965 test-list = "(" test *("," test) ")"
1968 filter points to the Sieve filter including its state
1969 n total number of tests
1970 num_true number of passed tests
1971 exec Execute parsed statements
1974 0 no test list found
1975 -1 syntax or execution error
1978 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1980 if (parse_white(filter)==-1) return -1;
1981 if (*filter->pc=='(')
1990 switch (parse_test(filter,&cond,exec))
1993 case 0: filter->errmsg=CUS "missing test"; return -1;
1994 default: ++*n; if (cond) ++*num_true; break;
1996 if (parse_white(filter)==-1) return -1;
1997 if (*filter->pc==',') ++filter->pc;
2000 if (*filter->pc==')')
2007 filter->errmsg=CUS "missing closing paren";
2015 /*************************************************
2016 * Parse and interpret an optional test *
2017 *************************************************/
2021 filter points to the Sieve filter including its state
2022 cond returned condition status
2023 exec Execute parsed statements
2027 -1 syntax or execution error
2030 static int parse_test(struct Sieve *filter, int *cond, int exec)
2032 if (parse_white(filter)==-1) return -1;
2033 if (parse_identifier(filter,CUS "address"))
2036 address-test = "address" { [address-part] [comparator] [match-type] }
2037 <header-list: string-list> <key-list: string-list>
2039 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2042 enum AddressPart addressPart=ADDRPART_ALL;
2043 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2044 enum MatchType matchType=MATCH_IS;
2045 struct String *hdr,*h,*key,*k;
2051 if (parse_white(filter)==-1) return -1;
2052 if ((m=parse_addresspart(filter,&addressPart))!=0)
2054 if (m==-1) return -1;
2057 filter->errmsg=CUS "address part already specified";
2062 else if ((m=parse_comparator(filter,&comparator))!=0)
2064 if (m==-1) return -1;
2067 filter->errmsg=CUS "comparator already specified";
2072 else if ((m=parse_matchtype(filter,&matchType))!=0)
2074 if (m==-1) return -1;
2077 filter->errmsg=CUS "match type already specified";
2084 if (parse_white(filter)==-1) return -1;
2085 if ((m=parse_stringlist(filter,&hdr))!=1)
2087 if (m==0) filter->errmsg=CUS "header string list expected";
2090 if (parse_white(filter)==-1) return -1;
2091 if ((m=parse_stringlist(filter,&key))!=1)
2093 if (m==0) filter->errmsg=CUS "key string list expected";
2097 for (h=hdr; h->length!=-1 && !*cond; ++h)
2099 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2103 !eq_asciicase(h,&str_from,0)
2104 && !eq_asciicase(h,&str_to,0)
2105 && !eq_asciicase(h,&str_cc,0)
2106 && !eq_asciicase(h,&str_bcc,0)
2107 && !eq_asciicase(h,&str_sender,0)
2108 && !eq_asciicase(h,&str_resent_from,0)
2109 && !eq_asciicase(h,&str_resent_to,0)
2112 filter->errmsg=CUS "invalid header field";
2117 /* We are only interested in addresses below, so no MIME decoding */
2118 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2119 if (header_value == NULL)
2121 filter->errmsg=CUS "header string expansion failed";
2124 parse_allow_group = TRUE;
2125 while (*header_value && !*cond)
2128 int start, end, domain;
2132 end_addr = parse_find_address_end(header_value, FALSE);
2133 saveend = *end_addr;
2135 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2137 if (extracted_addr) switch (addressPart)
2139 case ADDRPART_ALL: part=extracted_addr; break;
2143 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2144 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2146 case ADDRPART_DETAIL: part=NULL; break;
2150 *end_addr = saveend;
2153 for (k=key; k->length!=-1; ++k)
2155 struct String partStr;
2157 partStr.character=part;
2158 partStr.length=Ustrlen(part);
2161 *cond=compare(filter,k,&partStr,comparator,matchType);
2162 if (*cond==-1) return -1;
2167 if (saveend == 0) break;
2168 header_value = end_addr + 1;
2170 parse_allow_group = FALSE;
2171 parse_found_group = FALSE;
2176 else if (parse_identifier(filter,CUS "allof"))
2179 allof-test = "allof" <tests: test-list>
2184 switch (parse_testlist(filter,&n,&num_true,exec))
2187 case 0: filter->errmsg=CUS "missing test list"; return -1;
2188 default: *cond=(n==num_true); return 1;
2191 else if (parse_identifier(filter,CUS "anyof"))
2194 anyof-test = "anyof" <tests: test-list>
2199 switch (parse_testlist(filter,&n,&num_true,exec))
2202 case 0: filter->errmsg=CUS "missing test list"; return -1;
2203 default: *cond=(num_true>0); return 1;
2206 else if (parse_identifier(filter,CUS "exists"))
2209 exists-test = "exists" <header-names: string-list>
2212 struct String *hdr,*h;
2215 if (parse_white(filter)==-1) return -1;
2216 if ((m=parse_stringlist(filter,&hdr))!=1)
2218 if (m==0) filter->errmsg=CUS "header string list expected";
2224 for (h=hdr; h->length!=-1 && *cond; ++h)
2228 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2229 if (header_def == NULL)
2231 filter->errmsg=CUS "header string expansion failed";
2234 if (Ustrcmp(header_def,"false")==0) *cond=0;
2239 else if (parse_identifier(filter,CUS "false"))
2242 false-test = "false"
2248 else if (parse_identifier(filter,CUS "header"))
2251 header-test = "header" { [comparator] [match-type] }
2252 <header-names: string-list> <key-list: string-list>
2255 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2256 enum MatchType matchType=MATCH_IS;
2257 struct String *hdr,*h,*key,*k;
2263 if (parse_white(filter)==-1) return -1;
2264 if ((m=parse_comparator(filter,&comparator))!=0)
2266 if (m==-1) return -1;
2269 filter->errmsg=CUS "comparator already specified";
2274 else if ((m=parse_matchtype(filter,&matchType))!=0)
2276 if (m==-1) return -1;
2279 filter->errmsg=CUS "match type already specified";
2286 if (parse_white(filter)==-1) return -1;
2287 if ((m=parse_stringlist(filter,&hdr))!=1)
2289 if (m==0) filter->errmsg=CUS "header string list expected";
2292 if (parse_white(filter)==-1) return -1;
2293 if ((m=parse_stringlist(filter,&key))!=1)
2295 if (m==0) filter->errmsg=CUS "key string list expected";
2299 for (h=hdr; h->length!=-1 && !*cond; ++h)
2303 filter->errmsg=CUS "invalid header field";
2308 struct String header_value;
2311 expand_header(&header_value,h);
2312 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2313 if (header_value.character == NULL || header_def == NULL)
2315 filter->errmsg=CUS "header string expansion failed";
2318 for (k=key; k->length!=-1; ++k)
2320 if (Ustrcmp(header_def,"true")==0)
2322 *cond=compare(filter,k,&header_value,comparator,matchType);
2323 if (*cond==-1) return -1;
2331 else if (parse_identifier(filter,CUS "not"))
2333 if (parse_white(filter)==-1) return -1;
2334 switch (parse_test(filter,cond,exec))
2337 case 0: filter->errmsg=CUS "missing test"; return -1;
2338 default: *cond=!*cond; return 1;
2341 else if (parse_identifier(filter,CUS "size"))
2344 relop = ":over" / ":under"
2345 size-test = "size" relop <limit: number>
2348 unsigned long limit;
2351 if (parse_white(filter)==-1) return -1;
2352 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2353 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2356 filter->errmsg=CUS "missing :over or :under";
2359 if (parse_white(filter)==-1) return -1;
2360 if (parse_number(filter,&limit)==-1) return -1;
2361 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2364 else if (parse_identifier(filter,CUS "true"))
2369 else if (parse_identifier(filter,CUS "envelope"))
2372 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2373 <envelope-part: string-list> <key-list: string-list>
2375 envelope-part is case insensitive "from" or "to"
2376 #ifdef ENVELOPE_AUTH
2377 envelope-part =/ "auth"
2381 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2382 enum AddressPart addressPart=ADDRPART_ALL;
2383 enum MatchType matchType=MATCH_IS;
2384 struct String *env,*e,*key,*k;
2388 if (!filter->require_envelope)
2390 filter->errmsg=CUS "missing previous require \"envelope\";";
2395 if (parse_white(filter)==-1) return -1;
2396 if ((m=parse_comparator(filter,&comparator))!=0)
2398 if (m==-1) return -1;
2401 filter->errmsg=CUS "comparator already specified";
2406 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2408 if (m==-1) return -1;
2411 filter->errmsg=CUS "address part already specified";
2416 else if ((m=parse_matchtype(filter,&matchType))!=0)
2418 if (m==-1) return -1;
2421 filter->errmsg=CUS "match type already specified";
2428 if (parse_white(filter)==-1) return -1;
2429 if ((m=parse_stringlist(filter,&env))!=1)
2431 if (m==0) filter->errmsg=CUS "envelope string list expected";
2434 if (parse_white(filter)==-1) return -1;
2435 if ((m=parse_stringlist(filter,&key))!=1)
2437 if (m==0) filter->errmsg=CUS "key string list expected";
2441 for (e=env; e->length!=-1 && !*cond; ++e)
2443 const uschar *envelopeExpr=CUS 0;
2444 uschar *envelope=US 0;
2446 if (eq_asciicase(e,&str_from,0))
2448 switch (addressPart)
2450 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2454 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2455 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2457 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2461 else if (eq_asciicase(e,&str_to,0))
2463 switch (addressPart)
2465 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2467 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2468 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2470 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2471 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2474 #ifdef ENVELOPE_AUTH
2475 else if (eq_asciicase(e,&str_auth,0))
2477 switch (addressPart)
2479 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2483 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2484 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2486 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2493 filter->errmsg=CUS "invalid envelope string";
2496 if (exec && envelopeExpr)
2498 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2500 filter->errmsg=CUS "header string expansion failed";
2503 for (k=key; k->length!=-1; ++k)
2505 struct String envelopeStr;
2507 envelopeStr.character=envelope;
2508 envelopeStr.length=Ustrlen(envelope);
2509 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2510 if (*cond==-1) return -1;
2518 else if (parse_identifier(filter,CUS "valid_notify_method"))
2521 valid_notify_method = "valid_notify_method"
2522 <notification-uris: string-list>
2525 struct String *uris,*u;
2528 if (!filter->require_enotify)
2530 filter->errmsg=CUS "missing previous require \"enotify\";";
2533 if (parse_white(filter)==-1) return -1;
2534 if ((m=parse_stringlist(filter,&uris))!=1)
2536 if (m==0) filter->errmsg=CUS "URI string list expected";
2542 for (u=uris; u->length!=-1 && *cond; ++u)
2544 string_item *recipient;
2545 struct String header,subject,body;
2549 header.character=(uschar*)0;
2551 subject.character=(uschar*)0;
2553 body.character=(uschar*)0;
2554 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2560 else if (parse_identifier(filter,CUS "notify_method_capability"))
2563 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2564 <notification-uri: string>
2565 <notification-capability: string>
2566 <key-list: string-list>
2572 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2573 enum MatchType matchType=MATCH_IS;
2574 struct String uri,capa,*keys,*k;
2576 if (!filter->require_enotify)
2578 filter->errmsg=CUS "missing previous require \"enotify\";";
2583 if (parse_white(filter)==-1) return -1;
2584 if ((m=parse_comparator(filter,&comparator))!=0)
2586 if (m==-1) return -1;
2589 filter->errmsg=CUS "comparator already specified";
2594 else if ((m=parse_matchtype(filter,&matchType))!=0)
2596 if (m==-1) return -1;
2599 filter->errmsg=CUS "match type already specified";
2606 if ((m=parse_string(filter,&uri))!=1)
2608 if (m==0) filter->errmsg=CUS "missing notification URI string";
2611 if (parse_white(filter)==-1) return -1;
2612 if ((m=parse_string(filter,&capa))!=1)
2614 if (m==0) filter->errmsg=CUS "missing notification capability string";
2617 if (parse_white(filter)==-1) return -1;
2618 if ((m=parse_stringlist(filter,&keys))!=1)
2620 if (m==0) filter->errmsg=CUS "missing key string list";
2625 string_item *recipient;
2626 struct String header,subject,body;
2631 header.character=(uschar*)0;
2633 subject.character=(uschar*)0;
2635 body.character=(uschar*)0;
2636 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2638 if (eq_asciicase(&capa,&str_online,0)==1)
2639 for (k=keys; k->length!=-1; ++k)
2641 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2642 if (*cond==-1) return -1;
2654 /*************************************************
2655 * Parse and interpret an optional block *
2656 *************************************************/
2660 filter points to the Sieve filter including its state
2661 exec Execute parsed statements
2662 generated where to hang newly-generated addresses
2664 Returns: 2 success by stop
2666 0 no block command found
2667 -1 syntax or execution error
2670 static int parse_block(struct Sieve *filter, int exec,
2671 address_item **generated)
2675 if (parse_white(filter)==-1) return -1;
2676 if (*filter->pc=='{')
2679 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2680 if (*filter->pc=='}')
2687 filter->errmsg=CUS "expecting command or closing brace";
2695 /*************************************************
2696 * Match a semicolon *
2697 *************************************************/
2701 filter points to the Sieve filter including its state
2707 static int parse_semicolon(struct Sieve *filter)
2709 if (parse_white(filter)==-1) return -1;
2710 if (*filter->pc==';')
2717 filter->errmsg=CUS "missing semicolon";
2723 /*************************************************
2724 * Parse and interpret a Sieve command *
2725 *************************************************/
2729 filter points to the Sieve filter including its state
2730 exec Execute parsed statements
2731 generated where to hang newly-generated addresses
2733 Returns: 2 success by stop
2735 -1 syntax or execution error
2738 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2742 if (parse_white(filter)==-1) return -1;
2743 if (parse_identifier(filter,CUS "if"))
2746 if-command = "if" test block *( "elsif" test block ) [ else block ]
2749 int cond,m,unsuccessful;
2752 if (parse_white(filter)==-1) return -1;
2753 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2756 filter->errmsg=CUS "missing test";
2759 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2760 (debug_selector & D_filter) != 0)
2762 if (exec) debug_printf("if %s\n",cond?"true":"false");
2764 m=parse_block(filter,exec ? cond : 0, generated);
2765 if (m==-1 || m==2) return m;
2768 filter->errmsg=CUS "missing block";
2771 unsuccessful = !cond;
2772 for (;;) /* elsif test block */
2774 if (parse_white(filter)==-1) return -1;
2775 if (parse_identifier(filter,CUS "elsif"))
2777 if (parse_white(filter)==-1) return -1;
2778 m=parse_test(filter,&cond,exec && unsuccessful);
2779 if (m==-1 || m==2) return m;
2782 filter->errmsg=CUS "missing test";
2785 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2786 (debug_selector & D_filter) != 0)
2788 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2790 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2791 if (m==-1 || m==2) return m;
2794 filter->errmsg=CUS "missing block";
2797 if (exec && unsuccessful && cond) unsuccessful = 0;
2802 if (parse_white(filter)==-1) return -1;
2803 if (parse_identifier(filter,CUS "else"))
2805 m=parse_block(filter,exec && unsuccessful, generated);
2806 if (m==-1 || m==2) return m;
2809 filter->errmsg=CUS "missing block";
2814 else if (parse_identifier(filter,CUS "stop"))
2817 stop-command = "stop" { stop-options } ";"
2821 if (parse_semicolon(filter)==-1) return -1;
2824 filter->pc+=Ustrlen(filter->pc);
2828 else if (parse_identifier(filter,CUS "keep"))
2831 keep-command = "keep" { keep-options } ";"
2835 if (parse_semicolon(filter)==-1) return -1;
2838 add_addr(generated,US"inbox",1,0,0,0);
2842 else if (parse_identifier(filter,CUS "discard"))
2845 discard-command = "discard" { discard-options } ";"
2849 if (parse_semicolon(filter)==-1) return -1;
2850 if (exec) filter->keep=0;
2852 else if (parse_identifier(filter,CUS "redirect"))
2855 redirect-command = "redirect" redirect-options "string" ";"
2857 redirect-options =) ":copy"
2860 struct String recipient;
2866 if (parse_white(filter)==-1) return -1;
2867 if (parse_identifier(filter,CUS ":copy")==1)
2869 if (!filter->require_copy)
2871 filter->errmsg=CUS "missing previous require \"copy\";";
2878 if (parse_white(filter)==-1) return -1;
2879 if ((m=parse_string(filter,&recipient))!=1)
2881 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2884 if (strchr(CCS recipient.character,'@')==(char*)0)
2886 filter->errmsg=CUS "unqualified recipient address";
2891 add_addr(generated,recipient.character,0,0,0,0);
2892 if (!copy) filter->keep = 0;
2894 if (parse_semicolon(filter)==-1) return -1;
2896 else if (parse_identifier(filter,CUS "fileinto"))
2899 fileinto-command = "fileinto" { fileinto-options } string ";"
2901 fileinto-options =) [ ":copy" ]
2904 struct String folder;
2907 unsigned long maxage, maxmessages, maxstorage;
2910 maxage = maxmessages = maxstorage = 0;
2911 if (!filter->require_fileinto)
2913 filter->errmsg=CUS "missing previous require \"fileinto\";";
2918 if (parse_white(filter)==-1) return -1;
2919 if (parse_identifier(filter,CUS ":copy")==1)
2921 if (!filter->require_copy)
2923 filter->errmsg=CUS "missing previous require \"copy\";";
2930 if (parse_white(filter)==-1) return -1;
2931 if ((m=parse_string(filter,&folder))!=1)
2933 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2936 m=0; s=folder.character;
2937 if (folder.length==0) m=1;
2938 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2941 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2946 filter->errmsg=CUS "invalid folder";
2951 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2952 if (!copy) filter->keep = 0;
2954 if (parse_semicolon(filter)==-1) return -1;
2957 else if (parse_identifier(filter,CUS "notify"))
2960 notify-command = "notify" { notify-options } <method: string> ";"
2961 notify-options = [":from" string]
2962 [":importance" <"1" / "2" / "3">]
2963 [":options" 1*(string-list / number)]
2969 struct String importance;
2970 struct String message;
2971 struct String method;
2972 struct Notification *already;
2973 string_item *recipient;
2974 struct String header;
2975 struct String subject;
2977 uschar *envelope_from;
2978 struct String auto_submitted_value;
2979 uschar *auto_submitted_def;
2981 if (!filter->require_enotify)
2983 filter->errmsg=CUS "missing previous require \"enotify\";";
2986 from.character=(uschar*)0;
2988 importance.character=(uschar*)0;
2989 importance.length=-1;
2990 message.character=(uschar*)0;
2994 header.character=(uschar*)0;
2996 subject.character=(uschar*)0;
2998 body.character=(uschar*)0;
2999 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3002 if (parse_white(filter)==-1) return -1;
3003 if (parse_identifier(filter,CUS ":from")==1)
3005 if (parse_white(filter)==-1) return -1;
3006 if ((m=parse_string(filter,&from))!=1)
3008 if (m==0) filter->errmsg=CUS "from string expected";
3012 else if (parse_identifier(filter,CUS ":importance")==1)
3014 if (parse_white(filter)==-1) return -1;
3015 if ((m=parse_string(filter,&importance))!=1)
3017 if (m==0) filter->errmsg=CUS "importance string expected";
3020 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3022 filter->errmsg=CUS "invalid importance";
3026 else if (parse_identifier(filter,CUS ":options")==1)
3028 if (parse_white(filter)==-1) return -1;
3030 else if (parse_identifier(filter,CUS ":message")==1)
3032 if (parse_white(filter)==-1) return -1;
3033 if ((m=parse_string(filter,&message))!=1)
3035 if (m==0) filter->errmsg=CUS "message string expected";
3041 if (parse_white(filter)==-1) return -1;
3042 if ((m=parse_string(filter,&method))!=1)
3044 if (m==0) filter->errmsg=CUS "missing method string";
3047 if (parse_semicolon(filter)==-1) return -1;
3048 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3052 if (message.length==-1) message=subject;
3053 if (message.length==-1) expand_header(&message,&str_subject);
3054 expand_header(&auto_submitted_value,&str_auto_submitted);
3055 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3056 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3058 filter->errmsg=CUS "header string expansion failed";
3061 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3063 for (already=filter->notified; already; already=already->next)
3065 if (already->method.length==method.length
3066 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3067 && already->importance.length==importance.length
3068 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3069 && already->message.length==message.length
3070 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3073 if (already==(struct Notification*)0)
3074 /* New notification, process it */
3076 struct Notification *sent;
3077 sent=store_get(sizeof(struct Notification));
3078 sent->method=method;
3079 sent->importance=importance;
3080 sent->message=message;
3081 sent->next=filter->notified;
3082 filter->notified=sent;
3083 #ifndef COMPILE_SYNTAX_CHECKER
3084 if (filter_test == FTEST_NONE)
3089 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3093 int buffer_capacity;
3095 f = fdopen(fd, "wb");
3096 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3097 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3098 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3099 if (header.length>0) fprintf(f,"%s",header.character);
3100 if (message.length==-1)
3102 message.character=US"Notification";
3103 message.length=Ustrlen(message.character);
3105 /* Allocation is larger than neccessary, but enough even for split MIME words */
3106 buffer_capacity=32+4*message.length;
3107 buffer=store_get(buffer_capacity);
3108 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3110 if (body.length>0) fprintf(f,"%s\n",body.character);
3113 (void)child_close(pid, 0);
3116 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3118 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3124 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3126 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3132 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3134 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3141 else if (parse_identifier(filter,CUS "vacation"))
3144 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3145 vacation-options = [":days" number]
3148 [":addresses" string-list]
3155 struct String subject;
3157 struct String *addresses;
3159 string_item *aliases;
3160 struct String handle;
3161 struct String reason;
3163 if (!filter->require_vacation)
3165 filter->errmsg=CUS "missing previous require \"vacation\";";
3170 if (filter->vacation_ran)
3172 filter->errmsg=CUS "trying to execute vacation more than once";
3175 filter->vacation_ran=1;
3177 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3178 subject.character=(uschar*)0;
3180 from.character=(uschar*)0;
3182 addresses=(struct String*)0;
3185 handle.character=(uschar*)0;
3189 if (parse_white(filter)==-1) return -1;
3190 if (parse_identifier(filter,CUS ":days")==1)
3192 if (parse_white(filter)==-1) return -1;
3193 if (parse_number(filter,&days)==-1) return -1;
3194 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3195 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3197 else if (parse_identifier(filter,CUS ":subject")==1)
3199 if (parse_white(filter)==-1) return -1;
3200 if ((m=parse_string(filter,&subject))!=1)
3202 if (m==0) filter->errmsg=CUS "subject string expected";
3206 else if (parse_identifier(filter,CUS ":from")==1)
3208 if (parse_white(filter)==-1) return -1;
3209 if ((m=parse_string(filter,&from))!=1)
3211 if (m==0) filter->errmsg=CUS "from string expected";
3214 if (check_mail_address(filter,&from)!=1)
3217 else if (parse_identifier(filter,CUS ":addresses")==1)
3221 if (parse_white(filter)==-1) return -1;
3222 if ((m=parse_stringlist(filter,&addresses))!=1)
3224 if (m==0) filter->errmsg=CUS "addresses string list expected";
3227 for (a=addresses; a->length!=-1; ++a)
3231 new=store_get(sizeof(string_item));
3232 new->text=store_get(a->length+1);
3233 if (a->length) memcpy(new->text,a->character,a->length);
3234 new->text[a->length]='\0';
3239 else if (parse_identifier(filter,CUS ":mime")==1)
3241 else if (parse_identifier(filter,CUS ":handle")==1)
3243 if (parse_white(filter)==-1) return -1;
3244 if ((m=parse_string(filter,&from))!=1)
3246 if (m==0) filter->errmsg=CUS "handle string expected";
3252 if (parse_white(filter)==-1) return -1;
3253 if ((m=parse_string(filter,&reason))!=1)
3255 if (m==0) filter->errmsg=CUS "missing reason string";
3262 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3265 filter->errmsg=CUS "MIME reason string contains 8bit text";
3269 if (parse_semicolon(filter)==-1) return -1;
3276 int buffer_capacity;
3280 uschar hexdigest[33];
3284 if (filter_personal(aliases,TRUE))
3286 if (filter_test == FTEST_NONE)
3288 /* ensure oncelog directory exists; failure will be detected later */
3290 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3292 /* build oncelog filename */
3294 key.character=(uschar*)0;
3297 if (handle.length==-1)
3299 if (subject.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,subject.character,subject.length);
3300 if (from.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,from.character,from.length);
3301 key.character=string_catn(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3302 key.character=string_catn(key.character,&capacity,&key.length,reason.character,reason.length);
3307 md5_end(&base, key.character, key.length, digest);
3308 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3309 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3311 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3313 if (filter_test == FTEST_NONE)
3315 capacity=Ustrlen(filter->vacation_directory);
3317 once=string_catn(filter->vacation_directory,&capacity,&start,US"/",1);
3318 once=string_catn(once,&capacity,&start,hexdigest,33);
3321 /* process subject */
3323 if (subject.length==-1)
3325 uschar *subject_def;
3327 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3328 if (Ustrcmp(subject_def,"true")==0)
3330 expand_header(&subject,&str_subject);
3333 subject.character=string_catn(US"Auto: ",&capacity,&start,subject.character,subject.length);
3334 subject.length=start;
3338 subject.character=US"Automated reply";
3339 subject.length=Ustrlen(subject.character);
3343 /* add address to list of generated addresses */
3345 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3346 setflag(addr, af_pfr);
3347 setflag(addr, af_ignore_error);
3348 addr->next = *generated;
3350 addr->reply = store_get(sizeof(reply_item));
3351 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3352 addr->reply->to = string_copy(sender_address);
3353 if (from.length==-1)
3354 addr->reply->from = expand_string(US"$local_part@$domain");
3356 addr->reply->from = from.character;
3357 /* Allocation is larger than neccessary, but enough even for split MIME words */
3358 buffer_capacity=32+4*subject.length;
3359 buffer=store_get(buffer_capacity);
3360 /* deconst cast safe as we pass in a non-const item */
3361 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3362 addr->reply->oncelog=once;
3363 addr->reply->once_repeat=days*86400;
3365 /* build body and MIME headers */
3369 uschar *mime_body,*reason_end;
3370 static const uschar nlnl[]="\r\n\r\n";
3374 mime_body=reason.character,reason_end=reason.character+reason.length;
3375 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3380 addr->reply->headers = string_catn(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3381 addr->reply->headers[start] = '\0';
3384 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3385 else mime_body=reason_end-1;
3386 addr->reply->text = string_catn(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3387 addr->reply->text[start] = '\0';
3391 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3394 start = reason.length;
3395 addr->reply->headers = US"MIME-Version: 1.0\n"
3396 "Content-Type: text/plain;\n"
3397 "\tcharset=\"utf-8\"\n"
3398 "Content-Transfer-Encoding: quoted-printable";
3399 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3403 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3405 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3416 /*************************************************
3417 * Parse and interpret a sieve filter *
3418 *************************************************/
3422 filter points to the Sieve filter including its state
3423 exec Execute parsed statements
3424 generated where to hang newly-generated addresses
3427 -1 syntax or execution error
3431 parse_start(struct Sieve *filter, int exec, address_item **generated)
3433 filter->pc=filter->filter;
3436 filter->require_envelope=0;
3437 filter->require_fileinto=0;
3438 #ifdef ENCODED_CHARACTER
3439 filter->require_encoded_character=0;
3441 #ifdef ENVELOPE_AUTH
3442 filter->require_envelope_auth=0;
3445 filter->require_enotify=0;
3446 filter->notified=(struct Notification*)0;
3449 filter->require_subaddress=0;
3452 filter->require_vacation=0;
3453 filter->vacation_ran=0;
3455 filter->require_copy=0;
3456 filter->require_iascii_numeric=0;
3458 if (parse_white(filter)==-1) return -1;
3460 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3463 struct dirent *oncelog;
3464 struct stat properties;
3467 /* clean up old vacation log databases */
3469 oncelogdir=opendir(CS filter->vacation_directory);
3471 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3473 filter->errmsg=CUS "unable to open vacation directory";
3477 if (oncelogdir != NULL)
3481 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3483 if (strlen(oncelog->d_name)==32)
3485 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3486 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3490 closedir(oncelogdir);
3494 while (parse_identifier(filter,CUS "require"))
3497 require-command = "require" <capabilities: string-list>
3500 struct String *cap,*check;
3503 if (parse_white(filter)==-1) return -1;
3504 if ((m=parse_stringlist(filter,&cap))!=1)
3506 if (m==0) filter->errmsg=CUS "capability string list expected";
3509 for (check=cap; check->character; ++check)
3511 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3512 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3513 #ifdef ENCODED_CHARACTER
3514 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3516 #ifdef ENVELOPE_AUTH
3517 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3520 else if (eq_octet(check,&str_enotify,0))
3522 if (filter->enotify_mailto_owner == NULL)
3524 filter->errmsg=CUS "enotify disabled";
3527 filter->require_enotify=1;
3531 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3534 else if (eq_octet(check,&str_vacation,0))
3536 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3538 filter->errmsg=CUS "vacation disabled";
3541 filter->require_vacation=1;
3544 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3545 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3546 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3547 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3548 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3551 filter->errmsg=CUS "unknown capability";
3555 if (parse_semicolon(filter)==-1) return -1;
3557 if (parse_commands(filter,exec,generated)==-1) return -1;
3560 filter->errmsg=CUS "syntax error";
3567 /*************************************************
3568 * Interpret a sieve filter file *
3569 *************************************************/
3573 filter points to the entire file, read into store as a single string
3574 options controls whether various special things are allowed, and requests
3575 special actions (not currently used)
3576 vacation_directory where to store vacation "once" files
3577 enotify_mailto_owner owner of mailto notifications
3578 useraddress string expression for :user part of address
3579 subaddress string expression for :subaddress part of address
3580 generated where to hang newly-generated addresses
3581 error where to pass back an error text
3583 Returns: FF_DELIVERED success, a significant action was taken
3584 FF_NOTDELIVERED success, no significant action
3585 FF_DEFER defer requested
3586 FF_FAIL fail requested
3587 FF_FREEZE freeze requested
3588 FF_ERROR there was a problem
3592 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3593 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3594 address_item **generated, uschar **error)
3600 options = options; /* Keep picky compilers happy */
3603 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3604 sieve.filter=filter;
3606 if (vacation_directory == NULL)
3607 sieve.vacation_directory = NULL;
3610 sieve.vacation_directory=expand_string(vacation_directory);
3611 if (sieve.vacation_directory == NULL)
3613 *error = string_sprintf("failed to expand \"%s\" "
3614 "(sieve_vacation_directory): %s", vacation_directory,
3615 expand_string_message);
3620 if (enotify_mailto_owner == NULL)
3621 sieve.enotify_mailto_owner = NULL;
3624 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3625 if (sieve.enotify_mailto_owner == NULL)
3627 *error = string_sprintf("failed to expand \"%s\" "
3628 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3629 expand_string_message);
3634 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3635 sieve.subaddress = subaddress;
3637 #ifdef COMPILE_SYNTAX_CHECKER
3638 if (parse_start(&sieve,0,generated)==1)
3640 if (parse_start(&sieve,1,generated)==1)
3645 add_addr(generated,US"inbox",1,0,0,0);
3646 msg = string_sprintf("Implicit keep");
3651 msg = string_sprintf("No implicit keep");
3657 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3658 #ifdef COMPILE_SYNTAX_CHECKER
3662 add_addr(generated,US"inbox",1,0,0,0);
3667 #ifndef COMPILE_SYNTAX_CHECKER
3668 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3669 else debug_printf("%s\n", msg);
3672 DEBUG(D_route) debug_printf("Sieve: end of processing\n");