1 /* $Cambridge: exim/src/src/sieve.c,v 1.33 2008/01/28 12:18:56 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;
75 int require_subaddress;
81 uschar *vacation_directory;
82 const uschar *subaddress;
83 const uschar *useraddress;
85 int require_iascii_numeric;
88 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
89 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
91 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
93 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
95 enum RelOp { LT, LE, EQ, GE, GT, NE };
105 struct String method;
106 struct String importance;
107 struct String message;
108 struct Notification *next;
111 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
112 static int parse_test(struct Sieve *filter, int *cond, int exec);
113 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
115 static uschar str_from_c[]="From";
116 static const struct String str_from={ str_from_c, 4 };
117 static uschar str_to_c[]="To";
118 static const struct String str_to={ str_to_c, 2 };
119 static uschar str_cc_c[]="Cc";
120 static const struct String str_cc={ str_cc_c, 2 };
121 static uschar str_bcc_c[]="Bcc";
122 static const struct String str_bcc={ str_bcc_c, 3 };
123 static uschar str_auth_c[]="auth";
124 static const struct String str_auth={ str_auth_c, 4 };
125 static uschar str_sender_c[]="Sender";
126 static const struct String str_sender={ str_sender_c, 6 };
127 static uschar str_resent_from_c[]="Resent-From";
128 static const struct String str_resent_from={ str_resent_from_c, 11 };
129 static uschar str_resent_to_c[]="Resent-To";
130 static const struct String str_resent_to={ str_resent_to_c, 9 };
131 static uschar str_fileinto_c[]="fileinto";
132 static const struct String str_fileinto={ str_fileinto_c, 8 };
133 static uschar str_envelope_c[]="envelope";
134 static const struct String str_envelope={ str_envelope_c, 8 };
135 #ifdef ENCODED_CHARACTER
136 static uschar str_encoded_character_c[]="encoded-character";
137 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
140 static uschar str_envelope_auth_c[]="envelope-auth";
141 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
144 static uschar str_enotify_c[]="enotify";
145 static const struct String str_enotify={ str_enotify_c, 7 };
146 static uschar str_online_c[]="online";
147 static const struct String str_online={ str_online_c, 6 };
148 static uschar str_maybe_c[]="maybe";
149 static const struct String str_maybe={ str_maybe_c, 5 };
150 static uschar str_auto_submitted_c[]="Auto-Submitted";
151 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
154 static uschar str_subaddress_c[]="subaddress";
155 static const struct String str_subaddress={ str_subaddress_c, 10 };
158 static uschar str_vacation_c[]="vacation";
159 static const struct String str_vacation={ str_vacation_c, 8 };
160 static uschar str_subject_c[]="Subject";
161 static const struct String str_subject={ str_subject_c, 7 };
163 static uschar str_copy_c[]="copy";
164 static const struct String str_copy={ str_copy_c, 4 };
165 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
166 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
167 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
168 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
169 static uschar str_ioctet_c[]="i;octet";
170 static const struct String str_ioctet={ str_ioctet_c, 7 };
171 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
172 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
173 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
174 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
175 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
176 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
177 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
178 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
179 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
180 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
183 /*************************************************
184 * Encode to quoted-printable *
185 *************************************************/
196 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
199 const uschar *start,*end;
204 for (pass=0; pass<=1; ++pass)
211 dst->character=store_get(dst->length+1); /* plus one for \0 */
214 for (start=src->character,end=start+src->length; start<end; ++start)
231 || (ch>=62 && ch<=126)
236 && (*(start+1)!='\r' || *(start+2)!='\n')
246 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
264 sprintf(CS new,"=%02X",ch);
271 *new='\0'; /* not included in length, but nice */
276 /*************************************************
277 * Check mail address for correct syntax *
278 *************************************************/
281 Check mail address for being syntactically correct.
284 filter points to the Sieve filter including its state
285 address String containing one address
288 1 Mail address is syntactically OK
292 int check_mail_address(struct Sieve *filter, const struct String *address)
294 int start, end, domain;
297 if (address->length>0)
299 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
303 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
304 address->character, error);
312 filter->errmsg=CUS "empty address";
318 /*************************************************
319 * Decode URI encoded string *
320 *************************************************/
324 str URI encoded string
327 0 Decoding successful
332 static int uri_decode(struct String *str)
336 if (str->length==0) return 0;
337 for (s=str->character,t=s,e=s+str->length; s<e; )
341 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
343 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
344 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
353 str->length=t-str->character;
358 /*************************************************
360 *************************************************/
365 mailtoURI = "mailto:" [ to ] [ headers ]
366 to = [ addr-spec *("%2C" addr-spec ) ]
367 headers = "?" header *( "&" header )
368 header = hname "=" hvalue
373 filter points to the Sieve filter including its state
374 uri URI, excluding scheme
379 1 URI is syntactically OK
384 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *subject, struct String *body)
387 struct String to,hname,hvalue;
391 if (Ustrncmp(uri,"mailto:",7))
393 filter->errmsg=US "Unknown URI scheme";
397 if (*uri && *uri!='?')
401 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
405 to.character=(uschar*)0;
407 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
408 to.character[to.length]='\0';
409 if (uri_decode(&to)==-1)
411 filter->errmsg=US"Invalid URI encoding";
414 new=store_get(sizeof(string_item));
415 new->text=store_get(to.length+1);
416 if (to.length) memcpy(new->text,to.character,to.length);
417 new->text[to.length]='\0';
418 new->next=*recipient;
423 filter->errmsg=US"Missing addr-spec in URI";
426 if (*uri=='%') uri+=3;
435 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
439 hname.character=(uschar*)0;
441 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
442 hname.character[hname.length]='\0';
443 if (uri_decode(&hname)==-1)
445 filter->errmsg=US"Invalid URI encoding";
454 filter->errmsg=US"Missing equal after hname";
458 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
462 hvalue.character=(uschar*)0;
464 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
465 hvalue.character[hvalue.length]='\0';
466 if (uri_decode(&hvalue)==-1)
468 filter->errmsg=US"Invalid URI encoding";
472 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
474 new=store_get(sizeof(string_item));
475 new->text=store_get(hvalue.length+1);
476 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
477 new->text[hvalue.length]='\0';
478 new->next=*recipient;
481 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
483 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
487 static struct String ignore[]=
493 {US"auto-submitted",14}
495 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
498 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
501 if (header->length==-1) header->length=0;
502 capacity=header->length;
503 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
504 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
505 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
506 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
507 header->character[header->length]='\0';
510 if (*uri=='&') ++uri;
516 filter->errmsg=US"Syntactically invalid URI";
524 /*************************************************
525 * Octet-wise string comparison *
526 *************************************************/
530 needle UTF-8 string to search ...
531 haystack ... inside the haystack
532 match_prefix 1 to compare if needle is a prefix of haystack
534 Returns: 0 needle not found in haystack
538 static int eq_octet(const struct String *needle,
539 const struct String *haystack, int match_prefix)
547 h=haystack->character;
551 if (*n&0x80) return 0;
552 if (*h&0x80) return 0;
554 if (*n!=*h) return 0;
560 return (match_prefix ? nl==0 : nl==0 && hl==0);
564 /*************************************************
565 * ASCII case-insensitive string comparison *
566 *************************************************/
570 needle UTF-8 string to search ...
571 haystack ... inside the haystack
572 match_prefix 1 to compare if needle is a prefix of haystack
574 Returns: 0 needle not found in haystack
578 static int eq_asciicase(const struct String *needle,
579 const struct String *haystack, int match_prefix)
588 h=haystack->character;
594 if (nc&0x80) return 0;
595 if (hc&0x80) return 0;
597 /* tolower depends on the locale and only ASCII case must be insensitive */
598 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
604 return (match_prefix ? nl==0 : nl==0 && hl==0);
608 /*************************************************
609 * Glob pattern search *
610 *************************************************/
614 needle pattern to search ...
615 haystack ... inside the haystack
616 ascii_caseless ignore ASCII case
617 match_octet match octets, not UTF-8 multi-octet characters
619 Returns: 0 needle not found in haystack
624 static int eq_glob(const struct String *needle,
625 const struct String *haystack, int ascii_caseless, int match_octet)
627 const uschar *n,*h,*nend,*hend;
631 h=haystack->character;
632 nend=n+needle->length;
633 hend=h+haystack->length;
643 const uschar *npart,*hpart;
645 /* Try to match a non-star part of the needle at the current */
646 /* position in the haystack. */
650 while (npart<nend && *npart!='*') switch (*npart)
654 if (hpart==hend) return 0;
659 /* Match one UTF8 encoded character */
660 if ((*hpart&0xc0)==0xc0)
663 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
674 if (npart==nend) return -1;
679 if (hpart==hend) return 0;
680 /* tolower depends on the locale, but we need ASCII */
684 (*hpart&0x80) || (*npart&0x80) ||
687 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
692 /* string match after a star failed, advance and try again */
706 /* at this point, a part was matched successfully */
707 if (may_advance && npart==nend && hpart<hend)
708 /* needle ends, but haystack does not: if there was a star before, advance and try again */
718 return (h==hend ? 1 : may_advance);
722 /*************************************************
723 * ASCII numeric comparison *
724 *************************************************/
728 a first numeric string
729 b second numeric string
730 relop relational operator
732 Returns: 0 not (a relop b)
736 static int eq_asciinumeric(const struct String *a,
737 const struct String *b, enum RelOp relop)
740 const uschar *as,*aend,*bs,*bend;
744 aend=a->character+a->length;
746 bend=b->character+b->length;
748 while (*as>='0' && *as<='9' && as<aend) ++as;
750 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
753 if (al && bl==0) cmp=-1;
754 else if (al==0 && bl==0) cmp=0;
755 else if (al==0 && bl) cmp=1;
759 if (cmp==0) cmp=memcmp(a->character,b->character,al);
763 case LT: return cmp<0;
764 case LE: return cmp<=0;
765 case EQ: return cmp==0;
766 case GE: return cmp>=0;
767 case GT: return cmp>0;
768 case NE: return cmp!=0;
775 /*************************************************
777 *************************************************/
781 filter points to the Sieve filter including its state
782 needle UTF-8 pattern or string to search ...
783 haystack ... inside the haystack
787 Returns: 0 needle not found in haystack
789 -1 comparator does not offer matchtype
792 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
793 enum Comparator co, enum MatchType mt)
797 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
798 (debug_selector & D_filter) != 0)
800 debug_printf("String comparison (match ");
803 case MATCH_IS: debug_printf(":is"); break;
804 case MATCH_CONTAINS: debug_printf(":contains"); break;
805 case MATCH_MATCHES: debug_printf(":matches"); break;
807 debug_printf(", comparison \"");
810 case COMP_OCTET: debug_printf("i;octet"); break;
811 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
812 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
814 debug_printf("\"):\n");
815 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
816 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
826 if (eq_octet(needle,haystack,0)) r=1;
829 case COMP_EN_ASCII_CASEMAP:
831 if (eq_asciicase(needle,haystack,0)) r=1;
834 case COMP_ASCII_NUMERIC:
836 if (!filter->require_iascii_numeric)
838 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
841 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
855 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
858 case COMP_EN_ASCII_CASEMAP:
860 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
865 filter->errmsg=CUS "comparator does not offer specified matchtype";
877 if ((r=eq_glob(needle,haystack,0,1))==-1)
879 filter->errmsg=CUS "syntactically invalid pattern";
884 case COMP_EN_ASCII_CASEMAP:
886 if ((r=eq_glob(needle,haystack,1,1))==-1)
888 filter->errmsg=CUS "syntactically invalid pattern";
895 filter->errmsg=CUS "comparator does not offer specified matchtype";
902 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
903 (debug_selector & D_filter) != 0)
904 debug_printf(" Result %s\n",r?"true":"false");
909 /*************************************************
910 * Check header field syntax *
911 *************************************************/
914 RFC 2822, section 3.6.8 says:
918 ftext = %d33-57 / ; Any character except
919 %d59-126 ; controls, SP, and
922 That forbids 8-bit header fields. This implementation accepts them, since
923 all of Exim is 8-bit clean, so it adds %d128-%d255.
926 header header field to quote for suitable use in Exim expansions
928 Returns: 0 string is not a valid header field
929 1 string is a value header field
932 static int is_header(const struct String *header)
942 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
953 /*************************************************
954 * Quote special characters string *
955 *************************************************/
959 header header field to quote for suitable use in Exim expansions
962 Returns: quoted string
965 static const uschar *quote(const struct String *header)
980 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
987 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
991 quoted=string_cat(quoted,&size,&ptr,h,1);
997 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
1002 /*************************************************
1003 * Add address to list of generated addresses *
1004 *************************************************/
1007 According to RFC 5228, duplicate delivery to the same address must
1008 not happen, so the list is first searched for the address.
1011 generated list of generated addresses
1012 addr new address to add
1013 file address denotes a file
1018 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1020 address_item *new_addr;
1022 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1024 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1026 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1028 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1034 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1036 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1038 new_addr=deliver_make_addr(addr,TRUE);
1041 setflag(new_addr, af_pfr|af_file);
1044 new_addr->p.errors_address = NULL;
1045 new_addr->next = *generated;
1046 *generated = new_addr;
1050 /*************************************************
1051 * Return decoded header field *
1052 *************************************************/
1055 Unfold the header field as described in RFC 2822 and remove all
1056 leading and trailing white space, then perform MIME decoding and
1057 translate the header field to UTF-8.
1060 value returned value of the field
1061 header name of the header field
1063 Returns: nothing The expanded string is empty
1064 in case there is no such header
1067 static void expand_header(struct String *value, const struct String *header)
1073 value->character=(uschar*)0;
1075 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1076 while (*r==' ' || *r=='\t') ++r;
1084 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1086 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1090 /*************************************************
1091 * Parse remaining hash comment *
1092 *************************************************/
1096 Comment up to terminating CRLF
1099 filter points to the Sieve filter including its state
1105 static int parse_hashcomment(struct Sieve *filter)
1111 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1113 if (*filter->pc=='\n')
1126 filter->errmsg=CUS "missing end of comment";
1131 /*************************************************
1132 * Parse remaining C-style comment *
1133 *************************************************/
1137 Everything up to star slash
1140 filter points to the Sieve filter including its state
1146 static int parse_comment(struct Sieve *filter)
1151 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1158 filter->errmsg=CUS "missing end of comment";
1163 /*************************************************
1164 * Parse optional white space *
1165 *************************************************/
1169 Spaces, tabs, CRLFs, hash comments or C-style comments
1172 filter points to the Sieve filter including its state
1178 static int parse_white(struct Sieve *filter)
1182 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1184 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1186 else if (*filter->pc=='\n')
1196 else if (*filter->pc=='#')
1198 if (parse_hashcomment(filter)==-1) return -1;
1200 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1202 if (parse_comment(filter)==-1) return -1;
1210 #ifdef ENCODED_CHARACTER
1211 /*************************************************
1212 * Decode hex-encoded-character string *
1213 *************************************************/
1216 Encoding definition:
1217 blank = SP / TAB / CRLF
1218 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1219 hex-pair = 1*2HEXDIG
1222 src points to a hex-pair-seq
1223 end points to its end
1224 dst points to the destination of the decoded octets,
1225 optionally to (uschar*)0 for checking only
1227 Returns: >=0 number of decoded octets
1231 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1235 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1240 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);
1241 if (d==0) return -1;
1244 if (src==end) return decoded;
1245 if (*src==' ' || *src=='\t' || *src=='\n')
1246 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1255 /*************************************************
1256 * Decode unicode-encoded-character string *
1257 *************************************************/
1260 Encoding definition:
1261 blank = SP / TAB / CRLF
1262 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1263 unicode-hex = 1*HEXDIG
1265 It is an error for a script to use a hexadecimal value that isn't in
1266 either the range 0 to D7FF or the range E000 to 10FFFF.
1268 At this time, strings are already scanned, thus the CRLF is converted
1269 to the internally used \n (should RFC_EOL have been used).
1272 src points to a unicode-hex-seq
1273 end points to its end
1274 dst points to the destination of the decoded octets,
1275 optionally to (uschar*)0 for checking only
1277 Returns: >=0 number of decoded octets
1279 -2 semantic error (character range violation)
1282 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1286 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1293 for (hex_seq=src; src<end && *src=='0'; ++src);
1294 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);
1295 if (src==hex_seq) return -1;
1296 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1302 else if (c>=0x80 && c<=0x7ff)
1307 *dst++=128+(c&0x3f);
1311 else if (c>=0x800 && c<=0xffff)
1316 *dst++=128+((c>>6)&0x3f);
1317 *dst++=128+(c&0x3f);
1321 else if (c>=0x10000 && c<=0x1fffff)
1326 *dst++=128+((c>>10)&0x3f);
1327 *dst++=128+((c>>6)&0x3f);
1328 *dst++=128+(c&0x3f);
1332 if (*src==' ' || *src=='\t' || *src=='\n')
1334 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1335 if (src==end) return decoded;
1344 /*************************************************
1345 * Decode encoded-character string *
1346 *************************************************/
1349 Encoding definition:
1350 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1351 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1354 encoded points to an encoded string, returns decoded string
1355 filter points to the Sieve filter including its state
1361 static int string_decode(struct Sieve *filter, struct String *data)
1363 uschar *src,*dst,*end;
1365 src=data->character;
1367 end=data->character+data->length;
1373 strncmpic(src,US "${hex:",6)==0
1374 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1375 && (hex_decode(src+6,brace,(uschar*)0))>=0
1378 dst+=hex_decode(src+6,brace,dst);
1382 strncmpic(src,US "${unicode:",10)==0
1383 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1386 switch (unicode_decode(src+10,brace,(uschar*)0))
1390 filter->errmsg=CUS "unicode character out of range";
1400 dst+=unicode_decode(src+10,brace,dst);
1407 data->length=dst-data->character;
1414 /*************************************************
1415 * Parse an optional string *
1416 *************************************************/
1420 quoted-string = DQUOTE *CHAR DQUOTE
1421 ;; in general, \ CHAR inside a string maps to CHAR
1422 ;; so \" maps to " and \\ maps to \
1423 ;; note that newlines and other characters are all allowed
1426 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1427 *(multi-line-literal / multi-line-dotstuff)
1429 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1430 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1431 ;; A line containing only "." ends the multi-line.
1432 ;; Remove a leading '.' if followed by another '.'.
1433 string = quoted-string / multi-line
1436 filter points to the Sieve filter including its state
1437 id specifies identifier to match
1441 0 identifier not matched
1444 static int parse_string(struct Sieve *filter, struct String *data)
1449 data->character=(uschar*)0;
1450 if (*filter->pc=='"') /* quoted string */
1455 if (*filter->pc=='"') /* end of string */
1457 int foo=data->length;
1460 /* that way, there will be at least one character allocated */
1461 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1462 #ifdef ENCODED_CHARACTER
1463 if (filter->require_encoded_character
1464 && string_decode(filter,data)==-1)
1469 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1471 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1474 else /* regular character */
1477 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1479 if (*filter->pc=='\n')
1481 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1485 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1489 filter->errmsg=CUS "missing end of string";
1492 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1495 /* skip optional white space followed by hashed comment or CRLF */
1496 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1497 if (*filter->pc=='#')
1499 if (parse_hashcomment(filter)==-1) return -1;
1502 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1504 else if (*filter->pc=='\n')
1516 filter->errmsg=CUS "syntax error";
1522 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1524 if (*filter->pc=='\n') /* end of line */
1527 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1535 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1537 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1540 int foo=data->length;
1542 /* that way, there will be at least one character allocated */
1543 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1550 #ifdef ENCODED_CHARACTER
1551 if (filter->require_encoded_character
1552 && string_decode(filter,data)==-1)
1557 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1559 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1563 else /* regular character */
1565 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1569 filter->errmsg=CUS "missing end of multi line string";
1576 /*************************************************
1577 * Parse a specific identifier *
1578 *************************************************/
1582 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1585 filter points to the Sieve filter including its state
1586 id specifies identifier to match
1589 0 identifier not matched
1592 static int parse_identifier(struct Sieve *filter, const uschar *id)
1594 size_t idlen=Ustrlen(id);
1596 if (strncmpic(US filter->pc,US id,idlen)==0)
1598 uschar next=filter->pc[idlen];
1600 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1608 /*************************************************
1610 *************************************************/
1614 number = 1*DIGIT [QUANTIFIER]
1615 QUANTIFIER = "K" / "M" / "G"
1618 filter points to the Sieve filter including its state
1622 -1 no string list found
1625 static int parse_number(struct Sieve *filter, unsigned long *data)
1629 if (*filter->pc>='0' && *filter->pc<='9')
1634 d=Ustrtoul(filter->pc,&e,10);
1637 filter->errmsg=CUstrerror(ERANGE);
1642 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1643 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1644 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1645 if (d>(ULONG_MAX/u))
1647 filter->errmsg=CUstrerror(ERANGE);
1656 filter->errmsg=CUS "missing number";
1662 /*************************************************
1663 * Parse a string list *
1664 *************************************************/
1668 string-list = "[" string *("," string) "]" / string
1671 filter points to the Sieve filter including its state
1672 data returns string list
1675 -1 no string list found
1678 static int parse_stringlist(struct Sieve *filter, struct String **data)
1680 const uschar *orig=filter->pc;
1683 struct String *d=(struct String*)0;
1686 if (*filter->pc=='[') /* string list */
1691 if (parse_white(filter)==-1) goto error;
1692 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1695 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1696 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1697 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1699 filter->errmsg=CUstrerror(errno);
1702 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1704 dataCapacity=newCapacity;
1706 m=parse_string(filter,&d[dataLength]);
1709 if (dataLength==0) break;
1712 filter->errmsg=CUS "missing string";
1716 else if (m==-1) goto error;
1718 if (parse_white(filter)==-1) goto error;
1719 if (*filter->pc==',') ++filter->pc;
1722 if (*filter->pc==']')
1724 d[dataLength].character=(uschar*)0;
1725 d[dataLength].length=-1;
1732 filter->errmsg=CUS "missing closing bracket";
1736 else /* single string */
1738 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1742 m=parse_string(filter,&d[0]);
1754 d[1].character=(uschar*)0;
1761 filter->errmsg=CUS "missing string list";
1766 /*************************************************
1767 * Parse an optional address part specifier *
1768 *************************************************/
1772 address-part = ":localpart" / ":domain" / ":all"
1773 address-part =/ ":user" / ":detail"
1776 filter points to the Sieve filter including its state
1777 a returns address part specified
1780 0 no comparator found
1784 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1787 if (parse_identifier(filter,CUS ":user")==1)
1789 if (!filter->require_subaddress)
1791 filter->errmsg=CUS "missing previous require \"subaddress\";";
1797 else if (parse_identifier(filter,CUS ":detail")==1)
1799 if (!filter->require_subaddress)
1801 filter->errmsg=CUS "missing previous require \"subaddress\";";
1809 if (parse_identifier(filter,CUS ":localpart")==1)
1811 *a=ADDRPART_LOCALPART;
1814 else if (parse_identifier(filter,CUS ":domain")==1)
1819 else if (parse_identifier(filter,CUS ":all")==1)
1828 /*************************************************
1829 * Parse an optional comparator *
1830 *************************************************/
1834 comparator = ":comparator" <comparator-name: string>
1837 filter points to the Sieve filter including its state
1838 c returns comparator
1841 0 no comparator found
1842 -1 incomplete comparator found
1845 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1847 struct String comparator_name;
1849 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1850 if (parse_white(filter)==-1) return -1;
1851 switch (parse_string(filter,&comparator_name))
1856 filter->errmsg=CUS "missing comparator";
1863 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1868 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1870 *c=COMP_EN_ASCII_CASEMAP;
1873 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1875 *c=COMP_EN_ASCII_CASEMAP;
1878 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1880 *c=COMP_ASCII_NUMERIC;
1885 filter->errmsg=CUS "invalid comparator";
1894 /*************************************************
1895 * Parse an optional match type *
1896 *************************************************/
1900 match-type = ":is" / ":contains" / ":matches"
1903 filter points to the Sieve filter including its state
1904 m returns match type
1907 0 no match type found
1910 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1912 if (parse_identifier(filter,CUS ":is")==1)
1917 else if (parse_identifier(filter,CUS ":contains")==1)
1922 else if (parse_identifier(filter,CUS ":matches")==1)
1931 /*************************************************
1932 * Parse and interpret an optional test list *
1933 *************************************************/
1937 test-list = "(" test *("," test) ")"
1940 filter points to the Sieve filter including its state
1941 n total number of tests
1942 num_true number of passed tests
1943 exec Execute parsed statements
1946 0 no test list found
1947 -1 syntax or execution error
1950 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1952 if (parse_white(filter)==-1) return -1;
1953 if (*filter->pc=='(')
1962 switch (parse_test(filter,&cond,exec))
1965 case 0: filter->errmsg=CUS "missing test"; return -1;
1966 default: ++*n; if (cond) ++*num_true; break;
1968 if (parse_white(filter)==-1) return -1;
1969 if (*filter->pc==',') ++filter->pc;
1972 if (*filter->pc==')')
1979 filter->errmsg=CUS "missing closing paren";
1987 /*************************************************
1988 * Parse and interpret an optional test *
1989 *************************************************/
1993 filter points to the Sieve filter including its state
1994 cond returned condition status
1995 exec Execute parsed statements
1999 -1 syntax or execution error
2002 static int parse_test(struct Sieve *filter, int *cond, int exec)
2004 if (parse_white(filter)==-1) return -1;
2005 if (parse_identifier(filter,CUS "address"))
2008 address-test = "address" { [address-part] [comparator] [match-type] }
2009 <header-list: string-list> <key-list: string-list>
2011 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2014 enum AddressPart addressPart=ADDRPART_ALL;
2015 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2016 enum MatchType matchType=MATCH_IS;
2017 struct String *hdr,*h,*key,*k;
2023 if (parse_white(filter)==-1) return -1;
2024 if ((m=parse_addresspart(filter,&addressPart))!=0)
2026 if (m==-1) return -1;
2029 filter->errmsg=CUS "address part already specified";
2034 else if ((m=parse_comparator(filter,&comparator))!=0)
2036 if (m==-1) return -1;
2039 filter->errmsg=CUS "comparator already specified";
2044 else if ((m=parse_matchtype(filter,&matchType))!=0)
2046 if (m==-1) return -1;
2049 filter->errmsg=CUS "match type already specified";
2056 if (parse_white(filter)==-1) return -1;
2057 if ((m=parse_stringlist(filter,&hdr))!=1)
2059 if (m==0) filter->errmsg=CUS "header string list expected";
2062 if (parse_white(filter)==-1) return -1;
2063 if ((m=parse_stringlist(filter,&key))!=1)
2065 if (m==0) filter->errmsg=CUS "key string list expected";
2069 for (h=hdr; h->length!=-1 && !*cond; ++h)
2071 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2075 !eq_asciicase(h,&str_from,0)
2076 && !eq_asciicase(h,&str_to,0)
2077 && !eq_asciicase(h,&str_cc,0)
2078 && !eq_asciicase(h,&str_bcc,0)
2079 && !eq_asciicase(h,&str_sender,0)
2080 && !eq_asciicase(h,&str_resent_from,0)
2081 && !eq_asciicase(h,&str_resent_to,0)
2084 filter->errmsg=CUS "invalid header field";
2089 /* We are only interested in addresses below, so no MIME decoding */
2090 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2091 if (header_value == NULL)
2093 filter->errmsg=CUS "header string expansion failed";
2096 parse_allow_group = TRUE;
2097 while (*header_value && !*cond)
2100 int start, end, domain;
2104 end_addr = parse_find_address_end(header_value, FALSE);
2105 saveend = *end_addr;
2107 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2109 if (extracted_addr) switch (addressPart)
2111 case ADDRPART_ALL: part=extracted_addr; break;
2115 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2116 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2118 case ADDRPART_DETAIL: part=NULL; break;
2122 *end_addr = saveend;
2125 for (k=key; k->length!=-1; ++k)
2127 struct String partStr;
2129 partStr.character=part;
2130 partStr.length=Ustrlen(part);
2133 *cond=compare(filter,k,&partStr,comparator,matchType);
2134 if (*cond==-1) return -1;
2139 if (saveend == 0) break;
2140 header_value = end_addr + 1;
2142 parse_allow_group = FALSE;
2143 parse_found_group = FALSE;
2148 else if (parse_identifier(filter,CUS "allof"))
2151 allof-test = "allof" <tests: test-list>
2156 switch (parse_testlist(filter,&n,&num_true,exec))
2159 case 0: filter->errmsg=CUS "missing test list"; return -1;
2160 default: *cond=(n==num_true); return 1;
2163 else if (parse_identifier(filter,CUS "anyof"))
2166 anyof-test = "anyof" <tests: test-list>
2171 switch (parse_testlist(filter,&n,&num_true,exec))
2174 case 0: filter->errmsg=CUS "missing test list"; return -1;
2175 default: *cond=(num_true>0); return 1;
2178 else if (parse_identifier(filter,CUS "exists"))
2181 exists-test = "exists" <header-names: string-list>
2184 struct String *hdr,*h;
2187 if (parse_white(filter)==-1) return -1;
2188 if ((m=parse_stringlist(filter,&hdr))!=1)
2190 if (m==0) filter->errmsg=CUS "header string list expected";
2196 for (h=hdr; h->length!=-1 && *cond; ++h)
2200 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2201 if (header_def == NULL)
2203 filter->errmsg=CUS "header string expansion failed";
2206 if (Ustrcmp(header_def,"false")==0) *cond=0;
2211 else if (parse_identifier(filter,CUS "false"))
2214 false-test = "false"
2220 else if (parse_identifier(filter,CUS "header"))
2223 header-test = "header" { [comparator] [match-type] }
2224 <header-names: string-list> <key-list: string-list>
2227 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2228 enum MatchType matchType=MATCH_IS;
2229 struct String *hdr,*h,*key,*k;
2235 if (parse_white(filter)==-1) return -1;
2236 if ((m=parse_comparator(filter,&comparator))!=0)
2238 if (m==-1) return -1;
2241 filter->errmsg=CUS "comparator already specified";
2246 else if ((m=parse_matchtype(filter,&matchType))!=0)
2248 if (m==-1) return -1;
2251 filter->errmsg=CUS "match type already specified";
2258 if (parse_white(filter)==-1) return -1;
2259 if ((m=parse_stringlist(filter,&hdr))!=1)
2261 if (m==0) filter->errmsg=CUS "header string list expected";
2264 if (parse_white(filter)==-1) return -1;
2265 if ((m=parse_stringlist(filter,&key))!=1)
2267 if (m==0) filter->errmsg=CUS "key string list expected";
2271 for (h=hdr; h->length!=-1 && !*cond; ++h)
2275 filter->errmsg=CUS "invalid header field";
2280 struct String header_value;
2283 expand_header(&header_value,h);
2284 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2285 if (header_value.character == NULL || header_def == NULL)
2287 filter->errmsg=CUS "header string expansion failed";
2290 for (k=key; k->length!=-1; ++k)
2292 if (Ustrcmp(header_def,"true")==0)
2294 *cond=compare(filter,k,&header_value,comparator,matchType);
2295 if (*cond==-1) return -1;
2303 else if (parse_identifier(filter,CUS "not"))
2305 if (parse_white(filter)==-1) return -1;
2306 switch (parse_test(filter,cond,exec))
2309 case 0: filter->errmsg=CUS "missing test"; return -1;
2310 default: *cond=!*cond; return 1;
2313 else if (parse_identifier(filter,CUS "size"))
2316 relop = ":over" / ":under"
2317 size-test = "size" relop <limit: number>
2320 unsigned long limit;
2323 if (parse_white(filter)==-1) return -1;
2324 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2325 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2328 filter->errmsg=CUS "missing :over or :under";
2331 if (parse_white(filter)==-1) return -1;
2332 if (parse_number(filter,&limit)==-1) return -1;
2333 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2336 else if (parse_identifier(filter,CUS "true"))
2341 else if (parse_identifier(filter,CUS "envelope"))
2344 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2345 <envelope-part: string-list> <key-list: string-list>
2347 envelope-part is case insensitive "from" or "to"
2348 #ifdef ENVELOPE_AUTH
2349 envelope-part =/ "auth"
2353 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2354 enum AddressPart addressPart=ADDRPART_ALL;
2355 enum MatchType matchType=MATCH_IS;
2356 struct String *env,*e,*key,*k;
2360 if (!filter->require_envelope)
2362 filter->errmsg=CUS "missing previous require \"envelope\";";
2367 if (parse_white(filter)==-1) return -1;
2368 if ((m=parse_comparator(filter,&comparator))!=0)
2370 if (m==-1) return -1;
2373 filter->errmsg=CUS "comparator already specified";
2378 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2380 if (m==-1) return -1;
2383 filter->errmsg=CUS "address part already specified";
2388 else if ((m=parse_matchtype(filter,&matchType))!=0)
2390 if (m==-1) return -1;
2393 filter->errmsg=CUS "match type already specified";
2400 if (parse_white(filter)==-1) return -1;
2401 if ((m=parse_stringlist(filter,&env))!=1)
2403 if (m==0) filter->errmsg=CUS "envelope string list expected";
2406 if (parse_white(filter)==-1) return -1;
2407 if ((m=parse_stringlist(filter,&key))!=1)
2409 if (m==0) filter->errmsg=CUS "key string list expected";
2413 for (e=env; e->length!=-1 && !*cond; ++e)
2415 const uschar *envelopeExpr=CUS 0;
2416 uschar *envelope=US 0;
2418 if (eq_asciicase(e,&str_from,0))
2420 switch (addressPart)
2422 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2426 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2427 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2429 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2433 else if (eq_asciicase(e,&str_to,0))
2435 switch (addressPart)
2437 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2439 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2440 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2442 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2443 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2446 #ifdef ENVELOPE_AUTH
2447 else if (eq_asciicase(e,&str_auth,0))
2449 switch (addressPart)
2451 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2455 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2456 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2458 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2465 filter->errmsg=CUS "invalid envelope string";
2468 if (exec && envelopeExpr)
2470 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2472 filter->errmsg=CUS "header string expansion failed";
2475 for (k=key; k->length!=-1; ++k)
2477 struct String envelopeStr;
2479 envelopeStr.character=envelope;
2480 envelopeStr.length=Ustrlen(envelope);
2481 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2482 if (*cond==-1) return -1;
2490 else if (parse_identifier(filter,CUS "valid_notify_method"))
2493 valid_notify_method = "valid_notify_method"
2494 <notification-uris: string-list>
2497 struct String *uris,*u;
2500 if (!filter->require_enotify)
2502 filter->errmsg=CUS "missing previous require \"enotify\";";
2505 if (parse_white(filter)==-1) return -1;
2506 if ((m=parse_stringlist(filter,&uris))!=1)
2508 if (m==0) filter->errmsg=CUS "URI string list expected";
2514 for (u=uris; u->length!=-1 && *cond; ++u)
2516 string_item *recipient;
2517 struct String header,subject,body;
2521 header.character=(uschar*)0;
2523 subject.character=(uschar*)0;
2525 body.character=(uschar*)0;
2526 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2532 else if (parse_identifier(filter,CUS "notify_method_capability"))
2535 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2536 <notification-uri: string>
2537 <notification-capability: string>
2538 <key-list: string-list>
2544 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2545 enum MatchType matchType=MATCH_IS;
2546 struct String uri,capa,*keys,*k;
2548 if (!filter->require_enotify)
2550 filter->errmsg=CUS "missing previous require \"enotify\";";
2555 if (parse_white(filter)==-1) return -1;
2556 if ((m=parse_comparator(filter,&comparator))!=0)
2558 if (m==-1) return -1;
2561 filter->errmsg=CUS "comparator already specified";
2566 else if ((m=parse_matchtype(filter,&matchType))!=0)
2568 if (m==-1) return -1;
2571 filter->errmsg=CUS "match type already specified";
2578 if ((m=parse_string(filter,&uri))!=1)
2580 if (m==0) filter->errmsg=CUS "missing notification URI string";
2583 if (parse_white(filter)==-1) return -1;
2584 if ((m=parse_string(filter,&capa))!=1)
2586 if (m==0) filter->errmsg=CUS "missing notification capability string";
2589 if (parse_white(filter)==-1) return -1;
2590 if ((m=parse_stringlist(filter,&keys))!=1)
2592 if (m==0) filter->errmsg=CUS "missing key string list";
2597 string_item *recipient;
2598 struct String header,subject,body;
2603 header.character=(uschar*)0;
2605 subject.character=(uschar*)0;
2607 body.character=(uschar*)0;
2608 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2610 if (eq_asciicase(&capa,&str_online,0)==1)
2611 for (k=keys; k->length!=-1; ++k)
2613 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2614 if (*cond==-1) return -1;
2626 /*************************************************
2627 * Parse and interpret an optional block *
2628 *************************************************/
2632 filter points to the Sieve filter including its state
2633 exec Execute parsed statements
2634 generated where to hang newly-generated addresses
2636 Returns: 2 success by stop
2638 0 no block command found
2639 -1 syntax or execution error
2642 static int parse_block(struct Sieve *filter, int exec,
2643 address_item **generated)
2647 if (parse_white(filter)==-1) return -1;
2648 if (*filter->pc=='{')
2651 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2652 if (*filter->pc=='}')
2659 filter->errmsg=CUS "expecting command or closing brace";
2667 /*************************************************
2668 * Match a semicolon *
2669 *************************************************/
2673 filter points to the Sieve filter including its state
2679 static int parse_semicolon(struct Sieve *filter)
2681 if (parse_white(filter)==-1) return -1;
2682 if (*filter->pc==';')
2689 filter->errmsg=CUS "missing semicolon";
2695 /*************************************************
2696 * Parse and interpret a Sieve command *
2697 *************************************************/
2701 filter points to the Sieve filter including its state
2702 exec Execute parsed statements
2703 generated where to hang newly-generated addresses
2705 Returns: 2 success by stop
2707 -1 syntax or execution error
2709 static int parse_commands(struct Sieve *filter, int exec,
2710 address_item **generated)
2714 if (parse_white(filter)==-1) return -1;
2715 if (parse_identifier(filter,CUS "if"))
2718 if-command = "if" test block *( "elsif" test block ) [ else block ]
2721 int cond,m,unsuccessful;
2724 if (parse_white(filter)==-1) return -1;
2725 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2728 filter->errmsg=CUS "missing test";
2731 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2732 (debug_selector & D_filter) != 0)
2734 if (exec) debug_printf("if %s\n",cond?"true":"false");
2736 m=parse_block(filter,exec ? cond : 0, generated);
2737 if (m==-1 || m==2) return m;
2740 filter->errmsg=CUS "missing block";
2743 unsuccessful = !cond;
2744 for (;;) /* elsif test block */
2746 if (parse_white(filter)==-1) return -1;
2747 if (parse_identifier(filter,CUS "elsif"))
2749 if (parse_white(filter)==-1) return -1;
2750 m=parse_test(filter,&cond,exec && unsuccessful);
2751 if (m==-1 || m==2) return m;
2754 filter->errmsg=CUS "missing test";
2757 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2758 (debug_selector & D_filter) != 0)
2760 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2762 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2763 if (m==-1 || m==2) return m;
2766 filter->errmsg=CUS "missing block";
2769 if (exec && unsuccessful && cond) unsuccessful = 0;
2774 if (parse_white(filter)==-1) return -1;
2775 if (parse_identifier(filter,CUS "else"))
2777 m=parse_block(filter,exec && unsuccessful, generated);
2778 if (m==-1 || m==2) return m;
2781 filter->errmsg=CUS "missing block";
2786 else if (parse_identifier(filter,CUS "stop"))
2789 stop-command = "stop" { stop-options } ";"
2793 if (parse_semicolon(filter)==-1) return -1;
2796 filter->pc+=Ustrlen(filter->pc);
2800 else if (parse_identifier(filter,CUS "keep"))
2803 keep-command = "keep" { keep-options } ";"
2807 if (parse_semicolon(filter)==-1) return -1;
2810 add_addr(generated,US"inbox",1,0,0,0);
2814 else if (parse_identifier(filter,CUS "discard"))
2817 discard-command = "discard" { discard-options } ";"
2821 if (parse_semicolon(filter)==-1) return -1;
2822 if (exec) filter->keep=0;
2824 else if (parse_identifier(filter,CUS "redirect"))
2827 redirect-command = "redirect" redirect-options "string" ";"
2829 redirect-options =) ":copy"
2832 struct String recipient;
2838 if (parse_white(filter)==-1) return -1;
2839 if (parse_identifier(filter,CUS ":copy")==1)
2841 if (!filter->require_copy)
2843 filter->errmsg=CUS "missing previous require \"copy\";";
2850 if (parse_white(filter)==-1) return -1;
2851 if ((m=parse_string(filter,&recipient))!=1)
2853 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2856 if (strchr(CCS recipient.character,'@')==(char*)0)
2858 filter->errmsg=CUS "unqualified recipient address";
2863 add_addr(generated,recipient.character,0,0,0,0);
2864 if (!copy) filter->keep = 0;
2866 if (parse_semicolon(filter)==-1) return -1;
2868 else if (parse_identifier(filter,CUS "fileinto"))
2871 fileinto-command = "fileinto" { fileinto-options } string ";"
2873 fileinto-options =) [ ":copy" ]
2876 struct String folder;
2879 unsigned long maxage, maxmessages, maxstorage;
2882 maxage = maxmessages = maxstorage = 0;
2883 if (!filter->require_fileinto)
2885 filter->errmsg=CUS "missing previous require \"fileinto\";";
2890 if (parse_white(filter)==-1) return -1;
2891 if (parse_identifier(filter,CUS ":copy")==1)
2893 if (!filter->require_copy)
2895 filter->errmsg=CUS "missing previous require \"copy\";";
2902 if (parse_white(filter)==-1) return -1;
2903 if ((m=parse_string(filter,&folder))!=1)
2905 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2908 m=0; s=folder.character;
2909 if (folder.length==0) m=1;
2910 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2913 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2918 filter->errmsg=CUS "invalid folder";
2923 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2924 if (!copy) filter->keep = 0;
2926 if (parse_semicolon(filter)==-1) return -1;
2929 else if (parse_identifier(filter,CUS "notify"))
2932 notify-command = "notify" { notify-options } <method: string> ";"
2933 notify-options = [":from" string]
2934 [":importance" <"1" / "2" / "3">]
2935 [":options" 1*(string-list / number)]
2941 struct String importance;
2942 struct String *options;
2943 struct String message;
2944 struct String method;
2945 struct Notification *already;
2946 string_item *recipient;
2947 struct String header;
2948 struct String subject;
2950 uschar *envelope_from,*envelope_to;
2951 struct String auto_submitted_value;
2952 uschar *auto_submitted_def;
2954 if (!filter->require_enotify)
2956 filter->errmsg=CUS "missing previous require \"enotify\";";
2959 from.character=(uschar*)0;
2961 importance.character=(uschar*)0;
2962 importance.length=-1;
2963 options=(struct String*)0;
2964 message.character=(uschar*)0;
2968 header.character=(uschar*)0;
2970 subject.character=(uschar*)0;
2972 body.character=(uschar*)0;
2973 envelope_from=expand_string("$sender_address");
2974 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
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)
3025 if (message.length==-1) message=subject;
3026 if (message.length==-1) expand_header(&message,&str_subject);
3027 expand_header(&auto_submitted_value,&str_auto_submitted);
3028 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3029 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3031 filter->errmsg=CUS "header string expansion failed";
3034 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3036 for (already=filter->notified; already; already=already->next)
3038 if (already->method.length==method.length
3039 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
3040 && already->importance.length==importance.length
3041 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
3042 && already->message.length==message.length
3043 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
3046 if (already==(struct Notification*)0)
3047 /* New notification, process it */
3049 struct Notification *sent;
3050 sent=store_get(sizeof(struct Notification));
3051 sent->method=method;
3052 sent->importance=importance;
3053 sent->message=message;
3054 sent->next=filter->notified;
3055 filter->notified=sent;
3056 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3058 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3060 #ifndef COMPILE_SYNTAX_CHECKER
3061 if (exec && filter_test == FTEST_NONE)
3067 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
3071 int buffer_capacity;
3073 f = fdopen(fd, "wb");
3074 for (h = header_list; h != NULL; h = h->next)
3075 if (h->type == htype_received) fprintf(f,"%s",h->text);
3076 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
3077 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3078 fprintf(f,"Auto-submitted: sieve-notify\n");
3079 if (header.length>0) fprintf(f,"%s",header.character);
3080 if (message.length==-1)
3082 message.character=US"Notification";
3083 message.length=Ustrlen(message.character);
3085 /* Allocation is larger than neccessary, but enough even for split MIME words */
3086 buffer_capacity=32+4*message.length;
3087 buffer=store_get(buffer_capacity);
3088 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3090 if (body.length>0) fprintf(f,"%s\n",body.character);
3093 (void)child_close(pid, 0);
3100 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3102 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3108 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3110 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)) filter->require_enotify=1;
3497 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3500 else if (eq_octet(check,&str_vacation,0))
3502 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3504 filter->errmsg=CUS "vacation disabled";
3507 filter->require_vacation=1;
3510 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3511 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3512 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3513 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3514 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3517 filter->errmsg=CUS "unknown capability";
3521 if (parse_semicolon(filter)==-1) return -1;
3523 if (parse_commands(filter,exec,generated)==-1) return -1;
3526 filter->errmsg=CUS "syntax error";
3533 /*************************************************
3534 * Interpret a sieve filter file *
3535 *************************************************/
3539 filter points to the entire file, read into store as a single string
3540 options controls whether various special things are allowed, and requests
3541 special actions (not currently used)
3542 sieve_vacation_directory where to store vacation "once" files
3543 useraddress string expression for :user part of address
3544 subaddress string expression for :subaddress part of address
3545 generated where to hang newly-generated addresses
3546 error where to pass back an error text
3548 Returns: FF_DELIVERED success, a significant action was taken
3549 FF_NOTDELIVERED success, no significant action
3550 FF_DEFER defer requested
3551 FF_FAIL fail requested
3552 FF_FREEZE freeze requested
3553 FF_ERROR there was a problem
3557 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3558 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3564 options = options; /* Keep picky compilers happy */
3567 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3568 sieve.filter=filter;
3570 if (vacation_directory == NULL)
3571 sieve.vacation_directory = NULL;
3574 sieve.vacation_directory=expand_string(vacation_directory);
3575 if (sieve.vacation_directory == NULL)
3577 *error = string_sprintf("failed to expand \"%s\" "
3578 "(sieve_vacation_directory): %s", vacation_directory,
3579 expand_string_message);
3584 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3585 sieve.subaddress = subaddress;
3587 #ifdef COMPILE_SYNTAX_CHECKER
3588 if (parse_start(&sieve,0,generated)==1)
3590 if (parse_start(&sieve,1,generated)==1)
3595 add_addr(generated,US"inbox",1,0,0,0);
3596 msg = string_sprintf("Implicit keep");
3601 msg = string_sprintf("No implicit keep");
3607 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3608 #ifdef COMPILE_SYNTAX_CHECKER
3612 add_addr(generated,US"inbox",1,0,0,0);
3617 #ifndef COMPILE_SYNTAX_CHECKER
3618 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3619 else debug_printf("%s\n", msg);
3622 DEBUG(D_route) debug_printf("Sieve: end of processing\n");