1 /* $Cambridge: exim/src/src/sieve.c,v 1.37 2009/02/04 11:31:13 michael Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2008 */
8 /* 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 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
113 static int parse_test(struct Sieve *filter, int *cond, int exec);
114 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
116 static uschar str_from_c[]="From";
117 static const struct String str_from={ str_from_c, 4 };
118 static uschar str_to_c[]="To";
119 static const struct String str_to={ str_to_c, 2 };
120 static uschar str_cc_c[]="Cc";
121 static const struct String str_cc={ str_cc_c, 2 };
122 static uschar str_bcc_c[]="Bcc";
123 static const struct String str_bcc={ str_bcc_c, 3 };
124 static uschar str_auth_c[]="auth";
125 static const struct String str_auth={ str_auth_c, 4 };
126 static uschar str_sender_c[]="Sender";
127 static const struct String str_sender={ str_sender_c, 6 };
128 static uschar str_resent_from_c[]="Resent-From";
129 static const struct String str_resent_from={ str_resent_from_c, 11 };
130 static uschar str_resent_to_c[]="Resent-To";
131 static const struct String str_resent_to={ str_resent_to_c, 9 };
132 static uschar str_fileinto_c[]="fileinto";
133 static const struct String str_fileinto={ str_fileinto_c, 8 };
134 static uschar str_envelope_c[]="envelope";
135 static const struct String str_envelope={ str_envelope_c, 8 };
136 #ifdef ENCODED_CHARACTER
137 static uschar str_encoded_character_c[]="encoded-character";
138 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
141 static uschar str_envelope_auth_c[]="envelope-auth";
142 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
145 static uschar str_enotify_c[]="enotify";
146 static const struct String str_enotify={ str_enotify_c, 7 };
147 static uschar str_online_c[]="online";
148 static const struct String str_online={ str_online_c, 6 };
149 static uschar str_maybe_c[]="maybe";
150 static const struct String str_maybe={ str_maybe_c, 5 };
151 static uschar str_auto_submitted_c[]="Auto-Submitted";
152 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
155 static uschar str_subaddress_c[]="subaddress";
156 static const struct String str_subaddress={ str_subaddress_c, 10 };
159 static uschar str_vacation_c[]="vacation";
160 static const struct String str_vacation={ str_vacation_c, 8 };
161 static uschar str_subject_c[]="Subject";
162 static const struct String str_subject={ str_subject_c, 7 };
164 static uschar str_copy_c[]="copy";
165 static const struct String str_copy={ str_copy_c, 4 };
166 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
167 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
168 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
169 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
170 static uschar str_ioctet_c[]="i;octet";
171 static const struct String str_ioctet={ str_ioctet_c, 7 };
172 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
173 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
174 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
175 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
176 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
177 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
178 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
179 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
180 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
181 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
184 /*************************************************
185 * Encode to quoted-printable *
186 *************************************************/
197 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
200 const uschar *start,*end;
205 for (pass=0; pass<=1; ++pass)
212 dst->character=store_get(dst->length+1); /* plus one for \0 */
215 for (start=src->character,end=start+src->length; start<end; ++start)
232 || (ch>=62 && ch<=126)
237 && (*(start+1)!='\r' || *(start+2)!='\n')
247 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
265 sprintf(CS new,"=%02X",ch);
272 *new='\0'; /* not included in length, but nice */
277 /*************************************************
278 * Check mail address for correct syntax *
279 *************************************************/
282 Check mail address for being syntactically correct.
285 filter points to the Sieve filter including its state
286 address String containing one address
289 1 Mail address is syntactically OK
293 int check_mail_address(struct Sieve *filter, const struct String *address)
295 int start, end, domain;
298 if (address->length>0)
300 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
304 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
305 address->character, error);
313 filter->errmsg=CUS "empty address";
319 /*************************************************
320 * Decode URI encoded string *
321 *************************************************/
325 str URI encoded string
328 0 Decoding successful
333 static int uri_decode(struct String *str)
337 if (str->length==0) return 0;
338 for (s=str->character,t=s,e=s+str->length; s<e; )
342 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
344 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
345 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
354 str->length=t-str->character;
359 /*************************************************
361 *************************************************/
366 mailtoURI = "mailto:" [ to ] [ headers ]
367 to = [ addr-spec *("%2C" addr-spec ) ]
368 headers = "?" header *( "&" header )
369 header = hname "=" hvalue
374 filter points to the Sieve filter including its state
375 uri URI, excluding scheme
380 1 URI is syntactically OK
385 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
388 struct String to,hname,hvalue;
392 if (Ustrncmp(uri,"mailto:",7))
394 filter->errmsg=US "Unknown URI scheme";
398 if (*uri && *uri!='?')
402 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
406 to.character=(uschar*)0;
408 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
409 to.character[to.length]='\0';
410 if (uri_decode(&to)==-1)
412 filter->errmsg=US"Invalid URI encoding";
415 new=store_get(sizeof(string_item));
416 new->text=store_get(to.length+1);
417 if (to.length) memcpy(new->text,to.character,to.length);
418 new->text[to.length]='\0';
419 new->next=*recipient;
424 filter->errmsg=US"Missing addr-spec in URI";
427 if (*uri=='%') uri+=3;
436 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
440 hname.character=(uschar*)0;
442 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
443 hname.character[hname.length]='\0';
444 if (uri_decode(&hname)==-1)
446 filter->errmsg=US"Invalid URI encoding";
455 filter->errmsg=US"Missing equal after hname";
459 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
463 hvalue.character=(uschar*)0;
465 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
466 hvalue.character[hvalue.length]='\0';
467 if (uri_decode(&hvalue)==-1)
469 filter->errmsg=US"Invalid URI encoding";
473 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
475 new=store_get(sizeof(string_item));
476 new->text=store_get(hvalue.length+1);
477 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
478 new->text[hvalue.length]='\0';
479 new->next=*recipient;
482 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
484 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
488 static struct String ignore[]=
494 {US"auto-submitted",14}
496 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
499 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
502 if (header->length==-1) header->length=0;
503 capacity=header->length;
504 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
505 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
506 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
507 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
508 header->character[header->length]='\0';
511 if (*uri=='&') ++uri;
517 filter->errmsg=US"Syntactically invalid URI";
525 /*************************************************
526 * Octet-wise string comparison *
527 *************************************************/
531 needle UTF-8 string to search ...
532 haystack ... inside the haystack
533 match_prefix 1 to compare if needle is a prefix of haystack
535 Returns: 0 needle not found in haystack
539 static int eq_octet(const struct String *needle,
540 const struct String *haystack, int match_prefix)
548 h=haystack->character;
552 if (*n&0x80) return 0;
553 if (*h&0x80) return 0;
555 if (*n!=*h) return 0;
561 return (match_prefix ? nl==0 : nl==0 && hl==0);
565 /*************************************************
566 * ASCII case-insensitive string comparison *
567 *************************************************/
571 needle UTF-8 string to search ...
572 haystack ... inside the haystack
573 match_prefix 1 to compare if needle is a prefix of haystack
575 Returns: 0 needle not found in haystack
579 static int eq_asciicase(const struct String *needle,
580 const struct String *haystack, int match_prefix)
589 h=haystack->character;
595 if (nc&0x80) return 0;
596 if (hc&0x80) return 0;
598 /* tolower depends on the locale and only ASCII case must be insensitive */
599 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
605 return (match_prefix ? nl==0 : nl==0 && hl==0);
609 /*************************************************
610 * Glob pattern search *
611 *************************************************/
615 needle pattern to search ...
616 haystack ... inside the haystack
617 ascii_caseless ignore ASCII case
618 match_octet match octets, not UTF-8 multi-octet characters
620 Returns: 0 needle not found in haystack
625 static int eq_glob(const struct String *needle,
626 const struct String *haystack, int ascii_caseless, int match_octet)
628 const uschar *n,*h,*nend,*hend;
632 h=haystack->character;
633 nend=n+needle->length;
634 hend=h+haystack->length;
644 const uschar *npart,*hpart;
646 /* Try to match a non-star part of the needle at the current */
647 /* position in the haystack. */
651 while (npart<nend && *npart!='*') switch (*npart)
655 if (hpart==hend) return 0;
660 /* Match one UTF8 encoded character */
661 if ((*hpart&0xc0)==0xc0)
664 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
675 if (npart==nend) return -1;
680 if (hpart==hend) return 0;
681 /* tolower depends on the locale, but we need ASCII */
685 (*hpart&0x80) || (*npart&0x80) ||
688 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
693 /* string match after a star failed, advance and try again */
707 /* at this point, a part was matched successfully */
708 if (may_advance && npart==nend && hpart<hend)
709 /* needle ends, but haystack does not: if there was a star before, advance and try again */
719 return (h==hend ? 1 : may_advance);
723 /*************************************************
724 * ASCII numeric comparison *
725 *************************************************/
729 a first numeric string
730 b second numeric string
731 relop relational operator
733 Returns: 0 not (a relop b)
737 static int eq_asciinumeric(const struct String *a,
738 const struct String *b, enum RelOp relop)
741 const uschar *as,*aend,*bs,*bend;
745 aend=a->character+a->length;
747 bend=b->character+b->length;
749 while (*as>='0' && *as<='9' && as<aend) ++as;
751 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
754 if (al && bl==0) cmp=-1;
755 else if (al==0 && bl==0) cmp=0;
756 else if (al==0 && bl) cmp=1;
760 if (cmp==0) cmp=memcmp(a->character,b->character,al);
764 case LT: return cmp<0;
765 case LE: return cmp<=0;
766 case EQ: return cmp==0;
767 case GE: return cmp>=0;
768 case GT: return cmp>0;
769 case NE: return cmp!=0;
776 /*************************************************
778 *************************************************/
782 filter points to the Sieve filter including its state
783 needle UTF-8 pattern or string to search ...
784 haystack ... inside the haystack
788 Returns: 0 needle not found in haystack
790 -1 comparator does not offer matchtype
793 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
794 enum Comparator co, enum MatchType mt)
798 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
799 (debug_selector & D_filter) != 0)
801 debug_printf("String comparison (match ");
804 case MATCH_IS: debug_printf(":is"); break;
805 case MATCH_CONTAINS: debug_printf(":contains"); break;
806 case MATCH_MATCHES: debug_printf(":matches"); break;
808 debug_printf(", comparison \"");
811 case COMP_OCTET: debug_printf("i;octet"); break;
812 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
813 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
815 debug_printf("\"):\n");
816 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
817 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
827 if (eq_octet(needle,haystack,0)) r=1;
830 case COMP_EN_ASCII_CASEMAP:
832 if (eq_asciicase(needle,haystack,0)) r=1;
835 case COMP_ASCII_NUMERIC:
837 if (!filter->require_iascii_numeric)
839 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
842 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
856 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
859 case COMP_EN_ASCII_CASEMAP:
861 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
866 filter->errmsg=CUS "comparator does not offer specified matchtype";
878 if ((r=eq_glob(needle,haystack,0,1))==-1)
880 filter->errmsg=CUS "syntactically invalid pattern";
885 case COMP_EN_ASCII_CASEMAP:
887 if ((r=eq_glob(needle,haystack,1,1))==-1)
889 filter->errmsg=CUS "syntactically invalid pattern";
896 filter->errmsg=CUS "comparator does not offer specified matchtype";
903 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
904 (debug_selector & D_filter) != 0)
905 debug_printf(" Result %s\n",r?"true":"false");
910 /*************************************************
911 * Check header field syntax *
912 *************************************************/
915 RFC 2822, section 3.6.8 says:
919 ftext = %d33-57 / ; Any character except
920 %d59-126 ; controls, SP, and
923 That forbids 8-bit header fields. This implementation accepts them, since
924 all of Exim is 8-bit clean, so it adds %d128-%d255.
927 header header field to quote for suitable use in Exim expansions
929 Returns: 0 string is not a valid header field
930 1 string is a value header field
933 static int is_header(const struct String *header)
943 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
954 /*************************************************
955 * Quote special characters string *
956 *************************************************/
960 header header field to quote for suitable use in Exim expansions
963 Returns: quoted string
966 static const uschar *quote(const struct String *header)
981 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
988 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
992 quoted=string_cat(quoted,&size,&ptr,h,1);
998 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
1003 /*************************************************
1004 * Add address to list of generated addresses *
1005 *************************************************/
1008 According to RFC 5228, duplicate delivery to the same address must
1009 not happen, so the list is first searched for the address.
1012 generated list of generated addresses
1013 addr new address to add
1014 file address denotes a file
1019 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1021 address_item *new_addr;
1023 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1025 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1027 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1029 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1035 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1037 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1039 new_addr=deliver_make_addr(addr,TRUE);
1042 setflag(new_addr, af_pfr|af_file);
1045 new_addr->p.errors_address = NULL;
1046 new_addr->next = *generated;
1047 *generated = new_addr;
1051 /*************************************************
1052 * Return decoded header field *
1053 *************************************************/
1056 Unfold the header field as described in RFC 2822 and remove all
1057 leading and trailing white space, then perform MIME decoding and
1058 translate the header field to UTF-8.
1061 value returned value of the field
1062 header name of the header field
1064 Returns: nothing The expanded string is empty
1065 in case there is no such header
1068 static void expand_header(struct String *value, const struct String *header)
1074 value->character=(uschar*)0;
1076 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1077 while (*r==' ' || *r=='\t') ++r;
1085 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1087 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1091 /*************************************************
1092 * Parse remaining hash comment *
1093 *************************************************/
1097 Comment up to terminating CRLF
1100 filter points to the Sieve filter including its state
1106 static int parse_hashcomment(struct Sieve *filter)
1112 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1114 if (*filter->pc=='\n')
1127 filter->errmsg=CUS "missing end of comment";
1132 /*************************************************
1133 * Parse remaining C-style comment *
1134 *************************************************/
1138 Everything up to star slash
1141 filter points to the Sieve filter including its state
1147 static int parse_comment(struct Sieve *filter)
1152 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1159 filter->errmsg=CUS "missing end of comment";
1164 /*************************************************
1165 * Parse optional white space *
1166 *************************************************/
1170 Spaces, tabs, CRLFs, hash comments or C-style comments
1173 filter points to the Sieve filter including its state
1179 static int parse_white(struct Sieve *filter)
1183 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1185 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1187 else if (*filter->pc=='\n')
1197 else if (*filter->pc=='#')
1199 if (parse_hashcomment(filter)==-1) return -1;
1201 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1203 if (parse_comment(filter)==-1) return -1;
1211 #ifdef ENCODED_CHARACTER
1212 /*************************************************
1213 * Decode hex-encoded-character string *
1214 *************************************************/
1217 Encoding definition:
1218 blank = SP / TAB / CRLF
1219 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1220 hex-pair = 1*2HEXDIG
1223 src points to a hex-pair-seq
1224 end points to its end
1225 dst points to the destination of the decoded octets,
1226 optionally to (uschar*)0 for checking only
1228 Returns: >=0 number of decoded octets
1232 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1236 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1241 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);
1242 if (d==0) return -1;
1245 if (src==end) return decoded;
1246 if (*src==' ' || *src=='\t' || *src=='\n')
1247 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1256 /*************************************************
1257 * Decode unicode-encoded-character string *
1258 *************************************************/
1261 Encoding definition:
1262 blank = SP / TAB / CRLF
1263 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1264 unicode-hex = 1*HEXDIG
1266 It is an error for a script to use a hexadecimal value that isn't in
1267 either the range 0 to D7FF or the range E000 to 10FFFF.
1269 At this time, strings are already scanned, thus the CRLF is converted
1270 to the internally used \n (should RFC_EOL have been used).
1273 src points to a unicode-hex-seq
1274 end points to its end
1275 dst points to the destination of the decoded octets,
1276 optionally to (uschar*)0 for checking only
1278 Returns: >=0 number of decoded octets
1280 -2 semantic error (character range violation)
1283 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1287 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1294 for (hex_seq=src; src<end && *src=='0'; ++src);
1295 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);
1296 if (src==hex_seq) return -1;
1297 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1303 else if (c>=0x80 && c<=0x7ff)
1308 *dst++=128+(c&0x3f);
1312 else if (c>=0x800 && c<=0xffff)
1317 *dst++=128+((c>>6)&0x3f);
1318 *dst++=128+(c&0x3f);
1322 else if (c>=0x10000 && c<=0x1fffff)
1327 *dst++=128+((c>>10)&0x3f);
1328 *dst++=128+((c>>6)&0x3f);
1329 *dst++=128+(c&0x3f);
1333 if (*src==' ' || *src=='\t' || *src=='\n')
1335 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1336 if (src==end) return decoded;
1345 /*************************************************
1346 * Decode encoded-character string *
1347 *************************************************/
1350 Encoding definition:
1351 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1352 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1355 encoded points to an encoded string, returns decoded string
1356 filter points to the Sieve filter including its state
1362 static int string_decode(struct Sieve *filter, struct String *data)
1364 uschar *src,*dst,*end;
1366 src=data->character;
1368 end=data->character+data->length;
1374 strncmpic(src,US "${hex:",6)==0
1375 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1376 && (hex_decode(src+6,brace,(uschar*)0))>=0
1379 dst+=hex_decode(src+6,brace,dst);
1383 strncmpic(src,US "${unicode:",10)==0
1384 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1387 switch (unicode_decode(src+10,brace,(uschar*)0))
1391 filter->errmsg=CUS "unicode character out of range";
1401 dst+=unicode_decode(src+10,brace,dst);
1408 data->length=dst-data->character;
1415 /*************************************************
1416 * Parse an optional string *
1417 *************************************************/
1421 quoted-string = DQUOTE *CHAR DQUOTE
1422 ;; in general, \ CHAR inside a string maps to CHAR
1423 ;; so \" maps to " and \\ maps to \
1424 ;; note that newlines and other characters are all allowed
1427 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1428 *(multi-line-literal / multi-line-dotstuff)
1430 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1431 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1432 ;; A line containing only "." ends the multi-line.
1433 ;; Remove a leading '.' if followed by another '.'.
1434 string = quoted-string / multi-line
1437 filter points to the Sieve filter including its state
1438 id specifies identifier to match
1442 0 identifier not matched
1445 static int parse_string(struct Sieve *filter, struct String *data)
1450 data->character=(uschar*)0;
1451 if (*filter->pc=='"') /* quoted string */
1456 if (*filter->pc=='"') /* end of string */
1458 int foo=data->length;
1461 /* that way, there will be at least one character allocated */
1462 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1463 #ifdef ENCODED_CHARACTER
1464 if (filter->require_encoded_character
1465 && string_decode(filter,data)==-1)
1470 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1472 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1475 else /* regular character */
1478 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1480 if (*filter->pc=='\n')
1482 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1486 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1490 filter->errmsg=CUS "missing end of string";
1493 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1496 /* skip optional white space followed by hashed comment or CRLF */
1497 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1498 if (*filter->pc=='#')
1500 if (parse_hashcomment(filter)==-1) return -1;
1503 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1505 else if (*filter->pc=='\n')
1517 filter->errmsg=CUS "syntax error";
1523 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1525 if (*filter->pc=='\n') /* end of line */
1528 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1536 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1538 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1541 int foo=data->length;
1543 /* that way, there will be at least one character allocated */
1544 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1551 #ifdef ENCODED_CHARACTER
1552 if (filter->require_encoded_character
1553 && string_decode(filter,data)==-1)
1558 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1560 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1564 else /* regular character */
1566 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1570 filter->errmsg=CUS "missing end of multi line string";
1577 /*************************************************
1578 * Parse a specific identifier *
1579 *************************************************/
1583 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1586 filter points to the Sieve filter including its state
1587 id specifies identifier to match
1590 0 identifier not matched
1593 static int parse_identifier(struct Sieve *filter, const uschar *id)
1595 size_t idlen=Ustrlen(id);
1597 if (strncmpic(US filter->pc,US id,idlen)==0)
1599 uschar next=filter->pc[idlen];
1601 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1609 /*************************************************
1611 *************************************************/
1615 number = 1*DIGIT [QUANTIFIER]
1616 QUANTIFIER = "K" / "M" / "G"
1619 filter points to the Sieve filter including its state
1623 -1 no string list found
1626 static int parse_number(struct Sieve *filter, unsigned long *data)
1630 if (*filter->pc>='0' && *filter->pc<='9')
1635 d=Ustrtoul(filter->pc,&e,10);
1638 filter->errmsg=CUstrerror(ERANGE);
1643 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1644 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1645 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1646 if (d>(ULONG_MAX/u))
1648 filter->errmsg=CUstrerror(ERANGE);
1657 filter->errmsg=CUS "missing number";
1663 /*************************************************
1664 * Parse a string list *
1665 *************************************************/
1669 string-list = "[" string *("," string) "]" / string
1672 filter points to the Sieve filter including its state
1673 data returns string list
1676 -1 no string list found
1679 static int parse_stringlist(struct Sieve *filter, struct String **data)
1681 const uschar *orig=filter->pc;
1684 struct String *d=(struct String*)0;
1687 if (*filter->pc=='[') /* string list */
1692 if (parse_white(filter)==-1) goto error;
1693 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1696 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1697 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1698 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1700 filter->errmsg=CUstrerror(errno);
1703 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1705 dataCapacity=newCapacity;
1707 m=parse_string(filter,&d[dataLength]);
1710 if (dataLength==0) break;
1713 filter->errmsg=CUS "missing string";
1717 else if (m==-1) goto error;
1719 if (parse_white(filter)==-1) goto error;
1720 if (*filter->pc==',') ++filter->pc;
1723 if (*filter->pc==']')
1725 d[dataLength].character=(uschar*)0;
1726 d[dataLength].length=-1;
1733 filter->errmsg=CUS "missing closing bracket";
1737 else /* single string */
1739 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1743 m=parse_string(filter,&d[0]);
1755 d[1].character=(uschar*)0;
1762 filter->errmsg=CUS "missing string list";
1767 /*************************************************
1768 * Parse an optional address part specifier *
1769 *************************************************/
1773 address-part = ":localpart" / ":domain" / ":all"
1774 address-part =/ ":user" / ":detail"
1777 filter points to the Sieve filter including its state
1778 a returns address part specified
1781 0 no comparator found
1785 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1788 if (parse_identifier(filter,CUS ":user")==1)
1790 if (!filter->require_subaddress)
1792 filter->errmsg=CUS "missing previous require \"subaddress\";";
1798 else if (parse_identifier(filter,CUS ":detail")==1)
1800 if (!filter->require_subaddress)
1802 filter->errmsg=CUS "missing previous require \"subaddress\";";
1810 if (parse_identifier(filter,CUS ":localpart")==1)
1812 *a=ADDRPART_LOCALPART;
1815 else if (parse_identifier(filter,CUS ":domain")==1)
1820 else if (parse_identifier(filter,CUS ":all")==1)
1829 /*************************************************
1830 * Parse an optional comparator *
1831 *************************************************/
1835 comparator = ":comparator" <comparator-name: string>
1838 filter points to the Sieve filter including its state
1839 c returns comparator
1842 0 no comparator found
1843 -1 incomplete comparator found
1846 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1848 struct String comparator_name;
1850 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1851 if (parse_white(filter)==-1) return -1;
1852 switch (parse_string(filter,&comparator_name))
1857 filter->errmsg=CUS "missing comparator";
1864 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1869 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1871 *c=COMP_EN_ASCII_CASEMAP;
1874 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1876 *c=COMP_EN_ASCII_CASEMAP;
1879 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1881 *c=COMP_ASCII_NUMERIC;
1886 filter->errmsg=CUS "invalid comparator";
1895 /*************************************************
1896 * Parse an optional match type *
1897 *************************************************/
1901 match-type = ":is" / ":contains" / ":matches"
1904 filter points to the Sieve filter including its state
1905 m returns match type
1908 0 no match type found
1911 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1913 if (parse_identifier(filter,CUS ":is")==1)
1918 else if (parse_identifier(filter,CUS ":contains")==1)
1923 else if (parse_identifier(filter,CUS ":matches")==1)
1932 /*************************************************
1933 * Parse and interpret an optional test list *
1934 *************************************************/
1938 test-list = "(" test *("," test) ")"
1941 filter points to the Sieve filter including its state
1942 n total number of tests
1943 num_true number of passed tests
1944 exec Execute parsed statements
1947 0 no test list found
1948 -1 syntax or execution error
1951 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1953 if (parse_white(filter)==-1) return -1;
1954 if (*filter->pc=='(')
1963 switch (parse_test(filter,&cond,exec))
1966 case 0: filter->errmsg=CUS "missing test"; return -1;
1967 default: ++*n; if (cond) ++*num_true; break;
1969 if (parse_white(filter)==-1) return -1;
1970 if (*filter->pc==',') ++filter->pc;
1973 if (*filter->pc==')')
1980 filter->errmsg=CUS "missing closing paren";
1988 /*************************************************
1989 * Parse and interpret an optional test *
1990 *************************************************/
1994 filter points to the Sieve filter including its state
1995 cond returned condition status
1996 exec Execute parsed statements
2000 -1 syntax or execution error
2003 static int parse_test(struct Sieve *filter, int *cond, int exec)
2005 if (parse_white(filter)==-1) return -1;
2006 if (parse_identifier(filter,CUS "address"))
2009 address-test = "address" { [address-part] [comparator] [match-type] }
2010 <header-list: string-list> <key-list: string-list>
2012 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2015 enum AddressPart addressPart=ADDRPART_ALL;
2016 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2017 enum MatchType matchType=MATCH_IS;
2018 struct String *hdr,*h,*key,*k;
2024 if (parse_white(filter)==-1) return -1;
2025 if ((m=parse_addresspart(filter,&addressPart))!=0)
2027 if (m==-1) return -1;
2030 filter->errmsg=CUS "address part already specified";
2035 else if ((m=parse_comparator(filter,&comparator))!=0)
2037 if (m==-1) return -1;
2040 filter->errmsg=CUS "comparator already specified";
2045 else if ((m=parse_matchtype(filter,&matchType))!=0)
2047 if (m==-1) return -1;
2050 filter->errmsg=CUS "match type already specified";
2057 if (parse_white(filter)==-1) return -1;
2058 if ((m=parse_stringlist(filter,&hdr))!=1)
2060 if (m==0) filter->errmsg=CUS "header string list expected";
2063 if (parse_white(filter)==-1) return -1;
2064 if ((m=parse_stringlist(filter,&key))!=1)
2066 if (m==0) filter->errmsg=CUS "key string list expected";
2070 for (h=hdr; h->length!=-1 && !*cond; ++h)
2072 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2076 !eq_asciicase(h,&str_from,0)
2077 && !eq_asciicase(h,&str_to,0)
2078 && !eq_asciicase(h,&str_cc,0)
2079 && !eq_asciicase(h,&str_bcc,0)
2080 && !eq_asciicase(h,&str_sender,0)
2081 && !eq_asciicase(h,&str_resent_from,0)
2082 && !eq_asciicase(h,&str_resent_to,0)
2085 filter->errmsg=CUS "invalid header field";
2090 /* We are only interested in addresses below, so no MIME decoding */
2091 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2092 if (header_value == NULL)
2094 filter->errmsg=CUS "header string expansion failed";
2097 parse_allow_group = TRUE;
2098 while (*header_value && !*cond)
2101 int start, end, domain;
2105 end_addr = parse_find_address_end(header_value, FALSE);
2106 saveend = *end_addr;
2108 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2110 if (extracted_addr) switch (addressPart)
2112 case ADDRPART_ALL: part=extracted_addr; break;
2116 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2117 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2119 case ADDRPART_DETAIL: part=NULL; break;
2123 *end_addr = saveend;
2126 for (k=key; k->length!=-1; ++k)
2128 struct String partStr;
2130 partStr.character=part;
2131 partStr.length=Ustrlen(part);
2134 *cond=compare(filter,k,&partStr,comparator,matchType);
2135 if (*cond==-1) return -1;
2140 if (saveend == 0) break;
2141 header_value = end_addr + 1;
2143 parse_allow_group = FALSE;
2144 parse_found_group = FALSE;
2149 else if (parse_identifier(filter,CUS "allof"))
2152 allof-test = "allof" <tests: test-list>
2157 switch (parse_testlist(filter,&n,&num_true,exec))
2160 case 0: filter->errmsg=CUS "missing test list"; return -1;
2161 default: *cond=(n==num_true); return 1;
2164 else if (parse_identifier(filter,CUS "anyof"))
2167 anyof-test = "anyof" <tests: test-list>
2172 switch (parse_testlist(filter,&n,&num_true,exec))
2175 case 0: filter->errmsg=CUS "missing test list"; return -1;
2176 default: *cond=(num_true>0); return 1;
2179 else if (parse_identifier(filter,CUS "exists"))
2182 exists-test = "exists" <header-names: string-list>
2185 struct String *hdr,*h;
2188 if (parse_white(filter)==-1) return -1;
2189 if ((m=parse_stringlist(filter,&hdr))!=1)
2191 if (m==0) filter->errmsg=CUS "header string list expected";
2197 for (h=hdr; h->length!=-1 && *cond; ++h)
2201 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2202 if (header_def == NULL)
2204 filter->errmsg=CUS "header string expansion failed";
2207 if (Ustrcmp(header_def,"false")==0) *cond=0;
2212 else if (parse_identifier(filter,CUS "false"))
2215 false-test = "false"
2221 else if (parse_identifier(filter,CUS "header"))
2224 header-test = "header" { [comparator] [match-type] }
2225 <header-names: string-list> <key-list: string-list>
2228 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2229 enum MatchType matchType=MATCH_IS;
2230 struct String *hdr,*h,*key,*k;
2236 if (parse_white(filter)==-1) return -1;
2237 if ((m=parse_comparator(filter,&comparator))!=0)
2239 if (m==-1) return -1;
2242 filter->errmsg=CUS "comparator already specified";
2247 else if ((m=parse_matchtype(filter,&matchType))!=0)
2249 if (m==-1) return -1;
2252 filter->errmsg=CUS "match type already specified";
2259 if (parse_white(filter)==-1) return -1;
2260 if ((m=parse_stringlist(filter,&hdr))!=1)
2262 if (m==0) filter->errmsg=CUS "header string list expected";
2265 if (parse_white(filter)==-1) return -1;
2266 if ((m=parse_stringlist(filter,&key))!=1)
2268 if (m==0) filter->errmsg=CUS "key string list expected";
2272 for (h=hdr; h->length!=-1 && !*cond; ++h)
2276 filter->errmsg=CUS "invalid header field";
2281 struct String header_value;
2284 expand_header(&header_value,h);
2285 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2286 if (header_value.character == NULL || header_def == NULL)
2288 filter->errmsg=CUS "header string expansion failed";
2291 for (k=key; k->length!=-1; ++k)
2293 if (Ustrcmp(header_def,"true")==0)
2295 *cond=compare(filter,k,&header_value,comparator,matchType);
2296 if (*cond==-1) return -1;
2304 else if (parse_identifier(filter,CUS "not"))
2306 if (parse_white(filter)==-1) return -1;
2307 switch (parse_test(filter,cond,exec))
2310 case 0: filter->errmsg=CUS "missing test"; return -1;
2311 default: *cond=!*cond; return 1;
2314 else if (parse_identifier(filter,CUS "size"))
2317 relop = ":over" / ":under"
2318 size-test = "size" relop <limit: number>
2321 unsigned long limit;
2324 if (parse_white(filter)==-1) return -1;
2325 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2326 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2329 filter->errmsg=CUS "missing :over or :under";
2332 if (parse_white(filter)==-1) return -1;
2333 if (parse_number(filter,&limit)==-1) return -1;
2334 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2337 else if (parse_identifier(filter,CUS "true"))
2342 else if (parse_identifier(filter,CUS "envelope"))
2345 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2346 <envelope-part: string-list> <key-list: string-list>
2348 envelope-part is case insensitive "from" or "to"
2349 #ifdef ENVELOPE_AUTH
2350 envelope-part =/ "auth"
2354 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2355 enum AddressPart addressPart=ADDRPART_ALL;
2356 enum MatchType matchType=MATCH_IS;
2357 struct String *env,*e,*key,*k;
2361 if (!filter->require_envelope)
2363 filter->errmsg=CUS "missing previous require \"envelope\";";
2368 if (parse_white(filter)==-1) return -1;
2369 if ((m=parse_comparator(filter,&comparator))!=0)
2371 if (m==-1) return -1;
2374 filter->errmsg=CUS "comparator already specified";
2379 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2381 if (m==-1) return -1;
2384 filter->errmsg=CUS "address part already specified";
2389 else if ((m=parse_matchtype(filter,&matchType))!=0)
2391 if (m==-1) return -1;
2394 filter->errmsg=CUS "match type already specified";
2401 if (parse_white(filter)==-1) return -1;
2402 if ((m=parse_stringlist(filter,&env))!=1)
2404 if (m==0) filter->errmsg=CUS "envelope string list expected";
2407 if (parse_white(filter)==-1) return -1;
2408 if ((m=parse_stringlist(filter,&key))!=1)
2410 if (m==0) filter->errmsg=CUS "key string list expected";
2414 for (e=env; e->length!=-1 && !*cond; ++e)
2416 const uschar *envelopeExpr=CUS 0;
2417 uschar *envelope=US 0;
2419 if (eq_asciicase(e,&str_from,0))
2421 switch (addressPart)
2423 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2427 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2428 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2430 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2434 else if (eq_asciicase(e,&str_to,0))
2436 switch (addressPart)
2438 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2440 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2441 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2443 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2444 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2447 #ifdef ENVELOPE_AUTH
2448 else if (eq_asciicase(e,&str_auth,0))
2450 switch (addressPart)
2452 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2456 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2457 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2459 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2466 filter->errmsg=CUS "invalid envelope string";
2469 if (exec && envelopeExpr)
2471 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2473 filter->errmsg=CUS "header string expansion failed";
2476 for (k=key; k->length!=-1; ++k)
2478 struct String envelopeStr;
2480 envelopeStr.character=envelope;
2481 envelopeStr.length=Ustrlen(envelope);
2482 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2483 if (*cond==-1) return -1;
2491 else if (parse_identifier(filter,CUS "valid_notify_method"))
2494 valid_notify_method = "valid_notify_method"
2495 <notification-uris: string-list>
2498 struct String *uris,*u;
2501 if (!filter->require_enotify)
2503 filter->errmsg=CUS "missing previous require \"enotify\";";
2506 if (parse_white(filter)==-1) return -1;
2507 if ((m=parse_stringlist(filter,&uris))!=1)
2509 if (m==0) filter->errmsg=CUS "URI string list expected";
2515 for (u=uris; u->length!=-1 && *cond; ++u)
2517 string_item *recipient;
2518 struct String header,subject,body;
2522 header.character=(uschar*)0;
2524 subject.character=(uschar*)0;
2526 body.character=(uschar*)0;
2527 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2533 else if (parse_identifier(filter,CUS "notify_method_capability"))
2536 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2537 <notification-uri: string>
2538 <notification-capability: string>
2539 <key-list: string-list>
2545 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2546 enum MatchType matchType=MATCH_IS;
2547 struct String uri,capa,*keys,*k;
2549 if (!filter->require_enotify)
2551 filter->errmsg=CUS "missing previous require \"enotify\";";
2556 if (parse_white(filter)==-1) return -1;
2557 if ((m=parse_comparator(filter,&comparator))!=0)
2559 if (m==-1) return -1;
2562 filter->errmsg=CUS "comparator already specified";
2567 else if ((m=parse_matchtype(filter,&matchType))!=0)
2569 if (m==-1) return -1;
2572 filter->errmsg=CUS "match type already specified";
2579 if ((m=parse_string(filter,&uri))!=1)
2581 if (m==0) filter->errmsg=CUS "missing notification URI string";
2584 if (parse_white(filter)==-1) return -1;
2585 if ((m=parse_string(filter,&capa))!=1)
2587 if (m==0) filter->errmsg=CUS "missing notification capability string";
2590 if (parse_white(filter)==-1) return -1;
2591 if ((m=parse_stringlist(filter,&keys))!=1)
2593 if (m==0) filter->errmsg=CUS "missing key string list";
2598 string_item *recipient;
2599 struct String header,subject,body;
2604 header.character=(uschar*)0;
2606 subject.character=(uschar*)0;
2608 body.character=(uschar*)0;
2609 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2611 if (eq_asciicase(&capa,&str_online,0)==1)
2612 for (k=keys; k->length!=-1; ++k)
2614 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2615 if (*cond==-1) return -1;
2627 /*************************************************
2628 * Parse and interpret an optional block *
2629 *************************************************/
2633 filter points to the Sieve filter including its state
2634 exec Execute parsed statements
2635 generated where to hang newly-generated addresses
2637 Returns: 2 success by stop
2639 0 no block command found
2640 -1 syntax or execution error
2643 static int parse_block(struct Sieve *filter, int exec,
2644 address_item **generated)
2648 if (parse_white(filter)==-1) return -1;
2649 if (*filter->pc=='{')
2652 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2653 if (*filter->pc=='}')
2660 filter->errmsg=CUS "expecting command or closing brace";
2668 /*************************************************
2669 * Match a semicolon *
2670 *************************************************/
2674 filter points to the Sieve filter including its state
2680 static int parse_semicolon(struct Sieve *filter)
2682 if (parse_white(filter)==-1) return -1;
2683 if (*filter->pc==';')
2690 filter->errmsg=CUS "missing semicolon";
2696 /*************************************************
2697 * Parse and interpret a Sieve command *
2698 *************************************************/
2702 filter points to the Sieve filter including its state
2703 exec Execute parsed statements
2704 generated where to hang newly-generated addresses
2706 Returns: 2 success by stop
2708 -1 syntax or execution error
2710 static int parse_commands(struct Sieve *filter, int exec,
2711 address_item **generated)
2715 if (parse_white(filter)==-1) return -1;
2716 if (parse_identifier(filter,CUS "if"))
2719 if-command = "if" test block *( "elsif" test block ) [ else block ]
2722 int cond,m,unsuccessful;
2725 if (parse_white(filter)==-1) return -1;
2726 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2729 filter->errmsg=CUS "missing test";
2732 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2733 (debug_selector & D_filter) != 0)
2735 if (exec) debug_printf("if %s\n",cond?"true":"false");
2737 m=parse_block(filter,exec ? cond : 0, generated);
2738 if (m==-1 || m==2) return m;
2741 filter->errmsg=CUS "missing block";
2744 unsuccessful = !cond;
2745 for (;;) /* elsif test block */
2747 if (parse_white(filter)==-1) return -1;
2748 if (parse_identifier(filter,CUS "elsif"))
2750 if (parse_white(filter)==-1) return -1;
2751 m=parse_test(filter,&cond,exec && unsuccessful);
2752 if (m==-1 || m==2) return m;
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("elsif %s\n",cond?"true":"false");
2763 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2764 if (m==-1 || m==2) return m;
2767 filter->errmsg=CUS "missing block";
2770 if (exec && unsuccessful && cond) unsuccessful = 0;
2775 if (parse_white(filter)==-1) return -1;
2776 if (parse_identifier(filter,CUS "else"))
2778 m=parse_block(filter,exec && unsuccessful, generated);
2779 if (m==-1 || m==2) return m;
2782 filter->errmsg=CUS "missing block";
2787 else if (parse_identifier(filter,CUS "stop"))
2790 stop-command = "stop" { stop-options } ";"
2794 if (parse_semicolon(filter)==-1) return -1;
2797 filter->pc+=Ustrlen(filter->pc);
2801 else if (parse_identifier(filter,CUS "keep"))
2804 keep-command = "keep" { keep-options } ";"
2808 if (parse_semicolon(filter)==-1) return -1;
2811 add_addr(generated,US"inbox",1,0,0,0);
2815 else if (parse_identifier(filter,CUS "discard"))
2818 discard-command = "discard" { discard-options } ";"
2822 if (parse_semicolon(filter)==-1) return -1;
2823 if (exec) filter->keep=0;
2825 else if (parse_identifier(filter,CUS "redirect"))
2828 redirect-command = "redirect" redirect-options "string" ";"
2830 redirect-options =) ":copy"
2833 struct String recipient;
2839 if (parse_white(filter)==-1) return -1;
2840 if (parse_identifier(filter,CUS ":copy")==1)
2842 if (!filter->require_copy)
2844 filter->errmsg=CUS "missing previous require \"copy\";";
2851 if (parse_white(filter)==-1) return -1;
2852 if ((m=parse_string(filter,&recipient))!=1)
2854 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2857 if (strchr(CCS recipient.character,'@')==(char*)0)
2859 filter->errmsg=CUS "unqualified recipient address";
2864 add_addr(generated,recipient.character,0,0,0,0);
2865 if (!copy) filter->keep = 0;
2867 if (parse_semicolon(filter)==-1) return -1;
2869 else if (parse_identifier(filter,CUS "fileinto"))
2872 fileinto-command = "fileinto" { fileinto-options } string ";"
2874 fileinto-options =) [ ":copy" ]
2877 struct String folder;
2880 unsigned long maxage, maxmessages, maxstorage;
2883 maxage = maxmessages = maxstorage = 0;
2884 if (!filter->require_fileinto)
2886 filter->errmsg=CUS "missing previous require \"fileinto\";";
2891 if (parse_white(filter)==-1) return -1;
2892 if (parse_identifier(filter,CUS ":copy")==1)
2894 if (!filter->require_copy)
2896 filter->errmsg=CUS "missing previous require \"copy\";";
2903 if (parse_white(filter)==-1) return -1;
2904 if ((m=parse_string(filter,&folder))!=1)
2906 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2909 m=0; s=folder.character;
2910 if (folder.length==0) m=1;
2911 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2914 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2919 filter->errmsg=CUS "invalid folder";
2924 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2925 if (!copy) filter->keep = 0;
2927 if (parse_semicolon(filter)==-1) return -1;
2930 else if (parse_identifier(filter,CUS "notify"))
2933 notify-command = "notify" { notify-options } <method: string> ";"
2934 notify-options = [":from" string]
2935 [":importance" <"1" / "2" / "3">]
2936 [":options" 1*(string-list / number)]
2942 struct String importance;
2943 struct String *options;
2944 struct String message;
2945 struct String method;
2946 struct Notification *already;
2947 string_item *recipient;
2948 struct String header;
2949 struct String subject;
2951 uschar *envelope_from;
2952 struct String auto_submitted_value;
2953 uschar *auto_submitted_def;
2955 if (!filter->require_enotify)
2957 filter->errmsg=CUS "missing previous require \"enotify\";";
2960 from.character=(uschar*)0;
2962 importance.character=(uschar*)0;
2963 importance.length=-1;
2964 options=(struct String*)0;
2965 message.character=(uschar*)0;
2969 header.character=(uschar*)0;
2971 subject.character=(uschar*)0;
2973 body.character=(uschar*)0;
2974 envelope_from=(sender_address && sender_address[0]) ? expand_string("$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2977 if (parse_white(filter)==-1) return -1;
2978 if (parse_identifier(filter,CUS ":from")==1)
2980 if (parse_white(filter)==-1) return -1;
2981 if ((m=parse_string(filter,&from))!=1)
2983 if (m==0) filter->errmsg=CUS "from string expected";
2987 else if (parse_identifier(filter,CUS ":importance")==1)
2989 if (parse_white(filter)==-1) return -1;
2990 if ((m=parse_string(filter,&importance))!=1)
2992 if (m==0) filter->errmsg=CUS "importance string expected";
2995 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2997 filter->errmsg=CUS "invalid importance";
3001 else if (parse_identifier(filter,CUS ":options")==1)
3003 if (parse_white(filter)==-1) return -1;
3005 else if (parse_identifier(filter,CUS ":message")==1)
3007 if (parse_white(filter)==-1) return -1;
3008 if ((m=parse_string(filter,&message))!=1)
3010 if (m==0) filter->errmsg=CUS "message string expected";
3016 if (parse_white(filter)==-1) return -1;
3017 if ((m=parse_string(filter,&method))!=1)
3019 if (m==0) filter->errmsg=CUS "missing method string";
3022 if (parse_semicolon(filter)==-1) return -1;
3023 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3027 if (message.length==-1) message=subject;
3028 if (message.length==-1) expand_header(&message,&str_subject);
3029 expand_header(&auto_submitted_value,&str_auto_submitted);
3030 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3031 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3033 filter->errmsg=CUS "header string expansion failed";
3036 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3038 for (already=filter->notified; already; already=already->next)
3040 if (already->method.length==method.length
3041 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
3042 && already->importance.length==importance.length
3043 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
3044 && already->message.length==message.length
3045 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
3048 if (already==(struct Notification*)0)
3049 /* New notification, process it */
3051 struct Notification *sent;
3052 sent=store_get(sizeof(struct Notification));
3053 sent->method=method;
3054 sent->importance=importance;
3055 sent->message=message;
3056 sent->next=filter->notified;
3057 filter->notified=sent;
3058 #ifndef COMPILE_SYNTAX_CHECKER
3059 if (filter_test == FTEST_NONE)
3064 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3068 int buffer_capacity;
3070 f = fdopen(fd, "wb");
3071 fprintf(f,"From: %s\n",from.length==-1 ? expand_string("$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3072 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3073 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3074 if (header.length>0) fprintf(f,"%s",header.character);
3075 if (message.length==-1)
3077 message.character=US"Notification";
3078 message.length=Ustrlen(message.character);
3080 /* Allocation is larger than neccessary, but enough even for split MIME words */
3081 buffer_capacity=32+4*message.length;
3082 buffer=store_get(buffer_capacity);
3083 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3085 if (body.length>0) fprintf(f,"%s\n",body.character);
3088 (void)child_close(pid, 0);
3091 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3093 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3099 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3101 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3107 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3109 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3116 else if (parse_identifier(filter,CUS "vacation"))
3119 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3120 vacation-options = [":days" number]
3123 [":addresses" string-list]
3130 struct String subject;
3132 struct String *addresses;
3134 string_item *aliases;
3135 struct String handle;
3136 struct String reason;
3138 if (!filter->require_vacation)
3140 filter->errmsg=CUS "missing previous require \"vacation\";";
3145 if (filter->vacation_ran)
3147 filter->errmsg=CUS "trying to execute vacation more than once";
3150 filter->vacation_ran=1;
3152 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3153 subject.character=(uschar*)0;
3155 from.character=(uschar*)0;
3157 addresses=(struct String*)0;
3160 handle.character=(uschar*)0;
3164 if (parse_white(filter)==-1) return -1;
3165 if (parse_identifier(filter,CUS ":days")==1)
3167 if (parse_white(filter)==-1) return -1;
3168 if (parse_number(filter,&days)==-1) return -1;
3169 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3170 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3172 else if (parse_identifier(filter,CUS ":subject")==1)
3174 if (parse_white(filter)==-1) return -1;
3175 if ((m=parse_string(filter,&subject))!=1)
3177 if (m==0) filter->errmsg=CUS "subject string expected";
3181 else if (parse_identifier(filter,CUS ":from")==1)
3183 if (parse_white(filter)==-1) return -1;
3184 if ((m=parse_string(filter,&from))!=1)
3186 if (m==0) filter->errmsg=CUS "from string expected";
3189 if (check_mail_address(filter,&from)!=1)
3192 else if (parse_identifier(filter,CUS ":addresses")==1)
3196 if (parse_white(filter)==-1) return -1;
3197 if ((m=parse_stringlist(filter,&addresses))!=1)
3199 if (m==0) filter->errmsg=CUS "addresses string list expected";
3202 for (a=addresses; a->length!=-1; ++a)
3206 new=store_get(sizeof(string_item));
3207 new->text=store_get(a->length+1);
3208 if (a->length) memcpy(new->text,a->character,a->length);
3209 new->text[a->length]='\0';
3214 else if (parse_identifier(filter,CUS ":mime")==1)
3216 else if (parse_identifier(filter,CUS ":handle")==1)
3218 if (parse_white(filter)==-1) return -1;
3219 if ((m=parse_string(filter,&from))!=1)
3221 if (m==0) filter->errmsg=CUS "handle string expected";
3227 if (parse_white(filter)==-1) return -1;
3228 if ((m=parse_string(filter,&reason))!=1)
3230 if (m==0) filter->errmsg=CUS "missing reason string";
3237 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3240 filter->errmsg=CUS "MIME reason string contains 8bit text";
3244 if (parse_semicolon(filter)==-1) return -1;
3251 int buffer_capacity;
3255 uschar hexdigest[33];
3259 if (filter_personal(aliases,TRUE))
3261 if (filter_test == FTEST_NONE)
3263 /* ensure oncelog directory exists; failure will be detected later */
3265 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3267 /* build oncelog filename */
3269 key.character=(uschar*)0;
3272 if (handle.length==-1)
3274 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3275 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3276 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3277 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3282 md5_end(&base, key.character, key.length, digest);
3283 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3284 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3286 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3288 if (filter_test == FTEST_NONE)
3290 capacity=Ustrlen(filter->vacation_directory);
3292 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3293 once=string_cat(once,&capacity,&start,hexdigest,33);
3296 /* process subject */
3298 if (subject.length==-1)
3300 uschar *subject_def;
3302 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3303 if (Ustrcmp(subject_def,"true")==0)
3305 expand_header(&subject,&str_subject);
3308 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3309 subject.length=start;
3313 subject.character=US"Automated reply";
3314 subject.length=Ustrlen(subject.character);
3318 /* add address to list of generated addresses */
3320 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3321 setflag(addr, af_pfr);
3322 setflag(addr, af_ignore_error);
3323 addr->next = *generated;
3325 addr->reply = store_get(sizeof(reply_item));
3326 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3327 addr->reply->to = string_copy(sender_address);
3328 if (from.length==-1)
3329 addr->reply->from = expand_string(US"$local_part@$domain");
3331 addr->reply->from = from.character;
3332 /* Allocation is larger than neccessary, but enough even for split MIME words */
3333 buffer_capacity=32+4*subject.length;
3334 buffer=store_get(buffer_capacity);
3335 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3336 addr->reply->oncelog=once;
3337 addr->reply->once_repeat=days*86400;
3339 /* build body and MIME headers */
3343 uschar *mime_body,*reason_end;
3344 static const uschar nlnl[]="\r\n\r\n";
3348 mime_body=reason.character,reason_end=reason.character+reason.length;
3349 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3354 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3355 addr->reply->headers[start] = '\0';
3358 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3359 else mime_body=reason_end-1;
3360 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3361 addr->reply->text[start] = '\0';
3365 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3368 start = reason.length;
3369 addr->reply->headers = US"MIME-Version: 1.0\n"
3370 "Content-Type: text/plain;\n"
3371 "\tcharset=\"utf-8\"\n"
3372 "Content-Transfer-Encoding: quoted-printable";
3373 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3377 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3379 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3390 /*************************************************
3391 * Parse and interpret a sieve filter *
3392 *************************************************/
3396 filter points to the Sieve filter including its state
3397 exec Execute parsed statements
3398 generated where to hang newly-generated addresses
3401 -1 syntax or execution error
3404 static int parse_start(struct Sieve *filter, int exec,
3405 address_item **generated)
3407 filter->pc=filter->filter;
3410 filter->require_envelope=0;
3411 filter->require_fileinto=0;
3412 #ifdef ENCODED_CHARACTER
3413 filter->require_encoded_character=0;
3415 #ifdef ENVELOPE_AUTH
3416 filter->require_envelope_auth=0;
3419 filter->require_enotify=0;
3420 filter->notified=(struct Notification*)0;
3423 filter->require_subaddress=0;
3426 filter->require_vacation=0;
3427 filter->vacation_ran=0;
3429 filter->require_copy=0;
3430 filter->require_iascii_numeric=0;
3432 if (parse_white(filter)==-1) return -1;
3434 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3437 struct dirent *oncelog;
3438 struct stat properties;
3441 /* clean up old vacation log databases */
3443 oncelogdir=opendir(CS filter->vacation_directory);
3445 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3447 filter->errmsg=CUS "unable to open vacation directory";
3451 if (oncelogdir != NULL)
3455 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3457 if (strlen(oncelog->d_name)==32)
3459 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3460 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3464 closedir(oncelogdir);
3468 while (parse_identifier(filter,CUS "require"))
3471 require-command = "require" <capabilities: string-list>
3474 struct String *cap,*check;
3477 if (parse_white(filter)==-1) return -1;
3478 if ((m=parse_stringlist(filter,&cap))!=1)
3480 if (m==0) filter->errmsg=CUS "capability string list expected";
3483 for (check=cap; check->character; ++check)
3485 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3486 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3487 #ifdef ENCODED_CHARACTER
3488 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3490 #ifdef ENVELOPE_AUTH
3491 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3494 else if (eq_octet(check,&str_enotify,0))
3496 if (filter->enotify_mailto_owner == NULL)
3498 filter->errmsg=CUS "enotify disabled";
3501 filter->require_enotify=1;
3505 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3508 else if (eq_octet(check,&str_vacation,0))
3510 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3512 filter->errmsg=CUS "vacation disabled";
3515 filter->require_vacation=1;
3518 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3519 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3520 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3521 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3522 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3525 filter->errmsg=CUS "unknown capability";
3529 if (parse_semicolon(filter)==-1) return -1;
3531 if (parse_commands(filter,exec,generated)==-1) return -1;
3534 filter->errmsg=CUS "syntax error";
3541 /*************************************************
3542 * Interpret a sieve filter file *
3543 *************************************************/
3547 filter points to the entire file, read into store as a single string
3548 options controls whether various special things are allowed, and requests
3549 special actions (not currently used)
3550 vacation_directory where to store vacation "once" files
3551 enotify_mailto_owner owner of mailto notifications
3552 useraddress string expression for :user part of address
3553 subaddress string expression for :subaddress part of address
3554 generated where to hang newly-generated addresses
3555 error where to pass back an error text
3557 Returns: FF_DELIVERED success, a significant action was taken
3558 FF_NOTDELIVERED success, no significant action
3559 FF_DEFER defer requested
3560 FF_FAIL fail requested
3561 FF_FREEZE freeze requested
3562 FF_ERROR there was a problem
3566 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3567 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3568 address_item **generated, uschar **error)
3574 options = options; /* Keep picky compilers happy */
3577 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3578 sieve.filter=filter;
3580 if (vacation_directory == NULL)
3581 sieve.vacation_directory = NULL;
3584 sieve.vacation_directory=expand_string(vacation_directory);
3585 if (sieve.vacation_directory == NULL)
3587 *error = string_sprintf("failed to expand \"%s\" "
3588 "(sieve_vacation_directory): %s", vacation_directory,
3589 expand_string_message);
3594 if (enotify_mailto_owner == NULL)
3595 sieve.enotify_mailto_owner = NULL;
3598 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3599 if (sieve.enotify_mailto_owner == NULL)
3601 *error = string_sprintf("failed to expand \"%s\" "
3602 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3603 expand_string_message);
3608 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3609 sieve.subaddress = subaddress;
3611 #ifdef COMPILE_SYNTAX_CHECKER
3612 if (parse_start(&sieve,0,generated)==1)
3614 if (parse_start(&sieve,1,generated)==1)
3619 add_addr(generated,US"inbox",1,0,0,0);
3620 msg = string_sprintf("Implicit keep");
3625 msg = string_sprintf("No implicit keep");
3631 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3632 #ifdef COMPILE_SYNTAX_CHECKER
3636 add_addr(generated,US"inbox",1,0,0,0);
3641 #ifndef COMPILE_SYNTAX_CHECKER
3642 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3643 else debug_printf("%s\n", msg);
3646 DEBUG(D_route) debug_printf("Sieve: end of processing\n");