1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2016
7 * See the file NOTICE for conditions of use and distribution.
10 /* This code was contributed by Michael Haardt. */
13 /* Sieve mail filter. */
27 /* Define this for RFC compliant \r\n end-of-line terminators. */
28 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 /* Define this for development of the Sieve extension "encoded-character". */
32 #define ENCODED_CHARACTER
34 /* Define this for development of the Sieve extension "envelope-auth". */
37 /* Define this for development of the Sieve extension "enotify". */
40 /* Define this for the Sieve extension "subaddress". */
43 /* Define this for the Sieve extension "vacation". */
47 #define VACATION_MIN_DAYS 1
48 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
49 #define VACATION_MAX_DAYS 31
51 /* Keep this at 75 to accept only RFC compliant MIME words. */
52 /* Increase it if you want to match headers from buggy MUAs. */
53 #define MIMEWORD_LENGTH 75
64 #ifdef ENCODED_CHARACTER
65 int require_encoded_character;
68 int require_envelope_auth;
72 struct Notification *notified;
74 uschar *enotify_mailto_owner;
76 int require_subaddress;
82 uschar *vacation_directory;
83 const uschar *subaddress;
84 const uschar *useraddress;
86 int require_iascii_numeric;
89 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
90 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
92 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
96 enum RelOp { LT, LE, EQ, GE, GT, NE };
106 struct String method;
107 struct String importance;
108 struct String message;
109 struct Notification *next;
112 /* This should be a complete list of supported extensions, so that an external
113 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
114 list of extensions and provide correct information to a client.
116 We'll emit the list in the order given here; keep it alphabetically sorted, so
117 that callers don't get surprised.
119 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
121 const uschar *exim_sieve_extension_list[] = {
122 CUS"comparator-i;ascii-numeric",
124 #ifdef ENCODED_CHARACTER
125 CUS"encoded-character",
144 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
145 static int parse_test(struct Sieve *filter, int *cond, int exec);
146 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
148 static uschar str_from_c[]="From";
149 static const struct String str_from={ str_from_c, 4 };
150 static uschar str_to_c[]="To";
151 static const struct String str_to={ str_to_c, 2 };
152 static uschar str_cc_c[]="Cc";
153 static const struct String str_cc={ str_cc_c, 2 };
154 static uschar str_bcc_c[]="Bcc";
155 static const struct String str_bcc={ str_bcc_c, 3 };
156 static uschar str_auth_c[]="auth";
157 static const struct String str_auth={ str_auth_c, 4 };
158 static uschar str_sender_c[]="Sender";
159 static const struct String str_sender={ str_sender_c, 6 };
160 static uschar str_resent_from_c[]="Resent-From";
161 static const struct String str_resent_from={ str_resent_from_c, 11 };
162 static uschar str_resent_to_c[]="Resent-To";
163 static const struct String str_resent_to={ str_resent_to_c, 9 };
164 static uschar str_fileinto_c[]="fileinto";
165 static const struct String str_fileinto={ str_fileinto_c, 8 };
166 static uschar str_envelope_c[]="envelope";
167 static const struct String str_envelope={ str_envelope_c, 8 };
168 #ifdef ENCODED_CHARACTER
169 static uschar str_encoded_character_c[]="encoded-character";
170 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
173 static uschar str_envelope_auth_c[]="envelope-auth";
174 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
177 static uschar str_enotify_c[]="enotify";
178 static const struct String str_enotify={ str_enotify_c, 7 };
179 static uschar str_online_c[]="online";
180 static const struct String str_online={ str_online_c, 6 };
181 static uschar str_maybe_c[]="maybe";
182 static const struct String str_maybe={ str_maybe_c, 5 };
183 static uschar str_auto_submitted_c[]="Auto-Submitted";
184 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
187 static uschar str_subaddress_c[]="subaddress";
188 static const struct String str_subaddress={ str_subaddress_c, 10 };
191 static uschar str_vacation_c[]="vacation";
192 static const struct String str_vacation={ str_vacation_c, 8 };
193 static uschar str_subject_c[]="Subject";
194 static const struct String str_subject={ str_subject_c, 7 };
196 static uschar str_copy_c[]="copy";
197 static const struct String str_copy={ str_copy_c, 4 };
198 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
199 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
200 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
201 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
202 static uschar str_ioctet_c[]="i;octet";
203 static const struct String str_ioctet={ str_ioctet_c, 7 };
204 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
205 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
206 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
207 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
208 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
209 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
210 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
211 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
212 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
213 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
216 /*************************************************
217 * Encode to quoted-printable *
218 *************************************************/
229 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
232 const uschar *start,*end;
237 /* Two passes: one to count output allocation size, second
238 to do the encoding */
240 for (pass=0; pass<=1; ++pass)
247 dst->character=store_get(dst->length+1); /* plus one for \0 */
250 for (start=src->character,end=start+src->length; start<end; ++start)
253 if (line>=73) /* line length limit */
259 *new++='='; /* line split */
264 if ( (ch>='!' && ch<='<')
265 || (ch>='>' && ch<='~')
266 || ( (ch=='\t' || ch==' ')
268 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
275 *new++=*start; /* copy char */
278 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
283 *new++='\n'; /* NL */
285 ++start; /* consume extra input char */
293 new += sprintf(CS new,"=%02X",ch);
300 *new='\0'; /* not included in length, but nice */
305 /*************************************************
306 * Check mail address for correct syntax *
307 *************************************************/
310 Check mail address for being syntactically correct.
313 filter points to the Sieve filter including its state
314 address String containing one address
317 1 Mail address is syntactically OK
321 int check_mail_address(struct Sieve *filter, const struct String *address)
323 int start, end, domain;
326 if (address->length>0)
328 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
332 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
333 address->character, error);
341 filter->errmsg=CUS "empty address";
347 /*************************************************
348 * Decode URI encoded string *
349 *************************************************/
353 str URI encoded string
356 0 Decoding successful
361 static int uri_decode(struct String *str)
365 if (str->length==0) return 0;
366 for (s=str->character,t=s,e=s+str->length; s<e; )
370 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
372 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
373 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
382 str->length=t-str->character;
387 /*************************************************
389 *************************************************/
394 mailtoURI = "mailto:" [ to ] [ headers ]
395 to = [ addr-spec *("%2C" addr-spec ) ]
396 headers = "?" header *( "&" header )
397 header = hname "=" hvalue
402 filter points to the Sieve filter including its state
403 uri URI, excluding scheme
408 1 URI is syntactically OK
413 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
416 struct String to, hname;
417 struct String hvalue = {.character = NULL, .length = 0};
421 if (Ustrncmp(uri,"mailto:",7))
423 filter->errmsg=US "Unknown URI scheme";
428 if (*uri && *uri!='?')
432 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
438 to.character=string_catn(to.character, &capacity, &to.length, start, uri-start);
439 to.character[to.length]='\0';
440 if (uri_decode(&to)==-1)
442 filter->errmsg=US"Invalid URI encoding";
445 new=store_get(sizeof(string_item));
446 new->text=store_get(to.length+1);
447 if (to.length) memcpy(new->text,to.character,to.length);
448 new->text[to.length]='\0';
449 new->next=*recipient;
454 filter->errmsg=US"Missing addr-spec in URI";
457 if (*uri=='%') uri+=3;
466 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
470 hname.character= NULL;
472 hname.character = string_catn(hname.character, &capacity, &hname.length, start, uri-start);
473 hname.character[hname.length]='\0';
474 if (uri_decode(&hname)==-1)
476 filter->errmsg=US"Invalid URI encoding";
485 filter->errmsg=US"Missing equal after hname";
489 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
493 hvalue.character= NULL;
495 hvalue.character=string_catn(hvalue.character,&capacity,&hvalue.length,start,uri-start);
496 hvalue.character[hvalue.length]='\0';
497 if (uri_decode(&hvalue)==-1)
499 filter->errmsg=US"Invalid URI encoding";
503 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
505 new=store_get(sizeof(string_item));
506 new->text=store_get(hvalue.length+1);
507 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
508 new->text[hvalue.length]='\0';
509 new->next=*recipient;
512 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
514 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
518 static struct String ignore[]=
524 {US"auto-submitted",14}
526 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
529 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
532 if (header->length==-1) header->length=0;
533 capacity=header->length;
534 header->character=string_catn(header->character,&capacity,&header->length,hname.character,hname.length);
535 header->character=string_catn(header->character,&capacity,&header->length,CUS ": ",2);
536 header->character=string_catn(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
537 header->character=string_catn(header->character,&capacity,&header->length,CUS "\n",1);
538 header->character[header->length]='\0';
541 if (*uri=='&') ++uri;
547 filter->errmsg=US"Syntactically invalid URI";
555 /*************************************************
556 * Octet-wise string comparison *
557 *************************************************/
561 needle UTF-8 string to search ...
562 haystack ... inside the haystack
563 match_prefix 1 to compare if needle is a prefix of haystack
565 Returns: 0 needle not found in haystack
569 static int eq_octet(const struct String *needle,
570 const struct String *haystack, int match_prefix)
578 h=haystack->character;
582 if (*n&0x80) return 0;
583 if (*h&0x80) return 0;
585 if (*n!=*h) return 0;
591 return (match_prefix ? nl==0 : nl==0 && hl==0);
595 /*************************************************
596 * ASCII case-insensitive string comparison *
597 *************************************************/
601 needle UTF-8 string to search ...
602 haystack ... inside the haystack
603 match_prefix 1 to compare if needle is a prefix of haystack
605 Returns: 0 needle not found in haystack
609 static int eq_asciicase(const struct String *needle,
610 const struct String *haystack, int match_prefix)
619 h=haystack->character;
625 if (nc&0x80) return 0;
626 if (hc&0x80) return 0;
628 /* tolower depends on the locale and only ASCII case must be insensitive */
629 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
635 return (match_prefix ? nl==0 : nl==0 && hl==0);
639 /*************************************************
640 * Glob pattern search *
641 *************************************************/
645 needle pattern to search ...
646 haystack ... inside the haystack
647 ascii_caseless ignore ASCII case
648 match_octet match octets, not UTF-8 multi-octet characters
650 Returns: 0 needle not found in haystack
655 static int eq_glob(const struct String *needle,
656 const struct String *haystack, int ascii_caseless, int match_octet)
658 const uschar *n,*h,*nend,*hend;
662 h=haystack->character;
663 nend=n+needle->length;
664 hend=h+haystack->length;
674 const uschar *npart,*hpart;
676 /* Try to match a non-star part of the needle at the current */
677 /* position in the haystack. */
681 while (npart<nend && *npart!='*') switch (*npart)
685 if (hpart==hend) return 0;
690 /* Match one UTF8 encoded character */
691 if ((*hpart&0xc0)==0xc0)
694 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
705 if (npart==nend) return -1;
710 if (hpart==hend) return 0;
711 /* tolower depends on the locale, but we need ASCII */
715 (*hpart&0x80) || (*npart&0x80) ||
718 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
723 /* string match after a star failed, advance and try again */
737 /* at this point, a part was matched successfully */
738 if (may_advance && npart==nend && hpart<hend)
739 /* needle ends, but haystack does not: if there was a star before, advance and try again */
749 return (h==hend ? 1 : may_advance);
753 /*************************************************
754 * ASCII numeric comparison *
755 *************************************************/
759 a first numeric string
760 b second numeric string
761 relop relational operator
763 Returns: 0 not (a relop b)
767 static int eq_asciinumeric(const struct String *a,
768 const struct String *b, enum RelOp relop)
771 const uschar *as,*aend,*bs,*bend;
775 aend=a->character+a->length;
777 bend=b->character+b->length;
779 while (*as>='0' && *as<='9' && as<aend) ++as;
781 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
784 if (al && bl==0) cmp=-1;
785 else if (al==0 && bl==0) cmp=0;
786 else if (al==0 && bl) cmp=1;
790 if (cmp==0) cmp=memcmp(a->character,b->character,al);
794 case LT: return cmp<0;
795 case LE: return cmp<=0;
796 case EQ: return cmp==0;
797 case GE: return cmp>=0;
798 case GT: return cmp>0;
799 case NE: return cmp!=0;
806 /*************************************************
808 *************************************************/
812 filter points to the Sieve filter including its state
813 needle UTF-8 pattern or string to search ...
814 haystack ... inside the haystack
818 Returns: 0 needle not found in haystack
820 -1 comparator does not offer matchtype
823 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
824 enum Comparator co, enum MatchType mt)
828 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
829 (debug_selector & D_filter) != 0)
831 debug_printf("String comparison (match ");
834 case MATCH_IS: debug_printf(":is"); break;
835 case MATCH_CONTAINS: debug_printf(":contains"); break;
836 case MATCH_MATCHES: debug_printf(":matches"); break;
838 debug_printf(", comparison \"");
841 case COMP_OCTET: debug_printf("i;octet"); break;
842 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
843 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
845 debug_printf("\"):\n");
846 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
847 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
857 if (eq_octet(needle,haystack,0)) r=1;
860 case COMP_EN_ASCII_CASEMAP:
862 if (eq_asciicase(needle,haystack,0)) r=1;
865 case COMP_ASCII_NUMERIC:
867 if (!filter->require_iascii_numeric)
869 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
872 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
886 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
889 case COMP_EN_ASCII_CASEMAP:
891 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
896 filter->errmsg=CUS "comparator does not offer specified matchtype";
908 if ((r=eq_glob(needle,haystack,0,1))==-1)
910 filter->errmsg=CUS "syntactically invalid pattern";
915 case COMP_EN_ASCII_CASEMAP:
917 if ((r=eq_glob(needle,haystack,1,1))==-1)
919 filter->errmsg=CUS "syntactically invalid pattern";
926 filter->errmsg=CUS "comparator does not offer specified matchtype";
933 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
934 (debug_selector & D_filter) != 0)
935 debug_printf(" Result %s\n",r?"true":"false");
940 /*************************************************
941 * Check header field syntax *
942 *************************************************/
945 RFC 2822, section 3.6.8 says:
949 ftext = %d33-57 / ; Any character except
950 %d59-126 ; controls, SP, and
953 That forbids 8-bit header fields. This implementation accepts them, since
954 all of Exim is 8-bit clean, so it adds %d128-%d255.
957 header header field to quote for suitable use in Exim expansions
959 Returns: 0 string is not a valid header field
960 1 string is a value header field
963 static int is_header(const struct String *header)
973 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
984 /*************************************************
985 * Quote special characters string *
986 *************************************************/
990 header header field to quote for suitable use in Exim expansions
993 Returns: quoted string
996 static const uschar *quote(const struct String *header)
1004 h=header->character;
1011 quoted=string_catn(quoted,&size,&ptr,CUS "\\0",2);
1018 quoted=string_catn(quoted,&size,&ptr,CUS "\\",1);
1022 quoted=string_catn(quoted,&size,&ptr,h,1);
1028 quoted=string_catn(quoted,&size,&ptr,CUS "",1);
1033 /*************************************************
1034 * Add address to list of generated addresses *
1035 *************************************************/
1038 According to RFC 5228, duplicate delivery to the same address must
1039 not happen, so the list is first searched for the address.
1042 generated list of generated addresses
1043 addr new address to add
1044 file address denotes a file
1050 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1052 address_item *new_addr;
1054 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1055 if ( Ustrcmp(new_addr->address,addr) == 0
1057 || testflag(new_addr, af_pfr)
1058 || testflag(new_addr, af_file)
1062 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1063 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1068 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1069 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1071 new_addr = deliver_make_addr(addr,TRUE);
1074 setflag(new_addr, af_pfr);
1075 setflag(new_addr, af_file);
1078 new_addr->prop.errors_address = NULL;
1079 new_addr->next = *generated;
1080 *generated = new_addr;
1084 /*************************************************
1085 * Return decoded header field *
1086 *************************************************/
1089 Unfold the header field as described in RFC 2822 and remove all
1090 leading and trailing white space, then perform MIME decoding and
1091 translate the header field to UTF-8.
1094 value returned value of the field
1095 header name of the header field
1097 Returns: nothing The expanded string is empty
1098 in case there is no such header
1101 static void expand_header(struct String *value, const struct String *header)
1107 value->character=(uschar*)0;
1109 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1110 while (*r==' ' || *r=='\t') ++r;
1118 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1120 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1124 /*************************************************
1125 * Parse remaining hash comment *
1126 *************************************************/
1130 Comment up to terminating CRLF
1133 filter points to the Sieve filter including its state
1139 static int parse_hashcomment(struct Sieve *filter)
1145 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1147 if (*filter->pc=='\n')
1160 filter->errmsg=CUS "missing end of comment";
1165 /*************************************************
1166 * Parse remaining C-style comment *
1167 *************************************************/
1171 Everything up to star slash
1174 filter points to the Sieve filter including its state
1180 static int parse_comment(struct Sieve *filter)
1185 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1192 filter->errmsg=CUS "missing end of comment";
1197 /*************************************************
1198 * Parse optional white space *
1199 *************************************************/
1203 Spaces, tabs, CRLFs, hash comments or C-style comments
1206 filter points to the Sieve filter including its state
1212 static int parse_white(struct Sieve *filter)
1216 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1218 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1220 else if (*filter->pc=='\n')
1230 else if (*filter->pc=='#')
1232 if (parse_hashcomment(filter)==-1) return -1;
1234 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1236 if (parse_comment(filter)==-1) return -1;
1244 #ifdef ENCODED_CHARACTER
1245 /*************************************************
1246 * Decode hex-encoded-character string *
1247 *************************************************/
1250 Encoding definition:
1251 blank = SP / TAB / CRLF
1252 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1253 hex-pair = 1*2HEXDIG
1256 src points to a hex-pair-seq
1257 end points to its end
1258 dst points to the destination of the decoded octets,
1259 optionally to (uschar*)0 for checking only
1261 Returns: >=0 number of decoded octets
1265 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1269 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1274 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);
1275 if (d==0) return -1;
1278 if (src==end) return decoded;
1279 if (*src==' ' || *src=='\t' || *src=='\n')
1280 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1289 /*************************************************
1290 * Decode unicode-encoded-character string *
1291 *************************************************/
1294 Encoding definition:
1295 blank = SP / TAB / CRLF
1296 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1297 unicode-hex = 1*HEXDIG
1299 It is an error for a script to use a hexadecimal value that isn't in
1300 either the range 0 to D7FF or the range E000 to 10FFFF.
1302 At this time, strings are already scanned, thus the CRLF is converted
1303 to the internally used \n (should RFC_EOL have been used).
1306 src points to a unicode-hex-seq
1307 end points to its end
1308 dst points to the destination of the decoded octets,
1309 optionally to (uschar*)0 for checking only
1311 Returns: >=0 number of decoded octets
1313 -2 semantic error (character range violation)
1316 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1320 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1327 for (hex_seq=src; src<end && *src=='0'; ++src);
1328 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);
1329 if (src==hex_seq) return -1;
1330 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1336 else if (c>=0x80 && c<=0x7ff)
1341 *dst++=128+(c&0x3f);
1345 else if (c>=0x800 && c<=0xffff)
1350 *dst++=128+((c>>6)&0x3f);
1351 *dst++=128+(c&0x3f);
1355 else if (c>=0x10000 && c<=0x1fffff)
1360 *dst++=128+((c>>10)&0x3f);
1361 *dst++=128+((c>>6)&0x3f);
1362 *dst++=128+(c&0x3f);
1366 if (*src==' ' || *src=='\t' || *src=='\n')
1368 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1369 if (src==end) return decoded;
1378 /*************************************************
1379 * Decode encoded-character string *
1380 *************************************************/
1383 Encoding definition:
1384 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1385 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1388 encoded points to an encoded string, returns decoded string
1389 filter points to the Sieve filter including its state
1395 static int string_decode(struct Sieve *filter, struct String *data)
1397 uschar *src,*dst,*end;
1399 src=data->character;
1401 end=data->character+data->length;
1407 strncmpic(src,US "${hex:",6)==0
1408 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1409 && (hex_decode(src+6,brace,(uschar*)0))>=0
1412 dst+=hex_decode(src+6,brace,dst);
1416 strncmpic(src,US "${unicode:",10)==0
1417 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1420 switch (unicode_decode(src+10,brace,(uschar*)0))
1424 filter->errmsg=CUS "unicode character out of range";
1434 dst+=unicode_decode(src+10,brace,dst);
1441 data->length=dst-data->character;
1448 /*************************************************
1449 * Parse an optional string *
1450 *************************************************/
1454 quoted-string = DQUOTE *CHAR DQUOTE
1455 ;; in general, \ CHAR inside a string maps to CHAR
1456 ;; so \" maps to " and \\ maps to \
1457 ;; note that newlines and other characters are all allowed
1460 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1461 *(multi-line-literal / multi-line-dotstuff)
1463 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1464 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1465 ;; A line containing only "." ends the multi-line.
1466 ;; Remove a leading '.' if followed by another '.'.
1467 string = quoted-string / multi-line
1470 filter points to the Sieve filter including its state
1471 id specifies identifier to match
1475 0 identifier not matched
1478 static int parse_string(struct Sieve *filter, struct String *data)
1483 data->character=(uschar*)0;
1484 if (*filter->pc=='"') /* quoted string */
1489 if (*filter->pc=='"') /* end of string */
1491 int foo=data->length;
1494 /* that way, there will be at least one character allocated */
1495 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1496 #ifdef ENCODED_CHARACTER
1497 if (filter->require_encoded_character
1498 && string_decode(filter,data)==-1)
1503 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1505 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1508 else /* regular character */
1511 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1513 if (*filter->pc=='\n')
1515 data->character=string_catn(data->character,&dataCapacity,&data->length,US"\r",1);
1519 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1523 filter->errmsg=CUS "missing end of string";
1526 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1529 /* skip optional white space followed by hashed comment or CRLF */
1530 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1531 if (*filter->pc=='#')
1533 if (parse_hashcomment(filter)==-1) return -1;
1536 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1538 else if (*filter->pc=='\n')
1550 filter->errmsg=CUS "syntax error";
1556 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1558 if (*filter->pc=='\n') /* end of line */
1561 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1569 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1571 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1574 int foo=data->length;
1576 /* that way, there will be at least one character allocated */
1577 data->character=string_catn(data->character,&dataCapacity,&foo,CUS "",1);
1584 #ifdef ENCODED_CHARACTER
1585 if (filter->require_encoded_character
1586 && string_decode(filter,data)==-1)
1591 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1593 data->character=string_catn(data->character,&dataCapacity,&data->length,CUS ".",1);
1597 else /* regular character */
1599 data->character=string_catn(data->character,&dataCapacity,&data->length,filter->pc,1);
1603 filter->errmsg=CUS "missing end of multi line string";
1610 /*************************************************
1611 * Parse a specific identifier *
1612 *************************************************/
1616 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1619 filter points to the Sieve filter including its state
1620 id specifies identifier to match
1623 0 identifier not matched
1626 static int parse_identifier(struct Sieve *filter, const uschar *id)
1628 size_t idlen=Ustrlen(id);
1630 if (strncmpic(US filter->pc,US id,idlen)==0)
1632 uschar next=filter->pc[idlen];
1634 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1642 /*************************************************
1644 *************************************************/
1648 number = 1*DIGIT [QUANTIFIER]
1649 QUANTIFIER = "K" / "M" / "G"
1652 filter points to the Sieve filter including its state
1656 -1 no string list found
1659 static int parse_number(struct Sieve *filter, unsigned long *data)
1663 if (*filter->pc>='0' && *filter->pc<='9')
1668 d=Ustrtoul(filter->pc,&e,10);
1671 filter->errmsg=CUstrerror(ERANGE);
1676 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1677 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1678 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1679 if (d>(ULONG_MAX/u))
1681 filter->errmsg=CUstrerror(ERANGE);
1690 filter->errmsg=CUS "missing number";
1696 /*************************************************
1697 * Parse a string list *
1698 *************************************************/
1702 string-list = "[" string *("," string) "]" / string
1705 filter points to the Sieve filter including its state
1706 data returns string list
1709 -1 no string list found
1713 parse_stringlist(struct Sieve *filter, struct String **data)
1715 const uschar *orig=filter->pc;
1716 int dataCapacity = 0;
1718 struct String *d = NULL;
1721 if (*filter->pc=='[') /* string list */
1726 if (parse_white(filter)==-1) goto error;
1727 if (dataLength+1 >= dataCapacity) /* increase buffer */
1730 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1732 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1733 new = store_get(sizeof(struct String) * dataCapacity);
1735 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1739 m=parse_string(filter,&d[dataLength]);
1742 if (dataLength==0) break;
1745 filter->errmsg=CUS "missing string";
1749 else if (m==-1) goto error;
1751 if (parse_white(filter)==-1) goto error;
1752 if (*filter->pc==',') ++filter->pc;
1755 if (*filter->pc==']')
1757 d[dataLength].character=(uschar*)0;
1758 d[dataLength].length=-1;
1765 filter->errmsg=CUS "missing closing bracket";
1769 else /* single string */
1771 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1775 m=parse_string(filter,&d[0]);
1787 d[1].character=(uschar*)0;
1794 filter->errmsg=CUS "missing string list";
1799 /*************************************************
1800 * Parse an optional address part specifier *
1801 *************************************************/
1805 address-part = ":localpart" / ":domain" / ":all"
1806 address-part =/ ":user" / ":detail"
1809 filter points to the Sieve filter including its state
1810 a returns address part specified
1813 0 no comparator found
1817 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1820 if (parse_identifier(filter,CUS ":user")==1)
1822 if (!filter->require_subaddress)
1824 filter->errmsg=CUS "missing previous require \"subaddress\";";
1830 else if (parse_identifier(filter,CUS ":detail")==1)
1832 if (!filter->require_subaddress)
1834 filter->errmsg=CUS "missing previous require \"subaddress\";";
1842 if (parse_identifier(filter,CUS ":localpart")==1)
1844 *a=ADDRPART_LOCALPART;
1847 else if (parse_identifier(filter,CUS ":domain")==1)
1852 else if (parse_identifier(filter,CUS ":all")==1)
1861 /*************************************************
1862 * Parse an optional comparator *
1863 *************************************************/
1867 comparator = ":comparator" <comparator-name: string>
1870 filter points to the Sieve filter including its state
1871 c returns comparator
1874 0 no comparator found
1875 -1 incomplete comparator found
1878 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1880 struct String comparator_name;
1882 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1883 if (parse_white(filter)==-1) return -1;
1884 switch (parse_string(filter,&comparator_name))
1889 filter->errmsg=CUS "missing comparator";
1896 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1901 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1903 *c=COMP_EN_ASCII_CASEMAP;
1906 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1908 *c=COMP_EN_ASCII_CASEMAP;
1911 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1913 *c=COMP_ASCII_NUMERIC;
1918 filter->errmsg=CUS "invalid comparator";
1927 /*************************************************
1928 * Parse an optional match type *
1929 *************************************************/
1933 match-type = ":is" / ":contains" / ":matches"
1936 filter points to the Sieve filter including its state
1937 m returns match type
1940 0 no match type found
1943 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1945 if (parse_identifier(filter,CUS ":is")==1)
1950 else if (parse_identifier(filter,CUS ":contains")==1)
1955 else if (parse_identifier(filter,CUS ":matches")==1)
1964 /*************************************************
1965 * Parse and interpret an optional test list *
1966 *************************************************/
1970 test-list = "(" test *("," test) ")"
1973 filter points to the Sieve filter including its state
1974 n total number of tests
1975 num_true number of passed tests
1976 exec Execute parsed statements
1979 0 no test list found
1980 -1 syntax or execution error
1983 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1985 if (parse_white(filter)==-1) return -1;
1986 if (*filter->pc=='(')
1995 switch (parse_test(filter,&cond,exec))
1998 case 0: filter->errmsg=CUS "missing test"; return -1;
1999 default: ++*n; if (cond) ++*num_true; break;
2001 if (parse_white(filter)==-1) return -1;
2002 if (*filter->pc==',') ++filter->pc;
2005 if (*filter->pc==')')
2012 filter->errmsg=CUS "missing closing paren";
2020 /*************************************************
2021 * Parse and interpret an optional test *
2022 *************************************************/
2026 filter points to the Sieve filter including its state
2027 cond returned condition status
2028 exec Execute parsed statements
2032 -1 syntax or execution error
2035 static int parse_test(struct Sieve *filter, int *cond, int exec)
2037 if (parse_white(filter)==-1) return -1;
2038 if (parse_identifier(filter,CUS "address"))
2041 address-test = "address" { [address-part] [comparator] [match-type] }
2042 <header-list: string-list> <key-list: string-list>
2044 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2047 enum AddressPart addressPart=ADDRPART_ALL;
2048 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2049 enum MatchType matchType=MATCH_IS;
2050 struct String *hdr,*h,*key,*k;
2056 if (parse_white(filter)==-1) return -1;
2057 if ((m=parse_addresspart(filter,&addressPart))!=0)
2059 if (m==-1) return -1;
2062 filter->errmsg=CUS "address part already specified";
2067 else if ((m=parse_comparator(filter,&comparator))!=0)
2069 if (m==-1) return -1;
2072 filter->errmsg=CUS "comparator already specified";
2077 else if ((m=parse_matchtype(filter,&matchType))!=0)
2079 if (m==-1) return -1;
2082 filter->errmsg=CUS "match type already specified";
2089 if (parse_white(filter)==-1) return -1;
2090 if ((m=parse_stringlist(filter,&hdr))!=1)
2092 if (m==0) filter->errmsg=CUS "header string list expected";
2095 if (parse_white(filter)==-1) return -1;
2096 if ((m=parse_stringlist(filter,&key))!=1)
2098 if (m==0) filter->errmsg=CUS "key string list expected";
2102 for (h=hdr; h->length!=-1 && !*cond; ++h)
2104 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2108 !eq_asciicase(h,&str_from,0)
2109 && !eq_asciicase(h,&str_to,0)
2110 && !eq_asciicase(h,&str_cc,0)
2111 && !eq_asciicase(h,&str_bcc,0)
2112 && !eq_asciicase(h,&str_sender,0)
2113 && !eq_asciicase(h,&str_resent_from,0)
2114 && !eq_asciicase(h,&str_resent_to,0)
2117 filter->errmsg=CUS "invalid header field";
2122 /* We are only interested in addresses below, so no MIME decoding */
2123 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2124 if (header_value == NULL)
2126 filter->errmsg=CUS "header string expansion failed";
2129 parse_allow_group = TRUE;
2130 while (*header_value && !*cond)
2133 int start, end, domain;
2137 end_addr = parse_find_address_end(header_value, FALSE);
2138 saveend = *end_addr;
2140 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2142 if (extracted_addr) switch (addressPart)
2144 case ADDRPART_ALL: part=extracted_addr; break;
2148 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2149 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2151 case ADDRPART_DETAIL: part=NULL; break;
2155 *end_addr = saveend;
2158 for (k=key; k->length!=-1; ++k)
2160 struct String partStr;
2162 partStr.character=part;
2163 partStr.length=Ustrlen(part);
2166 *cond=compare(filter,k,&partStr,comparator,matchType);
2167 if (*cond==-1) return -1;
2172 if (saveend == 0) break;
2173 header_value = end_addr + 1;
2175 parse_allow_group = FALSE;
2176 parse_found_group = FALSE;
2181 else if (parse_identifier(filter,CUS "allof"))
2184 allof-test = "allof" <tests: test-list>
2189 switch (parse_testlist(filter,&n,&num_true,exec))
2192 case 0: filter->errmsg=CUS "missing test list"; return -1;
2193 default: *cond=(n==num_true); return 1;
2196 else if (parse_identifier(filter,CUS "anyof"))
2199 anyof-test = "anyof" <tests: test-list>
2204 switch (parse_testlist(filter,&n,&num_true,exec))
2207 case 0: filter->errmsg=CUS "missing test list"; return -1;
2208 default: *cond=(num_true>0); return 1;
2211 else if (parse_identifier(filter,CUS "exists"))
2214 exists-test = "exists" <header-names: string-list>
2217 struct String *hdr,*h;
2220 if (parse_white(filter)==-1) return -1;
2221 if ((m=parse_stringlist(filter,&hdr))!=1)
2223 if (m==0) filter->errmsg=CUS "header string list expected";
2229 for (h=hdr; h->length!=-1 && *cond; ++h)
2233 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2234 if (header_def == NULL)
2236 filter->errmsg=CUS "header string expansion failed";
2239 if (Ustrcmp(header_def,"false")==0) *cond=0;
2244 else if (parse_identifier(filter,CUS "false"))
2247 false-test = "false"
2253 else if (parse_identifier(filter,CUS "header"))
2256 header-test = "header" { [comparator] [match-type] }
2257 <header-names: string-list> <key-list: string-list>
2260 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2261 enum MatchType matchType=MATCH_IS;
2262 struct String *hdr,*h,*key,*k;
2268 if (parse_white(filter)==-1) return -1;
2269 if ((m=parse_comparator(filter,&comparator))!=0)
2271 if (m==-1) return -1;
2274 filter->errmsg=CUS "comparator already specified";
2279 else if ((m=parse_matchtype(filter,&matchType))!=0)
2281 if (m==-1) return -1;
2284 filter->errmsg=CUS "match type already specified";
2291 if (parse_white(filter)==-1) return -1;
2292 if ((m=parse_stringlist(filter,&hdr))!=1)
2294 if (m==0) filter->errmsg=CUS "header string list expected";
2297 if (parse_white(filter)==-1) return -1;
2298 if ((m=parse_stringlist(filter,&key))!=1)
2300 if (m==0) filter->errmsg=CUS "key string list expected";
2304 for (h=hdr; h->length!=-1 && !*cond; ++h)
2308 filter->errmsg=CUS "invalid header field";
2313 struct String header_value;
2316 expand_header(&header_value,h);
2317 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2318 if (header_value.character == NULL || header_def == NULL)
2320 filter->errmsg=CUS "header string expansion failed";
2323 for (k=key; k->length!=-1; ++k)
2325 if (Ustrcmp(header_def,"true")==0)
2327 *cond=compare(filter,k,&header_value,comparator,matchType);
2328 if (*cond==-1) return -1;
2336 else if (parse_identifier(filter,CUS "not"))
2338 if (parse_white(filter)==-1) return -1;
2339 switch (parse_test(filter,cond,exec))
2342 case 0: filter->errmsg=CUS "missing test"; return -1;
2343 default: *cond=!*cond; return 1;
2346 else if (parse_identifier(filter,CUS "size"))
2349 relop = ":over" / ":under"
2350 size-test = "size" relop <limit: number>
2353 unsigned long limit;
2356 if (parse_white(filter)==-1) return -1;
2357 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2358 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2361 filter->errmsg=CUS "missing :over or :under";
2364 if (parse_white(filter)==-1) return -1;
2365 if (parse_number(filter,&limit)==-1) return -1;
2366 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2369 else if (parse_identifier(filter,CUS "true"))
2374 else if (parse_identifier(filter,CUS "envelope"))
2377 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2378 <envelope-part: string-list> <key-list: string-list>
2380 envelope-part is case insensitive "from" or "to"
2381 #ifdef ENVELOPE_AUTH
2382 envelope-part =/ "auth"
2386 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2387 enum AddressPart addressPart=ADDRPART_ALL;
2388 enum MatchType matchType=MATCH_IS;
2389 struct String *env,*e,*key,*k;
2393 if (!filter->require_envelope)
2395 filter->errmsg=CUS "missing previous require \"envelope\";";
2400 if (parse_white(filter)==-1) return -1;
2401 if ((m=parse_comparator(filter,&comparator))!=0)
2403 if (m==-1) return -1;
2406 filter->errmsg=CUS "comparator already specified";
2411 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2413 if (m==-1) return -1;
2416 filter->errmsg=CUS "address part already specified";
2421 else if ((m=parse_matchtype(filter,&matchType))!=0)
2423 if (m==-1) return -1;
2426 filter->errmsg=CUS "match type already specified";
2433 if (parse_white(filter)==-1) return -1;
2434 if ((m=parse_stringlist(filter,&env))!=1)
2436 if (m==0) filter->errmsg=CUS "envelope string list expected";
2439 if (parse_white(filter)==-1) return -1;
2440 if ((m=parse_stringlist(filter,&key))!=1)
2442 if (m==0) filter->errmsg=CUS "key string list expected";
2446 for (e=env; e->length!=-1 && !*cond; ++e)
2448 const uschar *envelopeExpr=CUS 0;
2449 uschar *envelope=US 0;
2451 if (eq_asciicase(e,&str_from,0))
2453 switch (addressPart)
2455 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2459 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2460 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2462 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2466 else if (eq_asciicase(e,&str_to,0))
2468 switch (addressPart)
2470 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2472 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2473 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2475 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2476 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2479 #ifdef ENVELOPE_AUTH
2480 else if (eq_asciicase(e,&str_auth,0))
2482 switch (addressPart)
2484 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2488 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2489 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2491 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2498 filter->errmsg=CUS "invalid envelope string";
2501 if (exec && envelopeExpr)
2503 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2505 filter->errmsg=CUS "header string expansion failed";
2508 for (k=key; k->length!=-1; ++k)
2510 struct String envelopeStr;
2512 envelopeStr.character=envelope;
2513 envelopeStr.length=Ustrlen(envelope);
2514 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2515 if (*cond==-1) return -1;
2523 else if (parse_identifier(filter,CUS "valid_notify_method"))
2526 valid_notify_method = "valid_notify_method"
2527 <notification-uris: string-list>
2530 struct String *uris,*u;
2533 if (!filter->require_enotify)
2535 filter->errmsg=CUS "missing previous require \"enotify\";";
2538 if (parse_white(filter)==-1) return -1;
2539 if ((m=parse_stringlist(filter,&uris))!=1)
2541 if (m==0) filter->errmsg=CUS "URI string list expected";
2547 for (u=uris; u->length!=-1 && *cond; ++u)
2549 string_item *recipient;
2550 struct String header,subject,body;
2554 header.character=(uschar*)0;
2556 subject.character=(uschar*)0;
2558 body.character=(uschar*)0;
2559 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2565 else if (parse_identifier(filter,CUS "notify_method_capability"))
2568 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2569 <notification-uri: string>
2570 <notification-capability: string>
2571 <key-list: string-list>
2577 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2578 enum MatchType matchType=MATCH_IS;
2579 struct String uri,capa,*keys,*k;
2581 if (!filter->require_enotify)
2583 filter->errmsg=CUS "missing previous require \"enotify\";";
2588 if (parse_white(filter)==-1) return -1;
2589 if ((m=parse_comparator(filter,&comparator))!=0)
2591 if (m==-1) return -1;
2594 filter->errmsg=CUS "comparator already specified";
2599 else if ((m=parse_matchtype(filter,&matchType))!=0)
2601 if (m==-1) return -1;
2604 filter->errmsg=CUS "match type already specified";
2611 if ((m=parse_string(filter,&uri))!=1)
2613 if (m==0) filter->errmsg=CUS "missing notification URI string";
2616 if (parse_white(filter)==-1) return -1;
2617 if ((m=parse_string(filter,&capa))!=1)
2619 if (m==0) filter->errmsg=CUS "missing notification capability string";
2622 if (parse_white(filter)==-1) return -1;
2623 if ((m=parse_stringlist(filter,&keys))!=1)
2625 if (m==0) filter->errmsg=CUS "missing key string list";
2630 string_item *recipient;
2631 struct String header,subject,body;
2636 header.character=(uschar*)0;
2638 subject.character=(uschar*)0;
2640 body.character=(uschar*)0;
2641 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2643 if (eq_asciicase(&capa,&str_online,0)==1)
2644 for (k=keys; k->length!=-1; ++k)
2646 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2647 if (*cond==-1) return -1;
2659 /*************************************************
2660 * Parse and interpret an optional block *
2661 *************************************************/
2665 filter points to the Sieve filter including its state
2666 exec Execute parsed statements
2667 generated where to hang newly-generated addresses
2669 Returns: 2 success by stop
2671 0 no block command found
2672 -1 syntax or execution error
2675 static int parse_block(struct Sieve *filter, int exec,
2676 address_item **generated)
2680 if (parse_white(filter)==-1) return -1;
2681 if (*filter->pc=='{')
2684 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2685 if (*filter->pc=='}')
2692 filter->errmsg=CUS "expecting command or closing brace";
2700 /*************************************************
2701 * Match a semicolon *
2702 *************************************************/
2706 filter points to the Sieve filter including its state
2712 static int parse_semicolon(struct Sieve *filter)
2714 if (parse_white(filter)==-1) return -1;
2715 if (*filter->pc==';')
2722 filter->errmsg=CUS "missing semicolon";
2728 /*************************************************
2729 * Parse and interpret a Sieve command *
2730 *************************************************/
2734 filter points to the Sieve filter including its state
2735 exec Execute parsed statements
2736 generated where to hang newly-generated addresses
2738 Returns: 2 success by stop
2740 -1 syntax or execution error
2743 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2747 if (parse_white(filter)==-1) return -1;
2748 if (parse_identifier(filter,CUS "if"))
2751 if-command = "if" test block *( "elsif" test block ) [ else block ]
2754 int cond,m,unsuccessful;
2757 if (parse_white(filter)==-1) return -1;
2758 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2761 filter->errmsg=CUS "missing test";
2764 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2765 (debug_selector & D_filter) != 0)
2767 if (exec) debug_printf("if %s\n",cond?"true":"false");
2769 m=parse_block(filter,exec ? cond : 0, generated);
2770 if (m==-1 || m==2) return m;
2773 filter->errmsg=CUS "missing block";
2776 unsuccessful = !cond;
2777 for (;;) /* elsif test block */
2779 if (parse_white(filter)==-1) return -1;
2780 if (parse_identifier(filter,CUS "elsif"))
2782 if (parse_white(filter)==-1) return -1;
2783 m=parse_test(filter,&cond,exec && unsuccessful);
2784 if (m==-1 || m==2) return m;
2787 filter->errmsg=CUS "missing test";
2790 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2791 (debug_selector & D_filter) != 0)
2793 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2795 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2796 if (m==-1 || m==2) return m;
2799 filter->errmsg=CUS "missing block";
2802 if (exec && unsuccessful && cond) unsuccessful = 0;
2807 if (parse_white(filter)==-1) return -1;
2808 if (parse_identifier(filter,CUS "else"))
2810 m=parse_block(filter,exec && unsuccessful, generated);
2811 if (m==-1 || m==2) return m;
2814 filter->errmsg=CUS "missing block";
2819 else if (parse_identifier(filter,CUS "stop"))
2822 stop-command = "stop" { stop-options } ";"
2826 if (parse_semicolon(filter)==-1) return -1;
2829 filter->pc+=Ustrlen(filter->pc);
2833 else if (parse_identifier(filter,CUS "keep"))
2836 keep-command = "keep" { keep-options } ";"
2840 if (parse_semicolon(filter)==-1) return -1;
2843 add_addr(generated,US"inbox",1,0,0,0);
2847 else if (parse_identifier(filter,CUS "discard"))
2850 discard-command = "discard" { discard-options } ";"
2854 if (parse_semicolon(filter)==-1) return -1;
2855 if (exec) filter->keep=0;
2857 else if (parse_identifier(filter,CUS "redirect"))
2860 redirect-command = "redirect" redirect-options "string" ";"
2862 redirect-options =) ":copy"
2865 struct String recipient;
2871 if (parse_white(filter)==-1) return -1;
2872 if (parse_identifier(filter,CUS ":copy")==1)
2874 if (!filter->require_copy)
2876 filter->errmsg=CUS "missing previous require \"copy\";";
2883 if (parse_white(filter)==-1) return -1;
2884 if ((m=parse_string(filter,&recipient))!=1)
2886 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2889 if (strchr(CCS recipient.character,'@')==(char*)0)
2891 filter->errmsg=CUS "unqualified recipient address";
2896 add_addr(generated,recipient.character,0,0,0,0);
2897 if (!copy) filter->keep = 0;
2899 if (parse_semicolon(filter)==-1) return -1;
2901 else if (parse_identifier(filter,CUS "fileinto"))
2904 fileinto-command = "fileinto" { fileinto-options } string ";"
2906 fileinto-options =) [ ":copy" ]
2909 struct String folder;
2912 unsigned long maxage, maxmessages, maxstorage;
2915 maxage = maxmessages = maxstorage = 0;
2916 if (!filter->require_fileinto)
2918 filter->errmsg=CUS "missing previous require \"fileinto\";";
2923 if (parse_white(filter)==-1) return -1;
2924 if (parse_identifier(filter,CUS ":copy")==1)
2926 if (!filter->require_copy)
2928 filter->errmsg=CUS "missing previous require \"copy\";";
2935 if (parse_white(filter)==-1) return -1;
2936 if ((m=parse_string(filter,&folder))!=1)
2938 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2941 m=0; s=folder.character;
2942 if (folder.length==0) m=1;
2943 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2946 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2951 filter->errmsg=CUS "invalid folder";
2956 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2957 if (!copy) filter->keep = 0;
2959 if (parse_semicolon(filter)==-1) return -1;
2962 else if (parse_identifier(filter,CUS "notify"))
2965 notify-command = "notify" { notify-options } <method: string> ";"
2966 notify-options = [":from" string]
2967 [":importance" <"1" / "2" / "3">]
2968 [":options" 1*(string-list / number)]
2974 struct String importance;
2975 struct String message;
2976 struct String method;
2977 struct Notification *already;
2978 string_item *recipient;
2979 struct String header;
2980 struct String subject;
2982 uschar *envelope_from;
2983 struct String auto_submitted_value;
2984 uschar *auto_submitted_def;
2986 if (!filter->require_enotify)
2988 filter->errmsg=CUS "missing previous require \"enotify\";";
2991 from.character=(uschar*)0;
2993 importance.character=(uschar*)0;
2994 importance.length=-1;
2995 message.character=(uschar*)0;
2999 header.character=(uschar*)0;
3001 subject.character=(uschar*)0;
3003 body.character=(uschar*)0;
3004 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3007 if (parse_white(filter)==-1) return -1;
3008 if (parse_identifier(filter,CUS ":from")==1)
3010 if (parse_white(filter)==-1) return -1;
3011 if ((m=parse_string(filter,&from))!=1)
3013 if (m==0) filter->errmsg=CUS "from string expected";
3017 else if (parse_identifier(filter,CUS ":importance")==1)
3019 if (parse_white(filter)==-1) return -1;
3020 if ((m=parse_string(filter,&importance))!=1)
3022 if (m==0) filter->errmsg=CUS "importance string expected";
3025 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3027 filter->errmsg=CUS "invalid importance";
3031 else if (parse_identifier(filter,CUS ":options")==1)
3033 if (parse_white(filter)==-1) return -1;
3035 else if (parse_identifier(filter,CUS ":message")==1)
3037 if (parse_white(filter)==-1) return -1;
3038 if ((m=parse_string(filter,&message))!=1)
3040 if (m==0) filter->errmsg=CUS "message string expected";
3046 if (parse_white(filter)==-1) return -1;
3047 if ((m=parse_string(filter,&method))!=1)
3049 if (m==0) filter->errmsg=CUS "missing method string";
3052 if (parse_semicolon(filter)==-1) return -1;
3053 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3057 if (message.length==-1) message=subject;
3058 if (message.length==-1) expand_header(&message,&str_subject);
3059 expand_header(&auto_submitted_value,&str_auto_submitted);
3060 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3061 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3063 filter->errmsg=CUS "header string expansion failed";
3066 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3068 for (already=filter->notified; already; already=already->next)
3070 if (already->method.length==method.length
3071 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3072 && already->importance.length==importance.length
3073 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3074 && already->message.length==message.length
3075 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3078 if (already==(struct Notification*)0)
3079 /* New notification, process it */
3081 struct Notification *sent;
3082 sent=store_get(sizeof(struct Notification));
3083 sent->method=method;
3084 sent->importance=importance;
3085 sent->message=message;
3086 sent->next=filter->notified;
3087 filter->notified=sent;
3088 #ifndef COMPILE_SYNTAX_CHECKER
3089 if (filter_test == FTEST_NONE)
3094 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3098 int buffer_capacity;
3100 f = fdopen(fd, "wb");
3101 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3102 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3103 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3104 if (header.length>0) fprintf(f,"%s",header.character);
3105 if (message.length==-1)
3107 message.character=US"Notification";
3108 message.length=Ustrlen(message.character);
3110 /* Allocation is larger than necessary, but enough even for split MIME words */
3111 buffer_capacity=32+4*message.length;
3112 buffer=store_get(buffer_capacity);
3113 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3115 if (body.length>0) fprintf(f,"%s\n",body.character);
3118 (void)child_close(pid, 0);
3121 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3123 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3129 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3131 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3137 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3139 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3146 else if (parse_identifier(filter,CUS "vacation"))
3149 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3150 vacation-options = [":days" number]
3153 [":addresses" string-list]
3160 struct String subject;
3162 struct String *addresses;
3164 string_item *aliases;
3165 struct String handle;
3166 struct String reason;
3168 if (!filter->require_vacation)
3170 filter->errmsg=CUS "missing previous require \"vacation\";";
3175 if (filter->vacation_ran)
3177 filter->errmsg=CUS "trying to execute vacation more than once";
3180 filter->vacation_ran=1;
3182 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3183 subject.character=(uschar*)0;
3185 from.character=(uschar*)0;
3187 addresses=(struct String*)0;
3190 handle.character=(uschar*)0;
3194 if (parse_white(filter)==-1) return -1;
3195 if (parse_identifier(filter,CUS ":days")==1)
3197 if (parse_white(filter)==-1) return -1;
3198 if (parse_number(filter,&days)==-1) return -1;
3199 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3200 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3202 else if (parse_identifier(filter,CUS ":subject")==1)
3204 if (parse_white(filter)==-1) return -1;
3205 if ((m=parse_string(filter,&subject))!=1)
3207 if (m==0) filter->errmsg=CUS "subject string expected";
3211 else if (parse_identifier(filter,CUS ":from")==1)
3213 if (parse_white(filter)==-1) return -1;
3214 if ((m=parse_string(filter,&from))!=1)
3216 if (m==0) filter->errmsg=CUS "from string expected";
3219 if (check_mail_address(filter,&from)!=1)
3222 else if (parse_identifier(filter,CUS ":addresses")==1)
3226 if (parse_white(filter)==-1) return -1;
3227 if ((m=parse_stringlist(filter,&addresses))!=1)
3229 if (m==0) filter->errmsg=CUS "addresses string list expected";
3232 for (a=addresses; a->length!=-1; ++a)
3236 new=store_get(sizeof(string_item));
3237 new->text=store_get(a->length+1);
3238 if (a->length) memcpy(new->text,a->character,a->length);
3239 new->text[a->length]='\0';
3244 else if (parse_identifier(filter,CUS ":mime")==1)
3246 else if (parse_identifier(filter,CUS ":handle")==1)
3248 if (parse_white(filter)==-1) return -1;
3249 if ((m=parse_string(filter,&from))!=1)
3251 if (m==0) filter->errmsg=CUS "handle string expected";
3257 if (parse_white(filter)==-1) return -1;
3258 if ((m=parse_string(filter,&reason))!=1)
3260 if (m==0) filter->errmsg=CUS "missing reason string";
3267 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3270 filter->errmsg=CUS "MIME reason string contains 8bit text";
3274 if (parse_semicolon(filter)==-1) return -1;
3281 int buffer_capacity;
3285 uschar hexdigest[33];
3289 if (filter_personal(aliases,TRUE))
3291 if (filter_test == FTEST_NONE)
3293 /* ensure oncelog directory exists; failure will be detected later */
3295 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3297 /* build oncelog filename */
3299 key.character=(uschar*)0;
3302 if (handle.length==-1)
3304 if (subject.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,subject.character,subject.length);
3305 if (from.length!=-1) key.character=string_catn(key.character,&capacity,&key.length,from.character,from.length);
3306 key.character=string_catn(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3307 key.character=string_catn(key.character,&capacity,&key.length,reason.character,reason.length);
3312 md5_end(&base, key.character, key.length, digest);
3313 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3314 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3316 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3318 if (filter_test == FTEST_NONE)
3320 capacity=Ustrlen(filter->vacation_directory);
3322 once=string_catn(filter->vacation_directory,&capacity,&start,US"/",1);
3323 once=string_catn(once,&capacity,&start,hexdigest,33);
3326 /* process subject */
3328 if (subject.length==-1)
3330 uschar *subject_def;
3332 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3333 if (Ustrcmp(subject_def,"true")==0)
3335 expand_header(&subject,&str_subject);
3338 subject.character=string_catn(US"Auto: ",&capacity,&start,subject.character,subject.length);
3339 subject.length=start;
3343 subject.character=US"Automated reply";
3344 subject.length=Ustrlen(subject.character);
3348 /* add address to list of generated addresses */
3350 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3351 setflag(addr, af_pfr);
3352 addr->prop.ignore_error = TRUE;
3353 addr->next = *generated;
3355 addr->reply = store_get(sizeof(reply_item));
3356 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3357 addr->reply->to = string_copy(sender_address);
3358 if (from.length==-1)
3359 addr->reply->from = expand_string(US"$local_part@$domain");
3361 addr->reply->from = from.character;
3362 /* Allocation is larger than necessary, but enough even for split MIME words */
3363 buffer_capacity=32+4*subject.length;
3364 buffer=store_get(buffer_capacity);
3365 /* deconst cast safe as we pass in a non-const item */
3366 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3367 addr->reply->oncelog=once;
3368 addr->reply->once_repeat=days*86400;
3370 /* build body and MIME headers */
3374 uschar *mime_body,*reason_end;
3375 static const uschar nlnl[]="\r\n\r\n";
3379 mime_body=reason.character,reason_end=reason.character+reason.length;
3380 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3385 addr->reply->headers = string_catn(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3386 addr->reply->headers[start] = '\0';
3389 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3390 else mime_body=reason_end-1;
3391 addr->reply->text = string_catn(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3392 addr->reply->text[start] = '\0';
3396 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3399 start = reason.length;
3400 addr->reply->headers = US"MIME-Version: 1.0\n"
3401 "Content-Type: text/plain;\n"
3402 "\tcharset=\"utf-8\"\n"
3403 "Content-Transfer-Encoding: quoted-printable";
3404 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3408 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3410 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3421 /*************************************************
3422 * Parse and interpret a sieve filter *
3423 *************************************************/
3427 filter points to the Sieve filter including its state
3428 exec Execute parsed statements
3429 generated where to hang newly-generated addresses
3432 -1 syntax or execution error
3436 parse_start(struct Sieve *filter, int exec, address_item **generated)
3438 filter->pc=filter->filter;
3441 filter->require_envelope=0;
3442 filter->require_fileinto=0;
3443 #ifdef ENCODED_CHARACTER
3444 filter->require_encoded_character=0;
3446 #ifdef ENVELOPE_AUTH
3447 filter->require_envelope_auth=0;
3450 filter->require_enotify=0;
3451 filter->notified=(struct Notification*)0;
3454 filter->require_subaddress=0;
3457 filter->require_vacation=0;
3458 filter->vacation_ran=0;
3460 filter->require_copy=0;
3461 filter->require_iascii_numeric=0;
3463 if (parse_white(filter)==-1) return -1;
3465 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3468 struct dirent *oncelog;
3469 struct stat properties;
3472 /* clean up old vacation log databases */
3474 oncelogdir=opendir(CS filter->vacation_directory);
3476 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3478 filter->errmsg=CUS "unable to open vacation directory";
3482 if (oncelogdir != NULL)
3486 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3488 if (strlen(oncelog->d_name)==32)
3490 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3491 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3495 closedir(oncelogdir);
3499 while (parse_identifier(filter,CUS "require"))
3502 require-command = "require" <capabilities: string-list>
3505 struct String *cap,*check;
3508 if (parse_white(filter)==-1) return -1;
3509 if ((m=parse_stringlist(filter,&cap))!=1)
3511 if (m==0) filter->errmsg=CUS "capability string list expected";
3514 for (check=cap; check->character; ++check)
3516 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3517 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3518 #ifdef ENCODED_CHARACTER
3519 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3521 #ifdef ENVELOPE_AUTH
3522 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3525 else if (eq_octet(check,&str_enotify,0))
3527 if (filter->enotify_mailto_owner == NULL)
3529 filter->errmsg=CUS "enotify disabled";
3532 filter->require_enotify=1;
3536 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3539 else if (eq_octet(check,&str_vacation,0))
3541 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3543 filter->errmsg=CUS "vacation disabled";
3546 filter->require_vacation=1;
3549 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3550 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3551 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3552 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3553 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3556 filter->errmsg=CUS "unknown capability";
3560 if (parse_semicolon(filter)==-1) return -1;
3562 if (parse_commands(filter,exec,generated)==-1) return -1;
3565 filter->errmsg=CUS "syntax error";
3572 /*************************************************
3573 * Interpret a sieve filter file *
3574 *************************************************/
3578 filter points to the entire file, read into store as a single string
3579 options controls whether various special things are allowed, and requests
3580 special actions (not currently used)
3581 vacation_directory where to store vacation "once" files
3582 enotify_mailto_owner owner of mailto notifications
3583 useraddress string expression for :user part of address
3584 subaddress string expression for :subaddress part of address
3585 generated where to hang newly-generated addresses
3586 error where to pass back an error text
3588 Returns: FF_DELIVERED success, a significant action was taken
3589 FF_NOTDELIVERED success, no significant action
3590 FF_DEFER defer requested
3591 FF_FAIL fail requested
3592 FF_FREEZE freeze requested
3593 FF_ERROR there was a problem
3597 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3598 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3599 address_item **generated, uschar **error)
3605 options = options; /* Keep picky compilers happy */
3608 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3609 sieve.filter=filter;
3611 if (vacation_directory == NULL)
3612 sieve.vacation_directory = NULL;
3615 sieve.vacation_directory=expand_string(vacation_directory);
3616 if (sieve.vacation_directory == NULL)
3618 *error = string_sprintf("failed to expand \"%s\" "
3619 "(sieve_vacation_directory): %s", vacation_directory,
3620 expand_string_message);
3625 if (enotify_mailto_owner == NULL)
3626 sieve.enotify_mailto_owner = NULL;
3629 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3630 if (sieve.enotify_mailto_owner == NULL)
3632 *error = string_sprintf("failed to expand \"%s\" "
3633 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3634 expand_string_message);
3639 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3640 sieve.subaddress = subaddress;
3642 #ifdef COMPILE_SYNTAX_CHECKER
3643 if (parse_start(&sieve,0,generated)==1)
3645 if (parse_start(&sieve,1,generated)==1)
3650 add_addr(generated,US"inbox",1,0,0,0);
3651 msg = string_sprintf("Implicit keep");
3656 msg = string_sprintf("No implicit keep");
3662 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3663 #ifdef COMPILE_SYNTAX_CHECKER
3667 add_addr(generated,US"inbox",1,0,0,0);
3672 #ifndef COMPILE_SYNTAX_CHECKER
3673 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3674 else debug_printf("%s\n", msg);
3677 DEBUG(D_route) debug_printf("Sieve: end of processing\n");