1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* This code was contributed by Michael Haardt. */
11 /* Sieve mail filter. */
25 /* Define this for RFC compliant \r\n end-of-line terminators. */
26 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
29 /* Define this for development of the Sieve extension "encoded-character". */
30 #define ENCODED_CHARACTER
32 /* Define this for development of the Sieve extension "envelope-auth". */
35 /* Define this for development of the Sieve extension "enotify". */
38 /* Define this for the Sieve extension "subaddress". */
41 /* Define this for the Sieve extension "vacation". */
45 #define VACATION_MIN_DAYS 1
46 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
47 #define VACATION_MAX_DAYS 31
49 /* Keep this at 75 to accept only RFC compliant MIME words. */
50 /* Increase it if you want to match headers from buggy MUAs. */
51 #define MIMEWORD_LENGTH 75
62 #ifdef ENCODED_CHARACTER
63 int require_encoded_character;
66 int require_envelope_auth;
70 struct Notification *notified;
72 uschar *enotify_mailto_owner;
74 int require_subaddress;
80 uschar *vacation_directory;
81 const uschar *subaddress;
82 const uschar *useraddress;
84 int require_iascii_numeric;
87 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
88 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
90 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
92 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94 enum RelOp { LT, LE, EQ, GE, GT, NE };
104 struct String method;
105 struct String importance;
106 struct String message;
107 struct Notification *next;
110 /* This should be a complete list of supported extensions, so that an external
111 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
112 list of extensions and provide correct information to a client.
114 We'll emit the list in the order given here; keep it alphabetically sorted, so
115 that callers don't get surprised.
117 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
119 const uschar *exim_sieve_extension_list[] = {
120 CUS"comparator-i;ascii-numeric",
122 #ifdef ENCODED_CHARACTER
123 CUS"encoded-character",
142 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
143 static int parse_test(struct Sieve *filter, int *cond, int exec);
144 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
146 static uschar str_from_c[]="From";
147 static const struct String str_from={ str_from_c, 4 };
148 static uschar str_to_c[]="To";
149 static const struct String str_to={ str_to_c, 2 };
150 static uschar str_cc_c[]="Cc";
151 static const struct String str_cc={ str_cc_c, 2 };
152 static uschar str_bcc_c[]="Bcc";
153 static const struct String str_bcc={ str_bcc_c, 3 };
154 static uschar str_auth_c[]="auth";
155 static const struct String str_auth={ str_auth_c, 4 };
156 static uschar str_sender_c[]="Sender";
157 static const struct String str_sender={ str_sender_c, 6 };
158 static uschar str_resent_from_c[]="Resent-From";
159 static const struct String str_resent_from={ str_resent_from_c, 11 };
160 static uschar str_resent_to_c[]="Resent-To";
161 static const struct String str_resent_to={ str_resent_to_c, 9 };
162 static uschar str_fileinto_c[]="fileinto";
163 static const struct String str_fileinto={ str_fileinto_c, 8 };
164 static uschar str_envelope_c[]="envelope";
165 static const struct String str_envelope={ str_envelope_c, 8 };
166 #ifdef ENCODED_CHARACTER
167 static uschar str_encoded_character_c[]="encoded-character";
168 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
171 static uschar str_envelope_auth_c[]="envelope-auth";
172 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
175 static uschar str_enotify_c[]="enotify";
176 static const struct String str_enotify={ str_enotify_c, 7 };
177 static uschar str_online_c[]="online";
178 static const struct String str_online={ str_online_c, 6 };
179 static uschar str_maybe_c[]="maybe";
180 static const struct String str_maybe={ str_maybe_c, 5 };
181 static uschar str_auto_submitted_c[]="Auto-Submitted";
182 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
185 static uschar str_subaddress_c[]="subaddress";
186 static const struct String str_subaddress={ str_subaddress_c, 10 };
189 static uschar str_vacation_c[]="vacation";
190 static const struct String str_vacation={ str_vacation_c, 8 };
191 static uschar str_subject_c[]="Subject";
192 static const struct String str_subject={ str_subject_c, 7 };
194 static uschar str_copy_c[]="copy";
195 static const struct String str_copy={ str_copy_c, 4 };
196 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
197 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
198 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
199 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
200 static uschar str_ioctet_c[]="i;octet";
201 static const struct String str_ioctet={ str_ioctet_c, 7 };
202 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
203 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
204 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
205 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
206 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
207 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
208 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
209 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
210 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
211 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
214 /*************************************************
215 * Encode to quoted-printable *
216 *************************************************/
227 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
230 const uschar *start,*end;
235 /* Two passes: one to count output allocation size, second
236 to do the encoding */
238 for (pass=0; pass<=1; ++pass)
245 dst->character=store_get(dst->length+1); /* plus one for \0 */
248 for (start=src->character,end=start+src->length; start<end; ++start)
251 if (line>=73) /* line length limit */
257 *new++='='; /* line split */
262 if ( (ch>='!' && ch<='<')
263 || (ch>='>' && ch<='~')
264 || ( (ch=='\t' || ch==' ')
266 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
273 *new++=*start; /* copy char */
276 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
281 *new++='\n'; /* NL */
283 ++start; /* consume extra input char */
291 new += sprintf(CS new,"=%02X",ch);
298 *new='\0'; /* not included in length, but nice */
303 /*************************************************
304 * Check mail address for correct syntax *
305 *************************************************/
308 Check mail address for being syntactically correct.
311 filter points to the Sieve filter including its state
312 address String containing one address
315 1 Mail address is syntactically OK
319 int check_mail_address(struct Sieve *filter, const struct String *address)
321 int start, end, domain;
324 if (address->length>0)
326 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
330 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
331 address->character, error);
339 filter->errmsg=CUS "empty address";
345 /*************************************************
346 * Decode URI encoded string *
347 *************************************************/
351 str URI encoded string
354 0 Decoding successful
359 static int uri_decode(struct String *str)
363 if (str->length==0) return 0;
364 for (s=str->character,t=s,e=s+str->length; s<e; )
368 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
370 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
371 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
380 str->length=t-str->character;
385 /*************************************************
387 *************************************************/
392 mailtoURI = "mailto:" [ to ] [ headers ]
393 to = [ addr-spec *("%2C" addr-spec ) ]
394 headers = "?" header *( "&" header )
395 header = hname "=" hvalue
400 filter points to the Sieve filter including its state
401 uri URI, excluding scheme
406 1 URI is syntactically OK
411 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
414 struct String to,hname,hvalue;
418 if (Ustrncmp(uri,"mailto:",7))
420 filter->errmsg=US "Unknown URI scheme";
424 if (*uri && *uri!='?')
428 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
434 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
435 to.character[to.length]='\0';
436 if (uri_decode(&to)==-1)
438 filter->errmsg=US"Invalid URI encoding";
441 new=store_get(sizeof(string_item));
442 new->text=store_get(to.length+1);
443 if (to.length) memcpy(new->text,to.character,to.length);
444 new->text[to.length]='\0';
445 new->next=*recipient;
450 filter->errmsg=US"Missing addr-spec in URI";
453 if (*uri=='%') uri+=3;
462 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
466 hname.character= NULL;
468 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
469 hname.character[hname.length]='\0';
470 if (uri_decode(&hname)==-1)
472 filter->errmsg=US"Invalid URI encoding";
481 filter->errmsg=US"Missing equal after hname";
485 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
489 hvalue.character= NULL;
491 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
492 hvalue.character[hvalue.length]='\0';
493 if (uri_decode(&hvalue)==-1)
495 filter->errmsg=US"Invalid URI encoding";
499 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
501 new=store_get(sizeof(string_item));
502 new->text=store_get(hvalue.length+1);
503 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
504 new->text[hvalue.length]='\0';
505 new->next=*recipient;
508 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
510 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
514 static struct String ignore[]=
520 {US"auto-submitted",14}
522 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
525 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
528 if (header->length==-1) header->length=0;
529 capacity=header->length;
530 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
531 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
532 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
533 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
534 header->character[header->length]='\0';
537 if (*uri=='&') ++uri;
543 filter->errmsg=US"Syntactically invalid URI";
551 /*************************************************
552 * Octet-wise string comparison *
553 *************************************************/
557 needle UTF-8 string to search ...
558 haystack ... inside the haystack
559 match_prefix 1 to compare if needle is a prefix of haystack
561 Returns: 0 needle not found in haystack
565 static int eq_octet(const struct String *needle,
566 const struct String *haystack, int match_prefix)
574 h=haystack->character;
578 if (*n&0x80) return 0;
579 if (*h&0x80) return 0;
581 if (*n!=*h) return 0;
587 return (match_prefix ? nl==0 : nl==0 && hl==0);
591 /*************************************************
592 * ASCII case-insensitive string comparison *
593 *************************************************/
597 needle UTF-8 string to search ...
598 haystack ... inside the haystack
599 match_prefix 1 to compare if needle is a prefix of haystack
601 Returns: 0 needle not found in haystack
605 static int eq_asciicase(const struct String *needle,
606 const struct String *haystack, int match_prefix)
615 h=haystack->character;
621 if (nc&0x80) return 0;
622 if (hc&0x80) return 0;
624 /* tolower depends on the locale and only ASCII case must be insensitive */
625 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
631 return (match_prefix ? nl==0 : nl==0 && hl==0);
635 /*************************************************
636 * Glob pattern search *
637 *************************************************/
641 needle pattern to search ...
642 haystack ... inside the haystack
643 ascii_caseless ignore ASCII case
644 match_octet match octets, not UTF-8 multi-octet characters
646 Returns: 0 needle not found in haystack
651 static int eq_glob(const struct String *needle,
652 const struct String *haystack, int ascii_caseless, int match_octet)
654 const uschar *n,*h,*nend,*hend;
658 h=haystack->character;
659 nend=n+needle->length;
660 hend=h+haystack->length;
670 const uschar *npart,*hpart;
672 /* Try to match a non-star part of the needle at the current */
673 /* position in the haystack. */
677 while (npart<nend && *npart!='*') switch (*npart)
681 if (hpart==hend) return 0;
686 /* Match one UTF8 encoded character */
687 if ((*hpart&0xc0)==0xc0)
690 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
701 if (npart==nend) return -1;
706 if (hpart==hend) return 0;
707 /* tolower depends on the locale, but we need ASCII */
711 (*hpart&0x80) || (*npart&0x80) ||
714 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
719 /* string match after a star failed, advance and try again */
733 /* at this point, a part was matched successfully */
734 if (may_advance && npart==nend && hpart<hend)
735 /* needle ends, but haystack does not: if there was a star before, advance and try again */
745 return (h==hend ? 1 : may_advance);
749 /*************************************************
750 * ASCII numeric comparison *
751 *************************************************/
755 a first numeric string
756 b second numeric string
757 relop relational operator
759 Returns: 0 not (a relop b)
763 static int eq_asciinumeric(const struct String *a,
764 const struct String *b, enum RelOp relop)
767 const uschar *as,*aend,*bs,*bend;
771 aend=a->character+a->length;
773 bend=b->character+b->length;
775 while (*as>='0' && *as<='9' && as<aend) ++as;
777 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
780 if (al && bl==0) cmp=-1;
781 else if (al==0 && bl==0) cmp=0;
782 else if (al==0 && bl) cmp=1;
786 if (cmp==0) cmp=memcmp(a->character,b->character,al);
790 case LT: return cmp<0;
791 case LE: return cmp<=0;
792 case EQ: return cmp==0;
793 case GE: return cmp>=0;
794 case GT: return cmp>0;
795 case NE: return cmp!=0;
802 /*************************************************
804 *************************************************/
808 filter points to the Sieve filter including its state
809 needle UTF-8 pattern or string to search ...
810 haystack ... inside the haystack
814 Returns: 0 needle not found in haystack
816 -1 comparator does not offer matchtype
819 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
820 enum Comparator co, enum MatchType mt)
824 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
825 (debug_selector & D_filter) != 0)
827 debug_printf("String comparison (match ");
830 case MATCH_IS: debug_printf(":is"); break;
831 case MATCH_CONTAINS: debug_printf(":contains"); break;
832 case MATCH_MATCHES: debug_printf(":matches"); break;
834 debug_printf(", comparison \"");
837 case COMP_OCTET: debug_printf("i;octet"); break;
838 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
839 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
841 debug_printf("\"):\n");
842 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
843 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
853 if (eq_octet(needle,haystack,0)) r=1;
856 case COMP_EN_ASCII_CASEMAP:
858 if (eq_asciicase(needle,haystack,0)) r=1;
861 case COMP_ASCII_NUMERIC:
863 if (!filter->require_iascii_numeric)
865 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
868 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
882 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
885 case COMP_EN_ASCII_CASEMAP:
887 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
892 filter->errmsg=CUS "comparator does not offer specified matchtype";
904 if ((r=eq_glob(needle,haystack,0,1))==-1)
906 filter->errmsg=CUS "syntactically invalid pattern";
911 case COMP_EN_ASCII_CASEMAP:
913 if ((r=eq_glob(needle,haystack,1,1))==-1)
915 filter->errmsg=CUS "syntactically invalid pattern";
922 filter->errmsg=CUS "comparator does not offer specified matchtype";
929 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
930 (debug_selector & D_filter) != 0)
931 debug_printf(" Result %s\n",r?"true":"false");
936 /*************************************************
937 * Check header field syntax *
938 *************************************************/
941 RFC 2822, section 3.6.8 says:
945 ftext = %d33-57 / ; Any character except
946 %d59-126 ; controls, SP, and
949 That forbids 8-bit header fields. This implementation accepts them, since
950 all of Exim is 8-bit clean, so it adds %d128-%d255.
953 header header field to quote for suitable use in Exim expansions
955 Returns: 0 string is not a valid header field
956 1 string is a value header field
959 static int is_header(const struct String *header)
969 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
980 /*************************************************
981 * Quote special characters string *
982 *************************************************/
986 header header field to quote for suitable use in Exim expansions
989 Returns: quoted string
992 static const uschar *quote(const struct String *header)
1000 h=header->character;
1007 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
1014 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
1018 quoted=string_cat(quoted,&size,&ptr,h,1);
1024 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
1029 /*************************************************
1030 * Add address to list of generated addresses *
1031 *************************************************/
1034 According to RFC 5228, duplicate delivery to the same address must
1035 not happen, so the list is first searched for the address.
1038 generated list of generated addresses
1039 addr new address to add
1040 file address denotes a file
1045 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1047 address_item *new_addr;
1049 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1051 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1053 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1055 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1061 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1063 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1065 new_addr=deliver_make_addr(addr,TRUE);
1068 setflag(new_addr, af_pfr|af_file);
1071 new_addr->prop.errors_address = NULL;
1072 new_addr->next = *generated;
1073 *generated = new_addr;
1077 /*************************************************
1078 * Return decoded header field *
1079 *************************************************/
1082 Unfold the header field as described in RFC 2822 and remove all
1083 leading and trailing white space, then perform MIME decoding and
1084 translate the header field to UTF-8.
1087 value returned value of the field
1088 header name of the header field
1090 Returns: nothing The expanded string is empty
1091 in case there is no such header
1094 static void expand_header(struct String *value, const struct String *header)
1100 value->character=(uschar*)0;
1102 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1103 while (*r==' ' || *r=='\t') ++r;
1111 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1113 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1117 /*************************************************
1118 * Parse remaining hash comment *
1119 *************************************************/
1123 Comment up to terminating CRLF
1126 filter points to the Sieve filter including its state
1132 static int parse_hashcomment(struct Sieve *filter)
1138 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1140 if (*filter->pc=='\n')
1153 filter->errmsg=CUS "missing end of comment";
1158 /*************************************************
1159 * Parse remaining C-style comment *
1160 *************************************************/
1164 Everything up to star slash
1167 filter points to the Sieve filter including its state
1173 static int parse_comment(struct Sieve *filter)
1178 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1185 filter->errmsg=CUS "missing end of comment";
1190 /*************************************************
1191 * Parse optional white space *
1192 *************************************************/
1196 Spaces, tabs, CRLFs, hash comments or C-style comments
1199 filter points to the Sieve filter including its state
1205 static int parse_white(struct Sieve *filter)
1209 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1211 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1213 else if (*filter->pc=='\n')
1223 else if (*filter->pc=='#')
1225 if (parse_hashcomment(filter)==-1) return -1;
1227 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1229 if (parse_comment(filter)==-1) return -1;
1237 #ifdef ENCODED_CHARACTER
1238 /*************************************************
1239 * Decode hex-encoded-character string *
1240 *************************************************/
1243 Encoding definition:
1244 blank = SP / TAB / CRLF
1245 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1246 hex-pair = 1*2HEXDIG
1249 src points to a hex-pair-seq
1250 end points to its end
1251 dst points to the destination of the decoded octets,
1252 optionally to (uschar*)0 for checking only
1254 Returns: >=0 number of decoded octets
1258 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1262 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1267 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);
1268 if (d==0) return -1;
1271 if (src==end) return decoded;
1272 if (*src==' ' || *src=='\t' || *src=='\n')
1273 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1282 /*************************************************
1283 * Decode unicode-encoded-character string *
1284 *************************************************/
1287 Encoding definition:
1288 blank = SP / TAB / CRLF
1289 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1290 unicode-hex = 1*HEXDIG
1292 It is an error for a script to use a hexadecimal value that isn't in
1293 either the range 0 to D7FF or the range E000 to 10FFFF.
1295 At this time, strings are already scanned, thus the CRLF is converted
1296 to the internally used \n (should RFC_EOL have been used).
1299 src points to a unicode-hex-seq
1300 end points to its end
1301 dst points to the destination of the decoded octets,
1302 optionally to (uschar*)0 for checking only
1304 Returns: >=0 number of decoded octets
1306 -2 semantic error (character range violation)
1309 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1313 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1320 for (hex_seq=src; src<end && *src=='0'; ++src);
1321 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);
1322 if (src==hex_seq) return -1;
1323 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1329 else if (c>=0x80 && c<=0x7ff)
1334 *dst++=128+(c&0x3f);
1338 else if (c>=0x800 && c<=0xffff)
1343 *dst++=128+((c>>6)&0x3f);
1344 *dst++=128+(c&0x3f);
1348 else if (c>=0x10000 && c<=0x1fffff)
1353 *dst++=128+((c>>10)&0x3f);
1354 *dst++=128+((c>>6)&0x3f);
1355 *dst++=128+(c&0x3f);
1359 if (*src==' ' || *src=='\t' || *src=='\n')
1361 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1362 if (src==end) return decoded;
1371 /*************************************************
1372 * Decode encoded-character string *
1373 *************************************************/
1376 Encoding definition:
1377 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1378 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1381 encoded points to an encoded string, returns decoded string
1382 filter points to the Sieve filter including its state
1388 static int string_decode(struct Sieve *filter, struct String *data)
1390 uschar *src,*dst,*end;
1392 src=data->character;
1394 end=data->character+data->length;
1400 strncmpic(src,US "${hex:",6)==0
1401 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1402 && (hex_decode(src+6,brace,(uschar*)0))>=0
1405 dst+=hex_decode(src+6,brace,dst);
1409 strncmpic(src,US "${unicode:",10)==0
1410 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1413 switch (unicode_decode(src+10,brace,(uschar*)0))
1417 filter->errmsg=CUS "unicode character out of range";
1427 dst+=unicode_decode(src+10,brace,dst);
1434 data->length=dst-data->character;
1441 /*************************************************
1442 * Parse an optional string *
1443 *************************************************/
1447 quoted-string = DQUOTE *CHAR DQUOTE
1448 ;; in general, \ CHAR inside a string maps to CHAR
1449 ;; so \" maps to " and \\ maps to \
1450 ;; note that newlines and other characters are all allowed
1453 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1454 *(multi-line-literal / multi-line-dotstuff)
1456 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1457 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1458 ;; A line containing only "." ends the multi-line.
1459 ;; Remove a leading '.' if followed by another '.'.
1460 string = quoted-string / multi-line
1463 filter points to the Sieve filter including its state
1464 id specifies identifier to match
1468 0 identifier not matched
1471 static int parse_string(struct Sieve *filter, struct String *data)
1476 data->character=(uschar*)0;
1477 if (*filter->pc=='"') /* quoted string */
1482 if (*filter->pc=='"') /* end of string */
1484 int foo=data->length;
1487 /* that way, there will be at least one character allocated */
1488 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1489 #ifdef ENCODED_CHARACTER
1490 if (filter->require_encoded_character
1491 && string_decode(filter,data)==-1)
1496 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1498 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1501 else /* regular character */
1504 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1506 if (*filter->pc=='\n')
1508 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1512 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1516 filter->errmsg=CUS "missing end of string";
1519 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1522 /* skip optional white space followed by hashed comment or CRLF */
1523 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1524 if (*filter->pc=='#')
1526 if (parse_hashcomment(filter)==-1) return -1;
1529 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1531 else if (*filter->pc=='\n')
1543 filter->errmsg=CUS "syntax error";
1549 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1551 if (*filter->pc=='\n') /* end of line */
1554 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1562 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1564 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1567 int foo=data->length;
1569 /* that way, there will be at least one character allocated */
1570 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1577 #ifdef ENCODED_CHARACTER
1578 if (filter->require_encoded_character
1579 && string_decode(filter,data)==-1)
1584 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1586 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1590 else /* regular character */
1592 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1596 filter->errmsg=CUS "missing end of multi line string";
1603 /*************************************************
1604 * Parse a specific identifier *
1605 *************************************************/
1609 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1612 filter points to the Sieve filter including its state
1613 id specifies identifier to match
1616 0 identifier not matched
1619 static int parse_identifier(struct Sieve *filter, const uschar *id)
1621 size_t idlen=Ustrlen(id);
1623 if (strncmpic(US filter->pc,US id,idlen)==0)
1625 uschar next=filter->pc[idlen];
1627 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1635 /*************************************************
1637 *************************************************/
1641 number = 1*DIGIT [QUANTIFIER]
1642 QUANTIFIER = "K" / "M" / "G"
1645 filter points to the Sieve filter including its state
1649 -1 no string list found
1652 static int parse_number(struct Sieve *filter, unsigned long *data)
1656 if (*filter->pc>='0' && *filter->pc<='9')
1661 d=Ustrtoul(filter->pc,&e,10);
1664 filter->errmsg=CUstrerror(ERANGE);
1669 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1670 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1671 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1672 if (d>(ULONG_MAX/u))
1674 filter->errmsg=CUstrerror(ERANGE);
1683 filter->errmsg=CUS "missing number";
1689 /*************************************************
1690 * Parse a string list *
1691 *************************************************/
1695 string-list = "[" string *("," string) "]" / string
1698 filter points to the Sieve filter including its state
1699 data returns string list
1702 -1 no string list found
1705 static int parse_stringlist(struct Sieve *filter, struct String **data)
1707 const uschar *orig=filter->pc;
1710 struct String *d=(struct String*)0;
1713 if (*filter->pc=='[') /* string list */
1718 if (parse_white(filter)==-1) goto error;
1719 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1722 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1723 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1724 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1726 filter->errmsg=CUstrerror(errno);
1729 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1731 dataCapacity=newCapacity;
1733 m=parse_string(filter,&d[dataLength]);
1736 if (dataLength==0) break;
1739 filter->errmsg=CUS "missing string";
1743 else if (m==-1) goto error;
1745 if (parse_white(filter)==-1) goto error;
1746 if (*filter->pc==',') ++filter->pc;
1749 if (*filter->pc==']')
1751 d[dataLength].character=(uschar*)0;
1752 d[dataLength].length=-1;
1759 filter->errmsg=CUS "missing closing bracket";
1763 else /* single string */
1765 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1769 m=parse_string(filter,&d[0]);
1781 d[1].character=(uschar*)0;
1788 filter->errmsg=CUS "missing string list";
1793 /*************************************************
1794 * Parse an optional address part specifier *
1795 *************************************************/
1799 address-part = ":localpart" / ":domain" / ":all"
1800 address-part =/ ":user" / ":detail"
1803 filter points to the Sieve filter including its state
1804 a returns address part specified
1807 0 no comparator found
1811 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1814 if (parse_identifier(filter,CUS ":user")==1)
1816 if (!filter->require_subaddress)
1818 filter->errmsg=CUS "missing previous require \"subaddress\";";
1824 else if (parse_identifier(filter,CUS ":detail")==1)
1826 if (!filter->require_subaddress)
1828 filter->errmsg=CUS "missing previous require \"subaddress\";";
1836 if (parse_identifier(filter,CUS ":localpart")==1)
1838 *a=ADDRPART_LOCALPART;
1841 else if (parse_identifier(filter,CUS ":domain")==1)
1846 else if (parse_identifier(filter,CUS ":all")==1)
1855 /*************************************************
1856 * Parse an optional comparator *
1857 *************************************************/
1861 comparator = ":comparator" <comparator-name: string>
1864 filter points to the Sieve filter including its state
1865 c returns comparator
1868 0 no comparator found
1869 -1 incomplete comparator found
1872 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1874 struct String comparator_name;
1876 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1877 if (parse_white(filter)==-1) return -1;
1878 switch (parse_string(filter,&comparator_name))
1883 filter->errmsg=CUS "missing comparator";
1890 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1895 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1897 *c=COMP_EN_ASCII_CASEMAP;
1900 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1902 *c=COMP_EN_ASCII_CASEMAP;
1905 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1907 *c=COMP_ASCII_NUMERIC;
1912 filter->errmsg=CUS "invalid comparator";
1921 /*************************************************
1922 * Parse an optional match type *
1923 *************************************************/
1927 match-type = ":is" / ":contains" / ":matches"
1930 filter points to the Sieve filter including its state
1931 m returns match type
1934 0 no match type found
1937 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1939 if (parse_identifier(filter,CUS ":is")==1)
1944 else if (parse_identifier(filter,CUS ":contains")==1)
1949 else if (parse_identifier(filter,CUS ":matches")==1)
1958 /*************************************************
1959 * Parse and interpret an optional test list *
1960 *************************************************/
1964 test-list = "(" test *("," test) ")"
1967 filter points to the Sieve filter including its state
1968 n total number of tests
1969 num_true number of passed tests
1970 exec Execute parsed statements
1973 0 no test list found
1974 -1 syntax or execution error
1977 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1979 if (parse_white(filter)==-1) return -1;
1980 if (*filter->pc=='(')
1989 switch (parse_test(filter,&cond,exec))
1992 case 0: filter->errmsg=CUS "missing test"; return -1;
1993 default: ++*n; if (cond) ++*num_true; break;
1995 if (parse_white(filter)==-1) return -1;
1996 if (*filter->pc==',') ++filter->pc;
1999 if (*filter->pc==')')
2006 filter->errmsg=CUS "missing closing paren";
2014 /*************************************************
2015 * Parse and interpret an optional test *
2016 *************************************************/
2020 filter points to the Sieve filter including its state
2021 cond returned condition status
2022 exec Execute parsed statements
2026 -1 syntax or execution error
2029 static int parse_test(struct Sieve *filter, int *cond, int exec)
2031 if (parse_white(filter)==-1) return -1;
2032 if (parse_identifier(filter,CUS "address"))
2035 address-test = "address" { [address-part] [comparator] [match-type] }
2036 <header-list: string-list> <key-list: string-list>
2038 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2041 enum AddressPart addressPart=ADDRPART_ALL;
2042 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2043 enum MatchType matchType=MATCH_IS;
2044 struct String *hdr,*h,*key,*k;
2050 if (parse_white(filter)==-1) return -1;
2051 if ((m=parse_addresspart(filter,&addressPart))!=0)
2053 if (m==-1) return -1;
2056 filter->errmsg=CUS "address part already specified";
2061 else if ((m=parse_comparator(filter,&comparator))!=0)
2063 if (m==-1) return -1;
2066 filter->errmsg=CUS "comparator already specified";
2071 else if ((m=parse_matchtype(filter,&matchType))!=0)
2073 if (m==-1) return -1;
2076 filter->errmsg=CUS "match type already specified";
2083 if (parse_white(filter)==-1) return -1;
2084 if ((m=parse_stringlist(filter,&hdr))!=1)
2086 if (m==0) filter->errmsg=CUS "header string list expected";
2089 if (parse_white(filter)==-1) return -1;
2090 if ((m=parse_stringlist(filter,&key))!=1)
2092 if (m==0) filter->errmsg=CUS "key string list expected";
2096 for (h=hdr; h->length!=-1 && !*cond; ++h)
2098 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2102 !eq_asciicase(h,&str_from,0)
2103 && !eq_asciicase(h,&str_to,0)
2104 && !eq_asciicase(h,&str_cc,0)
2105 && !eq_asciicase(h,&str_bcc,0)
2106 && !eq_asciicase(h,&str_sender,0)
2107 && !eq_asciicase(h,&str_resent_from,0)
2108 && !eq_asciicase(h,&str_resent_to,0)
2111 filter->errmsg=CUS "invalid header field";
2116 /* We are only interested in addresses below, so no MIME decoding */
2117 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2118 if (header_value == NULL)
2120 filter->errmsg=CUS "header string expansion failed";
2123 parse_allow_group = TRUE;
2124 while (*header_value && !*cond)
2127 int start, end, domain;
2131 end_addr = parse_find_address_end(header_value, FALSE);
2132 saveend = *end_addr;
2134 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2136 if (extracted_addr) switch (addressPart)
2138 case ADDRPART_ALL: part=extracted_addr; break;
2142 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2143 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2145 case ADDRPART_DETAIL: part=NULL; break;
2149 *end_addr = saveend;
2152 for (k=key; k->length!=-1; ++k)
2154 struct String partStr;
2156 partStr.character=part;
2157 partStr.length=Ustrlen(part);
2160 *cond=compare(filter,k,&partStr,comparator,matchType);
2161 if (*cond==-1) return -1;
2166 if (saveend == 0) break;
2167 header_value = end_addr + 1;
2169 parse_allow_group = FALSE;
2170 parse_found_group = FALSE;
2175 else if (parse_identifier(filter,CUS "allof"))
2178 allof-test = "allof" <tests: test-list>
2183 switch (parse_testlist(filter,&n,&num_true,exec))
2186 case 0: filter->errmsg=CUS "missing test list"; return -1;
2187 default: *cond=(n==num_true); return 1;
2190 else if (parse_identifier(filter,CUS "anyof"))
2193 anyof-test = "anyof" <tests: test-list>
2198 switch (parse_testlist(filter,&n,&num_true,exec))
2201 case 0: filter->errmsg=CUS "missing test list"; return -1;
2202 default: *cond=(num_true>0); return 1;
2205 else if (parse_identifier(filter,CUS "exists"))
2208 exists-test = "exists" <header-names: string-list>
2211 struct String *hdr,*h;
2214 if (parse_white(filter)==-1) return -1;
2215 if ((m=parse_stringlist(filter,&hdr))!=1)
2217 if (m==0) filter->errmsg=CUS "header string list expected";
2223 for (h=hdr; h->length!=-1 && *cond; ++h)
2227 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2228 if (header_def == NULL)
2230 filter->errmsg=CUS "header string expansion failed";
2233 if (Ustrcmp(header_def,"false")==0) *cond=0;
2238 else if (parse_identifier(filter,CUS "false"))
2241 false-test = "false"
2247 else if (parse_identifier(filter,CUS "header"))
2250 header-test = "header" { [comparator] [match-type] }
2251 <header-names: string-list> <key-list: string-list>
2254 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2255 enum MatchType matchType=MATCH_IS;
2256 struct String *hdr,*h,*key,*k;
2262 if (parse_white(filter)==-1) return -1;
2263 if ((m=parse_comparator(filter,&comparator))!=0)
2265 if (m==-1) return -1;
2268 filter->errmsg=CUS "comparator already specified";
2273 else if ((m=parse_matchtype(filter,&matchType))!=0)
2275 if (m==-1) return -1;
2278 filter->errmsg=CUS "match type already specified";
2285 if (parse_white(filter)==-1) return -1;
2286 if ((m=parse_stringlist(filter,&hdr))!=1)
2288 if (m==0) filter->errmsg=CUS "header string list expected";
2291 if (parse_white(filter)==-1) return -1;
2292 if ((m=parse_stringlist(filter,&key))!=1)
2294 if (m==0) filter->errmsg=CUS "key string list expected";
2298 for (h=hdr; h->length!=-1 && !*cond; ++h)
2302 filter->errmsg=CUS "invalid header field";
2307 struct String header_value;
2310 expand_header(&header_value,h);
2311 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2312 if (header_value.character == NULL || header_def == NULL)
2314 filter->errmsg=CUS "header string expansion failed";
2317 for (k=key; k->length!=-1; ++k)
2319 if (Ustrcmp(header_def,"true")==0)
2321 *cond=compare(filter,k,&header_value,comparator,matchType);
2322 if (*cond==-1) return -1;
2330 else if (parse_identifier(filter,CUS "not"))
2332 if (parse_white(filter)==-1) return -1;
2333 switch (parse_test(filter,cond,exec))
2336 case 0: filter->errmsg=CUS "missing test"; return -1;
2337 default: *cond=!*cond; return 1;
2340 else if (parse_identifier(filter,CUS "size"))
2343 relop = ":over" / ":under"
2344 size-test = "size" relop <limit: number>
2347 unsigned long limit;
2350 if (parse_white(filter)==-1) return -1;
2351 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2352 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2355 filter->errmsg=CUS "missing :over or :under";
2358 if (parse_white(filter)==-1) return -1;
2359 if (parse_number(filter,&limit)==-1) return -1;
2360 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2363 else if (parse_identifier(filter,CUS "true"))
2368 else if (parse_identifier(filter,CUS "envelope"))
2371 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2372 <envelope-part: string-list> <key-list: string-list>
2374 envelope-part is case insensitive "from" or "to"
2375 #ifdef ENVELOPE_AUTH
2376 envelope-part =/ "auth"
2380 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2381 enum AddressPart addressPart=ADDRPART_ALL;
2382 enum MatchType matchType=MATCH_IS;
2383 struct String *env,*e,*key,*k;
2387 if (!filter->require_envelope)
2389 filter->errmsg=CUS "missing previous require \"envelope\";";
2394 if (parse_white(filter)==-1) return -1;
2395 if ((m=parse_comparator(filter,&comparator))!=0)
2397 if (m==-1) return -1;
2400 filter->errmsg=CUS "comparator already specified";
2405 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2407 if (m==-1) return -1;
2410 filter->errmsg=CUS "address part already specified";
2415 else if ((m=parse_matchtype(filter,&matchType))!=0)
2417 if (m==-1) return -1;
2420 filter->errmsg=CUS "match type already specified";
2427 if (parse_white(filter)==-1) return -1;
2428 if ((m=parse_stringlist(filter,&env))!=1)
2430 if (m==0) filter->errmsg=CUS "envelope string list expected";
2433 if (parse_white(filter)==-1) return -1;
2434 if ((m=parse_stringlist(filter,&key))!=1)
2436 if (m==0) filter->errmsg=CUS "key string list expected";
2440 for (e=env; e->length!=-1 && !*cond; ++e)
2442 const uschar *envelopeExpr=CUS 0;
2443 uschar *envelope=US 0;
2445 if (eq_asciicase(e,&str_from,0))
2447 switch (addressPart)
2449 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2453 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2454 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2456 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2460 else if (eq_asciicase(e,&str_to,0))
2462 switch (addressPart)
2464 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2466 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2467 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2469 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2470 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2473 #ifdef ENVELOPE_AUTH
2474 else if (eq_asciicase(e,&str_auth,0))
2476 switch (addressPart)
2478 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2482 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2483 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2485 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2492 filter->errmsg=CUS "invalid envelope string";
2495 if (exec && envelopeExpr)
2497 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2499 filter->errmsg=CUS "header string expansion failed";
2502 for (k=key; k->length!=-1; ++k)
2504 struct String envelopeStr;
2506 envelopeStr.character=envelope;
2507 envelopeStr.length=Ustrlen(envelope);
2508 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2509 if (*cond==-1) return -1;
2517 else if (parse_identifier(filter,CUS "valid_notify_method"))
2520 valid_notify_method = "valid_notify_method"
2521 <notification-uris: string-list>
2524 struct String *uris,*u;
2527 if (!filter->require_enotify)
2529 filter->errmsg=CUS "missing previous require \"enotify\";";
2532 if (parse_white(filter)==-1) return -1;
2533 if ((m=parse_stringlist(filter,&uris))!=1)
2535 if (m==0) filter->errmsg=CUS "URI string list expected";
2541 for (u=uris; u->length!=-1 && *cond; ++u)
2543 string_item *recipient;
2544 struct String header,subject,body;
2548 header.character=(uschar*)0;
2550 subject.character=(uschar*)0;
2552 body.character=(uschar*)0;
2553 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2559 else if (parse_identifier(filter,CUS "notify_method_capability"))
2562 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2563 <notification-uri: string>
2564 <notification-capability: string>
2565 <key-list: string-list>
2571 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2572 enum MatchType matchType=MATCH_IS;
2573 struct String uri,capa,*keys,*k;
2575 if (!filter->require_enotify)
2577 filter->errmsg=CUS "missing previous require \"enotify\";";
2582 if (parse_white(filter)==-1) return -1;
2583 if ((m=parse_comparator(filter,&comparator))!=0)
2585 if (m==-1) return -1;
2588 filter->errmsg=CUS "comparator already specified";
2593 else if ((m=parse_matchtype(filter,&matchType))!=0)
2595 if (m==-1) return -1;
2598 filter->errmsg=CUS "match type already specified";
2605 if ((m=parse_string(filter,&uri))!=1)
2607 if (m==0) filter->errmsg=CUS "missing notification URI string";
2610 if (parse_white(filter)==-1) return -1;
2611 if ((m=parse_string(filter,&capa))!=1)
2613 if (m==0) filter->errmsg=CUS "missing notification capability string";
2616 if (parse_white(filter)==-1) return -1;
2617 if ((m=parse_stringlist(filter,&keys))!=1)
2619 if (m==0) filter->errmsg=CUS "missing key string list";
2624 string_item *recipient;
2625 struct String header,subject,body;
2630 header.character=(uschar*)0;
2632 subject.character=(uschar*)0;
2634 body.character=(uschar*)0;
2635 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2637 if (eq_asciicase(&capa,&str_online,0)==1)
2638 for (k=keys; k->length!=-1; ++k)
2640 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2641 if (*cond==-1) return -1;
2653 /*************************************************
2654 * Parse and interpret an optional block *
2655 *************************************************/
2659 filter points to the Sieve filter including its state
2660 exec Execute parsed statements
2661 generated where to hang newly-generated addresses
2663 Returns: 2 success by stop
2665 0 no block command found
2666 -1 syntax or execution error
2669 static int parse_block(struct Sieve *filter, int exec,
2670 address_item **generated)
2674 if (parse_white(filter)==-1) return -1;
2675 if (*filter->pc=='{')
2678 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2679 if (*filter->pc=='}')
2686 filter->errmsg=CUS "expecting command or closing brace";
2694 /*************************************************
2695 * Match a semicolon *
2696 *************************************************/
2700 filter points to the Sieve filter including its state
2706 static int parse_semicolon(struct Sieve *filter)
2708 if (parse_white(filter)==-1) return -1;
2709 if (*filter->pc==';')
2716 filter->errmsg=CUS "missing semicolon";
2722 /*************************************************
2723 * Parse and interpret a Sieve command *
2724 *************************************************/
2728 filter points to the Sieve filter including its state
2729 exec Execute parsed statements
2730 generated where to hang newly-generated addresses
2732 Returns: 2 success by stop
2734 -1 syntax or execution error
2737 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2741 if (parse_white(filter)==-1) return -1;
2742 if (parse_identifier(filter,CUS "if"))
2745 if-command = "if" test block *( "elsif" test block ) [ else block ]
2748 int cond,m,unsuccessful;
2751 if (parse_white(filter)==-1) return -1;
2752 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2755 filter->errmsg=CUS "missing test";
2758 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2759 (debug_selector & D_filter) != 0)
2761 if (exec) debug_printf("if %s\n",cond?"true":"false");
2763 m=parse_block(filter,exec ? cond : 0, generated);
2764 if (m==-1 || m==2) return m;
2767 filter->errmsg=CUS "missing block";
2770 unsuccessful = !cond;
2771 for (;;) /* elsif test block */
2773 if (parse_white(filter)==-1) return -1;
2774 if (parse_identifier(filter,CUS "elsif"))
2776 if (parse_white(filter)==-1) return -1;
2777 m=parse_test(filter,&cond,exec && unsuccessful);
2778 if (m==-1 || m==2) return m;
2781 filter->errmsg=CUS "missing test";
2784 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2785 (debug_selector & D_filter) != 0)
2787 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2789 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2790 if (m==-1 || m==2) return m;
2793 filter->errmsg=CUS "missing block";
2796 if (exec && unsuccessful && cond) unsuccessful = 0;
2801 if (parse_white(filter)==-1) return -1;
2802 if (parse_identifier(filter,CUS "else"))
2804 m=parse_block(filter,exec && unsuccessful, generated);
2805 if (m==-1 || m==2) return m;
2808 filter->errmsg=CUS "missing block";
2813 else if (parse_identifier(filter,CUS "stop"))
2816 stop-command = "stop" { stop-options } ";"
2820 if (parse_semicolon(filter)==-1) return -1;
2823 filter->pc+=Ustrlen(filter->pc);
2827 else if (parse_identifier(filter,CUS "keep"))
2830 keep-command = "keep" { keep-options } ";"
2834 if (parse_semicolon(filter)==-1) return -1;
2837 add_addr(generated,US"inbox",1,0,0,0);
2841 else if (parse_identifier(filter,CUS "discard"))
2844 discard-command = "discard" { discard-options } ";"
2848 if (parse_semicolon(filter)==-1) return -1;
2849 if (exec) filter->keep=0;
2851 else if (parse_identifier(filter,CUS "redirect"))
2854 redirect-command = "redirect" redirect-options "string" ";"
2856 redirect-options =) ":copy"
2859 struct String recipient;
2865 if (parse_white(filter)==-1) return -1;
2866 if (parse_identifier(filter,CUS ":copy")==1)
2868 if (!filter->require_copy)
2870 filter->errmsg=CUS "missing previous require \"copy\";";
2877 if (parse_white(filter)==-1) return -1;
2878 if ((m=parse_string(filter,&recipient))!=1)
2880 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2883 if (strchr(CCS recipient.character,'@')==(char*)0)
2885 filter->errmsg=CUS "unqualified recipient address";
2890 add_addr(generated,recipient.character,0,0,0,0);
2891 if (!copy) filter->keep = 0;
2893 if (parse_semicolon(filter)==-1) return -1;
2895 else if (parse_identifier(filter,CUS "fileinto"))
2898 fileinto-command = "fileinto" { fileinto-options } string ";"
2900 fileinto-options =) [ ":copy" ]
2903 struct String folder;
2906 unsigned long maxage, maxmessages, maxstorage;
2909 maxage = maxmessages = maxstorage = 0;
2910 if (!filter->require_fileinto)
2912 filter->errmsg=CUS "missing previous require \"fileinto\";";
2917 if (parse_white(filter)==-1) return -1;
2918 if (parse_identifier(filter,CUS ":copy")==1)
2920 if (!filter->require_copy)
2922 filter->errmsg=CUS "missing previous require \"copy\";";
2929 if (parse_white(filter)==-1) return -1;
2930 if ((m=parse_string(filter,&folder))!=1)
2932 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2935 m=0; s=folder.character;
2936 if (folder.length==0) m=1;
2937 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2940 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2945 filter->errmsg=CUS "invalid folder";
2950 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2951 if (!copy) filter->keep = 0;
2953 if (parse_semicolon(filter)==-1) return -1;
2956 else if (parse_identifier(filter,CUS "notify"))
2959 notify-command = "notify" { notify-options } <method: string> ";"
2960 notify-options = [":from" string]
2961 [":importance" <"1" / "2" / "3">]
2962 [":options" 1*(string-list / number)]
2968 struct String importance;
2969 struct String message;
2970 struct String method;
2971 struct Notification *already;
2972 string_item *recipient;
2973 struct String header;
2974 struct String subject;
2976 uschar *envelope_from;
2977 struct String auto_submitted_value;
2978 uschar *auto_submitted_def;
2980 if (!filter->require_enotify)
2982 filter->errmsg=CUS "missing previous require \"enotify\";";
2985 from.character=(uschar*)0;
2987 importance.character=(uschar*)0;
2988 importance.length=-1;
2989 message.character=(uschar*)0;
2993 header.character=(uschar*)0;
2995 subject.character=(uschar*)0;
2997 body.character=(uschar*)0;
2998 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3001 if (parse_white(filter)==-1) return -1;
3002 if (parse_identifier(filter,CUS ":from")==1)
3004 if (parse_white(filter)==-1) return -1;
3005 if ((m=parse_string(filter,&from))!=1)
3007 if (m==0) filter->errmsg=CUS "from string expected";
3011 else if (parse_identifier(filter,CUS ":importance")==1)
3013 if (parse_white(filter)==-1) return -1;
3014 if ((m=parse_string(filter,&importance))!=1)
3016 if (m==0) filter->errmsg=CUS "importance string expected";
3019 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3021 filter->errmsg=CUS "invalid importance";
3025 else if (parse_identifier(filter,CUS ":options")==1)
3027 if (parse_white(filter)==-1) return -1;
3029 else if (parse_identifier(filter,CUS ":message")==1)
3031 if (parse_white(filter)==-1) return -1;
3032 if ((m=parse_string(filter,&message))!=1)
3034 if (m==0) filter->errmsg=CUS "message string expected";
3040 if (parse_white(filter)==-1) return -1;
3041 if ((m=parse_string(filter,&method))!=1)
3043 if (m==0) filter->errmsg=CUS "missing method string";
3046 if (parse_semicolon(filter)==-1) return -1;
3047 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3051 if (message.length==-1) message=subject;
3052 if (message.length==-1) expand_header(&message,&str_subject);
3053 expand_header(&auto_submitted_value,&str_auto_submitted);
3054 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3055 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3057 filter->errmsg=CUS "header string expansion failed";
3060 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3062 for (already=filter->notified; already; already=already->next)
3064 if (already->method.length==method.length
3065 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3066 && already->importance.length==importance.length
3067 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3068 && already->message.length==message.length
3069 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3072 if (already==(struct Notification*)0)
3073 /* New notification, process it */
3075 struct Notification *sent;
3076 sent=store_get(sizeof(struct Notification));
3077 sent->method=method;
3078 sent->importance=importance;
3079 sent->message=message;
3080 sent->next=filter->notified;
3081 filter->notified=sent;
3082 #ifndef COMPILE_SYNTAX_CHECKER
3083 if (filter_test == FTEST_NONE)
3088 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3092 int buffer_capacity;
3094 f = fdopen(fd, "wb");
3095 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3096 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3097 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3098 if (header.length>0) fprintf(f,"%s",header.character);
3099 if (message.length==-1)
3101 message.character=US"Notification";
3102 message.length=Ustrlen(message.character);
3104 /* Allocation is larger than neccessary, but enough even for split MIME words */
3105 buffer_capacity=32+4*message.length;
3106 buffer=store_get(buffer_capacity);
3107 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3109 if (body.length>0) fprintf(f,"%s\n",body.character);
3112 (void)child_close(pid, 0);
3115 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3117 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3123 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3125 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3131 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3133 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3140 else if (parse_identifier(filter,CUS "vacation"))
3143 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3144 vacation-options = [":days" number]
3147 [":addresses" string-list]
3154 struct String subject;
3156 struct String *addresses;
3158 string_item *aliases;
3159 struct String handle;
3160 struct String reason;
3162 if (!filter->require_vacation)
3164 filter->errmsg=CUS "missing previous require \"vacation\";";
3169 if (filter->vacation_ran)
3171 filter->errmsg=CUS "trying to execute vacation more than once";
3174 filter->vacation_ran=1;
3176 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3177 subject.character=(uschar*)0;
3179 from.character=(uschar*)0;
3181 addresses=(struct String*)0;
3184 handle.character=(uschar*)0;
3188 if (parse_white(filter)==-1) return -1;
3189 if (parse_identifier(filter,CUS ":days")==1)
3191 if (parse_white(filter)==-1) return -1;
3192 if (parse_number(filter,&days)==-1) return -1;
3193 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3194 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3196 else if (parse_identifier(filter,CUS ":subject")==1)
3198 if (parse_white(filter)==-1) return -1;
3199 if ((m=parse_string(filter,&subject))!=1)
3201 if (m==0) filter->errmsg=CUS "subject string expected";
3205 else if (parse_identifier(filter,CUS ":from")==1)
3207 if (parse_white(filter)==-1) return -1;
3208 if ((m=parse_string(filter,&from))!=1)
3210 if (m==0) filter->errmsg=CUS "from string expected";
3213 if (check_mail_address(filter,&from)!=1)
3216 else if (parse_identifier(filter,CUS ":addresses")==1)
3220 if (parse_white(filter)==-1) return -1;
3221 if ((m=parse_stringlist(filter,&addresses))!=1)
3223 if (m==0) filter->errmsg=CUS "addresses string list expected";
3226 for (a=addresses; a->length!=-1; ++a)
3230 new=store_get(sizeof(string_item));
3231 new->text=store_get(a->length+1);
3232 if (a->length) memcpy(new->text,a->character,a->length);
3233 new->text[a->length]='\0';
3238 else if (parse_identifier(filter,CUS ":mime")==1)
3240 else if (parse_identifier(filter,CUS ":handle")==1)
3242 if (parse_white(filter)==-1) return -1;
3243 if ((m=parse_string(filter,&from))!=1)
3245 if (m==0) filter->errmsg=CUS "handle string expected";
3251 if (parse_white(filter)==-1) return -1;
3252 if ((m=parse_string(filter,&reason))!=1)
3254 if (m==0) filter->errmsg=CUS "missing reason string";
3261 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3264 filter->errmsg=CUS "MIME reason string contains 8bit text";
3268 if (parse_semicolon(filter)==-1) return -1;
3275 int buffer_capacity;
3279 uschar hexdigest[33];
3283 if (filter_personal(aliases,TRUE))
3285 if (filter_test == FTEST_NONE)
3287 /* ensure oncelog directory exists; failure will be detected later */
3289 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3291 /* build oncelog filename */
3293 key.character=(uschar*)0;
3296 if (handle.length==-1)
3298 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3299 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3300 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3301 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3306 md5_end(&base, key.character, key.length, digest);
3307 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3308 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3310 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3312 if (filter_test == FTEST_NONE)
3314 capacity=Ustrlen(filter->vacation_directory);
3316 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3317 once=string_cat(once,&capacity,&start,hexdigest,33);
3320 /* process subject */
3322 if (subject.length==-1)
3324 uschar *subject_def;
3326 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3327 if (Ustrcmp(subject_def,"true")==0)
3329 expand_header(&subject,&str_subject);
3332 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3333 subject.length=start;
3337 subject.character=US"Automated reply";
3338 subject.length=Ustrlen(subject.character);
3342 /* add address to list of generated addresses */
3344 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3345 setflag(addr, af_pfr);
3346 setflag(addr, af_ignore_error);
3347 addr->next = *generated;
3349 addr->reply = store_get(sizeof(reply_item));
3350 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3351 addr->reply->to = string_copy(sender_address);
3352 if (from.length==-1)
3353 addr->reply->from = expand_string(US"$local_part@$domain");
3355 addr->reply->from = from.character;
3356 /* Allocation is larger than neccessary, but enough even for split MIME words */
3357 buffer_capacity=32+4*subject.length;
3358 buffer=store_get(buffer_capacity);
3359 /* deconst cast safe as we pass in a non-const item */
3360 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3361 addr->reply->oncelog=once;
3362 addr->reply->once_repeat=days*86400;
3364 /* build body and MIME headers */
3368 uschar *mime_body,*reason_end;
3369 static const uschar nlnl[]="\r\n\r\n";
3373 mime_body=reason.character,reason_end=reason.character+reason.length;
3374 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3379 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3380 addr->reply->headers[start] = '\0';
3383 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3384 else mime_body=reason_end-1;
3385 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3386 addr->reply->text[start] = '\0';
3390 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3393 start = reason.length;
3394 addr->reply->headers = US"MIME-Version: 1.0\n"
3395 "Content-Type: text/plain;\n"
3396 "\tcharset=\"utf-8\"\n"
3397 "Content-Transfer-Encoding: quoted-printable";
3398 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3402 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3404 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3415 /*************************************************
3416 * Parse and interpret a sieve filter *
3417 *************************************************/
3421 filter points to the Sieve filter including its state
3422 exec Execute parsed statements
3423 generated where to hang newly-generated addresses
3426 -1 syntax or execution error
3430 parse_start(struct Sieve *filter, int exec, address_item **generated)
3432 filter->pc=filter->filter;
3435 filter->require_envelope=0;
3436 filter->require_fileinto=0;
3437 #ifdef ENCODED_CHARACTER
3438 filter->require_encoded_character=0;
3440 #ifdef ENVELOPE_AUTH
3441 filter->require_envelope_auth=0;
3444 filter->require_enotify=0;
3445 filter->notified=(struct Notification*)0;
3448 filter->require_subaddress=0;
3451 filter->require_vacation=0;
3452 filter->vacation_ran=0;
3454 filter->require_copy=0;
3455 filter->require_iascii_numeric=0;
3457 if (parse_white(filter)==-1) return -1;
3459 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3462 struct dirent *oncelog;
3463 struct stat properties;
3466 /* clean up old vacation log databases */
3468 oncelogdir=opendir(CS filter->vacation_directory);
3470 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3472 filter->errmsg=CUS "unable to open vacation directory";
3476 if (oncelogdir != NULL)
3480 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3482 if (strlen(oncelog->d_name)==32)
3484 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3485 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3489 closedir(oncelogdir);
3493 while (parse_identifier(filter,CUS "require"))
3496 require-command = "require" <capabilities: string-list>
3499 struct String *cap,*check;
3502 if (parse_white(filter)==-1) return -1;
3503 if ((m=parse_stringlist(filter,&cap))!=1)
3505 if (m==0) filter->errmsg=CUS "capability string list expected";
3508 for (check=cap; check->character; ++check)
3510 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3511 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3512 #ifdef ENCODED_CHARACTER
3513 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3515 #ifdef ENVELOPE_AUTH
3516 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3519 else if (eq_octet(check,&str_enotify,0))
3521 if (filter->enotify_mailto_owner == NULL)
3523 filter->errmsg=CUS "enotify disabled";
3526 filter->require_enotify=1;
3530 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3533 else if (eq_octet(check,&str_vacation,0))
3535 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3537 filter->errmsg=CUS "vacation disabled";
3540 filter->require_vacation=1;
3543 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3544 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3545 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3546 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3547 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3550 filter->errmsg=CUS "unknown capability";
3554 if (parse_semicolon(filter)==-1) return -1;
3556 if (parse_commands(filter,exec,generated)==-1) return -1;
3559 filter->errmsg=CUS "syntax error";
3566 /*************************************************
3567 * Interpret a sieve filter file *
3568 *************************************************/
3572 filter points to the entire file, read into store as a single string
3573 options controls whether various special things are allowed, and requests
3574 special actions (not currently used)
3575 vacation_directory where to store vacation "once" files
3576 enotify_mailto_owner owner of mailto notifications
3577 useraddress string expression for :user part of address
3578 subaddress string expression for :subaddress part of address
3579 generated where to hang newly-generated addresses
3580 error where to pass back an error text
3582 Returns: FF_DELIVERED success, a significant action was taken
3583 FF_NOTDELIVERED success, no significant action
3584 FF_DEFER defer requested
3585 FF_FAIL fail requested
3586 FF_FREEZE freeze requested
3587 FF_ERROR there was a problem
3591 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3592 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3593 address_item **generated, uschar **error)
3599 options = options; /* Keep picky compilers happy */
3602 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3603 sieve.filter=filter;
3605 if (vacation_directory == NULL)
3606 sieve.vacation_directory = NULL;
3609 sieve.vacation_directory=expand_string(vacation_directory);
3610 if (sieve.vacation_directory == NULL)
3612 *error = string_sprintf("failed to expand \"%s\" "
3613 "(sieve_vacation_directory): %s", vacation_directory,
3614 expand_string_message);
3619 if (enotify_mailto_owner == NULL)
3620 sieve.enotify_mailto_owner = NULL;
3623 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3624 if (sieve.enotify_mailto_owner == NULL)
3626 *error = string_sprintf("failed to expand \"%s\" "
3627 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3628 expand_string_message);
3633 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3634 sieve.subaddress = subaddress;
3636 #ifdef COMPILE_SYNTAX_CHECKER
3637 if (parse_start(&sieve,0,generated)==1)
3639 if (parse_start(&sieve,1,generated)==1)
3644 add_addr(generated,US"inbox",1,0,0,0);
3645 msg = string_sprintf("Implicit keep");
3650 msg = string_sprintf("No implicit keep");
3656 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3657 #ifdef COMPILE_SYNTAX_CHECKER
3661 add_addr(generated,US"inbox",1,0,0,0);
3666 #ifndef COMPILE_SYNTAX_CHECKER
3667 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3668 else debug_printf("%s\n", msg);
3671 DEBUG(D_route) debug_printf("Sieve: end of processing\n");