1 /* $Cambridge: exim/src/src/sieve.c,v 1.34 2008/01/29 12:08:43 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;
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=(sender_address && sender_address[0]) ? expand_string("$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
2976 if (parse_white(filter)==-1) return -1;
2977 if (parse_identifier(filter,CUS ":from")==1)
2979 if (parse_white(filter)==-1) return -1;
2980 if ((m=parse_string(filter,&from))!=1)
2982 if (m==0) filter->errmsg=CUS "from string expected";
2986 else if (parse_identifier(filter,CUS ":importance")==1)
2988 if (parse_white(filter)==-1) return -1;
2989 if ((m=parse_string(filter,&importance))!=1)
2991 if (m==0) filter->errmsg=CUS "importance string expected";
2994 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2996 filter->errmsg=CUS "invalid importance";
3000 else if (parse_identifier(filter,CUS ":options")==1)
3002 if (parse_white(filter)==-1) return -1;
3004 else if (parse_identifier(filter,CUS ":message")==1)
3006 if (parse_white(filter)==-1) return -1;
3007 if ((m=parse_string(filter,&message))!=1)
3009 if (m==0) filter->errmsg=CUS "message string expected";
3015 if (parse_white(filter)==-1) return -1;
3016 if ((m=parse_string(filter,&method))!=1)
3018 if (m==0) filter->errmsg=CUS "missing method string";
3021 if (parse_semicolon(filter)==-1) return -1;
3022 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3026 if (message.length==-1) message=subject;
3027 if (message.length==-1) expand_header(&message,&str_subject);
3028 expand_header(&auto_submitted_value,&str_auto_submitted);
3029 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3030 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3032 filter->errmsg=CUS "header string expansion failed";
3035 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3037 for (already=filter->notified; already; already=already->next)
3039 if (already->method.length==method.length
3040 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
3041 && already->importance.length==importance.length
3042 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
3043 && already->message.length==message.length
3044 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
3047 if (already==(struct Notification*)0)
3048 /* New notification, process it */
3050 struct Notification *sent;
3051 sent=store_get(sizeof(struct Notification));
3052 sent->method=method;
3053 sent->importance=importance;
3054 sent->message=message;
3055 sent->next=filter->notified;
3056 filter->notified=sent;
3057 #ifndef COMPILE_SYNTAX_CHECKER
3058 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 for (h = header_list; h != NULL; h = h->next)
3072 if (h->type == htype_received) fprintf(f,"%s",h->text);
3073 fprintf(f,"From: %s\n",from.length==-1 ? expand_string("$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3074 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3075 fprintf(f,"Auto-submitted: sieve-notify\n");
3076 if (header.length>0) fprintf(f,"%s",header.character);
3077 if (message.length==-1)
3079 message.character=US"Notification";
3080 message.length=Ustrlen(message.character);
3082 /* Allocation is larger than neccessary, but enough even for split MIME words */
3083 buffer_capacity=32+4*message.length;
3084 buffer=store_get(buffer_capacity);
3085 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3087 if (body.length>0) fprintf(f,"%s\n",body.character);
3090 (void)child_close(pid, 0);
3093 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3095 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3101 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3103 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3109 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3111 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3118 else if (parse_identifier(filter,CUS "vacation"))
3121 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3122 vacation-options = [":days" number]
3125 [":addresses" string-list]
3132 struct String subject;
3134 struct String *addresses;
3136 string_item *aliases;
3137 struct String handle;
3138 struct String reason;
3140 if (!filter->require_vacation)
3142 filter->errmsg=CUS "missing previous require \"vacation\";";
3147 if (filter->vacation_ran)
3149 filter->errmsg=CUS "trying to execute vacation more than once";
3152 filter->vacation_ran=1;
3154 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3155 subject.character=(uschar*)0;
3157 from.character=(uschar*)0;
3159 addresses=(struct String*)0;
3162 handle.character=(uschar*)0;
3166 if (parse_white(filter)==-1) return -1;
3167 if (parse_identifier(filter,CUS ":days")==1)
3169 if (parse_white(filter)==-1) return -1;
3170 if (parse_number(filter,&days)==-1) return -1;
3171 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3172 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3174 else if (parse_identifier(filter,CUS ":subject")==1)
3176 if (parse_white(filter)==-1) return -1;
3177 if ((m=parse_string(filter,&subject))!=1)
3179 if (m==0) filter->errmsg=CUS "subject string expected";
3183 else if (parse_identifier(filter,CUS ":from")==1)
3185 if (parse_white(filter)==-1) return -1;
3186 if ((m=parse_string(filter,&from))!=1)
3188 if (m==0) filter->errmsg=CUS "from string expected";
3191 if (check_mail_address(filter,&from)!=1)
3194 else if (parse_identifier(filter,CUS ":addresses")==1)
3198 if (parse_white(filter)==-1) return -1;
3199 if ((m=parse_stringlist(filter,&addresses))!=1)
3201 if (m==0) filter->errmsg=CUS "addresses string list expected";
3204 for (a=addresses; a->length!=-1; ++a)
3208 new=store_get(sizeof(string_item));
3209 new->text=store_get(a->length+1);
3210 if (a->length) memcpy(new->text,a->character,a->length);
3211 new->text[a->length]='\0';
3216 else if (parse_identifier(filter,CUS ":mime")==1)
3218 else if (parse_identifier(filter,CUS ":handle")==1)
3220 if (parse_white(filter)==-1) return -1;
3221 if ((m=parse_string(filter,&from))!=1)
3223 if (m==0) filter->errmsg=CUS "handle string expected";
3229 if (parse_white(filter)==-1) return -1;
3230 if ((m=parse_string(filter,&reason))!=1)
3232 if (m==0) filter->errmsg=CUS "missing reason string";
3239 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3242 filter->errmsg=CUS "MIME reason string contains 8bit text";
3246 if (parse_semicolon(filter)==-1) return -1;
3253 int buffer_capacity;
3257 uschar hexdigest[33];
3261 if (filter_personal(aliases,TRUE))
3263 if (filter_test == FTEST_NONE)
3265 /* ensure oncelog directory exists; failure will be detected later */
3267 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3269 /* build oncelog filename */
3271 key.character=(uschar*)0;
3274 if (handle.length==-1)
3276 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3277 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3278 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3279 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3284 md5_end(&base, key.character, key.length, digest);
3285 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3286 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3288 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3290 if (filter_test == FTEST_NONE)
3292 capacity=Ustrlen(filter->vacation_directory);
3294 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3295 once=string_cat(once,&capacity,&start,hexdigest,33);
3298 /* process subject */
3300 if (subject.length==-1)
3302 uschar *subject_def;
3304 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3305 if (Ustrcmp(subject_def,"true")==0)
3307 expand_header(&subject,&str_subject);
3310 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3311 subject.length=start;
3315 subject.character=US"Automated reply";
3316 subject.length=Ustrlen(subject.character);
3320 /* add address to list of generated addresses */
3322 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3323 setflag(addr, af_pfr);
3324 setflag(addr, af_ignore_error);
3325 addr->next = *generated;
3327 addr->reply = store_get(sizeof(reply_item));
3328 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3329 addr->reply->to = string_copy(sender_address);
3330 if (from.length==-1)
3331 addr->reply->from = expand_string(US"$local_part@$domain");
3333 addr->reply->from = from.character;
3334 /* Allocation is larger than neccessary, but enough even for split MIME words */
3335 buffer_capacity=32+4*subject.length;
3336 buffer=store_get(buffer_capacity);
3337 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3338 addr->reply->oncelog=once;
3339 addr->reply->once_repeat=days*86400;
3341 /* build body and MIME headers */
3345 uschar *mime_body,*reason_end;
3346 static const uschar nlnl[]="\r\n\r\n";
3350 mime_body=reason.character,reason_end=reason.character+reason.length;
3351 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3356 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3357 addr->reply->headers[start] = '\0';
3360 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3361 else mime_body=reason_end-1;
3362 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3363 addr->reply->text[start] = '\0';
3367 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3370 start = reason.length;
3371 addr->reply->headers = US"MIME-Version: 1.0\n"
3372 "Content-Type: text/plain;\n"
3373 "\tcharset=\"utf-8\"\n"
3374 "Content-Transfer-Encoding: quoted-printable";
3375 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3379 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3381 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3392 /*************************************************
3393 * Parse and interpret a sieve filter *
3394 *************************************************/
3398 filter points to the Sieve filter including its state
3399 exec Execute parsed statements
3400 generated where to hang newly-generated addresses
3403 -1 syntax or execution error
3406 static int parse_start(struct Sieve *filter, int exec,
3407 address_item **generated)
3409 filter->pc=filter->filter;
3412 filter->require_envelope=0;
3413 filter->require_fileinto=0;
3414 #ifdef ENCODED_CHARACTER
3415 filter->require_encoded_character=0;
3417 #ifdef ENVELOPE_AUTH
3418 filter->require_envelope_auth=0;
3421 filter->require_enotify=0;
3422 filter->notified=(struct Notification*)0;
3425 filter->require_subaddress=0;
3428 filter->require_vacation=0;
3429 filter->vacation_ran=0;
3431 filter->require_copy=0;
3432 filter->require_iascii_numeric=0;
3434 if (parse_white(filter)==-1) return -1;
3436 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3439 struct dirent *oncelog;
3440 struct stat properties;
3443 /* clean up old vacation log databases */
3445 oncelogdir=opendir(CS filter->vacation_directory);
3447 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3449 filter->errmsg=CUS "unable to open vacation directory";
3453 if (oncelogdir != NULL)
3457 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3459 if (strlen(oncelog->d_name)==32)
3461 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3462 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3466 closedir(oncelogdir);
3470 while (parse_identifier(filter,CUS "require"))
3473 require-command = "require" <capabilities: string-list>
3476 struct String *cap,*check;
3479 if (parse_white(filter)==-1) return -1;
3480 if ((m=parse_stringlist(filter,&cap))!=1)
3482 if (m==0) filter->errmsg=CUS "capability string list expected";
3485 for (check=cap; check->character; ++check)
3487 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3488 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3489 #ifdef ENCODED_CHARACTER
3490 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3492 #ifdef ENVELOPE_AUTH
3493 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3496 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3499 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3502 else if (eq_octet(check,&str_vacation,0))
3504 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3506 filter->errmsg=CUS "vacation disabled";
3509 filter->require_vacation=1;
3512 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3513 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3514 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3515 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3516 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3519 filter->errmsg=CUS "unknown capability";
3523 if (parse_semicolon(filter)==-1) return -1;
3525 if (parse_commands(filter,exec,generated)==-1) return -1;
3528 filter->errmsg=CUS "syntax error";
3535 /*************************************************
3536 * Interpret a sieve filter file *
3537 *************************************************/
3541 filter points to the entire file, read into store as a single string
3542 options controls whether various special things are allowed, and requests
3543 special actions (not currently used)
3544 sieve_vacation_directory where to store vacation "once" files
3545 useraddress string expression for :user part of address
3546 subaddress string expression for :subaddress part of address
3547 generated where to hang newly-generated addresses
3548 error where to pass back an error text
3550 Returns: FF_DELIVERED success, a significant action was taken
3551 FF_NOTDELIVERED success, no significant action
3552 FF_DEFER defer requested
3553 FF_FAIL fail requested
3554 FF_FREEZE freeze requested
3555 FF_ERROR there was a problem
3559 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3560 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3566 options = options; /* Keep picky compilers happy */
3569 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3570 sieve.filter=filter;
3572 if (vacation_directory == NULL)
3573 sieve.vacation_directory = NULL;
3576 sieve.vacation_directory=expand_string(vacation_directory);
3577 if (sieve.vacation_directory == NULL)
3579 *error = string_sprintf("failed to expand \"%s\" "
3580 "(sieve_vacation_directory): %s", vacation_directory,
3581 expand_string_message);
3586 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3587 sieve.subaddress = subaddress;
3589 #ifdef COMPILE_SYNTAX_CHECKER
3590 if (parse_start(&sieve,0,generated)==1)
3592 if (parse_start(&sieve,1,generated)==1)
3597 add_addr(generated,US"inbox",1,0,0,0);
3598 msg = string_sprintf("Implicit keep");
3603 msg = string_sprintf("No implicit keep");
3609 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3610 #ifdef COMPILE_SYNTAX_CHECKER
3614 add_addr(generated,US"inbox",1,0,0,0);
3619 #ifndef COMPILE_SYNTAX_CHECKER
3620 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3621 else debug_printf("%s\n", msg);
3624 DEBUG(D_route) debug_printf("Sieve: end of processing\n");