1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Michael Haardt 2003 - 2015
6 * Copyright (c) The Exim Maintainers 2016
7 * See the file NOTICE for conditions of use and distribution.
10 /* This code was contributed by Michael Haardt. */
13 /* Sieve mail filter. */
27 /* Define this for RFC compliant \r\n end-of-line terminators. */
28 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 /* Define this for development of the Sieve extension "encoded-character". */
32 #define ENCODED_CHARACTER
34 /* Define this for development of the Sieve extension "envelope-auth". */
37 /* Define this for development of the Sieve extension "enotify". */
40 /* Define this for the Sieve extension "subaddress". */
43 /* Define this for the Sieve extension "vacation". */
47 #define VACATION_MIN_DAYS 1
48 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
49 #define VACATION_MAX_DAYS 31
51 /* Keep this at 75 to accept only RFC compliant MIME words. */
52 /* Increase it if you want to match headers from buggy MUAs. */
53 #define MIMEWORD_LENGTH 75
64 #ifdef ENCODED_CHARACTER
65 int require_encoded_character;
68 int require_envelope_auth;
72 struct Notification *notified;
74 uschar *enotify_mailto_owner;
76 int require_subaddress;
82 uschar *vacation_directory;
83 const uschar *subaddress;
84 const uschar *useraddress;
86 int require_iascii_numeric;
89 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
90 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
92 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
96 enum RelOp { LT, LE, EQ, GE, GT, NE };
106 struct String method;
107 struct String importance;
108 struct String message;
109 struct Notification *next;
112 /* This should be a complete list of supported extensions, so that an external
113 ManageSieve (RFC 5804) program can interrogate the current Exim binary for the
114 list of extensions and provide correct information to a client.
116 We'll emit the list in the order given here; keep it alphabetically sorted, so
117 that callers don't get surprised.
119 List *MUST* end with a NULL. Which at least makes ifdef-vs-comma easier. */
121 const uschar *exim_sieve_extension_list[] = {
122 CUS"comparator-i;ascii-numeric",
124 #ifdef ENCODED_CHARACTER
125 CUS"encoded-character",
144 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
145 static int parse_test(struct Sieve *filter, int *cond, int exec);
146 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
148 static uschar str_from_c[]="From";
149 static const struct String str_from={ str_from_c, 4 };
150 static uschar str_to_c[]="To";
151 static const struct String str_to={ str_to_c, 2 };
152 static uschar str_cc_c[]="Cc";
153 static const struct String str_cc={ str_cc_c, 2 };
154 static uschar str_bcc_c[]="Bcc";
155 static const struct String str_bcc={ str_bcc_c, 3 };
156 static uschar str_auth_c[]="auth";
157 static const struct String str_auth={ str_auth_c, 4 };
158 static uschar str_sender_c[]="Sender";
159 static const struct String str_sender={ str_sender_c, 6 };
160 static uschar str_resent_from_c[]="Resent-From";
161 static const struct String str_resent_from={ str_resent_from_c, 11 };
162 static uschar str_resent_to_c[]="Resent-To";
163 static const struct String str_resent_to={ str_resent_to_c, 9 };
164 static uschar str_fileinto_c[]="fileinto";
165 static const struct String str_fileinto={ str_fileinto_c, 8 };
166 static uschar str_envelope_c[]="envelope";
167 static const struct String str_envelope={ str_envelope_c, 8 };
168 #ifdef ENCODED_CHARACTER
169 static uschar str_encoded_character_c[]="encoded-character";
170 static const struct String str_encoded_character={ str_encoded_character_c, 17 };
173 static uschar str_envelope_auth_c[]="envelope-auth";
174 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
177 static uschar str_enotify_c[]="enotify";
178 static const struct String str_enotify={ str_enotify_c, 7 };
179 static uschar str_online_c[]="online";
180 static const struct String str_online={ str_online_c, 6 };
181 static uschar str_maybe_c[]="maybe";
182 static const struct String str_maybe={ str_maybe_c, 5 };
183 static uschar str_auto_submitted_c[]="Auto-Submitted";
184 static const struct String str_auto_submitted={ str_auto_submitted_c, 14 };
187 static uschar str_subaddress_c[]="subaddress";
188 static const struct String str_subaddress={ str_subaddress_c, 10 };
191 static uschar str_vacation_c[]="vacation";
192 static const struct String str_vacation={ str_vacation_c, 8 };
193 static uschar str_subject_c[]="Subject";
194 static const struct String str_subject={ str_subject_c, 7 };
196 static uschar str_copy_c[]="copy";
197 static const struct String str_copy={ str_copy_c, 4 };
198 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
199 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
200 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
201 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
202 static uschar str_ioctet_c[]="i;octet";
203 static const struct String str_ioctet={ str_ioctet_c, 7 };
204 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
205 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
206 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
207 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
208 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
209 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
210 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
211 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
212 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
213 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
216 /*************************************************
217 * Encode to quoted-printable *
218 *************************************************/
229 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
232 const uschar *start,*end;
237 /* Two passes: one to count output allocation size, second
238 to do the encoding */
240 for (pass=0; pass<=1; ++pass)
247 dst->character=store_get(dst->length+1); /* plus one for \0 */
250 for (start=src->character,end=start+src->length; start<end; ++start)
253 if (line>=73) /* line length limit */
259 *new++='='; /* line split */
264 if ( (ch>='!' && ch<='<')
265 || (ch>='>' && ch<='~')
266 || ( (ch=='\t' || ch==' ')
268 && (*(start+1)!='\r' || *(start+2)!='\n') /* CRLF */
275 *new++=*start; /* copy char */
278 else if (ch=='\r' && start+1<end && *(start+1)=='\n') /* CRLF */
283 *new++='\n'; /* NL */
285 ++start; /* consume extra input char */
293 new += sprintf(CS new,"=%02X",ch);
299 *new='\0'; /* not included in length, but nice */
304 /*************************************************
305 * Check mail address for correct syntax *
306 *************************************************/
309 Check mail address for being syntactically correct.
312 filter points to the Sieve filter including its state
313 address String containing one address
316 1 Mail address is syntactically OK
320 int check_mail_address(struct Sieve *filter, const struct String *address)
322 int start, end, domain;
325 if (address->length>0)
327 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
331 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
332 address->character, error);
340 filter->errmsg=CUS "empty address";
346 /*************************************************
347 * Decode URI encoded string *
348 *************************************************/
352 str URI encoded string
355 0 Decoding successful
360 static int uri_decode(struct String *str)
364 if (str->length==0) return 0;
365 for (s=str->character,t=s,e=s+str->length; s<e; )
369 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
371 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
372 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
381 str->length=t-str->character;
386 /*************************************************
388 *************************************************/
393 mailtoURI = "mailto:" [ to ] [ headers ]
394 to = [ addr-spec *("%2C" addr-spec ) ]
395 headers = "?" header *( "&" header )
396 header = hname "=" hvalue
401 filter points to the Sieve filter including its state
402 uri URI, excluding scheme
407 1 URI is syntactically OK
413 parse_mailto_uri(struct Sieve *filter, const uschar *uri,
414 string_item **recipient, struct String *header, struct String *subject,
418 struct String to, hname;
419 struct String hvalue = {.character = NULL, .length = 0};
422 if (Ustrncmp(uri,"mailto:",7))
424 filter->errmsg=US "Unknown URI scheme";
429 if (*uri && *uri!='?')
433 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
436 gstring * g = string_catn(NULL, start, uri-start);
438 to.character = string_from_gstring(g);
440 if (uri_decode(&to)==-1)
442 filter->errmsg=US"Invalid URI encoding";
445 new=store_get(sizeof(string_item));
446 new->text=store_get(to.length+1);
447 if (to.length) memcpy(new->text,to.character,to.length);
448 new->text[to.length]='\0';
449 new->next=*recipient;
454 filter->errmsg=US"Missing addr-spec in URI";
457 if (*uri=='%') uri+=3;
466 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
469 gstring * g = string_catn(NULL, start, uri-start);
471 hname.character = string_from_gstring(g);
472 hname.length = g->ptr;
473 if (uri_decode(&hname)==-1)
475 filter->errmsg=US"Invalid URI encoding";
484 filter->errmsg=US"Missing equal after hname";
488 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
491 gstring * g = string_catn(NULL, start, uri-start);
493 hname.character = string_from_gstring(g);
494 hname.length = g->ptr;
495 if (uri_decode(&hvalue)==-1)
497 filter->errmsg=US"Invalid URI encoding";
501 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
503 new=store_get(sizeof(string_item));
504 new->text=store_get(hvalue.length+1);
505 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
506 new->text[hvalue.length]='\0';
507 new->next=*recipient;
510 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
512 else if (hname.length==7 && strcmpic(hname.character, US"subject")==0)
516 static struct String ignore[]=
522 {US"auto-submitted",14}
524 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
527 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
532 if (header->length==-1) header->length = 0;
534 g = string_catn(NULL, header->character, header->length);
535 g = string_catn(g, hname.character, hname.length);
536 g = string_catn(g, CUS ": ", 2);
537 g = string_catn(g, hvalue.character, hvalue.length);
538 g = string_catn(g, CUS "\n", 1);
540 header->character = string_from_gstring(g);
541 header->length = g->ptr;
544 if (*uri=='&') ++uri;
550 filter->errmsg=US"Syntactically invalid URI";
558 /*************************************************
559 * Octet-wise string comparison *
560 *************************************************/
564 needle UTF-8 string to search ...
565 haystack ... inside the haystack
566 match_prefix 1 to compare if needle is a prefix of haystack
568 Returns: 0 needle not found in haystack
572 static int eq_octet(const struct String *needle,
573 const struct String *haystack, int match_prefix)
581 h=haystack->character;
585 if (*n&0x80) return 0;
586 if (*h&0x80) return 0;
588 if (*n!=*h) return 0;
594 return (match_prefix ? nl==0 : nl==0 && hl==0);
598 /*************************************************
599 * ASCII case-insensitive string comparison *
600 *************************************************/
604 needle UTF-8 string to search ...
605 haystack ... inside the haystack
606 match_prefix 1 to compare if needle is a prefix of haystack
608 Returns: 0 needle not found in haystack
612 static int eq_asciicase(const struct String *needle,
613 const struct String *haystack, int match_prefix)
622 h=haystack->character;
628 if (nc&0x80) return 0;
629 if (hc&0x80) return 0;
631 /* tolower depends on the locale and only ASCII case must be insensitive */
632 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
638 return (match_prefix ? nl==0 : nl==0 && hl==0);
642 /*************************************************
643 * Glob pattern search *
644 *************************************************/
648 needle pattern to search ...
649 haystack ... inside the haystack
650 ascii_caseless ignore ASCII case
651 match_octet match octets, not UTF-8 multi-octet characters
653 Returns: 0 needle not found in haystack
658 static int eq_glob(const struct String *needle,
659 const struct String *haystack, int ascii_caseless, int match_octet)
661 const uschar *n,*h,*nend,*hend;
665 h=haystack->character;
666 nend=n+needle->length;
667 hend=h+haystack->length;
677 const uschar *npart,*hpart;
679 /* Try to match a non-star part of the needle at the current */
680 /* position in the haystack. */
684 while (npart<nend && *npart!='*') switch (*npart)
688 if (hpart==hend) return 0;
693 /* Match one UTF8 encoded character */
694 if ((*hpart&0xc0)==0xc0)
697 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
708 if (npart==nend) return -1;
713 if (hpart==hend) return 0;
714 /* tolower depends on the locale, but we need ASCII */
718 (*hpart&0x80) || (*npart&0x80) ||
721 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
726 /* string match after a star failed, advance and try again */
740 /* at this point, a part was matched successfully */
741 if (may_advance && npart==nend && hpart<hend)
742 /* needle ends, but haystack does not: if there was a star before, advance and try again */
752 return (h==hend ? 1 : may_advance);
756 /*************************************************
757 * ASCII numeric comparison *
758 *************************************************/
762 a first numeric string
763 b second numeric string
764 relop relational operator
766 Returns: 0 not (a relop b)
770 static int eq_asciinumeric(const struct String *a,
771 const struct String *b, enum RelOp relop)
774 const uschar *as,*aend,*bs,*bend;
778 aend=a->character+a->length;
780 bend=b->character+b->length;
782 while (*as>='0' && *as<='9' && as<aend) ++as;
784 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
787 if (al && bl==0) cmp=-1;
788 else if (al==0 && bl==0) cmp=0;
789 else if (al==0 && bl) cmp=1;
793 if (cmp==0) cmp=memcmp(a->character,b->character,al);
797 case LT: return cmp<0;
798 case LE: return cmp<=0;
799 case EQ: return cmp==0;
800 case GE: return cmp>=0;
801 case GT: return cmp>0;
802 case NE: return cmp!=0;
809 /*************************************************
811 *************************************************/
815 filter points to the Sieve filter including its state
816 needle UTF-8 pattern or string to search ...
817 haystack ... inside the haystack
821 Returns: 0 needle not found in haystack
823 -1 comparator does not offer matchtype
826 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
827 enum Comparator co, enum MatchType mt)
831 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
832 (debug_selector & D_filter) != 0)
834 debug_printf("String comparison (match ");
837 case MATCH_IS: debug_printf(":is"); break;
838 case MATCH_CONTAINS: debug_printf(":contains"); break;
839 case MATCH_MATCHES: debug_printf(":matches"); break;
841 debug_printf(", comparison \"");
844 case COMP_OCTET: debug_printf("i;octet"); break;
845 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
846 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
848 debug_printf("\"):\n");
849 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
850 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
860 if (eq_octet(needle,haystack,0)) r=1;
863 case COMP_EN_ASCII_CASEMAP:
865 if (eq_asciicase(needle,haystack,0)) r=1;
868 case COMP_ASCII_NUMERIC:
870 if (!filter->require_iascii_numeric)
872 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
875 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
889 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
892 case COMP_EN_ASCII_CASEMAP:
894 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
899 filter->errmsg=CUS "comparator does not offer specified matchtype";
911 if ((r=eq_glob(needle,haystack,0,1))==-1)
913 filter->errmsg=CUS "syntactically invalid pattern";
918 case COMP_EN_ASCII_CASEMAP:
920 if ((r=eq_glob(needle,haystack,1,1))==-1)
922 filter->errmsg=CUS "syntactically invalid pattern";
929 filter->errmsg=CUS "comparator does not offer specified matchtype";
936 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
937 (debug_selector & D_filter) != 0)
938 debug_printf(" Result %s\n",r?"true":"false");
943 /*************************************************
944 * Check header field syntax *
945 *************************************************/
948 RFC 2822, section 3.6.8 says:
952 ftext = %d33-57 / ; Any character except
953 %d59-126 ; controls, SP, and
956 That forbids 8-bit header fields. This implementation accepts them, since
957 all of Exim is 8-bit clean, so it adds %d128-%d255.
960 header header field to quote for suitable use in Exim expansions
962 Returns: 0 string is not a valid header field
963 1 string is a value header field
966 static int is_header(const struct String *header)
976 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
987 /*************************************************
988 * Quote special characters string *
989 *************************************************/
993 header header field to quote for suitable use in Exim expansions
996 Returns: quoted string
999 static const uschar *
1000 quote(const struct String *header)
1002 gstring * quoted = NULL;
1007 h=header->character;
1013 quoted = string_catn(quoted, CUS "\\0", 2);
1018 quoted = string_catn(quoted, CUS "\\", 1);
1020 quoted = string_catn(quoted, h, 1);
1025 quoted = string_catn(quoted, CUS "", 1);
1026 return string_from_gstring(quoted);
1030 /*************************************************
1031 * Add address to list of generated addresses *
1032 *************************************************/
1035 According to RFC 5228, duplicate delivery to the same address must
1036 not happen, so the list is first searched for the address.
1039 generated list of generated addresses
1040 addr new address to add
1041 file address denotes a file
1047 add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1049 address_item *new_addr;
1051 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1052 if ( Ustrcmp(new_addr->address,addr) == 0
1054 || testflag(new_addr, af_pfr)
1055 || testflag(new_addr, af_file)
1059 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1060 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1065 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1066 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1068 new_addr = deliver_make_addr(addr,TRUE);
1071 setflag(new_addr, af_pfr);
1072 setflag(new_addr, af_file);
1075 new_addr->prop.errors_address = NULL;
1076 new_addr->next = *generated;
1077 *generated = new_addr;
1081 /*************************************************
1082 * Return decoded header field *
1083 *************************************************/
1086 Unfold the header field as described in RFC 2822 and remove all
1087 leading and trailing white space, then perform MIME decoding and
1088 translate the header field to UTF-8.
1091 value returned value of the field
1092 header name of the header field
1094 Returns: nothing The expanded string is empty
1095 in case there is no such header
1098 static void expand_header(struct String *value, const struct String *header)
1104 value->character=(uschar*)0;
1106 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1107 while (*r==' ' || *r=='\t') ++r;
1115 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1117 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1121 /*************************************************
1122 * Parse remaining hash comment *
1123 *************************************************/
1127 Comment up to terminating CRLF
1130 filter points to the Sieve filter including its state
1136 static int parse_hashcomment(struct Sieve *filter)
1142 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1144 if (*filter->pc=='\n')
1157 filter->errmsg=CUS "missing end of comment";
1162 /*************************************************
1163 * Parse remaining C-style comment *
1164 *************************************************/
1168 Everything up to star slash
1171 filter points to the Sieve filter including its state
1177 static int parse_comment(struct Sieve *filter)
1182 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1189 filter->errmsg=CUS "missing end of comment";
1194 /*************************************************
1195 * Parse optional white space *
1196 *************************************************/
1200 Spaces, tabs, CRLFs, hash comments or C-style comments
1203 filter points to the Sieve filter including its state
1209 static int parse_white(struct Sieve *filter)
1213 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1215 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1217 else if (*filter->pc=='\n')
1227 else if (*filter->pc=='#')
1229 if (parse_hashcomment(filter)==-1) return -1;
1231 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1233 if (parse_comment(filter)==-1) return -1;
1241 #ifdef ENCODED_CHARACTER
1242 /*************************************************
1243 * Decode hex-encoded-character string *
1244 *************************************************/
1247 Encoding definition:
1248 blank = SP / TAB / CRLF
1249 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1250 hex-pair = 1*2HEXDIG
1253 src points to a hex-pair-seq
1254 end points to its end
1255 dst points to the destination of the decoded octets,
1256 optionally to (uschar*)0 for checking only
1258 Returns: >=0 number of decoded octets
1262 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1266 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1271 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);
1272 if (d==0) return -1;
1275 if (src==end) return decoded;
1276 if (*src==' ' || *src=='\t' || *src=='\n')
1277 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1286 /*************************************************
1287 * Decode unicode-encoded-character string *
1288 *************************************************/
1291 Encoding definition:
1292 blank = SP / TAB / CRLF
1293 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1294 unicode-hex = 1*HEXDIG
1296 It is an error for a script to use a hexadecimal value that isn't in
1297 either the range 0 to D7FF or the range E000 to 10FFFF.
1299 At this time, strings are already scanned, thus the CRLF is converted
1300 to the internally used \n (should RFC_EOL have been used).
1303 src points to a unicode-hex-seq
1304 end points to its end
1305 dst points to the destination of the decoded octets,
1306 optionally to (uschar*)0 for checking only
1308 Returns: >=0 number of decoded octets
1310 -2 semantic error (character range violation)
1313 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1317 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1324 for (hex_seq=src; src<end && *src=='0'; ++src);
1325 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);
1326 if (src==hex_seq) return -1;
1327 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1333 else if (c>=0x80 && c<=0x7ff)
1338 *dst++=128+(c&0x3f);
1342 else if (c>=0x800 && c<=0xffff)
1347 *dst++=128+((c>>6)&0x3f);
1348 *dst++=128+(c&0x3f);
1352 else if (c>=0x10000 && c<=0x1fffff)
1357 *dst++=128+((c>>10)&0x3f);
1358 *dst++=128+((c>>6)&0x3f);
1359 *dst++=128+(c&0x3f);
1363 if (*src==' ' || *src=='\t' || *src=='\n')
1365 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1366 if (src==end) return decoded;
1375 /*************************************************
1376 * Decode encoded-character string *
1377 *************************************************/
1380 Encoding definition:
1381 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1382 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1385 encoded points to an encoded string, returns decoded string
1386 filter points to the Sieve filter including its state
1392 static int string_decode(struct Sieve *filter, struct String *data)
1394 uschar *src,*dst,*end;
1396 src=data->character;
1398 end=data->character+data->length;
1404 strncmpic(src,US "${hex:",6)==0
1405 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1406 && (hex_decode(src+6,brace,(uschar*)0))>=0
1409 dst+=hex_decode(src+6,brace,dst);
1413 strncmpic(src,US "${unicode:",10)==0
1414 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1417 switch (unicode_decode(src+10,brace,(uschar*)0))
1421 filter->errmsg=CUS "unicode character out of range";
1431 dst+=unicode_decode(src+10,brace,dst);
1438 data->length=dst-data->character;
1445 /*************************************************
1446 * Parse an optional string *
1447 *************************************************/
1451 quoted-string = DQUOTE *CHAR DQUOTE
1452 ;; in general, \ CHAR inside a string maps to CHAR
1453 ;; so \" maps to " and \\ maps to \
1454 ;; note that newlines and other characters are all allowed
1457 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1458 *(multi-line-literal / multi-line-dotstuff)
1460 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1461 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1462 ;; A line containing only "." ends the multi-line.
1463 ;; Remove a leading '.' if followed by another '.'.
1464 string = quoted-string / multi-line
1467 filter points to the Sieve filter including its state
1468 id specifies identifier to match
1472 0 identifier not matched
1475 static int parse_string(struct Sieve *filter, struct String *data)
1481 data->character = NULL;
1483 if (*filter->pc=='"') /* quoted string */
1488 if (*filter->pc=='"') /* end of string */
1494 data->character = string_from_gstring(g);
1495 data->length = g->ptr;
1498 data->character = US"\0";
1499 /* that way, there will be at least one character allocated */
1501 #ifdef ENCODED_CHARACTER
1502 if (filter->require_encoded_character
1503 && string_decode(filter,data)==-1)
1508 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1510 g = string_catn(g, filter->pc+1, 1);
1513 else /* regular character */
1516 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1518 if (*filter->pc=='\n')
1520 g = string_catn(g, US"\r", 1);
1524 g = string_catn(g, filter->pc, 1);
1528 filter->errmsg=CUS "missing end of string";
1531 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1534 /* skip optional white space followed by hashed comment or CRLF */
1535 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1536 if (*filter->pc=='#')
1538 if (parse_hashcomment(filter)==-1) return -1;
1541 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1543 else if (*filter->pc=='\n')
1555 filter->errmsg=CUS "syntax error";
1561 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1563 if (*filter->pc=='\n') /* end of line */
1566 g = string_catn(g, CUS "\r\n", 2);
1574 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1576 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1581 data->character = string_from_gstring(g);
1582 data->length = g->ptr;
1585 data->character = US"\0";
1586 /* that way, there will be at least one character allocated */
1594 #ifdef ENCODED_CHARACTER
1595 if (filter->require_encoded_character
1596 && string_decode(filter,data)==-1)
1601 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1603 g = string_catn(g, CUS ".", 1);
1607 else /* regular character */
1609 g = string_catn(g, filter->pc, 1);
1613 filter->errmsg=CUS "missing end of multi line string";
1620 /*************************************************
1621 * Parse a specific identifier *
1622 *************************************************/
1626 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1629 filter points to the Sieve filter including its state
1630 id specifies identifier to match
1633 0 identifier not matched
1636 static int parse_identifier(struct Sieve *filter, const uschar *id)
1638 size_t idlen=Ustrlen(id);
1640 if (strncmpic(US filter->pc,US id,idlen)==0)
1642 uschar next=filter->pc[idlen];
1644 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1652 /*************************************************
1654 *************************************************/
1658 number = 1*DIGIT [QUANTIFIER]
1659 QUANTIFIER = "K" / "M" / "G"
1662 filter points to the Sieve filter including its state
1666 -1 no string list found
1669 static int parse_number(struct Sieve *filter, unsigned long *data)
1673 if (*filter->pc>='0' && *filter->pc<='9')
1678 d=Ustrtoul(filter->pc,&e,10);
1681 filter->errmsg=CUstrerror(ERANGE);
1686 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1687 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1688 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1689 if (d>(ULONG_MAX/u))
1691 filter->errmsg=CUstrerror(ERANGE);
1700 filter->errmsg=CUS "missing number";
1706 /*************************************************
1707 * Parse a string list *
1708 *************************************************/
1712 string-list = "[" string *("," string) "]" / string
1715 filter points to the Sieve filter including its state
1716 data returns string list
1719 -1 no string list found
1723 parse_stringlist(struct Sieve *filter, struct String **data)
1725 const uschar *orig=filter->pc;
1726 int dataCapacity = 0;
1728 struct String *d = NULL;
1731 if (*filter->pc=='[') /* string list */
1736 if (parse_white(filter)==-1) goto error;
1737 if (dataLength+1 >= dataCapacity) /* increase buffer */
1740 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1742 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1743 new = store_get(sizeof(struct String) * dataCapacity);
1745 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1749 m=parse_string(filter,&d[dataLength]);
1752 if (dataLength==0) break;
1755 filter->errmsg=CUS "missing string";
1759 else if (m==-1) goto error;
1761 if (parse_white(filter)==-1) goto error;
1762 if (*filter->pc==',') ++filter->pc;
1765 if (*filter->pc==']')
1767 d[dataLength].character=(uschar*)0;
1768 d[dataLength].length=-1;
1775 filter->errmsg=CUS "missing closing bracket";
1779 else /* single string */
1781 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1785 m=parse_string(filter,&d[0]);
1797 d[1].character=(uschar*)0;
1804 filter->errmsg=CUS "missing string list";
1809 /*************************************************
1810 * Parse an optional address part specifier *
1811 *************************************************/
1815 address-part = ":localpart" / ":domain" / ":all"
1816 address-part =/ ":user" / ":detail"
1819 filter points to the Sieve filter including its state
1820 a returns address part specified
1823 0 no comparator found
1827 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1830 if (parse_identifier(filter,CUS ":user")==1)
1832 if (!filter->require_subaddress)
1834 filter->errmsg=CUS "missing previous require \"subaddress\";";
1840 else if (parse_identifier(filter,CUS ":detail")==1)
1842 if (!filter->require_subaddress)
1844 filter->errmsg=CUS "missing previous require \"subaddress\";";
1852 if (parse_identifier(filter,CUS ":localpart")==1)
1854 *a=ADDRPART_LOCALPART;
1857 else if (parse_identifier(filter,CUS ":domain")==1)
1862 else if (parse_identifier(filter,CUS ":all")==1)
1871 /*************************************************
1872 * Parse an optional comparator *
1873 *************************************************/
1877 comparator = ":comparator" <comparator-name: string>
1880 filter points to the Sieve filter including its state
1881 c returns comparator
1884 0 no comparator found
1885 -1 incomplete comparator found
1888 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1890 struct String comparator_name;
1892 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1893 if (parse_white(filter)==-1) return -1;
1894 switch (parse_string(filter,&comparator_name))
1899 filter->errmsg=CUS "missing comparator";
1906 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1911 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1913 *c=COMP_EN_ASCII_CASEMAP;
1916 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1918 *c=COMP_EN_ASCII_CASEMAP;
1921 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1923 *c=COMP_ASCII_NUMERIC;
1928 filter->errmsg=CUS "invalid comparator";
1937 /*************************************************
1938 * Parse an optional match type *
1939 *************************************************/
1943 match-type = ":is" / ":contains" / ":matches"
1946 filter points to the Sieve filter including its state
1947 m returns match type
1950 0 no match type found
1953 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1955 if (parse_identifier(filter,CUS ":is")==1)
1960 else if (parse_identifier(filter,CUS ":contains")==1)
1965 else if (parse_identifier(filter,CUS ":matches")==1)
1974 /*************************************************
1975 * Parse and interpret an optional test list *
1976 *************************************************/
1980 test-list = "(" test *("," test) ")"
1983 filter points to the Sieve filter including its state
1984 n total number of tests
1985 num_true number of passed tests
1986 exec Execute parsed statements
1989 0 no test list found
1990 -1 syntax or execution error
1993 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1995 if (parse_white(filter)==-1) return -1;
1996 if (*filter->pc=='(')
2005 switch (parse_test(filter,&cond,exec))
2008 case 0: filter->errmsg=CUS "missing test"; return -1;
2009 default: ++*n; if (cond) ++*num_true; break;
2011 if (parse_white(filter)==-1) return -1;
2012 if (*filter->pc==',') ++filter->pc;
2015 if (*filter->pc==')')
2022 filter->errmsg=CUS "missing closing paren";
2030 /*************************************************
2031 * Parse and interpret an optional test *
2032 *************************************************/
2036 filter points to the Sieve filter including its state
2037 cond returned condition status
2038 exec Execute parsed statements
2042 -1 syntax or execution error
2045 static int parse_test(struct Sieve *filter, int *cond, int exec)
2047 if (parse_white(filter)==-1) return -1;
2048 if (parse_identifier(filter,CUS "address"))
2051 address-test = "address" { [address-part] [comparator] [match-type] }
2052 <header-list: string-list> <key-list: string-list>
2054 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2057 enum AddressPart addressPart=ADDRPART_ALL;
2058 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2059 enum MatchType matchType=MATCH_IS;
2060 struct String *hdr,*h,*key,*k;
2066 if (parse_white(filter)==-1) return -1;
2067 if ((m=parse_addresspart(filter,&addressPart))!=0)
2069 if (m==-1) return -1;
2072 filter->errmsg=CUS "address part already specified";
2077 else if ((m=parse_comparator(filter,&comparator))!=0)
2079 if (m==-1) return -1;
2082 filter->errmsg=CUS "comparator already specified";
2087 else if ((m=parse_matchtype(filter,&matchType))!=0)
2089 if (m==-1) return -1;
2092 filter->errmsg=CUS "match type already specified";
2099 if (parse_white(filter)==-1) return -1;
2100 if ((m=parse_stringlist(filter,&hdr))!=1)
2102 if (m==0) filter->errmsg=CUS "header string list expected";
2105 if (parse_white(filter)==-1) return -1;
2106 if ((m=parse_stringlist(filter,&key))!=1)
2108 if (m==0) filter->errmsg=CUS "key string list expected";
2112 for (h=hdr; h->length!=-1 && !*cond; ++h)
2114 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2118 !eq_asciicase(h,&str_from,0)
2119 && !eq_asciicase(h,&str_to,0)
2120 && !eq_asciicase(h,&str_cc,0)
2121 && !eq_asciicase(h,&str_bcc,0)
2122 && !eq_asciicase(h,&str_sender,0)
2123 && !eq_asciicase(h,&str_resent_from,0)
2124 && !eq_asciicase(h,&str_resent_to,0)
2127 filter->errmsg=CUS "invalid header field";
2132 /* We are only interested in addresses below, so no MIME decoding */
2133 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2134 if (header_value == NULL)
2136 filter->errmsg=CUS "header string expansion failed";
2139 parse_allow_group = TRUE;
2140 while (*header_value && !*cond)
2143 int start, end, domain;
2147 end_addr = parse_find_address_end(header_value, FALSE);
2148 saveend = *end_addr;
2150 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2152 if (extracted_addr) switch (addressPart)
2154 case ADDRPART_ALL: part=extracted_addr; break;
2158 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2159 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2161 case ADDRPART_DETAIL: part=NULL; break;
2165 *end_addr = saveend;
2168 for (k=key; k->length!=-1; ++k)
2170 struct String partStr;
2172 partStr.character=part;
2173 partStr.length=Ustrlen(part);
2176 *cond=compare(filter,k,&partStr,comparator,matchType);
2177 if (*cond==-1) return -1;
2182 if (saveend == 0) break;
2183 header_value = end_addr + 1;
2185 parse_allow_group = FALSE;
2186 parse_found_group = FALSE;
2191 else if (parse_identifier(filter,CUS "allof"))
2194 allof-test = "allof" <tests: test-list>
2199 switch (parse_testlist(filter,&n,&num_true,exec))
2202 case 0: filter->errmsg=CUS "missing test list"; return -1;
2203 default: *cond=(n==num_true); return 1;
2206 else if (parse_identifier(filter,CUS "anyof"))
2209 anyof-test = "anyof" <tests: test-list>
2214 switch (parse_testlist(filter,&n,&num_true,exec))
2217 case 0: filter->errmsg=CUS "missing test list"; return -1;
2218 default: *cond=(num_true>0); return 1;
2221 else if (parse_identifier(filter,CUS "exists"))
2224 exists-test = "exists" <header-names: string-list>
2227 struct String *hdr,*h;
2230 if (parse_white(filter)==-1) return -1;
2231 if ((m=parse_stringlist(filter,&hdr))!=1)
2233 if (m==0) filter->errmsg=CUS "header string list expected";
2239 for (h=hdr; h->length!=-1 && *cond; ++h)
2243 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2244 if (header_def == NULL)
2246 filter->errmsg=CUS "header string expansion failed";
2249 if (Ustrcmp(header_def,"false")==0) *cond=0;
2254 else if (parse_identifier(filter,CUS "false"))
2257 false-test = "false"
2263 else if (parse_identifier(filter,CUS "header"))
2266 header-test = "header" { [comparator] [match-type] }
2267 <header-names: string-list> <key-list: string-list>
2270 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2271 enum MatchType matchType=MATCH_IS;
2272 struct String *hdr,*h,*key,*k;
2278 if (parse_white(filter)==-1) return -1;
2279 if ((m=parse_comparator(filter,&comparator))!=0)
2281 if (m==-1) return -1;
2284 filter->errmsg=CUS "comparator already specified";
2289 else if ((m=parse_matchtype(filter,&matchType))!=0)
2291 if (m==-1) return -1;
2294 filter->errmsg=CUS "match type already specified";
2301 if (parse_white(filter)==-1) return -1;
2302 if ((m=parse_stringlist(filter,&hdr))!=1)
2304 if (m==0) filter->errmsg=CUS "header string list expected";
2307 if (parse_white(filter)==-1) return -1;
2308 if ((m=parse_stringlist(filter,&key))!=1)
2310 if (m==0) filter->errmsg=CUS "key string list expected";
2314 for (h=hdr; h->length!=-1 && !*cond; ++h)
2318 filter->errmsg=CUS "invalid header field";
2323 struct String header_value;
2326 expand_header(&header_value,h);
2327 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2328 if (header_value.character == NULL || header_def == NULL)
2330 filter->errmsg=CUS "header string expansion failed";
2333 for (k=key; k->length!=-1; ++k)
2335 if (Ustrcmp(header_def,"true")==0)
2337 *cond=compare(filter,k,&header_value,comparator,matchType);
2338 if (*cond==-1) return -1;
2346 else if (parse_identifier(filter,CUS "not"))
2348 if (parse_white(filter)==-1) return -1;
2349 switch (parse_test(filter,cond,exec))
2352 case 0: filter->errmsg=CUS "missing test"; return -1;
2353 default: *cond=!*cond; return 1;
2356 else if (parse_identifier(filter,CUS "size"))
2359 relop = ":over" / ":under"
2360 size-test = "size" relop <limit: number>
2363 unsigned long limit;
2366 if (parse_white(filter)==-1) return -1;
2367 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2368 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2371 filter->errmsg=CUS "missing :over or :under";
2374 if (parse_white(filter)==-1) return -1;
2375 if (parse_number(filter,&limit)==-1) return -1;
2376 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2379 else if (parse_identifier(filter,CUS "true"))
2384 else if (parse_identifier(filter,CUS "envelope"))
2387 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2388 <envelope-part: string-list> <key-list: string-list>
2390 envelope-part is case insensitive "from" or "to"
2391 #ifdef ENVELOPE_AUTH
2392 envelope-part =/ "auth"
2396 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2397 enum AddressPart addressPart=ADDRPART_ALL;
2398 enum MatchType matchType=MATCH_IS;
2399 struct String *env,*e,*key,*k;
2403 if (!filter->require_envelope)
2405 filter->errmsg=CUS "missing previous require \"envelope\";";
2410 if (parse_white(filter)==-1) return -1;
2411 if ((m=parse_comparator(filter,&comparator))!=0)
2413 if (m==-1) return -1;
2416 filter->errmsg=CUS "comparator already specified";
2421 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2423 if (m==-1) return -1;
2426 filter->errmsg=CUS "address part already specified";
2431 else if ((m=parse_matchtype(filter,&matchType))!=0)
2433 if (m==-1) return -1;
2436 filter->errmsg=CUS "match type already specified";
2443 if (parse_white(filter)==-1) return -1;
2444 if ((m=parse_stringlist(filter,&env))!=1)
2446 if (m==0) filter->errmsg=CUS "envelope string list expected";
2449 if (parse_white(filter)==-1) return -1;
2450 if ((m=parse_stringlist(filter,&key))!=1)
2452 if (m==0) filter->errmsg=CUS "key string list expected";
2456 for (e=env; e->length!=-1 && !*cond; ++e)
2458 const uschar *envelopeExpr=CUS 0;
2459 uschar *envelope=US 0;
2461 if (eq_asciicase(e,&str_from,0))
2463 switch (addressPart)
2465 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2469 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2470 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2472 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2476 else if (eq_asciicase(e,&str_to,0))
2478 switch (addressPart)
2480 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2482 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2483 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2485 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2486 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2489 #ifdef ENVELOPE_AUTH
2490 else if (eq_asciicase(e,&str_auth,0))
2492 switch (addressPart)
2494 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2498 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2499 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2501 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2508 filter->errmsg=CUS "invalid envelope string";
2511 if (exec && envelopeExpr)
2513 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2515 filter->errmsg=CUS "header string expansion failed";
2518 for (k=key; k->length!=-1; ++k)
2520 struct String envelopeStr;
2522 envelopeStr.character=envelope;
2523 envelopeStr.length=Ustrlen(envelope);
2524 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2525 if (*cond==-1) return -1;
2533 else if (parse_identifier(filter,CUS "valid_notify_method"))
2536 valid_notify_method = "valid_notify_method"
2537 <notification-uris: string-list>
2540 struct String *uris,*u;
2543 if (!filter->require_enotify)
2545 filter->errmsg=CUS "missing previous require \"enotify\";";
2548 if (parse_white(filter)==-1) return -1;
2549 if ((m=parse_stringlist(filter,&uris))!=1)
2551 if (m==0) filter->errmsg=CUS "URI string list expected";
2557 for (u=uris; u->length!=-1 && *cond; ++u)
2559 string_item *recipient;
2560 struct String header,subject,body;
2564 header.character=(uschar*)0;
2566 subject.character=(uschar*)0;
2568 body.character=(uschar*)0;
2569 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2575 else if (parse_identifier(filter,CUS "notify_method_capability"))
2578 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2579 <notification-uri: string>
2580 <notification-capability: string>
2581 <key-list: string-list>
2587 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2588 enum MatchType matchType=MATCH_IS;
2589 struct String uri,capa,*keys,*k;
2591 if (!filter->require_enotify)
2593 filter->errmsg=CUS "missing previous require \"enotify\";";
2598 if (parse_white(filter)==-1) return -1;
2599 if ((m=parse_comparator(filter,&comparator))!=0)
2601 if (m==-1) return -1;
2604 filter->errmsg=CUS "comparator already specified";
2609 else if ((m=parse_matchtype(filter,&matchType))!=0)
2611 if (m==-1) return -1;
2614 filter->errmsg=CUS "match type already specified";
2621 if ((m=parse_string(filter,&uri))!=1)
2623 if (m==0) filter->errmsg=CUS "missing notification URI string";
2626 if (parse_white(filter)==-1) return -1;
2627 if ((m=parse_string(filter,&capa))!=1)
2629 if (m==0) filter->errmsg=CUS "missing notification capability string";
2632 if (parse_white(filter)==-1) return -1;
2633 if ((m=parse_stringlist(filter,&keys))!=1)
2635 if (m==0) filter->errmsg=CUS "missing key string list";
2640 string_item *recipient;
2641 struct String header,subject,body;
2646 header.character=(uschar*)0;
2648 subject.character=(uschar*)0;
2650 body.character=(uschar*)0;
2651 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2653 if (eq_asciicase(&capa,&str_online,0)==1)
2654 for (k=keys; k->length!=-1; ++k)
2656 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2657 if (*cond==-1) return -1;
2669 /*************************************************
2670 * Parse and interpret an optional block *
2671 *************************************************/
2675 filter points to the Sieve filter including its state
2676 exec Execute parsed statements
2677 generated where to hang newly-generated addresses
2679 Returns: 2 success by stop
2681 0 no block command found
2682 -1 syntax or execution error
2685 static int parse_block(struct Sieve *filter, int exec,
2686 address_item **generated)
2690 if (parse_white(filter)==-1) return -1;
2691 if (*filter->pc=='{')
2694 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2695 if (*filter->pc=='}')
2702 filter->errmsg=CUS "expecting command or closing brace";
2710 /*************************************************
2711 * Match a semicolon *
2712 *************************************************/
2716 filter points to the Sieve filter including its state
2722 static int parse_semicolon(struct Sieve *filter)
2724 if (parse_white(filter)==-1) return -1;
2725 if (*filter->pc==';')
2732 filter->errmsg=CUS "missing semicolon";
2738 /*************************************************
2739 * Parse and interpret a Sieve command *
2740 *************************************************/
2744 filter points to the Sieve filter including its state
2745 exec Execute parsed statements
2746 generated where to hang newly-generated addresses
2748 Returns: 2 success by stop
2750 -1 syntax or execution error
2753 parse_commands(struct Sieve *filter, int exec, address_item **generated)
2757 if (parse_white(filter)==-1) return -1;
2758 if (parse_identifier(filter,CUS "if"))
2761 if-command = "if" test block *( "elsif" test block ) [ else block ]
2764 int cond,m,unsuccessful;
2767 if (parse_white(filter)==-1) return -1;
2768 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2771 filter->errmsg=CUS "missing test";
2774 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2775 (debug_selector & D_filter) != 0)
2777 if (exec) debug_printf("if %s\n",cond?"true":"false");
2779 m=parse_block(filter,exec ? cond : 0, generated);
2780 if (m==-1 || m==2) return m;
2783 filter->errmsg=CUS "missing block";
2786 unsuccessful = !cond;
2787 for (;;) /* elsif test block */
2789 if (parse_white(filter)==-1) return -1;
2790 if (parse_identifier(filter,CUS "elsif"))
2792 if (parse_white(filter)==-1) return -1;
2793 m=parse_test(filter,&cond,exec && unsuccessful);
2794 if (m==-1 || m==2) return m;
2797 filter->errmsg=CUS "missing test";
2800 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2801 (debug_selector & D_filter) != 0)
2803 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2805 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2806 if (m==-1 || m==2) return m;
2809 filter->errmsg=CUS "missing block";
2812 if (exec && unsuccessful && cond) unsuccessful = 0;
2817 if (parse_white(filter)==-1) return -1;
2818 if (parse_identifier(filter,CUS "else"))
2820 m=parse_block(filter,exec && unsuccessful, generated);
2821 if (m==-1 || m==2) return m;
2824 filter->errmsg=CUS "missing block";
2829 else if (parse_identifier(filter,CUS "stop"))
2832 stop-command = "stop" { stop-options } ";"
2836 if (parse_semicolon(filter)==-1) return -1;
2839 filter->pc+=Ustrlen(filter->pc);
2843 else if (parse_identifier(filter,CUS "keep"))
2846 keep-command = "keep" { keep-options } ";"
2850 if (parse_semicolon(filter)==-1) return -1;
2853 add_addr(generated,US"inbox",1,0,0,0);
2857 else if (parse_identifier(filter,CUS "discard"))
2860 discard-command = "discard" { discard-options } ";"
2864 if (parse_semicolon(filter)==-1) return -1;
2865 if (exec) filter->keep=0;
2867 else if (parse_identifier(filter,CUS "redirect"))
2870 redirect-command = "redirect" redirect-options "string" ";"
2872 redirect-options =) ":copy"
2875 struct String recipient;
2881 if (parse_white(filter)==-1) return -1;
2882 if (parse_identifier(filter,CUS ":copy")==1)
2884 if (!filter->require_copy)
2886 filter->errmsg=CUS "missing previous require \"copy\";";
2893 if (parse_white(filter)==-1) return -1;
2894 if ((m=parse_string(filter,&recipient))!=1)
2896 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2899 if (strchr(CCS recipient.character,'@')==(char*)0)
2901 filter->errmsg=CUS "unqualified recipient address";
2906 add_addr(generated,recipient.character,0,0,0,0);
2907 if (!copy) filter->keep = 0;
2909 if (parse_semicolon(filter)==-1) return -1;
2911 else if (parse_identifier(filter,CUS "fileinto"))
2914 fileinto-command = "fileinto" { fileinto-options } string ";"
2916 fileinto-options =) [ ":copy" ]
2919 struct String folder;
2922 unsigned long maxage, maxmessages, maxstorage;
2925 maxage = maxmessages = maxstorage = 0;
2926 if (!filter->require_fileinto)
2928 filter->errmsg=CUS "missing previous require \"fileinto\";";
2933 if (parse_white(filter)==-1) return -1;
2934 if (parse_identifier(filter,CUS ":copy")==1)
2936 if (!filter->require_copy)
2938 filter->errmsg=CUS "missing previous require \"copy\";";
2945 if (parse_white(filter)==-1) return -1;
2946 if ((m=parse_string(filter,&folder))!=1)
2948 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2951 m=0; s=folder.character;
2952 if (folder.length==0) m=1;
2953 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2956 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2961 filter->errmsg=CUS "invalid folder";
2966 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2967 if (!copy) filter->keep = 0;
2969 if (parse_semicolon(filter)==-1) return -1;
2972 else if (parse_identifier(filter,CUS "notify"))
2975 notify-command = "notify" { notify-options } <method: string> ";"
2976 notify-options = [":from" string]
2977 [":importance" <"1" / "2" / "3">]
2978 [":options" 1*(string-list / number)]
2984 struct String importance;
2985 struct String message;
2986 struct String method;
2987 struct Notification *already;
2988 string_item *recipient;
2989 struct String header;
2990 struct String subject;
2992 uschar *envelope_from;
2993 struct String auto_submitted_value;
2994 uschar *auto_submitted_def;
2996 if (!filter->require_enotify)
2998 filter->errmsg=CUS "missing previous require \"enotify\";";
3001 from.character=(uschar*)0;
3003 importance.character=(uschar*)0;
3004 importance.length=-1;
3005 message.character=(uschar*)0;
3009 header.character=(uschar*)0;
3011 subject.character=(uschar*)0;
3013 body.character=(uschar*)0;
3014 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
3017 if (parse_white(filter)==-1) return -1;
3018 if (parse_identifier(filter,CUS ":from")==1)
3020 if (parse_white(filter)==-1) return -1;
3021 if ((m=parse_string(filter,&from))!=1)
3023 if (m==0) filter->errmsg=CUS "from string expected";
3027 else if (parse_identifier(filter,CUS ":importance")==1)
3029 if (parse_white(filter)==-1) return -1;
3030 if ((m=parse_string(filter,&importance))!=1)
3032 if (m==0) filter->errmsg=CUS "importance string expected";
3035 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3037 filter->errmsg=CUS "invalid importance";
3041 else if (parse_identifier(filter,CUS ":options")==1)
3043 if (parse_white(filter)==-1) return -1;
3045 else if (parse_identifier(filter,CUS ":message")==1)
3047 if (parse_white(filter)==-1) return -1;
3048 if ((m=parse_string(filter,&message))!=1)
3050 if (m==0) filter->errmsg=CUS "message string expected";
3056 if (parse_white(filter)==-1) return -1;
3057 if ((m=parse_string(filter,&method))!=1)
3059 if (m==0) filter->errmsg=CUS "missing method string";
3062 if (parse_semicolon(filter)==-1) return -1;
3063 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3067 if (message.length==-1) message=subject;
3068 if (message.length==-1) expand_header(&message,&str_subject);
3069 expand_header(&auto_submitted_value,&str_auto_submitted);
3070 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3071 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3073 filter->errmsg=CUS "header string expansion failed";
3076 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3078 for (already=filter->notified; already; already=already->next)
3080 if (already->method.length==method.length
3081 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
3082 && already->importance.length==importance.length
3083 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
3084 && already->message.length==message.length
3085 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
3088 if (already==(struct Notification*)0)
3089 /* New notification, process it */
3091 struct Notification *sent;
3092 sent=store_get(sizeof(struct Notification));
3093 sent->method=method;
3094 sent->importance=importance;
3095 sent->message=message;
3096 sent->next=filter->notified;
3097 filter->notified=sent;
3098 #ifndef COMPILE_SYNTAX_CHECKER
3099 if (filter_test == FTEST_NONE)
3104 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
3108 int buffer_capacity;
3110 f = fdopen(fd, "wb");
3111 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
3112 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3113 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
3114 if (header.length>0) fprintf(f,"%s",header.character);
3115 if (message.length==-1)
3117 message.character=US"Notification";
3118 message.length=Ustrlen(message.character);
3120 /* Allocation is larger than necessary, but enough even for split MIME words */
3121 buffer_capacity=32+4*message.length;
3122 buffer=store_get(buffer_capacity);
3123 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3125 if (body.length>0) fprintf(f,"%s\n",body.character);
3128 (void)child_close(pid, 0);
3131 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3133 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3139 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3141 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3147 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3149 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3156 else if (parse_identifier(filter,CUS "vacation"))
3159 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3160 vacation-options = [":days" number]
3163 [":addresses" string-list]
3170 struct String subject;
3172 struct String *addresses;
3174 string_item *aliases;
3175 struct String handle;
3176 struct String reason;
3178 if (!filter->require_vacation)
3180 filter->errmsg=CUS "missing previous require \"vacation\";";
3185 if (filter->vacation_ran)
3187 filter->errmsg=CUS "trying to execute vacation more than once";
3190 filter->vacation_ran=1;
3192 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3193 subject.character=(uschar*)0;
3195 from.character=(uschar*)0;
3197 addresses=(struct String*)0;
3200 handle.character=(uschar*)0;
3204 if (parse_white(filter)==-1) return -1;
3205 if (parse_identifier(filter,CUS ":days")==1)
3207 if (parse_white(filter)==-1) return -1;
3208 if (parse_number(filter,&days)==-1) return -1;
3209 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3210 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3212 else if (parse_identifier(filter,CUS ":subject")==1)
3214 if (parse_white(filter)==-1) return -1;
3215 if ((m=parse_string(filter,&subject))!=1)
3217 if (m==0) filter->errmsg=CUS "subject string expected";
3221 else if (parse_identifier(filter,CUS ":from")==1)
3223 if (parse_white(filter)==-1) return -1;
3224 if ((m=parse_string(filter,&from))!=1)
3226 if (m==0) filter->errmsg=CUS "from string expected";
3229 if (check_mail_address(filter,&from)!=1)
3232 else if (parse_identifier(filter,CUS ":addresses")==1)
3236 if (parse_white(filter)==-1) return -1;
3237 if ((m=parse_stringlist(filter,&addresses))!=1)
3239 if (m==0) filter->errmsg=CUS "addresses string list expected";
3242 for (a=addresses; a->length!=-1; ++a)
3246 new=store_get(sizeof(string_item));
3247 new->text=store_get(a->length+1);
3248 if (a->length) memcpy(new->text,a->character,a->length);
3249 new->text[a->length]='\0';
3254 else if (parse_identifier(filter,CUS ":mime")==1)
3256 else if (parse_identifier(filter,CUS ":handle")==1)
3258 if (parse_white(filter)==-1) return -1;
3259 if ((m=parse_string(filter,&from))!=1)
3261 if (m==0) filter->errmsg=CUS "handle string expected";
3267 if (parse_white(filter)==-1) return -1;
3268 if ((m=parse_string(filter,&reason))!=1)
3270 if (m==0) filter->errmsg=CUS "missing reason string";
3277 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3280 filter->errmsg=CUS "MIME reason string contains 8bit text";
3284 if (parse_semicolon(filter)==-1) return -1;
3291 int buffer_capacity;
3294 uschar hexdigest[33];
3298 if (filter_personal(aliases,TRUE))
3300 if (filter_test == FTEST_NONE)
3302 /* ensure oncelog directory exists; failure will be detected later */
3304 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3306 /* build oncelog filename */
3310 if (handle.length==-1)
3312 gstring * key = NULL;
3313 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3314 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3315 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3316 key = string_catn(key, reason.character, reason.length);
3317 md5_end(&base, key->s, key->ptr, digest);
3320 md5_end(&base, handle.character, handle.length, digest);
3322 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3324 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3325 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3327 if (filter_test == FTEST_NONE)
3329 once = string_cat (NULL, filter->vacation_directory);
3330 once = string_catn(once, US"/", 1);
3331 once = string_catn(once, hexdigest, 33);
3333 /* process subject */
3335 if (subject.length==-1)
3337 uschar *subject_def;
3339 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3340 if (Ustrcmp(subject_def,"true")==0)
3342 gstring * g = string_catn(NULL, US"Auto: ", 6);
3344 expand_header(&subject,&str_subject);
3345 g = string_catn(g, subject.character, subject.length);
3346 subject.character = string_from_gstring(g);
3347 subject.length = g->ptr;
3351 subject.character=US"Automated reply";
3352 subject.length=Ustrlen(subject.character);
3356 /* add address to list of generated addresses */
3358 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3359 setflag(addr, af_pfr);
3360 addr->prop.ignore_error = TRUE;
3361 addr->next = *generated;
3363 addr->reply = store_get(sizeof(reply_item));
3364 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3365 addr->reply->to = string_copy(sender_address);
3366 if (from.length==-1)
3367 addr->reply->from = expand_string(US"$local_part@$domain");
3369 addr->reply->from = from.character;
3370 /* Allocation is larger than necessary, but enough even for split MIME words */
3371 buffer_capacity=32+4*subject.length;
3372 buffer=store_get(buffer_capacity);
3373 /* deconst cast safe as we pass in a non-const item */
3374 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3375 addr->reply->oncelog = string_from_gstring(once);
3376 addr->reply->once_repeat=days*86400;
3378 /* build body and MIME headers */
3382 uschar *mime_body,*reason_end;
3383 static const uschar nlnl[]="\r\n\r\n";
3387 mime_body=reason.character,reason_end=reason.character+reason.length;
3388 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3392 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3394 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3395 else mime_body=reason_end-1;
3396 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
3400 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
3402 start = reason.length;
3403 addr->reply->headers = US"MIME-Version: 1.0\n"
3404 "Content-Type: text/plain;\n"
3405 "\tcharset=\"utf-8\"\n"
3406 "Content-Transfer-Encoding: quoted-printable";
3407 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3411 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3412 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3422 /*************************************************
3423 * Parse and interpret a sieve filter *
3424 *************************************************/
3428 filter points to the Sieve filter including its state
3429 exec Execute parsed statements
3430 generated where to hang newly-generated addresses
3433 -1 syntax or execution error
3437 parse_start(struct Sieve *filter, int exec, address_item **generated)
3439 filter->pc=filter->filter;
3442 filter->require_envelope=0;
3443 filter->require_fileinto=0;
3444 #ifdef ENCODED_CHARACTER
3445 filter->require_encoded_character=0;
3447 #ifdef ENVELOPE_AUTH
3448 filter->require_envelope_auth=0;
3451 filter->require_enotify=0;
3452 filter->notified=(struct Notification*)0;
3455 filter->require_subaddress=0;
3458 filter->require_vacation=0;
3459 filter->vacation_ran=0;
3461 filter->require_copy=0;
3462 filter->require_iascii_numeric=0;
3464 if (parse_white(filter)==-1) return -1;
3466 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3469 struct dirent *oncelog;
3470 struct stat properties;
3473 /* clean up old vacation log databases */
3475 oncelogdir=opendir(CS filter->vacation_directory);
3477 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3479 filter->errmsg=CUS "unable to open vacation directory";
3483 if (oncelogdir != NULL)
3487 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3489 if (strlen(oncelog->d_name)==32)
3491 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3492 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3496 closedir(oncelogdir);
3500 while (parse_identifier(filter,CUS "require"))
3503 require-command = "require" <capabilities: string-list>
3506 struct String *cap,*check;
3509 if (parse_white(filter)==-1) return -1;
3510 if ((m=parse_stringlist(filter,&cap))!=1)
3512 if (m==0) filter->errmsg=CUS "capability string list expected";
3515 for (check=cap; check->character; ++check)
3517 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3518 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3519 #ifdef ENCODED_CHARACTER
3520 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3522 #ifdef ENVELOPE_AUTH
3523 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3526 else if (eq_octet(check,&str_enotify,0))
3528 if (filter->enotify_mailto_owner == NULL)
3530 filter->errmsg=CUS "enotify disabled";
3533 filter->require_enotify=1;
3537 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3540 else if (eq_octet(check,&str_vacation,0))
3542 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3544 filter->errmsg=CUS "vacation disabled";
3547 filter->require_vacation=1;
3550 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3551 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3552 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3553 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3554 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3557 filter->errmsg=CUS "unknown capability";
3561 if (parse_semicolon(filter)==-1) return -1;
3563 if (parse_commands(filter,exec,generated)==-1) return -1;
3566 filter->errmsg=CUS "syntax error";
3573 /*************************************************
3574 * Interpret a sieve filter file *
3575 *************************************************/
3579 filter points to the entire file, read into store as a single string
3580 options controls whether various special things are allowed, and requests
3581 special actions (not currently used)
3582 vacation_directory where to store vacation "once" files
3583 enotify_mailto_owner owner of mailto notifications
3584 useraddress string expression for :user part of address
3585 subaddress string expression for :subaddress part of address
3586 generated where to hang newly-generated addresses
3587 error where to pass back an error text
3589 Returns: FF_DELIVERED success, a significant action was taken
3590 FF_NOTDELIVERED success, no significant action
3591 FF_DEFER defer requested
3592 FF_FAIL fail requested
3593 FF_FREEZE freeze requested
3594 FF_ERROR there was a problem
3598 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3599 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3600 address_item **generated, uschar **error)
3606 options = options; /* Keep picky compilers happy */
3609 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3610 sieve.filter=filter;
3612 if (vacation_directory == NULL)
3613 sieve.vacation_directory = NULL;
3616 sieve.vacation_directory=expand_string(vacation_directory);
3617 if (sieve.vacation_directory == NULL)
3619 *error = string_sprintf("failed to expand \"%s\" "
3620 "(sieve_vacation_directory): %s", vacation_directory,
3621 expand_string_message);
3626 if (enotify_mailto_owner == NULL)
3627 sieve.enotify_mailto_owner = NULL;
3630 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3631 if (sieve.enotify_mailto_owner == NULL)
3633 *error = string_sprintf("failed to expand \"%s\" "
3634 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3635 expand_string_message);
3640 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3641 sieve.subaddress = subaddress;
3643 #ifdef COMPILE_SYNTAX_CHECKER
3644 if (parse_start(&sieve,0,generated)==1)
3646 if (parse_start(&sieve,1,generated)==1)
3651 add_addr(generated,US"inbox",1,0,0,0);
3652 msg = string_sprintf("Implicit keep");
3657 msg = string_sprintf("No implicit keep");
3663 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3664 #ifdef COMPILE_SYNTAX_CHECKER
3668 add_addr(generated,US"inbox",1,0,0,0);
3673 #ifndef COMPILE_SYNTAX_CHECKER
3674 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3675 else debug_printf("%s\n", msg);
3678 DEBUG(D_route) debug_printf("Sieve: end of processing\n");