1 /* $Cambridge: exim/src/src/sieve.c,v 1.32 2007/10/11 12:44:30 michael Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2007 */
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
617 Returns: 0 needle not found in haystack
622 static int eq_glob(const struct String *needle,
623 const struct String *haystack, int ascii_caseless, int match_octet)
625 const uschar *n,*h,*nend,*hend;
629 h=haystack->character;
630 nend=n+needle->length;
631 hend=h+haystack->length;
641 const uschar *npart,*hpart;
643 /* Try to match a non-star part of the needle at the current */
644 /* position in the haystack. */
648 while (npart<nend && *npart!='*') switch (*npart)
652 if (hpart==hend) return 0;
657 /* Match one UTF8 encoded character */
658 if ((*hpart&0xc0)==0xc0)
661 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
672 if (npart==nend) return -1;
677 if (hpart==hend) return 0;
678 /* tolower depends on the locale, but we need ASCII */
682 (*hpart&0x80) || (*npart&0x80) ||
685 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
690 /* string match after a star failed, advance and try again */
704 /* at this point, a part was matched successfully */
705 if (may_advance && npart==nend && hpart<hend)
706 /* needle ends, but haystack does not: if there was a star before, advance and try again */
716 return (h==hend ? 1 : may_advance);
720 /*************************************************
721 * ASCII numeric comparison *
722 *************************************************/
726 a first numeric string
727 b second numeric string
728 relop relational operator
730 Returns: 0 not (a relop b)
734 static int eq_asciinumeric(const struct String *a,
735 const struct String *b, enum RelOp relop)
738 const uschar *as,*aend,*bs,*bend;
742 aend=a->character+a->length;
744 bend=b->character+b->length;
746 while (*as>='0' && *as<='9' && as<aend) ++as;
748 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
751 if (al && bl==0) cmp=-1;
752 else if (al==0 && bl==0) cmp=0;
753 else if (al==0 && bl) cmp=1;
757 if (cmp==0) cmp=memcmp(a->character,b->character,al);
761 case LT: return cmp<0;
762 case LE: return cmp<=0;
763 case EQ: return cmp==0;
764 case GE: return cmp>=0;
765 case GT: return cmp>0;
766 case NE: return cmp!=0;
773 /*************************************************
775 *************************************************/
779 filter points to the Sieve filter including its state
780 needle UTF-8 pattern or string to search ...
781 haystack ... inside the haystack
785 Returns: 0 needle not found in haystack
787 -1 comparator does not offer matchtype
790 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
791 enum Comparator co, enum MatchType mt)
795 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
796 (debug_selector & D_filter) != 0)
798 debug_printf("String comparison (match ");
801 case MATCH_IS: debug_printf(":is"); break;
802 case MATCH_CONTAINS: debug_printf(":contains"); break;
803 case MATCH_MATCHES: debug_printf(":matches"); break;
805 debug_printf(", comparison \"");
808 case COMP_OCTET: debug_printf("i;octet"); break;
809 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
810 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
812 debug_printf("\"):\n");
813 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
814 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
824 if (eq_octet(needle,haystack,0)) r=1;
827 case COMP_EN_ASCII_CASEMAP:
829 if (eq_asciicase(needle,haystack,0)) r=1;
832 case COMP_ASCII_NUMERIC:
834 if (!filter->require_iascii_numeric)
836 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
839 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
853 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
856 case COMP_EN_ASCII_CASEMAP:
858 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
863 filter->errmsg=CUS "comparator does not offer specified matchtype";
875 if ((r=eq_glob(needle,haystack,0,1))==-1)
877 filter->errmsg=CUS "syntactically invalid pattern";
882 case COMP_EN_ASCII_CASEMAP:
884 if ((r=eq_glob(needle,haystack,1,1))==-1)
886 filter->errmsg=CUS "syntactically invalid pattern";
893 filter->errmsg=CUS "comparator does not offer specified matchtype";
900 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
901 (debug_selector & D_filter) != 0)
902 debug_printf(" Result %s\n",r?"true":"false");
907 /*************************************************
908 * Check header field syntax *
909 *************************************************/
912 RFC 2822, section 3.6.8 says:
916 ftext = %d33-57 / ; Any character except
917 %d59-126 ; controls, SP, and
920 That forbids 8-bit header fields. This implementation accepts them, since
921 all of Exim is 8-bit clean, so it adds %d128-%d255.
924 header header field to quote for suitable use in Exim expansions
926 Returns: 0 string is not a valid header field
927 1 string is a value header field
930 static int is_header(const struct String *header)
940 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
951 /*************************************************
952 * Quote special characters string *
953 *************************************************/
957 header header field to quote for suitable use in Exim expansions
960 Returns: quoted string
963 static const uschar *quote(const struct String *header)
978 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
985 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
989 quoted=string_cat(quoted,&size,&ptr,h,1);
995 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
1000 /*************************************************
1001 * Add address to list of generated addresses *
1002 *************************************************/
1005 According to RFC 3028, duplicate delivery to the same address must
1006 not happen, so the list is first searched for the address.
1009 generated list of generated addresses
1010 addr new address to add
1011 file address denotes a file
1016 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1018 address_item *new_addr;
1020 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1022 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1024 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1026 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1032 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1034 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1036 new_addr=deliver_make_addr(addr,TRUE);
1039 setflag(new_addr, af_pfr|af_file);
1042 new_addr->p.errors_address = NULL;
1043 new_addr->next = *generated;
1044 *generated = new_addr;
1048 /*************************************************
1049 * Return decoded header field *
1050 *************************************************/
1053 Unfold the header field as described in RFC 2822 and remove all
1054 leading and trailing white space, then perform MIME decoding and
1055 translate the header field to UTF-8.
1058 value returned value of the field
1059 header name of the header field
1061 Returns: nothing The expanded string is empty
1062 in case there is no such header
1065 static void expand_header(struct String *value, const struct String *header)
1071 value->character=(uschar*)0;
1073 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1074 while (*r==' ' || *r=='\t') ++r;
1082 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1084 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1088 /*************************************************
1089 * Parse remaining hash comment *
1090 *************************************************/
1094 Comment up to terminating CRLF
1097 filter points to the Sieve filter including its state
1103 static int parse_hashcomment(struct Sieve *filter)
1109 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1111 if (*filter->pc=='\n')
1124 filter->errmsg=CUS "missing end of comment";
1129 /*************************************************
1130 * Parse remaining C-style comment *
1131 *************************************************/
1135 Everything up to star slash
1138 filter points to the Sieve filter including its state
1144 static int parse_comment(struct Sieve *filter)
1149 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1156 filter->errmsg=CUS "missing end of comment";
1161 /*************************************************
1162 * Parse optional white space *
1163 *************************************************/
1167 Spaces, tabs, CRLFs, hash comments or C-style comments
1170 filter points to the Sieve filter including its state
1176 static int parse_white(struct Sieve *filter)
1180 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1182 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1184 else if (*filter->pc=='\n')
1194 else if (*filter->pc=='#')
1196 if (parse_hashcomment(filter)==-1) return -1;
1198 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1200 if (parse_comment(filter)==-1) return -1;
1208 #ifdef ENCODED_CHARACTER
1209 /*************************************************
1210 * Decode hex-encoded-character string *
1211 *************************************************/
1214 Encoding definition:
1215 blank = SP / TAB / CRLF
1216 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1217 hex-pair = 1*2HEXDIG
1220 src points to a hex-pair-seq
1221 end points to its end
1222 dst points to the destination of the decoded octets,
1223 optionally to (uschar*)0 for checking only
1225 Returns: >=0 number of decoded octets
1229 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1233 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1238 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);
1239 if (d==0) return -1;
1242 if (src==end) return decoded;
1243 if (*src==' ' || *src=='\t' || *src=='\n')
1244 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1253 /*************************************************
1254 * Decode unicode-encoded-character string *
1255 *************************************************/
1258 Encoding definition:
1259 blank = SP / TAB / CRLF
1260 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1261 unicode-hex = 1*HEXDIG
1263 It is an error for a script to use a hexadecimal value that isn't in
1264 either the range 0 to D7FF or the range E000 to 10FFFF.
1266 At this time, strings are already scanned, thus the CRLF is converted
1267 to the internally used \n (should RFC_EOL have been used).
1270 src points to a unicode-hex-seq
1271 end points to its end
1272 dst points to the destination of the decoded octets,
1273 optionally to (uschar*)0 for checking only
1275 Returns: >=0 number of decoded octets
1277 -2 semantic error (character range violation)
1280 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1284 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1291 for (hex_seq=src; src<end && *src=='0'; ++src);
1292 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);
1293 if (src==hex_seq) return -1;
1294 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1300 else if (c>=0x80 && c<=0x7ff)
1305 *dst++=128+(c&0x3f);
1309 else if (c>=0x800 && c<=0xffff)
1314 *dst++=128+((c>>6)&0x3f);
1315 *dst++=128+(c&0x3f);
1319 else if (c>=0x10000 && c<=0x1fffff)
1324 *dst++=128+((c>>10)&0x3f);
1325 *dst++=128+((c>>6)&0x3f);
1326 *dst++=128+(c&0x3f);
1330 if (*src==' ' || *src=='\t' || *src=='\n')
1332 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1333 if (src==end) return decoded;
1342 /*************************************************
1343 * Decode encoded-character string *
1344 *************************************************/
1347 Encoding definition:
1348 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1349 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1352 encoded points to an encoded string, returns decoded string
1353 filter points to the Sieve filter including its state
1359 static int string_decode(struct Sieve *filter, struct String *data)
1361 uschar *src,*dst,*end;
1363 src=data->character;
1365 end=data->character+data->length;
1371 strncmpic(src,US "${hex:",6)==0
1372 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1373 && (hex_decode(src+6,brace,(uschar*)0))>=0
1376 dst+=hex_decode(src+6,brace,dst);
1380 strncmpic(src,US "${unicode:",10)==0
1381 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1384 switch (unicode_decode(src+10,brace,(uschar*)0))
1388 filter->errmsg=CUS "unicode character out of range";
1398 dst+=unicode_decode(src+10,brace,dst);
1405 data->length=dst-data->character;
1412 /*************************************************
1413 * Parse an optional string *
1414 *************************************************/
1418 quoted-string = DQUOTE *CHAR DQUOTE
1419 ;; in general, \ CHAR inside a string maps to CHAR
1420 ;; so \" maps to " and \\ maps to \
1421 ;; note that newlines and other characters are all allowed
1424 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1425 *(multi-line-literal / multi-line-dotstuff)
1427 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1428 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1429 ;; A line containing only "." ends the multi-line.
1430 ;; Remove a leading '.' if followed by another '.'.
1431 string = quoted-string / multi-line
1434 filter points to the Sieve filter including its state
1435 id specifies identifier to match
1439 0 identifier not matched
1442 static int parse_string(struct Sieve *filter, struct String *data)
1447 data->character=(uschar*)0;
1448 if (*filter->pc=='"') /* quoted string */
1453 if (*filter->pc=='"') /* end of string */
1455 int foo=data->length;
1458 /* that way, there will be at least one character allocated */
1459 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1460 #ifdef ENCODED_CHARACTER
1461 if (filter->require_encoded_character
1462 && string_decode(filter,data)==-1)
1467 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1469 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1472 else /* regular character */
1475 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1477 if (*filter->pc=='\n')
1479 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1483 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1487 filter->errmsg=CUS "missing end of string";
1490 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1493 /* skip optional white space followed by hashed comment or CRLF */
1494 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1495 if (*filter->pc=='#')
1497 if (parse_hashcomment(filter)==-1) return -1;
1500 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1502 else if (*filter->pc=='\n')
1514 filter->errmsg=CUS "syntax error";
1520 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1522 if (*filter->pc=='\n') /* end of line */
1525 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1533 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1535 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1538 int foo=data->length;
1540 /* that way, there will be at least one character allocated */
1541 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1548 #ifdef ENCODED_CHARACTER
1549 if (filter->require_encoded_character
1550 && string_decode(filter,data)==-1)
1555 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1557 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1561 else /* regular character */
1563 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1567 filter->errmsg=CUS "missing end of multi line string";
1574 /*************************************************
1575 * Parse a specific identifier *
1576 *************************************************/
1580 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1583 filter points to the Sieve filter including its state
1584 id specifies identifier to match
1587 0 identifier not matched
1590 static int parse_identifier(struct Sieve *filter, const uschar *id)
1592 size_t idlen=Ustrlen(id);
1594 if (strncmpic(US filter->pc,US id,idlen)==0)
1596 uschar next=filter->pc[idlen];
1598 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1606 /*************************************************
1608 *************************************************/
1612 number = 1*DIGIT [QUANTIFIER]
1613 QUANTIFIER = "K" / "M" / "G"
1616 filter points to the Sieve filter including its state
1620 -1 no string list found
1623 static int parse_number(struct Sieve *filter, unsigned long *data)
1627 if (*filter->pc>='0' && *filter->pc<='9')
1632 d=Ustrtoul(filter->pc,&e,10);
1635 filter->errmsg=CUstrerror(ERANGE);
1640 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1641 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1642 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1643 if (d>(ULONG_MAX/u))
1645 filter->errmsg=CUstrerror(ERANGE);
1654 filter->errmsg=CUS "missing number";
1660 /*************************************************
1661 * Parse a string list *
1662 *************************************************/
1666 string-list = "[" string *("," string) "]" / string
1669 filter points to the Sieve filter including its state
1670 data returns string list
1673 -1 no string list found
1676 static int parse_stringlist(struct Sieve *filter, struct String **data)
1678 const uschar *orig=filter->pc;
1681 struct String *d=(struct String*)0;
1684 if (*filter->pc=='[') /* string list */
1689 if (parse_white(filter)==-1) goto error;
1690 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1693 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1694 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1695 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1697 filter->errmsg=CUstrerror(errno);
1700 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1702 dataCapacity=newCapacity;
1704 m=parse_string(filter,&d[dataLength]);
1707 if (dataLength==0) break;
1710 filter->errmsg=CUS "missing string";
1714 else if (m==-1) goto error;
1716 if (parse_white(filter)==-1) goto error;
1717 if (*filter->pc==',') ++filter->pc;
1720 if (*filter->pc==']')
1722 d[dataLength].character=(uschar*)0;
1723 d[dataLength].length=-1;
1730 filter->errmsg=CUS "missing closing bracket";
1734 else /* single string */
1736 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1740 m=parse_string(filter,&d[0]);
1752 d[1].character=(uschar*)0;
1759 filter->errmsg=CUS "missing string list";
1764 /*************************************************
1765 * Parse an optional address part specifier *
1766 *************************************************/
1770 address-part = ":localpart" / ":domain" / ":all"
1771 address-part =/ ":user" / ":detail"
1774 filter points to the Sieve filter including its state
1775 a returns address part specified
1778 0 no comparator found
1782 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1785 if (parse_identifier(filter,CUS ":user")==1)
1787 if (!filter->require_subaddress)
1789 filter->errmsg=CUS "missing previous require \"subaddress\";";
1795 else if (parse_identifier(filter,CUS ":detail")==1)
1797 if (!filter->require_subaddress)
1799 filter->errmsg=CUS "missing previous require \"subaddress\";";
1807 if (parse_identifier(filter,CUS ":localpart")==1)
1809 *a=ADDRPART_LOCALPART;
1812 else if (parse_identifier(filter,CUS ":domain")==1)
1817 else if (parse_identifier(filter,CUS ":all")==1)
1826 /*************************************************
1827 * Parse an optional comparator *
1828 *************************************************/
1832 comparator = ":comparator" <comparator-name: string>
1835 filter points to the Sieve filter including its state
1836 c returns comparator
1839 0 no comparator found
1840 -1 incomplete comparator found
1843 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1845 struct String comparator_name;
1847 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1848 if (parse_white(filter)==-1) return -1;
1849 switch (parse_string(filter,&comparator_name))
1854 filter->errmsg=CUS "missing comparator";
1861 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1866 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1868 *c=COMP_EN_ASCII_CASEMAP;
1871 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1873 *c=COMP_EN_ASCII_CASEMAP;
1876 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1878 *c=COMP_ASCII_NUMERIC;
1883 filter->errmsg=CUS "invalid comparator";
1892 /*************************************************
1893 * Parse an optional match type *
1894 *************************************************/
1898 match-type = ":is" / ":contains" / ":matches"
1901 filter points to the Sieve filter including its state
1902 m returns match type
1905 0 no match type found
1908 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1910 if (parse_identifier(filter,CUS ":is")==1)
1915 else if (parse_identifier(filter,CUS ":contains")==1)
1920 else if (parse_identifier(filter,CUS ":matches")==1)
1929 /*************************************************
1930 * Parse and interpret an optional test list *
1931 *************************************************/
1935 test-list = "(" test *("," test) ")"
1938 filter points to the Sieve filter including its state
1939 n total number of tests
1940 true number of passed tests
1941 exec Execute parsed statements
1944 0 no test list found
1945 -1 syntax or execution error
1948 static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
1950 if (parse_white(filter)==-1) return -1;
1951 if (*filter->pc=='(')
1960 switch (parse_test(filter,&cond,exec))
1963 case 0: filter->errmsg=CUS "missing test"; return -1;
1964 default: ++*n; if (cond) ++*num_true; break;
1966 if (parse_white(filter)==-1) return -1;
1967 if (*filter->pc==',') ++filter->pc;
1970 if (*filter->pc==')')
1977 filter->errmsg=CUS "missing closing paren";
1985 /*************************************************
1986 * Parse and interpret an optional test *
1987 *************************************************/
1991 filter points to the Sieve filter including its state
1992 cond returned condition status
1993 exec Execute parsed statements
1997 -1 syntax or execution error
2000 static int parse_test(struct Sieve *filter, int *cond, int exec)
2002 if (parse_white(filter)==-1) return -1;
2003 if (parse_identifier(filter,CUS "address"))
2006 address-test = "address" { [address-part] [comparator] [match-type] }
2007 <header-list: string-list> <key-list: string-list>
2009 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2012 enum AddressPart addressPart=ADDRPART_ALL;
2013 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2014 enum MatchType matchType=MATCH_IS;
2015 struct String *hdr,*h,*key,*k;
2021 if (parse_white(filter)==-1) return -1;
2022 if ((m=parse_addresspart(filter,&addressPart))!=0)
2024 if (m==-1) return -1;
2027 filter->errmsg=CUS "address part already specified";
2032 else if ((m=parse_comparator(filter,&comparator))!=0)
2034 if (m==-1) return -1;
2037 filter->errmsg=CUS "comparator already specified";
2042 else if ((m=parse_matchtype(filter,&matchType))!=0)
2044 if (m==-1) return -1;
2047 filter->errmsg=CUS "match type already specified";
2054 if (parse_white(filter)==-1) return -1;
2055 if ((m=parse_stringlist(filter,&hdr))!=1)
2057 if (m==0) filter->errmsg=CUS "header string list expected";
2060 if (parse_white(filter)==-1) return -1;
2061 if ((m=parse_stringlist(filter,&key))!=1)
2063 if (m==0) filter->errmsg=CUS "key string list expected";
2067 for (h=hdr; h->length!=-1 && !*cond; ++h)
2069 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2073 !eq_asciicase(h,&str_from,0)
2074 && !eq_asciicase(h,&str_to,0)
2075 && !eq_asciicase(h,&str_cc,0)
2076 && !eq_asciicase(h,&str_bcc,0)
2077 && !eq_asciicase(h,&str_sender,0)
2078 && !eq_asciicase(h,&str_resent_from,0)
2079 && !eq_asciicase(h,&str_resent_to,0)
2082 filter->errmsg=CUS "invalid header field";
2087 /* We are only interested in addresses below, so no MIME decoding */
2088 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2089 if (header_value == NULL)
2091 filter->errmsg=CUS "header string expansion failed";
2094 parse_allow_group = TRUE;
2095 while (*header_value && !*cond)
2098 int start, end, domain;
2102 end_addr = parse_find_address_end(header_value, FALSE);
2103 saveend = *end_addr;
2105 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2107 if (extracted_addr) switch (addressPart)
2109 case ADDRPART_ALL: part=extracted_addr; break;
2113 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2114 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2116 case ADDRPART_DETAIL: part=NULL; break;
2120 *end_addr = saveend;
2123 for (k=key; k->length!=-1; ++k)
2125 struct String partStr;
2127 partStr.character=part;
2128 partStr.length=Ustrlen(part);
2131 *cond=compare(filter,k,&partStr,comparator,matchType);
2132 if (*cond==-1) return -1;
2137 if (saveend == 0) break;
2138 header_value = end_addr + 1;
2140 parse_allow_group = FALSE;
2141 parse_found_group = FALSE;
2146 else if (parse_identifier(filter,CUS "allof"))
2149 allof-test = "allof" <tests: test-list>
2154 switch (parse_testlist(filter,&n,&num_true,exec))
2157 case 0: filter->errmsg=CUS "missing test list"; return -1;
2158 default: *cond=(n==num_true); return 1;
2161 else if (parse_identifier(filter,CUS "anyof"))
2164 anyof-test = "anyof" <tests: test-list>
2169 switch (parse_testlist(filter,&n,&num_true,exec))
2172 case 0: filter->errmsg=CUS "missing test list"; return -1;
2173 default: *cond=(num_true>0); return 1;
2176 else if (parse_identifier(filter,CUS "exists"))
2179 exists-test = "exists" <header-names: string-list>
2182 struct String *hdr,*h;
2185 if (parse_white(filter)==-1) return -1;
2186 if ((m=parse_stringlist(filter,&hdr))!=1)
2188 if (m==0) filter->errmsg=CUS "header string list expected";
2194 for (h=hdr; h->length!=-1 && *cond; ++h)
2198 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2199 if (header_def == NULL)
2201 filter->errmsg=CUS "header string expansion failed";
2204 if (Ustrcmp(header_def,"false")==0) *cond=0;
2209 else if (parse_identifier(filter,CUS "false"))
2212 false-test = "false"
2218 else if (parse_identifier(filter,CUS "header"))
2221 header-test = "header" { [comparator] [match-type] }
2222 <header-names: string-list> <key-list: string-list>
2225 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2226 enum MatchType matchType=MATCH_IS;
2227 struct String *hdr,*h,*key,*k;
2233 if (parse_white(filter)==-1) return -1;
2234 if ((m=parse_comparator(filter,&comparator))!=0)
2236 if (m==-1) return -1;
2239 filter->errmsg=CUS "comparator already specified";
2244 else if ((m=parse_matchtype(filter,&matchType))!=0)
2246 if (m==-1) return -1;
2249 filter->errmsg=CUS "match type already specified";
2256 if (parse_white(filter)==-1) return -1;
2257 if ((m=parse_stringlist(filter,&hdr))!=1)
2259 if (m==0) filter->errmsg=CUS "header string list expected";
2262 if (parse_white(filter)==-1) return -1;
2263 if ((m=parse_stringlist(filter,&key))!=1)
2265 if (m==0) filter->errmsg=CUS "key string list expected";
2269 for (h=hdr; h->length!=-1 && !*cond; ++h)
2273 filter->errmsg=CUS "invalid header field";
2278 struct String header_value;
2281 expand_header(&header_value,h);
2282 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2283 if (header_value.character == NULL || header_def == NULL)
2285 filter->errmsg=CUS "header string expansion failed";
2288 for (k=key; k->length!=-1; ++k)
2290 if (Ustrcmp(header_def,"true")==0)
2292 *cond=compare(filter,k,&header_value,comparator,matchType);
2293 if (*cond==-1) return -1;
2301 else if (parse_identifier(filter,CUS "not"))
2303 if (parse_white(filter)==-1) return -1;
2304 switch (parse_test(filter,cond,exec))
2307 case 0: filter->errmsg=CUS "missing test"; return -1;
2308 default: *cond=!*cond; return 1;
2311 else if (parse_identifier(filter,CUS "size"))
2314 relop = ":over" / ":under"
2315 size-test = "size" relop <limit: number>
2318 unsigned long limit;
2321 if (parse_white(filter)==-1) return -1;
2322 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2323 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2326 filter->errmsg=CUS "missing :over or :under";
2329 if (parse_white(filter)==-1) return -1;
2330 if (parse_number(filter,&limit)==-1) return -1;
2331 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2334 else if (parse_identifier(filter,CUS "true"))
2339 else if (parse_identifier(filter,CUS "envelope"))
2342 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2343 <envelope-part: string-list> <key-list: string-list>
2345 envelope-part is case insensitive "from" or "to"
2346 #ifdef ENVELOPE_AUTH
2347 envelope-part =/ "auth"
2351 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2352 enum AddressPart addressPart=ADDRPART_ALL;
2353 enum MatchType matchType=MATCH_IS;
2354 struct String *env,*e,*key,*k;
2358 if (!filter->require_envelope)
2360 filter->errmsg=CUS "missing previous require \"envelope\";";
2365 if (parse_white(filter)==-1) return -1;
2366 if ((m=parse_comparator(filter,&comparator))!=0)
2368 if (m==-1) return -1;
2371 filter->errmsg=CUS "comparator already specified";
2376 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2378 if (m==-1) return -1;
2381 filter->errmsg=CUS "address part already specified";
2386 else if ((m=parse_matchtype(filter,&matchType))!=0)
2388 if (m==-1) return -1;
2391 filter->errmsg=CUS "match type already specified";
2398 if (parse_white(filter)==-1) return -1;
2399 if ((m=parse_stringlist(filter,&env))!=1)
2401 if (m==0) filter->errmsg=CUS "envelope string list expected";
2404 if (parse_white(filter)==-1) return -1;
2405 if ((m=parse_stringlist(filter,&key))!=1)
2407 if (m==0) filter->errmsg=CUS "key string list expected";
2411 for (e=env; e->length!=-1 && !*cond; ++e)
2413 const uschar *envelopeExpr=CUS 0;
2414 uschar *envelope=US 0;
2416 if (eq_asciicase(e,&str_from,0))
2418 switch (addressPart)
2420 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2424 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2425 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2427 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2431 else if (eq_asciicase(e,&str_to,0))
2433 switch (addressPart)
2435 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2437 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2438 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2440 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2441 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2444 #ifdef ENVELOPE_AUTH
2445 else if (eq_asciicase(e,&str_auth,0))
2447 switch (addressPart)
2449 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2453 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2454 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2456 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2463 filter->errmsg=CUS "invalid envelope string";
2466 if (exec && envelopeExpr)
2468 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2470 filter->errmsg=CUS "header string expansion failed";
2473 for (k=key; k->length!=-1; ++k)
2475 struct String envelopeStr;
2477 envelopeStr.character=envelope;
2478 envelopeStr.length=Ustrlen(envelope);
2479 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2480 if (*cond==-1) return -1;
2488 else if (parse_identifier(filter,CUS "valid_notify_method"))
2491 valid_notify_method = "valid_notify_method"
2492 <notification-uris: string-list>
2495 struct String *uris,*u;
2498 if (!filter->require_enotify)
2500 filter->errmsg=CUS "missing previous require \"enotify\";";
2503 if (parse_white(filter)==-1) return -1;
2504 if ((m=parse_stringlist(filter,&uris))!=1)
2506 if (m==0) filter->errmsg=CUS "URI string list expected";
2512 for (u=uris; u->length!=-1 && *cond; ++u)
2514 string_item *recipient;
2515 struct String header,subject,body;
2519 header.character=(uschar*)0;
2521 subject.character=(uschar*)0;
2523 body.character=(uschar*)0;
2524 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
2530 else if (parse_identifier(filter,CUS "notify_method_capability"))
2533 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2534 <notification-uri: string>
2535 <notification-capability: string>
2536 <key-list: string-list>
2542 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2543 enum MatchType matchType=MATCH_IS;
2544 struct String uri,capa,*keys,*k;
2546 if (!filter->require_enotify)
2548 filter->errmsg=CUS "missing previous require \"enotify\";";
2553 if (parse_white(filter)==-1) return -1;
2554 if ((m=parse_comparator(filter,&comparator))!=0)
2556 if (m==-1) return -1;
2559 filter->errmsg=CUS "comparator already specified";
2564 else if ((m=parse_matchtype(filter,&matchType))!=0)
2566 if (m==-1) return -1;
2569 filter->errmsg=CUS "match type already specified";
2576 if ((m=parse_string(filter,&uri))!=1)
2578 if (m==0) filter->errmsg=CUS "missing notification URI string";
2581 if (parse_white(filter)==-1) return -1;
2582 if ((m=parse_string(filter,&capa))!=1)
2584 if (m==0) filter->errmsg=CUS "missing notification capability string";
2587 if (parse_white(filter)==-1) return -1;
2588 if ((m=parse_stringlist(filter,&keys))!=1)
2590 if (m==0) filter->errmsg=CUS "missing key string list";
2595 string_item *recipient;
2596 struct String header,subject,body;
2601 header.character=(uschar*)0;
2603 subject.character=(uschar*)0;
2605 body.character=(uschar*)0;
2606 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
2608 if (eq_asciicase(&capa,&str_online,0)==1)
2609 for (k=keys; k->length!=-1; ++k)
2611 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2612 if (*cond==-1) return -1;
2624 /*************************************************
2625 * Parse and interpret an optional block *
2626 *************************************************/
2630 filter points to the Sieve filter including its state
2631 exec Execute parsed statements
2632 generated where to hang newly-generated addresses
2634 Returns: 2 success by stop
2636 0 no block command found
2637 -1 syntax or execution error
2640 static int parse_block(struct Sieve *filter, int exec,
2641 address_item **generated)
2645 if (parse_white(filter)==-1) return -1;
2646 if (*filter->pc=='{')
2649 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2650 if (*filter->pc=='}')
2657 filter->errmsg=CUS "expecting command or closing brace";
2665 /*************************************************
2666 * Match a semicolon *
2667 *************************************************/
2671 filter points to the Sieve filter including its state
2677 static int parse_semicolon(struct Sieve *filter)
2679 if (parse_white(filter)==-1) return -1;
2680 if (*filter->pc==';')
2687 filter->errmsg=CUS "missing semicolon";
2693 /*************************************************
2694 * Parse and interpret a Sieve command *
2695 *************************************************/
2699 filter points to the Sieve filter including its state
2700 exec Execute parsed statements
2701 generated where to hang newly-generated addresses
2703 Returns: 2 success by stop
2705 -1 syntax or execution error
2707 static int parse_commands(struct Sieve *filter, int exec,
2708 address_item **generated)
2712 if (parse_white(filter)==-1) return -1;
2713 if (parse_identifier(filter,CUS "if"))
2716 if-command = "if" test block *( "elsif" test block ) [ else block ]
2719 int cond,m,unsuccessful;
2722 if (parse_white(filter)==-1) return -1;
2723 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2726 filter->errmsg=CUS "missing test";
2729 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2730 (debug_selector & D_filter) != 0)
2732 if (exec) debug_printf("if %s\n",cond?"true":"false");
2734 m=parse_block(filter,exec ? cond : 0, generated);
2735 if (m==-1 || m==2) return m;
2738 filter->errmsg=CUS "missing block";
2741 unsuccessful = !cond;
2742 for (;;) /* elsif test block */
2744 if (parse_white(filter)==-1) return -1;
2745 if (parse_identifier(filter,CUS "elsif"))
2747 if (parse_white(filter)==-1) return -1;
2748 m=parse_test(filter,&cond,exec && unsuccessful);
2749 if (m==-1 || m==2) return m;
2752 filter->errmsg=CUS "missing test";
2755 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2756 (debug_selector & D_filter) != 0)
2758 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2760 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2761 if (m==-1 || m==2) return m;
2764 filter->errmsg=CUS "missing block";
2767 if (exec && unsuccessful && cond) unsuccessful = 0;
2772 if (parse_white(filter)==-1) return -1;
2773 if (parse_identifier(filter,CUS "else"))
2775 m=parse_block(filter,exec && unsuccessful, generated);
2776 if (m==-1 || m==2) return m;
2779 filter->errmsg=CUS "missing block";
2784 else if (parse_identifier(filter,CUS "stop"))
2787 stop-command = "stop" { stop-options } ";"
2791 if (parse_semicolon(filter)==-1) return -1;
2794 filter->pc+=Ustrlen(filter->pc);
2798 else if (parse_identifier(filter,CUS "keep"))
2801 keep-command = "keep" { keep-options } ";"
2805 if (parse_semicolon(filter)==-1) return -1;
2808 add_addr(generated,US"inbox",1,0,0,0);
2812 else if (parse_identifier(filter,CUS "discard"))
2815 discard-command = "discard" { discard-options } ";"
2819 if (parse_semicolon(filter)==-1) return -1;
2820 if (exec) filter->keep=0;
2822 else if (parse_identifier(filter,CUS "redirect"))
2825 redirect-command = "redirect" redirect-options "string" ";"
2827 redirect-options =) ":copy"
2830 struct String recipient;
2836 if (parse_white(filter)==-1) return -1;
2837 if (parse_identifier(filter,CUS ":copy")==1)
2839 if (!filter->require_copy)
2841 filter->errmsg=CUS "missing previous require \"copy\";";
2848 if (parse_white(filter)==-1) return -1;
2849 if ((m=parse_string(filter,&recipient))!=1)
2851 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2854 if (strchr(CCS recipient.character,'@')==(char*)0)
2856 filter->errmsg=CUS "unqualified recipient address";
2861 add_addr(generated,recipient.character,0,0,0,0);
2862 if (!copy) filter->keep = 0;
2864 if (parse_semicolon(filter)==-1) return -1;
2866 else if (parse_identifier(filter,CUS "fileinto"))
2869 fileinto-command = "fileinto" { fileinto-options } string ";"
2871 fileinto-options =) [ ":copy" ]
2874 struct String folder;
2877 unsigned long maxage, maxmessages, maxstorage;
2880 maxage = maxmessages = maxstorage = 0;
2881 if (!filter->require_fileinto)
2883 filter->errmsg=CUS "missing previous require \"fileinto\";";
2888 if (parse_white(filter)==-1) return -1;
2889 if (parse_identifier(filter,CUS ":copy")==1)
2891 if (!filter->require_copy)
2893 filter->errmsg=CUS "missing previous require \"copy\";";
2900 if (parse_white(filter)==-1) return -1;
2901 if ((m=parse_string(filter,&folder))!=1)
2903 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2906 m=0; s=folder.character;
2907 if (folder.length==0) m=1;
2908 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2911 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2916 filter->errmsg=CUS "invalid folder";
2921 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2922 if (!copy) filter->keep = 0;
2924 if (parse_semicolon(filter)==-1) return -1;
2927 else if (parse_identifier(filter,CUS "notify"))
2930 notify-command = "notify" { notify-options } <method: string> ";"
2931 notify-options = [":from" string]
2932 [":importance" <"1" / "2" / "3">]
2933 [":options" 1*(string-list / number)]
2939 struct String importance;
2940 struct String *options;
2941 struct String message;
2942 struct String method;
2943 struct Notification *already;
2944 string_item *recipient;
2945 struct String header;
2946 struct String subject;
2948 uschar *envelope_from,*envelope_to;
2949 struct String auto_submitted_value;
2950 uschar *auto_submitted_def;
2952 if (!filter->require_enotify)
2954 filter->errmsg=CUS "missing previous require \"enotify\";";
2957 from.character=(uschar*)0;
2959 importance.character=(uschar*)0;
2960 importance.length=-1;
2961 options=(struct String*)0;
2962 message.character=(uschar*)0;
2966 header.character=(uschar*)0;
2968 subject.character=(uschar*)0;
2970 body.character=(uschar*)0;
2971 envelope_from=expand_string("$sender_address");
2972 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2975 if (parse_white(filter)==-1) return -1;
2976 if (parse_identifier(filter,CUS ":from")==1)
2978 if (parse_white(filter)==-1) return -1;
2979 if ((m=parse_string(filter,&from))!=1)
2981 if (m==0) filter->errmsg=CUS "from string expected";
2985 else if (parse_identifier(filter,CUS ":importance")==1)
2987 if (parse_white(filter)==-1) return -1;
2988 if ((m=parse_string(filter,&importance))!=1)
2990 if (m==0) filter->errmsg=CUS "importance string expected";
2993 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2995 filter->errmsg=CUS "invalid importance";
2999 else if (parse_identifier(filter,CUS ":options")==1)
3001 if (parse_white(filter)==-1) return -1;
3003 else if (parse_identifier(filter,CUS ":message")==1)
3005 if (parse_white(filter)==-1) return -1;
3006 if ((m=parse_string(filter,&message))!=1)
3008 if (m==0) filter->errmsg=CUS "message string expected";
3014 if (parse_white(filter)==-1) return -1;
3015 if ((m=parse_string(filter,&method))!=1)
3017 if (m==0) filter->errmsg=CUS "missing method string";
3020 if (parse_semicolon(filter)==-1) return -1;
3021 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3023 if (message.length==-1) message=subject;
3024 if (message.length==-1) expand_header(&message,&str_subject);
3025 expand_header(&auto_submitted_value,&str_auto_submitted);
3026 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3027 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
3029 filter->errmsg=CUS "header string expansion failed";
3032 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
3034 for (already=filter->notified; already; already=already->next)
3036 if (already->method.length==method.length
3037 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
3038 && already->importance.length==importance.length
3039 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
3040 && already->message.length==message.length
3041 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
3044 if (already==(struct Notification*)0)
3045 /* New notification, process it */
3047 struct Notification *sent;
3048 sent=store_get(sizeof(struct Notification));
3049 sent->method=method;
3050 sent->importance=importance;
3051 sent->message=message;
3052 sent->next=filter->notified;
3053 filter->notified=sent;
3054 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3056 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3058 #ifndef COMPILE_SYNTAX_CHECKER
3059 if (exec && filter_test == FTEST_NONE)
3065 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
3069 int buffer_capacity;
3071 f = fdopen(fd, "wb");
3072 for (h = header_list; h != NULL; h = h->next)
3073 if (h->type == htype_received) fprintf(f,"%s",h->text);
3074 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
3075 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
3076 fprintf(f,"Auto-submitted: sieve-notify\n");
3077 if (header.length>0) fprintf(f,"%s",header.character);
3078 if (message.length==-1)
3080 message.character=US"Notification";
3081 message.length=Ustrlen(message.character);
3083 /* Allocation is larger than neccessary, but enough even for split MIME words */
3084 buffer_capacity=32+4*message.length;
3085 buffer=store_get(buffer_capacity);
3086 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3088 if (body.length>0) fprintf(f,"%s\n",body.character);
3091 (void)child_close(pid, 0);
3098 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3100 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3106 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3108 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
3114 else if (parse_identifier(filter,CUS "vacation"))
3117 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3118 vacation-options = [":days" number]
3121 [":addresses" string-list]
3128 struct String subject;
3130 struct String *addresses;
3132 string_item *aliases;
3133 struct String handle;
3134 struct String reason;
3136 if (!filter->require_vacation)
3138 filter->errmsg=CUS "missing previous require \"vacation\";";
3143 if (filter->vacation_ran)
3145 filter->errmsg=CUS "trying to execute vacation more than once";
3148 filter->vacation_ran=1;
3150 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3151 subject.character=(uschar*)0;
3153 from.character=(uschar*)0;
3155 addresses=(struct String*)0;
3158 handle.character=(uschar*)0;
3162 if (parse_white(filter)==-1) return -1;
3163 if (parse_identifier(filter,CUS ":days")==1)
3165 if (parse_white(filter)==-1) return -1;
3166 if (parse_number(filter,&days)==-1) return -1;
3167 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3168 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3170 else if (parse_identifier(filter,CUS ":subject")==1)
3172 if (parse_white(filter)==-1) return -1;
3173 if ((m=parse_string(filter,&subject))!=1)
3175 if (m==0) filter->errmsg=CUS "subject string expected";
3179 else if (parse_identifier(filter,CUS ":from")==1)
3181 if (parse_white(filter)==-1) return -1;
3182 if ((m=parse_string(filter,&from))!=1)
3184 if (m==0) filter->errmsg=CUS "from string expected";
3187 if (check_mail_address(filter,&from)!=1)
3190 else if (parse_identifier(filter,CUS ":addresses")==1)
3194 if (parse_white(filter)==-1) return -1;
3195 if ((m=parse_stringlist(filter,&addresses))!=1)
3197 if (m==0) filter->errmsg=CUS "addresses string list expected";
3200 for (a=addresses; a->length!=-1; ++a)
3204 new=store_get(sizeof(string_item));
3205 new->text=store_get(a->length+1);
3206 if (a->length) memcpy(new->text,a->character,a->length);
3207 new->text[a->length]='\0';
3212 else if (parse_identifier(filter,CUS ":mime")==1)
3214 else if (parse_identifier(filter,CUS ":handle")==1)
3216 if (parse_white(filter)==-1) return -1;
3217 if ((m=parse_string(filter,&from))!=1)
3219 if (m==0) filter->errmsg=CUS "handle string expected";
3225 if (parse_white(filter)==-1) return -1;
3226 if ((m=parse_string(filter,&reason))!=1)
3228 if (m==0) filter->errmsg=CUS "missing reason string";
3235 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3238 filter->errmsg=CUS "MIME reason string contains 8bit text";
3242 if (parse_semicolon(filter)==-1) return -1;
3249 int buffer_capacity;
3253 uschar hexdigest[33];
3257 if (filter_personal(aliases,TRUE))
3259 if (filter_test == FTEST_NONE)
3261 /* ensure oncelog directory exists; failure will be detected later */
3263 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3265 /* build oncelog filename */
3267 key.character=(uschar*)0;
3270 if (handle.length==-1)
3272 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3273 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3274 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3275 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3280 md5_end(&base, key.character, key.length, digest);
3281 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3282 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3284 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3286 if (filter_test == FTEST_NONE)
3288 capacity=Ustrlen(filter->vacation_directory);
3290 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3291 once=string_cat(once,&capacity,&start,hexdigest,33);
3294 /* process subject */
3296 if (subject.length==-1)
3298 uschar *subject_def;
3300 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3301 if (Ustrcmp(subject_def,"true")==0)
3303 expand_header(&subject,&str_subject);
3306 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3307 subject.length=start;
3311 subject.character=US"Automated reply";
3312 subject.length=Ustrlen(subject.character);
3316 /* add address to list of generated addresses */
3318 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3319 setflag(addr, af_pfr);
3320 setflag(addr, af_ignore_error);
3321 addr->next = *generated;
3323 addr->reply = store_get(sizeof(reply_item));
3324 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3325 addr->reply->to = string_copy(sender_address);
3326 if (from.length==-1)
3327 addr->reply->from = expand_string(US"$local_part@$domain");
3329 addr->reply->from = from.character;
3330 /* Allocation is larger than neccessary, but enough even for split MIME words */
3331 buffer_capacity=32+4*subject.length;
3332 buffer=store_get(buffer_capacity);
3333 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3334 addr->reply->oncelog=once;
3335 addr->reply->once_repeat=days*86400;
3337 /* build body and MIME headers */
3341 uschar *mime_body,*reason_end;
3342 static const uschar nlnl[]="\r\n\r\n";
3346 mime_body=reason.character,reason_end=reason.character+reason.length;
3347 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3352 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3353 addr->reply->headers[start] = '\0';
3356 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3357 else mime_body=reason_end-1;
3358 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3359 addr->reply->text[start] = '\0';
3363 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3366 start = reason.length;
3367 addr->reply->headers = US"MIME-Version: 1.0\n"
3368 "Content-Type: text/plain;\n"
3369 "\tcharset=\"utf-8\"\n"
3370 "Content-Transfer-Encoding: quoted-printable";
3371 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3375 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3377 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3388 /*************************************************
3389 * Parse and interpret a sieve filter *
3390 *************************************************/
3394 filter points to the Sieve filter including its state
3395 exec Execute parsed statements
3396 generated where to hang newly-generated addresses
3399 -1 syntax or execution error
3402 static int parse_start(struct Sieve *filter, int exec,
3403 address_item **generated)
3405 filter->pc=filter->filter;
3408 filter->require_envelope=0;
3409 filter->require_fileinto=0;
3410 #ifdef ENCODED_CHARACTER
3411 filter->require_encoded_character=0;
3413 #ifdef ENVELOPE_AUTH
3414 filter->require_envelope_auth=0;
3417 filter->require_enotify=0;
3418 filter->notified=(struct Notification*)0;
3421 filter->require_subaddress=0;
3424 filter->require_vacation=0;
3425 filter->vacation_ran=0;
3427 filter->require_copy=0;
3428 filter->require_iascii_numeric=0;
3430 if (parse_white(filter)==-1) return -1;
3432 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3435 struct dirent *oncelog;
3436 struct stat properties;
3439 /* clean up old vacation log databases */
3441 oncelogdir=opendir(CS filter->vacation_directory);
3443 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3445 filter->errmsg=CUS "unable to open vacation directory";
3449 if (oncelogdir != NULL)
3453 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3455 if (strlen(oncelog->d_name)==32)
3457 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3458 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3462 closedir(oncelogdir);
3466 while (parse_identifier(filter,CUS "require"))
3469 require-command = "require" <capabilities: string-list>
3472 struct String *cap,*check;
3475 if (parse_white(filter)==-1) return -1;
3476 if ((m=parse_stringlist(filter,&cap))!=1)
3478 if (m==0) filter->errmsg=CUS "capability string list expected";
3481 for (check=cap; check->character; ++check)
3483 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3484 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3485 #ifdef ENCODED_CHARACTER
3486 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3488 #ifdef ENVELOPE_AUTH
3489 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3492 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3495 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3498 else if (eq_octet(check,&str_vacation,0))
3500 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3502 filter->errmsg=CUS "vacation disabled";
3505 filter->require_vacation=1;
3508 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3509 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3510 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3511 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3512 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3515 filter->errmsg=CUS "unknown capability";
3519 if (parse_semicolon(filter)==-1) return -1;
3521 if (parse_commands(filter,exec,generated)==-1) return -1;
3524 filter->errmsg=CUS "syntax error";
3531 /*************************************************
3532 * Interpret a sieve filter file *
3533 *************************************************/
3537 filter points to the entire file, read into store as a single string
3538 options controls whether various special things are allowed, and requests
3539 special actions (not currently used)
3540 sieve_vacation_directory where to store vacation "once" files
3541 useraddress string expression for :user part of address
3542 subaddress string expression for :subaddress part of address
3543 generated where to hang newly-generated addresses
3544 error where to pass back an error text
3546 Returns: FF_DELIVERED success, a significant action was taken
3547 FF_NOTDELIVERED success, no significant action
3548 FF_DEFER defer requested
3549 FF_FAIL fail requested
3550 FF_FREEZE freeze requested
3551 FF_ERROR there was a problem
3555 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3556 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3562 options = options; /* Keep picky compilers happy */
3565 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3566 sieve.filter=filter;
3568 if (vacation_directory == NULL)
3569 sieve.vacation_directory = NULL;
3572 sieve.vacation_directory=expand_string(vacation_directory);
3573 if (sieve.vacation_directory == NULL)
3575 *error = string_sprintf("failed to expand \"%s\" "
3576 "(sieve_vacation_directory): %s", vacation_directory,
3577 expand_string_message);
3582 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3583 sieve.subaddress = subaddress;
3585 #ifdef COMPILE_SYNTAX_CHECKER
3586 if (parse_start(&sieve,0,generated)==1)
3588 if (parse_start(&sieve,1,generated)==1)
3593 add_addr(generated,US"inbox",1,0,0,0);
3594 msg = string_sprintf("Implicit keep");
3599 msg = string_sprintf("No implicit keep");
3605 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3606 #ifdef COMPILE_SYNTAX_CHECKER
3610 add_addr(generated,US"inbox",1,0,0,0);
3615 #ifndef COMPILE_SYNTAX_CHECKER
3616 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3617 else debug_printf("%s\n", msg);
3620 DEBUG(D_route) debug_printf("Sieve: end of processing\n");