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);
299 *new='\0'; /* not included in length, but nice */
304 /*************************************************
305 * Check mail address for correct syntax *
306 *************************************************/
309 Check mail address for being syntactically correct.
312 filter points to the Sieve filter including its state
313 address String containing one address
316 1 Mail address is syntactically OK
320 int check_mail_address(struct Sieve *filter, const struct String *address)
322 int start, end, domain;
325 if (address->length>0)
327 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
331 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
332 address->character, error);
340 filter->errmsg=CUS "empty address";
346 /*************************************************
347 * Decode URI encoded string *
348 *************************************************/
352 str URI encoded string
355 0 Decoding successful
360 static int uri_decode(struct String *str)
364 if (str->length==0) return 0;
365 for (s=str->character,t=s,e=s+str->length; s<e; )
369 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
371 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
372 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
381 str->length=t-str->character;
386 /*************************************************
388 *************************************************/
393 mailtoURI = "mailto:" [ to ] [ headers ]
394 to = [ addr-spec *("%2C" addr-spec ) ]
395 headers = "?" header *( "&" header )
396 header = hname "=" hvalue
401 filter points to the Sieve filter including its state
402 uri URI, excluding scheme
407 1 URI is syntactically OK
412 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
415 struct String to, hname;
416 struct String hvalue = {.character = NULL, .length = 0};
420 if (Ustrncmp(uri,"mailto:",7))
422 filter->errmsg=US "Unknown URI scheme";
427 if (*uri && *uri!='?')
431 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
437 to.character=string_catn(to.character, &capacity, &to.length, start, uri-start);
438 to.character[to.length]='\0';
439 if (uri_decode(&to)==-1)
441 filter->errmsg=US"Invalid URI encoding";
444 new=store_get(sizeof(string_item));
445 new->text=store_get(to.length+1);
446 if (to.length) memcpy(new->text,to.character,to.length);
447 new->text[to.length]='\0';
448 new->next=*recipient;
453 filter->errmsg=US"Missing addr-spec in URI";
456 if (*uri=='%') uri+=3;
465 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
469 hname.character= NULL;
471 hname.character = string_catn(hname.character, &capacity, &hname.length, start, uri-start);
472 hname.character[hname.length]='\0';
473 if (uri_decode(&hname)==-1)
475 filter->errmsg=US"Invalid URI encoding";
484 filter->errmsg=US"Missing equal after hname";
488 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
492 hvalue.character= NULL;
494 hvalue.character=string_catn(hvalue.character,&capacity,&hvalue.length,start,uri-start);
495 hvalue.character[hvalue.length]='\0';
496 if (uri_decode(&hvalue)==-1)
498 filter->errmsg=US"Invalid URI encoding";
502 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
504 new=store_get(sizeof(string_item));
505 new->text=store_get(hvalue.length+1);
506 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
507 new->text[hvalue.length]='\0';
508 new->next=*recipient;
511 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
513 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
517 static struct String ignore[]=
523 {US"auto-submitted",14}
525 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
528 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
531 if (header->length==-1) header->length=0;
532 capacity=header->length;
533 header->character=string_catn(header->character,&capacity,&header->length,hname.character,hname.length);
534 header->character=string_catn(header->character,&capacity,&header->length,CUS ": ",2);
535 header->character=string_catn(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
536 header->character=string_catn(header->character,&capacity,&header->length,CUS "\n",1);
537 header->character[header->length]='\0';
540 if (*uri=='&') ++uri;
546 filter->errmsg=US"Syntactically invalid URI";
554 /*************************************************
555 * Octet-wise string comparison *
556 *************************************************/
560 needle UTF-8 string to search ...
561 haystack ... inside the haystack
562 match_prefix 1 to compare if needle is a prefix of haystack
564 Returns: 0 needle not found in haystack
568 static int eq_octet(const struct String *needle,
569 const struct String *haystack, int match_prefix)
577 h=haystack->character;
581 if (*n&0x80) return 0;
582 if (*h&0x80) return 0;
584 if (*n!=*h) return 0;
590 return (match_prefix ? nl==0 : nl==0 && hl==0);
594 /*************************************************
595 * ASCII case-insensitive string comparison *
596 *************************************************/
600 needle UTF-8 string to search ...
601 haystack ... inside the haystack
602 match_prefix 1 to compare if needle is a prefix of haystack
604 Returns: 0 needle not found in haystack
608 static int eq_asciicase(const struct String *needle,
609 const struct String *haystack, int match_prefix)
618 h=haystack->character;
624 if (nc&0x80) return 0;
625 if (hc&0x80) return 0;
627 /* tolower depends on the locale and only ASCII case must be insensitive */
628 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
634 return (match_prefix ? nl==0 : nl==0 && hl==0);
638 /*************************************************
639 * Glob pattern search *
640 *************************************************/
644 needle pattern to search ...
645 haystack ... inside the haystack
646 ascii_caseless ignore ASCII case
647 match_octet match octets, not UTF-8 multi-octet characters
649 Returns: 0 needle not found in haystack
654 static int eq_glob(const struct String *needle,
655 const struct String *haystack, int ascii_caseless, int match_octet)
657 const uschar *n,*h,*nend,*hend;
661 h=haystack->character;
662 nend=n+needle->length;
663 hend=h+haystack->length;
673 const uschar *npart,*hpart;
675 /* Try to match a non-star part of the needle at the current */
676 /* position in the haystack. */
680 while (npart<nend && *npart!='*') switch (*npart)
684 if (hpart==hend) return 0;
689 /* Match one UTF8 encoded character */
690 if ((*hpart&0xc0)==0xc0)
693 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
704 if (npart==nend) return -1;
709 if (hpart==hend) return 0;
710 /* tolower depends on the locale, but we need ASCII */
714 (*hpart&0x80) || (*npart&0x80) ||
717 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
722 /* string match after a star failed, advance and try again */
736 /* at this point, a part was matched successfully */
737 if (may_advance && npart==nend && hpart<hend)
738 /* needle ends, but haystack does not: if there was a star before, advance and try again */
748 return (h==hend ? 1 : may_advance);
752 /*************************************************
753 * ASCII numeric comparison *
754 *************************************************/
758 a first numeric string
759 b second numeric string
760 relop relational operator
762 Returns: 0 not (a relop b)
766 static int eq_asciinumeric(const struct String *a,
767 const struct String *b, enum RelOp relop)
770 const uschar *as,*aend,*bs,*bend;
774 aend=a->character+a->length;
776 bend=b->character+b->length;
778 while (*as>='0' && *as<='9' && as<aend) ++as;
780 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
783 if (al && bl==0) cmp=-1;
784 else if (al==0 && bl==0) cmp=0;
785 else if (al==0 && bl) cmp=1;
789 if (cmp==0) cmp=memcmp(a->character,b->character,al);
793 case LT: return cmp<0;
794 case LE: return cmp<=0;
795 case EQ: return cmp==0;
796 case GE: return cmp>=0;
797 case GT: return cmp>0;
798 case NE: return cmp!=0;
805 /*************************************************
807 *************************************************/
811 filter points to the Sieve filter including its state
812 needle UTF-8 pattern or string to search ...
813 haystack ... inside the haystack
817 Returns: 0 needle not found in haystack
819 -1 comparator does not offer matchtype
822 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
823 enum Comparator co, enum MatchType mt)
827 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
828 (debug_selector & D_filter) != 0)
830 debug_printf("String comparison (match ");
833 case MATCH_IS: debug_printf(":is"); break;
834 case MATCH_CONTAINS: debug_printf(":contains"); break;
835 case MATCH_MATCHES: debug_printf(":matches"); break;
837 debug_printf(", comparison \"");
840 case COMP_OCTET: debug_printf("i;octet"); break;
841 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
842 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
844 debug_printf("\"):\n");
845 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
846 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
856 if (eq_octet(needle,haystack,0)) r=1;
859 case COMP_EN_ASCII_CASEMAP:
861 if (eq_asciicase(needle,haystack,0)) r=1;
864 case COMP_ASCII_NUMERIC:
866 if (!filter->require_iascii_numeric)
868 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
871 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
885 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
888 case COMP_EN_ASCII_CASEMAP:
890 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
895 filter->errmsg=CUS "comparator does not offer specified matchtype";
907 if ((r=eq_glob(needle,haystack,0,1))==-1)
909 filter->errmsg=CUS "syntactically invalid pattern";
914 case COMP_EN_ASCII_CASEMAP:
916 if ((r=eq_glob(needle,haystack,1,1))==-1)
918 filter->errmsg=CUS "syntactically invalid pattern";
925 filter->errmsg=CUS "comparator does not offer specified matchtype";
932 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
933 (debug_selector & D_filter) != 0)
934 debug_printf(" Result %s\n",r?"true":"false");
939 /*************************************************
940 * Check header field syntax *
941 *************************************************/
944 RFC 2822, section 3.6.8 says:
948 ftext = %d33-57 / ; Any character except
949 %d59-126 ; controls, SP, and
952 That forbids 8-bit header fields. This implementation accepts them, since
953 all of Exim is 8-bit clean, so it adds %d128-%d255.
956 header header field to quote for suitable use in Exim expansions
958 Returns: 0 string is not a valid header field
959 1 string is a value header field
962 static int is_header(const struct String *header)
972 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
983 /*************************************************
984 * Quote special characters string *
985 *************************************************/
989 header header field to quote for suitable use in Exim expansions
992 Returns: quoted string
995 static const uschar *quote(const struct String *header)
1003 h=header->character;
1010 quoted=string_catn(quoted,&size,&ptr,CUS "\\0",2);
1017 quoted=string_catn(quoted,&size,&ptr,CUS "\\",1);
1021 quoted=string_catn(quoted,&size,&ptr,h,1);
1027 quoted=string_catn(quoted,&size,&ptr,CUS "",1);
1032 /*************************************************
1033 * Add address to list of generated addresses *
1034 *************************************************/
1037 According to RFC 5228, duplicate delivery to the same address must
1038 not happen, so the list is first searched for the address.
1041 generated list of generated addresses
1042 addr new address to add
1043 file address denotes a file
1049 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1051 address_item *new_addr;
1053 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1054 if ( Ustrcmp(new_addr->address,addr) == 0
1056 || testflag(new_addr, af_pfr)
1057 || testflag(new_addr, af_file)
1061 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1062 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1067 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1068 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1070 new_addr = deliver_make_addr(addr,TRUE);
1073 setflag(new_addr, af_pfr);
1074 setflag(new_addr, af_file);
1077 new_addr->prop.errors_address = NULL;
1078 new_addr->next = *generated;
1079 *generated = new_addr;
1083 /*************************************************
1084 * Return decoded header field *
1085 *************************************************/
1088 Unfold the header field as described in RFC 2822 and remove all
1089 leading and trailing white space, then perform MIME decoding and
1090 translate the header field to UTF-8.
1093 value returned value of the field
1094 header name of the header field
1096 Returns: nothing The expanded string is empty
1097 in case there is no such header
1100 static void expand_header(struct String *value, const struct String *header)
1106 value->character=(uschar*)0;
1108 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1109 while (*r==' ' || *r=='\t') ++r;
1117 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1119 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1123 /*************************************************
1124 * Parse remaining hash comment *
1125 *************************************************/
1129 Comment up to terminating CRLF
1132 filter points to the Sieve filter including its state
1138 static int parse_hashcomment(struct Sieve *filter)
1144 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1146 if (*filter->pc=='\n')
1159 filter->errmsg=CUS "missing end of comment";
1164 /*************************************************
1165 * Parse remaining C-style comment *
1166 *************************************************/
1170 Everything up to star slash
1173 filter points to the Sieve filter including its state
1179 static int parse_comment(struct Sieve *filter)
1184 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1191 filter->errmsg=CUS "missing end of comment";
1196 /*************************************************
1197 * Parse optional white space *
1198 *************************************************/
1202 Spaces, tabs, CRLFs, hash comments or C-style comments
1205 filter points to the Sieve filter including its state
1211 static int parse_white(struct Sieve *filter)
1215 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1217 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1219 else if (*filter->pc=='\n')
1229 else if (*filter->pc=='#')
1231 if (parse_hashcomment(filter)==-1) return -1;
1233 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1235 if (parse_comment(filter)==-1) return -1;
1243 #ifdef ENCODED_CHARACTER
1244 /*************************************************
1245 * Decode hex-encoded-character string *
1246 *************************************************/
1249 Encoding definition:
1250 blank = SP / TAB / CRLF
1251 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1252 hex-pair = 1*2HEXDIG
1255 src points to a hex-pair-seq
1256 end points to its end
1257 dst points to the destination of the decoded octets,
1258 optionally to (uschar*)0 for checking only
1260 Returns: >=0 number of decoded octets
1264 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1268 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1273 for (x=0,d=0; d<2 && src<end && isxdigit(n=tolower(*src)); x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')),++d,++src);
1274 if (d==0) return -1;
1277 if (src==end) return decoded;
1278 if (*src==' ' || *src=='\t' || *src=='\n')
1279 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1288 /*************************************************
1289 * Decode unicode-encoded-character string *
1290 *************************************************/
1293 Encoding definition:
1294 blank = SP / TAB / CRLF
1295 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1296 unicode-hex = 1*HEXDIG
1298 It is an error for a script to use a hexadecimal value that isn't in
1299 either the range 0 to D7FF or the range E000 to 10FFFF.
1301 At this time, strings are already scanned, thus the CRLF is converted
1302 to the internally used \n (should RFC_EOL have been used).
1305 src points to a unicode-hex-seq
1306 end points to its end
1307 dst points to the destination of the decoded octets,
1308 optionally to (uschar*)0 for checking only
1310 Returns: >=0 number of decoded octets
1312 -2 semantic error (character range violation)
1315 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1319 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1326 for (hex_seq=src; src<end && *src=='0'; ++src);
1327 for (c=0,d=0; d<7 && src<end && isxdigit(n=tolower(*src)); c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')),++d,++src);
1328 if (src==hex_seq) return -1;
1329 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1335 else if (c>=0x80 && c<=0x7ff)
1340 *dst++=128+(c&0x3f);
1344 else if (c>=0x800 && c<=0xffff)
1349 *dst++=128+((c>>6)&0x3f);
1350 *dst++=128+(c&0x3f);
1354 else if (c>=0x10000 && c<=0x1fffff)
1359 *dst++=128+((c>>10)&0x3f);
1360 *dst++=128+((c>>6)&0x3f);
1361 *dst++=128+(c&0x3f);
1365 if (*src==' ' || *src=='\t' || *src=='\n')
1367 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1368 if (src==end) return decoded;
1377 /*************************************************
1378 * Decode encoded-character string *
1379 *************************************************/
1382 Encoding definition:
1383 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1384 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1387 encoded points to an encoded string, returns decoded string
1388 filter points to the Sieve filter including its state
1394 static int string_decode(struct Sieve *filter, struct String *data)
1396 uschar *src,*dst,*end;
1398 src=data->character;
1400 end=data->character+data->length;
1406 strncmpic(src,US "${hex:",6)==0
1407 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1408 && (hex_decode(src+6,brace,(uschar*)0))>=0
1411 dst+=hex_decode(src+6,brace,dst);
1415 strncmpic(src,US "${unicode:",10)==0
1416 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1419 switch (unicode_decode(src+10,brace,(uschar*)0))
1423 filter->errmsg=CUS "unicode character out of range";
1433 dst+=unicode_decode(src+10,brace,dst);
1440 data->length=dst-data->character;
1447 /*************************************************
1448 * Parse an optional string *
1449 *************************************************/
1453 quoted-string = DQUOTE *CHAR DQUOTE
1454 ;; in general, \ CHAR inside a string maps to CHAR
1455 ;; so \" maps to " and \\ maps to \
1456 ;; note that newlines and other characters are all allowed
1459 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1460 *(multi-line-literal / multi-line-dotstuff)
1462 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1463 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1464 ;; A line containing only "." ends the multi-line.
1465 ;; Remove a leading '.' if followed by another '.'.
1466 string = quoted-string / multi-line
1469 filter points to the Sieve filter including its state
1470 id specifies identifier to match
1474 0 identifier not matched
1477 static int parse_string(struct Sieve *filter, struct String *data)
1482 data->character=(uschar*)0;
1483 if (*filter->pc=='"') /* quoted string */
1488 if (*filter->pc=='"') /* end of string */
1490 int foo=data->length;
1493 /* that way, there will be at least one character allocated */
1494 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1495 #ifdef ENCODED_CHARACTER
1496 if (filter->require_encoded_character
1497 && string_decode(filter,data)==-1)
1502 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1504 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1507 else /* regular character */
1510 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1512 if (*filter->pc=='\n')
1514 data->character=string_catn(data->character,&dataCapacity,&data->length,US"\r",1);
1518 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1522 filter->errmsg=CUS "missing end of string";
1525 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1528 /* skip optional white space followed by hashed comment or CRLF */
1529 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1530 if (*filter->pc=='#')
1532 if (parse_hashcomment(filter)==-1) return -1;
1535 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1537 else if (*filter->pc=='\n')
1549 filter->errmsg=CUS "syntax error";
1555 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1557 if (*filter->pc=='\n') /* end of line */
1560 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1568 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1570 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1573 int foo=data->length;
1575 /* that way, there will be at least one character allocated */
1576 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1583 #ifdef ENCODED_CHARACTER
1584 if (filter->require_encoded_character
1585 && string_decode(filter,data)==-1)
1590 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1592 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS ".",1);
1596 else /* regular character */
1598 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1602 filter->errmsg=CUS "missing end of multi line string";
1609 /*************************************************
1610 * Parse a specific identifier *
1611 *************************************************/
1615 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1618 filter points to the Sieve filter including its state
1619 id specifies identifier to match
1622 0 identifier not matched
1625 static int parse_identifier(struct Sieve *filter, const uschar *id)
1627 size_t idlen=Ustrlen(id);
1629 if (strncmpic(US filter->pc,US id,idlen)==0)
1631 uschar next=filter->pc[idlen];
1633 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1641 /*************************************************
1643 *************************************************/
1647 number = 1*DIGIT [QUANTIFIER]
1648 QUANTIFIER = "K" / "M" / "G"
1651 filter points to the Sieve filter including its state
1655 -1 no string list found
1658 static int parse_number(struct Sieve *filter, unsigned long *data)
1662 if (*filter->pc>='0' && *filter->pc<='9')
1667 d=Ustrtoul(filter->pc,&e,10);
1670 filter->errmsg=CUstrerror(ERANGE);
1675 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1676 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1677 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1678 if (d>(ULONG_MAX/u))
1680 filter->errmsg=CUstrerror(ERANGE);
1689 filter->errmsg=CUS "missing number";
1695 /*************************************************
1696 * Parse a string list *
1697 *************************************************/
1701 string-list = "[" string *("," string) "]" / string
1704 filter points to the Sieve filter including its state
1705 data returns string list
1708 -1 no string list found
1712 parse_stringlist(struct Sieve *filter, struct String **data)
1714 const uschar *orig=filter->pc;
1715 int dataCapacity = 0;
1717 struct String *d = NULL;
1720 if (*filter->pc=='[') /* string list */
1725 if (parse_white(filter)==-1) goto error;
1726 if (dataLength+1 >= dataCapacity) /* increase buffer */
1729 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1731 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1732 new = store_get(sizeof(struct String) * dataCapacity);
1734 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1738 m=parse_string(filter,&d[dataLength]);
1741 if (dataLength==0) break;
1744 filter->errmsg=CUS "missing string";
1748 else if (m==-1) goto error;
1750 if (parse_white(filter)==-1) goto error;
1751 if (*filter->pc==',') ++filter->pc;
1754 if (*filter->pc==']')
1756 d[dataLength].character=(uschar*)0;
1757 d[dataLength].length=-1;
1764 filter->errmsg=CUS "missing closing bracket";
1768 else /* single string */
1770 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1774 m=parse_string(filter,&d[0]);
1786 d[1].character=(uschar*)0;
1793 filter->errmsg=CUS "missing string list";
1798 /*************************************************
1799 * Parse an optional address part specifier *
1800 *************************************************/
1804 address-part = ":localpart" / ":domain" / ":all"
1805 address-part =/ ":user" / ":detail"
1808 filter points to the Sieve filter including its state
1809 a returns address part specified
1812 0 no comparator found
1816 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1819 if (parse_identifier(filter,CUS ":user")==1)
1821 if (!filter->require_subaddress)
1823 filter->errmsg=CUS "missing previous require \"subaddress\";";
1829 else if (parse_identifier(filter,CUS ":detail")==1)
1831 if (!filter->require_subaddress)
1833 filter->errmsg=CUS "missing previous require \"subaddress\";";
1841 if (parse_identifier(filter,CUS ":localpart")==1)
1843 *a=ADDRPART_LOCALPART;
1846 else if (parse_identifier(filter,CUS ":domain")==1)
1851 else if (parse_identifier(filter,CUS ":all")==1)
1860 /*************************************************
1861 * Parse an optional comparator *
1862 *************************************************/
1866 comparator = ":comparator" <comparator-name: string>
1869 filter points to the Sieve filter including its state
1870 c returns comparator
1873 0 no comparator found
1874 -1 incomplete comparator found
1877 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1879 struct String comparator_name;
1881 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1882 if (parse_white(filter)==-1) return -1;
1883 switch (parse_string(filter,&comparator_name))
1888 filter->errmsg=CUS "missing comparator";
1895 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1900 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1902 *c=COMP_EN_ASCII_CASEMAP;
1905 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1907 *c=COMP_EN_ASCII_CASEMAP;
1910 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1912 *c=COMP_ASCII_NUMERIC;
1917 filter->errmsg=CUS "invalid comparator";
1926 /*************************************************
1927 * Parse an optional match type *
1928 *************************************************/
1932 match-type = ":is" / ":contains" / ":matches"
1935 filter points to the Sieve filter including its state
1936 m returns match type
1939 0 no match type found
1942 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1944 if (parse_identifier(filter,CUS ":is")==1)
1949 else if (parse_identifier(filter,CUS ":contains")==1)
1954 else if (parse_identifier(filter,CUS ":matches")==1)
1963 /*************************************************
1964 * Parse and interpret an optional test list *
1965 *************************************************/
1969 test-list = "(" test *("," test) ")"
1972 filter points to the Sieve filter including its state
1973 n total number of tests
1974 num_true number of passed tests
1975 exec Execute parsed statements
1978 0 no test list found
1979 -1 syntax or execution error
1982 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1984 if (parse_white(filter)==-1) return -1;
1985 if (*filter->pc=='(')
1994 switch (parse_test(filter,&cond,exec))
1997 case 0: filter->errmsg=CUS "missing test"; return -1;
1998 default: ++*n; if (cond) ++*num_true; break;
2000 if (parse_white(filter)==-1) return -1;
2001 if (*filter->pc==',') ++filter->pc;
2004 if (*filter->pc==')')
2011 filter->errmsg=CUS "missing closing paren";
2019 /*************************************************
2020 * Parse and interpret an optional test *
2021 *************************************************/
2025 filter points to the Sieve filter including its state
2026 cond returned condition status
2027 exec Execute parsed statements
2031 -1 syntax or execution error
2034 static int parse_test(struct Sieve *filter, int *cond, int exec)
2036 if (parse_white(filter)==-1) return -1;
2037 if (parse_identifier(filter,CUS "address"))
2040 address-test = "address" { [address-part] [comparator] [match-type] }
2041 <header-list: string-list> <key-list: string-list>
2043 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2046 enum AddressPart addressPart=ADDRPART_ALL;
2047 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2048 enum MatchType matchType=MATCH_IS;
2049 struct String *hdr,*h,*key,*k;
2055 if (parse_white(filter)==-1) return -1;
2056 if ((m=parse_addresspart(filter,&addressPart))!=0)
2058 if (m==-1) return -1;
2061 filter->errmsg=CUS "address part already specified";
2066 else if ((m=parse_comparator(filter,&comparator))!=0)
2068 if (m==-1) return -1;
2071 filter->errmsg=CUS "comparator already specified";
2076 else if ((m=parse_matchtype(filter,&matchType))!=0)
2078 if (m==-1) return -1;
2081 filter->errmsg=CUS "match type already specified";
2088 if (parse_white(filter)==-1) return -1;
2089 if ((m=parse_stringlist(filter,&hdr))!=1)
2091 if (m==0) filter->errmsg=CUS "header string list expected";
2094 if (parse_white(filter)==-1) return -1;
2095 if ((m=parse_stringlist(filter,&key))!=1)
2097 if (m==0) filter->errmsg=CUS "key string list expected";
2101 for (h=hdr; h->length!=-1 && !*cond; ++h)
2103 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2107 !eq_asciicase(h,&str_from,0)
2108 && !eq_asciicase(h,&str_to,0)
2109 && !eq_asciicase(h,&str_cc,0)
2110 && !eq_asciicase(h,&str_bcc,0)
2111 && !eq_asciicase(h,&str_sender,0)
2112 && !eq_asciicase(h,&str_resent_from,0)
2113 && !eq_asciicase(h,&str_resent_to,0)
2116 filter->errmsg=CUS "invalid header field";
2121 /* We are only interested in addresses below, so no MIME decoding */
2122 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2123 if (header_value == NULL)
2125 filter->errmsg=CUS "header string expansion failed";
2128 parse_allow_group = TRUE;
2129 while (*header_value && !*cond)
2132 int start, end, domain;
2136 end_addr = parse_find_address_end(header_value, FALSE);
2137 saveend = *end_addr;
2139 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2141 if (extracted_addr) switch (addressPart)
2143 case ADDRPART_ALL: part=extracted_addr; break;
2147 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2148 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2150 case ADDRPART_DETAIL: part=NULL; break;
2154 *end_addr = saveend;
2157 for (k=key; k->length!=-1; ++k)
2159 struct String partStr;
2161 partStr.character=part;
2162 partStr.length=Ustrlen(part);
2165 *cond=compare(filter,k,&partStr,comparator,matchType);
2166 if (*cond==-1) return -1;
2171 if (saveend == 0) break;
2172 header_value = end_addr + 1;
2174 parse_allow_group = FALSE;
2175 parse_found_group = FALSE;
2180 else if (parse_identifier(filter,CUS "allof"))
2183 allof-test = "allof" <tests: test-list>
2188 switch (parse_testlist(filter,&n,&num_true,exec))
2191 case 0: filter->errmsg=CUS "missing test list"; return -1;
2192 default: *cond=(n==num_true); return 1;
2195 else if (parse_identifier(filter,CUS "anyof"))
2198 anyof-test = "anyof" <tests: test-list>
2203 switch (parse_testlist(filter,&n,&num_true,exec))
2206 case 0: filter->errmsg=CUS "missing test list"; return -1;
2207 default: *cond=(num_true>0); return 1;
2210 else if (parse_identifier(filter,CUS "exists"))
2213 exists-test = "exists" <header-names: string-list>
2216 struct String *hdr,*h;
2219 if (parse_white(filter)==-1) return -1;
2220 if ((m=parse_stringlist(filter,&hdr))!=1)
2222 if (m==0) filter->errmsg=CUS "header string list expected";
2228 for (h=hdr; h->length!=-1 && *cond; ++h)
2232 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2233 if (header_def == NULL)
2235 filter->errmsg=CUS "header string expansion failed";
2238 if (Ustrcmp(header_def,"false")==0) *cond=0;
2243 else if (parse_identifier(filter,CUS "false"))
2246 false-test = "false"
2252 else if (parse_identifier(filter,CUS "header"))
2255 header-test = "header" { [comparator] [match-type] }
2256 <header-names: string-list> <key-list: string-list>
2259 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2260 enum MatchType matchType=MATCH_IS;
2261 struct String *hdr,*h,*key,*k;
2267 if (parse_white(filter)==-1) return -1;
2268 if ((m=parse_comparator(filter,&comparator))!=0)
2270 if (m==-1) return -1;
2273 filter->errmsg=CUS "comparator already specified";
2278 else if ((m=parse_matchtype(filter,&matchType))!=0)
2280 if (m==-1) return -1;
2283 filter->errmsg=CUS "match type already specified";
2290 if (parse_white(filter)==-1) return -1;
2291 if ((m=parse_stringlist(filter,&hdr))!=1)
2293 if (m==0) filter->errmsg=CUS "header string list expected";
2296 if (parse_white(filter)==-1) return -1;
2297 if ((m=parse_stringlist(filter,&key))!=1)
2299 if (m==0) filter->errmsg=CUS "key string list expected";
2303 for (h=hdr; h->length!=-1 && !*cond; ++h)
2307 filter->errmsg=CUS "invalid header field";
2312 struct String header_value;
2315 expand_header(&header_value,h);
2316 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2317 if (header_value.character == NULL || header_def == NULL)
2319 filter->errmsg=CUS "header string expansion failed";
2322 for (k=key; k->length!=-1; ++k)
2324 if (Ustrcmp(header_def,"true")==0)
2326 *cond=compare(filter,k,&header_value,comparator,matchType);
2327 if (*cond==-1) return -1;
2335 else if (parse_identifier(filter,CUS "not"))
2337 if (parse_white(filter)==-1) return -1;
2338 switch (parse_test(filter,cond,exec))
2341 case 0: filter->errmsg=CUS "missing test"; return -1;
2342 default: *cond=!*cond; return 1;
2345 else if (parse_identifier(filter,CUS "size"))
2348 relop = ":over" / ":under"
2349 size-test = "size" relop <limit: number>
2352 unsigned long limit;
2355 if (parse_white(filter)==-1) return -1;
2356 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2357 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2360 filter->errmsg=CUS "missing :over or :under";
2363 if (parse_white(filter)==-1) return -1;
2364 if (parse_number(filter,&limit)==-1) return -1;
2365 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2368 else if (parse_identifier(filter,CUS "true"))
2373 else if (parse_identifier(filter,CUS "envelope"))
2376 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2377 <envelope-part: string-list> <key-list: string-list>
2379 envelope-part is case insensitive "from" or "to"
2380 #ifdef ENVELOPE_AUTH
2381 envelope-part =/ "auth"
2385 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2386 enum AddressPart addressPart=ADDRPART_ALL;
2387 enum MatchType matchType=MATCH_IS;
2388 struct String *env,*e,*key,*k;
2392 if (!filter->require_envelope)
2394 filter->errmsg=CUS "missing previous require \"envelope\";";
2399 if (parse_white(filter)==-1) return -1;
2400 if ((m=parse_comparator(filter,&comparator))!=0)
2402 if (m==-1) return -1;
2405 filter->errmsg=CUS "comparator already specified";
2410 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2412 if (m==-1) return -1;
2415 filter->errmsg=CUS "address part already specified";
2420 else if ((m=parse_matchtype(filter,&matchType))!=0)
2422 if (m==-1) return -1;
2425 filter->errmsg=CUS "match type already specified";
2432 if (parse_white(filter)==-1) return -1;
2433 if ((m=parse_stringlist(filter,&env))!=1)
2435 if (m==0) filter->errmsg=CUS "envelope string list expected";
2438 if (parse_white(filter)==-1) return -1;
2439 if ((m=parse_stringlist(filter,&key))!=1)
2441 if (m==0) filter->errmsg=CUS "key string list expected";
2445 for (e=env; e->length!=-1 && !*cond; ++e)
2447 const uschar *envelopeExpr=CUS 0;
2448 uschar *envelope=US 0;
2450 if (eq_asciicase(e,&str_from,0))
2452 switch (addressPart)
2454 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2458 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2459 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2461 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2465 else if (eq_asciicase(e,&str_to,0))
2467 switch (addressPart)
2469 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2471 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2472 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2474 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2475 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2478 #ifdef ENVELOPE_AUTH
2479 else if (eq_asciicase(e,&str_auth,0))
2481 switch (addressPart)
2483 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2487 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2488 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2490 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2497 filter->errmsg=CUS "invalid envelope string";
2500 if (exec && envelopeExpr)
2502 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2504 filter->errmsg=CUS "header string expansion failed";
2507 for (k=key; k->length!=-1; ++k)
2509 struct String envelopeStr;
2511 envelopeStr.character=envelope;
2512 envelopeStr.length=Ustrlen(envelope);
2513 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2514 if (*cond==-1) return -1;
2522 else if (parse_identifier(filter,CUS "valid_notify_method"))
2525 valid_notify_method = "valid_notify_method"
2526 <notification-uris: string-list>
2529 struct String *uris,*u;
2532 if (!filter->require_enotify)
2534 filter->errmsg=CUS "missing previous require \"enotify\";";
2537 if (parse_white(filter)==-1) return -1;
2538 if ((m=parse_stringlist(filter,&uris))!=1)
2540 if (m==0) filter->errmsg=CUS "URI string list expected";
2546 for (u=uris; u->length!=-1 && *cond; ++u)
2548 string_item *recipient;
2549 struct String header,subject,body;
2553 header.character=(uschar*)0;
2555 subject.character=(uschar*)0;
2557 body.character=(uschar*)0;
2558 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2564 else if (parse_identifier(filter,CUS "notify_method_capability"))
2567 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2568 <notification-uri: string>
2569 <notification-capability: string>
2570 <key-list: string-list>
2576 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2577 enum MatchType matchType=MATCH_IS;
2578 struct String uri,capa,*keys,*k;
2580 if (!filter->require_enotify)
2582 filter->errmsg=CUS "missing previous require \"enotify\";";
2587 if (parse_white(filter)==-1) return -1;
2588 if ((m=parse_comparator(filter,&comparator))!=0)
2590 if (m==-1) return -1;
2593 filter->errmsg=CUS "comparator already specified";
2598 else if ((m=parse_matchtype(filter,&matchType))!=0)
2600 if (m==-1) return -1;
2603 filter->errmsg=CUS "match type already specified";
2610 if ((m=parse_string(filter,&uri))!=1)
2612 if (m==0) filter->errmsg=CUS "missing notification URI string";
2615 if (parse_white(filter)==-1) return -1;
2616 if ((m=parse_string(filter,&capa))!=1)
2618 if (m==0) filter->errmsg=CUS "missing notification capability string";
2621 if (parse_white(filter)==-1) return -1;
2622 if ((m=parse_stringlist(filter,&keys))!=1)
2624 if (m==0) filter->errmsg=CUS "missing key string list";
2629 string_item *recipient;
2630 struct String header,subject,body;
2635 header.character=(uschar*)0;
2637 subject.character=(uschar*)0;
2639 body.character=(uschar*)0;
2640 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2642 if (eq_asciicase(&capa,&str_online,0)==1)
2643 for (k=keys; k->length!=-1; ++k)
2645 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2646 if (*cond==-1) return -1;
2658 /*************************************************
2659 * Parse and interpret an optional block *
2660 *************************************************/
2664 filter points to the Sieve filter including its state
2665 exec Execute parsed statements
2666 generated where to hang newly-generated addresses
2668 Returns: 2 success by stop
2670 0 no block command found
2671 -1 syntax or execution error
2674 static int parse_block(struct Sieve *filter, int exec,
2675 address_item **generated)
2679 if (parse_white(filter)==-1) return -1;
2680 if (*filter->pc=='{')
2683 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2684 if (*filter->pc=='}')
2691 filter->errmsg=CUS "expecting command or closing brace";
2699 /*************************************************
2700 * Match a semicolon *
2701 *************************************************/
2705 filter points to the Sieve filter including its state
2711 static int parse_semicolon(struct Sieve *filter)
2713 if (parse_white(filter)==-1) return -1;
2714 if (*filter->pc==';')
2721 filter->errmsg=CUS "missing semicolon";
2727 /*************************************************
2728 * Parse and interpret a Sieve command *
2729 *************************************************/
2733 filter points to the Sieve filter including its state
2734 exec Execute parsed statements
2735 generated where to hang newly-generated addresses
2737 Returns: 2 success by stop
2739 -1 syntax or execution error
2742 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2746 if (parse_white(filter)==-1) return -1;
2747 if (parse_identifier(filter,CUS "if"))
2750 if-command = "if" test block *( "elsif" test block ) [ else block ]
2753 int cond,m,unsuccessful;
2756 if (parse_white(filter)==-1) return -1;
2757 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2760 filter->errmsg=CUS "missing test";
2763 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2764 (debug_selector & D_filter) != 0)
2766 if (exec) debug_printf("if %s\n",cond?"true":"false");
2768 m=parse_block(filter,exec ? cond : 0, generated);
2769 if (m==-1 || m==2) return m;
2772 filter->errmsg=CUS "missing block";
2775 unsuccessful = !cond;
2776 for (;;) /* elsif test block */
2778 if (parse_white(filter)==-1) return -1;
2779 if (parse_identifier(filter,CUS "elsif"))
2781 if (parse_white(filter)==-1) return -1;
2782 m=parse_test(filter,&cond,exec && unsuccessful);
2783 if (m==-1 || m==2) return m;
2786 filter->errmsg=CUS "missing test";
2789 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2790 (debug_selector & D_filter) != 0)
2792 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2794 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2795 if (m==-1 || m==2) return m;
2798 filter->errmsg=CUS "missing block";
2801 if (exec && unsuccessful && cond) unsuccessful = 0;
2806 if (parse_white(filter)==-1) return -1;
2807 if (parse_identifier(filter,CUS "else"))
2809 m=parse_block(filter,exec && unsuccessful, generated);
2810 if (m==-1 || m==2) return m;
2813 filter->errmsg=CUS "missing block";
2818 else if (parse_identifier(filter,CUS "stop"))
2821 stop-command = "stop" { stop-options } ";"
2825 if (parse_semicolon(filter)==-1) return -1;
2828 filter->pc+=Ustrlen(filter->pc);
2832 else if (parse_identifier(filter,CUS "keep"))
2835 keep-command = "keep" { keep-options } ";"
2839 if (parse_semicolon(filter)==-1) return -1;
2842 add_addr(generated,US"inbox",1,0,0,0);
2846 else if (parse_identifier(filter,CUS "discard"))
2849 discard-command = "discard" { discard-options } ";"
2853 if (parse_semicolon(filter)==-1) return -1;
2854 if (exec) filter->keep=0;
2856 else if (parse_identifier(filter,CUS "redirect"))
2859 redirect-command = "redirect" redirect-options "string" ";"
2861 redirect-options =) ":copy"
2864 struct String recipient;
2870 if (parse_white(filter)==-1) return -1;
2871 if (parse_identifier(filter,CUS ":copy")==1)
2873 if (!filter->require_copy)
2875 filter->errmsg=CUS "missing previous require \"copy\";";
2882 if (parse_white(filter)==-1) return -1;
2883 if ((m=parse_string(filter,&recipient))!=1)
2885 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2888 if (strchr(CCS recipient.character,'@')==(char*)0)
2890 filter->errmsg=CUS "unqualified recipient address";
2895 add_addr(generated,recipient.character,0,0,0,0);
2896 if (!copy) filter->keep = 0;
2898 if (parse_semicolon(filter)==-1) return -1;
2900 else if (parse_identifier(filter,CUS "fileinto"))
2903 fileinto-command = "fileinto" { fileinto-options } string ";"
2905 fileinto-options =) [ ":copy" ]
2908 struct String folder;
2911 unsigned long maxage, maxmessages, maxstorage;
2914 maxage = maxmessages = maxstorage = 0;
2915 if (!filter->require_fileinto)
2917 filter->errmsg=CUS "missing previous require \"fileinto\";";
2922 if (parse_white(filter)==-1) return -1;
2923 if (parse_identifier(filter,CUS ":copy")==1)
2925 if (!filter->require_copy)
2927 filter->errmsg=CUS "missing previous require \"copy\";";
2934 if (parse_white(filter)==-1) return -1;
2935 if ((m=parse_string(filter,&folder))!=1)
2937 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2940 m=0; s=folder.character;
2941 if (folder.length==0) m=1;
2942 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2945 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2950 filter->errmsg=CUS "invalid folder";
2955 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2956 if (!copy) filter->keep = 0;
2958 if (parse_semicolon(filter)==-1) return -1;
2961 else if (parse_identifier(filter,CUS "notify"))
2964 notify-command = "notify" { notify-options } <method: string> ";"
2965 notify-options = [":from" string]
2966 [":importance" <"1" / "2" / "3">]
2967 [":options" 1*(string-list / number)]
2973 struct String importance;
2974 struct String message;
2975 struct String method;
2976 struct Notification *already;
2977 string_item *recipient;
2978 struct String header;
2979 struct String subject;
2981 uschar *envelope_from;
2982 struct String auto_submitted_value;
2983 uschar *auto_submitted_def;
2985 if (!filter->require_enotify)
2987 filter->errmsg=CUS "missing previous require \"enotify\";";
2990 from.character=(uschar*)0;
2992 importance.character=(uschar*)0;
2993 importance.length=-1;
2994 message.character=(uschar*)0;
2998 header.character=(uschar*)0;
3000 subject.character=(uschar*)0;
3002 body.character=(uschar*)0;
3003 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3006 if (parse_white(filter)==-1) return -1;
3007 if (parse_identifier(filter,CUS ":from")==1)
3009 if (parse_white(filter)==-1) return -1;
3010 if ((m=parse_string(filter,&from))!=1)
3012 if (m==0) filter->errmsg=CUS "from string expected";
3016 else if (parse_identifier(filter,CUS ":importance")==1)
3018 if (parse_white(filter)==-1) return -1;
3019 if ((m=parse_string(filter,&importance))!=1)
3021 if (m==0) filter->errmsg=CUS "importance string expected";
3024 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3026 filter->errmsg=CUS "invalid importance";
3030 else if (parse_identifier(filter,CUS ":options")==1)
3032 if (parse_white(filter)==-1) return -1;
3034 else if (parse_identifier(filter,CUS ":message")==1)
3036 if (parse_white(filter)==-1) return -1;
3037 if ((m=parse_string(filter,&message))!=1)
3039 if (m==0) filter->errmsg=CUS "message string expected";
3045 if (parse_white(filter)==-1) return -1;
3046 if ((m=parse_string(filter,&method))!=1)
3048 if (m==0) filter->errmsg=CUS "missing method string";
3051 if (parse_semicolon(filter)==-1) return -1;
3052 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3056 if (message.length==-1) message=subject;
3057 if (message.length==-1) expand_header(&message,&str_subject);
3058 expand_header(&auto_submitted_value,&str_auto_submitted);
3059 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3060 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3062 filter->errmsg=CUS "header string expansion failed";
3065 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3067 for (already=filter->notified; already; already=already->next)
3069 if (already->method.length==method.length
3070 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3071 && already->importance.length==importance.length
3072 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3073 && already->message.length==message.length
3074 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3077 if (already==(struct Notification*)0)
3078 /* New notification, process it */
3080 struct Notification *sent;
3081 sent=store_get(sizeof(struct Notification));
3082 sent->method=method;
3083 sent->importance=importance;
3084 sent->message=message;
3085 sent->next=filter->notified;
3086 filter->notified=sent;
3087 #ifndef COMPILE_SYNTAX_CHECKER
3088 if (filter_test == FTEST_NONE)
3093 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3097 int buffer_capacity;
3099 f = fdopen(fd, "wb");
3100 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3101 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3102 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3103 if (header.length>0) fprintf(f,"%s",header.character);
3104 if (message.length==-1)
3106 message.character=US"Notification";
3107 message.length=Ustrlen(message.character);
3109 /* Allocation is larger than necessary, but enough even for split MIME words */
3110 buffer_capacity=32+4*message.length;
3111 buffer=store_get(buffer_capacity);
3112 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3114 if (body.length>0) fprintf(f,"%s\n",body.character);
3117 (void)child_close(pid, 0);
3120 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3122 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3128 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3130 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3136 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3138 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3145 else if (parse_identifier(filter,CUS "vacation"))
3148 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3149 vacation-options = [":days" number]
3152 [":addresses" string-list]
3159 struct String subject;
3161 struct String *addresses;
3163 string_item *aliases;
3164 struct String handle;
3165 struct String reason;
3167 if (!filter->require_vacation)
3169 filter->errmsg=CUS "missing previous require \"vacation\";";
3174 if (filter->vacation_ran)
3176 filter->errmsg=CUS "trying to execute vacation more than once";
3179 filter->vacation_ran=1;
3181 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3182 subject.character=(uschar*)0;
3184 from.character=(uschar*)0;
3186 addresses=(struct String*)0;
3189 handle.character=(uschar*)0;
3193 if (parse_white(filter)==-1) return -1;
3194 if (parse_identifier(filter,CUS ":days")==1)
3196 if (parse_white(filter)==-1) return -1;
3197 if (parse_number(filter,&days)==-1) return -1;
3198 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3199 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3201 else if (parse_identifier(filter,CUS ":subject")==1)
3203 if (parse_white(filter)==-1) return -1;
3204 if ((m=parse_string(filter,&subject))!=1)
3206 if (m==0) filter->errmsg=CUS "subject string expected";
3210 else if (parse_identifier(filter,CUS ":from")==1)
3212 if (parse_white(filter)==-1) return -1;
3213 if ((m=parse_string(filter,&from))!=1)
3215 if (m==0) filter->errmsg=CUS "from string expected";
3218 if (check_mail_address(filter,&from)!=1)
3221 else if (parse_identifier(filter,CUS ":addresses")==1)
3225 if (parse_white(filter)==-1) return -1;
3226 if ((m=parse_stringlist(filter,&addresses))!=1)
3228 if (m==0) filter->errmsg=CUS "addresses string list expected";
3231 for (a=addresses; a->length!=-1; ++a)
3235 new=store_get(sizeof(string_item));
3236 new->text=store_get(a->length+1);
3237 if (a->length) memcpy(new->text,a->character,a->length);
3238 new->text[a->length]='\0';
3243 else if (parse_identifier(filter,CUS ":mime")==1)
3245 else if (parse_identifier(filter,CUS ":handle")==1)
3247 if (parse_white(filter)==-1) return -1;
3248 if ((m=parse_string(filter,&from))!=1)
3250 if (m==0) filter->errmsg=CUS "handle string expected";
3256 if (parse_white(filter)==-1) return -1;
3257 if ((m=parse_string(filter,&reason))!=1)
3259 if (m==0) filter->errmsg=CUS "missing reason string";
3266 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3269 filter->errmsg=CUS "MIME reason string contains 8bit text";
3273 if (parse_semicolon(filter)==-1) return -1;
3280 int buffer_capacity;
3284 uschar hexdigest[33];
3288 if (filter_personal(aliases,TRUE))
3290 if (filter_test == FTEST_NONE)
3292 /* ensure oncelog directory exists; failure will be detected later */
3294 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3296 /* build oncelog filename */
3298 key.character=(uschar*)0;
3301 if (handle.length==-1)
3303 if (subject.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,subject.character,subject.length);
3304 if (from.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,from.character,from.length);
3305 key.character=string_catn(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3306 key.character=string_catn(key.character,&capacity,&key.length,reason.character,reason.length);
3311 md5_end(&base, key.character, key.length, digest);
3312 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3313 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3315 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3317 if (filter_test == FTEST_NONE)
3319 capacity=Ustrlen(filter->vacation_directory);
3321 once=string_catn(filter->vacation_directory,&capacity,&start,US"/",1);
3322 once=string_catn(once,&capacity,&start,hexdigest,33);
3325 /* process subject */
3327 if (subject.length==-1)
3329 uschar *subject_def;
3331 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3332 if (Ustrcmp(subject_def,"true")==0)
3334 expand_header(&subject,&str_subject);
3337 subject.character=string_catn(US"Auto: ",&capacity,&start,subject.character,subject.length);
3338 subject.length=start;
3342 subject.character=US"Automated reply";
3343 subject.length=Ustrlen(subject.character);
3347 /* add address to list of generated addresses */
3349 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3350 setflag(addr, af_pfr);
3351 addr->prop.ignore_error = TRUE;
3352 addr->next = *generated;
3354 addr->reply = store_get(sizeof(reply_item));
3355 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3356 addr->reply->to = string_copy(sender_address);
3357 if (from.length==-1)
3358 addr->reply->from = expand_string(US"$local_part@$domain");
3360 addr->reply->from = from.character;
3361 /* Allocation is larger than necessary, but enough even for split MIME words */
3362 buffer_capacity=32+4*subject.length;
3363 buffer=store_get(buffer_capacity);
3364 /* deconst cast safe as we pass in a non-const item */
3365 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3366 addr->reply->oncelog=once;
3367 addr->reply->once_repeat=days*86400;
3369 /* build body and MIME headers */
3373 uschar *mime_body,*reason_end;
3374 static const uschar nlnl[]="\r\n\r\n";
3378 mime_body=reason.character,reason_end=reason.character+reason.length;
3379 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3384 addr->reply->headers = string_catn(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3385 addr->reply->headers[start] = '\0';
3388 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3389 else mime_body=reason_end-1;
3390 addr->reply->text = string_catn(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3391 addr->reply->text[start] = '\0';
3395 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3398 start = reason.length;
3399 addr->reply->headers = US"MIME-Version: 1.0\n"
3400 "Content-Type: text/plain;\n"
3401 "\tcharset=\"utf-8\"\n"
3402 "Content-Transfer-Encoding: quoted-printable";
3403 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3407 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3409 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3420 /*************************************************
3421 * Parse and interpret a sieve filter *
3422 *************************************************/
3426 filter points to the Sieve filter including its state
3427 exec Execute parsed statements
3428 generated where to hang newly-generated addresses
3431 -1 syntax or execution error
3435 parse_start(struct Sieve *filter, int exec, address_item **generated)
3437 filter->pc=filter->filter;
3440 filter->require_envelope=0;
3441 filter->require_fileinto=0;
3442 #ifdef ENCODED_CHARACTER
3443 filter->require_encoded_character=0;
3445 #ifdef ENVELOPE_AUTH
3446 filter->require_envelope_auth=0;
3449 filter->require_enotify=0;
3450 filter->notified=(struct Notification*)0;
3453 filter->require_subaddress=0;
3456 filter->require_vacation=0;
3457 filter->vacation_ran=0;
3459 filter->require_copy=0;
3460 filter->require_iascii_numeric=0;
3462 if (parse_white(filter)==-1) return -1;
3464 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3467 struct dirent *oncelog;
3468 struct stat properties;
3471 /* clean up old vacation log databases */
3473 oncelogdir=opendir(CS filter->vacation_directory);
3475 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3477 filter->errmsg=CUS "unable to open vacation directory";
3481 if (oncelogdir != NULL)
3485 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3487 if (strlen(oncelog->d_name)==32)
3489 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3490 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3494 closedir(oncelogdir);
3498 while (parse_identifier(filter,CUS "require"))
3501 require-command = "require" <capabilities: string-list>
3504 struct String *cap,*check;
3507 if (parse_white(filter)==-1) return -1;
3508 if ((m=parse_stringlist(filter,&cap))!=1)
3510 if (m==0) filter->errmsg=CUS "capability string list expected";
3513 for (check=cap; check->character; ++check)
3515 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3516 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3517 #ifdef ENCODED_CHARACTER
3518 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3520 #ifdef ENVELOPE_AUTH
3521 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3524 else if (eq_octet(check,&str_enotify,0))
3526 if (filter->enotify_mailto_owner == NULL)
3528 filter->errmsg=CUS "enotify disabled";
3531 filter->require_enotify=1;
3535 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3538 else if (eq_octet(check,&str_vacation,0))
3540 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3542 filter->errmsg=CUS "vacation disabled";
3545 filter->require_vacation=1;
3548 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3549 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3550 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3551 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3552 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3555 filter->errmsg=CUS "unknown capability";
3559 if (parse_semicolon(filter)==-1) return -1;
3561 if (parse_commands(filter,exec,generated)==-1) return -1;
3564 filter->errmsg=CUS "syntax error";
3571 /*************************************************
3572 * Interpret a sieve filter file *
3573 *************************************************/
3577 filter points to the entire file, read into store as a single string
3578 options controls whether various special things are allowed, and requests
3579 special actions (not currently used)
3580 vacation_directory where to store vacation "once" files
3581 enotify_mailto_owner owner of mailto notifications
3582 useraddress string expression for :user part of address
3583 subaddress string expression for :subaddress part of address
3584 generated where to hang newly-generated addresses
3585 error where to pass back an error text
3587 Returns: FF_DELIVERED success, a significant action was taken
3588 FF_NOTDELIVERED success, no significant action
3589 FF_DEFER defer requested
3590 FF_FAIL fail requested
3591 FF_FREEZE freeze requested
3592 FF_ERROR there was a problem
3596 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3597 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3598 address_item **generated, uschar **error)
3604 options = options; /* Keep picky compilers happy */
3607 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3608 sieve.filter=filter;
3610 if (vacation_directory == NULL)
3611 sieve.vacation_directory = NULL;
3614 sieve.vacation_directory=expand_string(vacation_directory);
3615 if (sieve.vacation_directory == NULL)
3617 *error = string_sprintf("failed to expand \"%s\" "
3618 "(sieve_vacation_directory): %s", vacation_directory,
3619 expand_string_message);
3624 if (enotify_mailto_owner == NULL)
3625 sieve.enotify_mailto_owner = NULL;
3628 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3629 if (sieve.enotify_mailto_owner == NULL)
3631 *error = string_sprintf("failed to expand \"%s\" "
3632 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3633 expand_string_message);
3638 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3639 sieve.subaddress = subaddress;
3641 #ifdef COMPILE_SYNTAX_CHECKER
3642 if (parse_start(&sieve,0,generated)==1)
3644 if (parse_start(&sieve,1,generated)==1)
3649 add_addr(generated,US"inbox",1,0,0,0);
3650 msg = string_sprintf("Implicit keep");
3655 msg = string_sprintf("No implicit keep");
3661 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3662 #ifdef COMPILE_SYNTAX_CHECKER
3666 add_addr(generated,US"inbox",1,0,0,0);
3671 #ifndef COMPILE_SYNTAX_CHECKER
3672 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3673 else debug_printf("%s\n", msg);
3676 DEBUG(D_route) debug_printf("Sieve: end of processing\n");