1 /* $Cambridge: exim/src/src/sieve.c,v 1.28 2007/04/19 13:19:06 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2006 */
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 #undef 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 };
148 static uschar str_subaddress_c[]="subaddress";
149 static const struct String str_subaddress={ str_subaddress_c, 10 };
152 static uschar str_vacation_c[]="vacation";
153 static const struct String str_vacation={ str_vacation_c, 8 };
154 static uschar str_subject_c[]="Subject";
155 static const struct String str_subject={ str_subject_c, 7 };
157 static uschar str_copy_c[]="copy";
158 static const struct String str_copy={ str_copy_c, 4 };
159 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
160 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
161 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
162 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
163 static uschar str_ioctet_c[]="i;octet";
164 static const struct String str_ioctet={ str_ioctet_c, 7 };
165 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
166 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
167 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
168 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
169 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
170 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
171 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
172 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
173 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
174 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
177 /*************************************************
178 * Encode to quoted-printable *
179 *************************************************/
190 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
193 const uschar *start,*end;
198 for (pass=0; pass<=1; ++pass)
205 dst->character=store_get(dst->length+1); /* plus one for \0 */
208 for (start=src->character,end=start+src->length; start<end; ++start)
225 || (ch>=62 && ch<=126)
230 && (*(start+1)!='\r' || *(start+2)!='\n')
240 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
258 sprintf(CS new,"=%02X",ch);
265 *new='\0'; /* not included in length, but nice */
270 /*************************************************
271 * Check mail address for correct syntax *
272 *************************************************/
275 Check mail address for being syntactically correct.
278 filter points to the Sieve filter including its state
279 address String containing one address
282 1 Mail address is syntactically OK
286 int check_mail_address(struct Sieve *filter, const struct String *address)
288 int start, end, domain;
291 if (address->length>0)
293 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
297 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
298 address->character, error);
306 filter->errmsg=CUS "empty address";
312 /*************************************************
313 * Decode URI encoded string *
314 *************************************************/
318 str URI encoded string
321 0 Decoding successful
326 static int uri_decode(struct String *str)
330 if (str->length==0) return 0;
331 for (s=str->character,t=s,e=s+str->length; s<e; )
335 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
337 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
338 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
347 str->length=t-str->character;
352 /*************************************************
354 *************************************************/
359 mailtoURI = "mailto:" [ to ] [ headers ]
360 to = [ addr-spec *("%2C" addr-spec ) ]
361 headers = "?" header *( "&" header )
362 header = hname "=" hvalue
367 filter points to the Sieve filter including its state
368 uri URI, excluding scheme
373 1 URI is syntactically OK
378 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *body)
381 struct String to,hname,hvalue;
385 if (Ustrncmp(uri,"mailto:",7))
387 filter->errmsg=US "Unknown URI scheme";
391 if (*uri && *uri!='?')
395 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
399 to.character=(uschar*)0;
401 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
402 to.character[to.length]='\0';
403 if (uri_decode(&to)==-1)
405 filter->errmsg=US"Invalid URI encoding";
408 new=store_get(sizeof(string_item));
409 new->text=store_get(to.length+1);
410 if (to.length) memcpy(new->text,to.character,to.length);
411 new->text[to.length]='\0';
412 new->next=*recipient;
417 filter->errmsg=US"Missing addr-spec in URI";
420 if (*uri=='%') uri+=3;
429 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
433 hname.character=(uschar*)0;
435 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
436 hname.character[hname.length]='\0';
437 if (uri_decode(&hname)==-1)
439 filter->errmsg=US"Invalid URI encoding";
448 filter->errmsg=US"Missing equal after hname";
452 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
456 hvalue.character=(uschar*)0;
458 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
459 hvalue.character[hvalue.length]='\0';
460 if (uri_decode(&hvalue)==-1)
462 filter->errmsg=US"Invalid URI encoding";
466 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
468 new=store_get(sizeof(string_item));
469 new->text=store_get(hvalue.length+1);
470 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
471 new->text[hvalue.length]='\0';
472 new->next=*recipient;
475 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
479 static struct String ignore[]=
485 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
488 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
491 if (header->length==-1) header->length=0;
492 capacity=header->length;
493 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
494 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
495 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
496 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
497 header->character[header->length]='\0';
500 if (*uri=='&') ++uri;
506 filter->errmsg=US"Syntactically invalid URI";
514 /*************************************************
515 * Octet-wise string comparison *
516 *************************************************/
520 needle UTF-8 string to search ...
521 haystack ... inside the haystack
522 match_prefix 1 to compare if needle is a prefix of haystack
524 Returns: 0 needle not found in haystack
528 static int eq_octet(const struct String *needle,
529 const struct String *haystack, int match_prefix)
537 h=haystack->character;
541 if (*n&0x80) return 0;
542 if (*h&0x80) return 0;
544 if (*n!=*h) return 0;
550 return (match_prefix ? nl==0 : nl==0 && hl==0);
554 /*************************************************
555 * ASCII case-insensitive string comparison *
556 *************************************************/
560 needle UTF-8 string to search ...
561 haystack ... inside the haystack
562 match_prefix 1 to compare if needle is a prefix of haystack
564 Returns: 0 needle not found in haystack
568 static int eq_asciicase(const struct String *needle,
569 const struct String *haystack, int match_prefix)
578 h=haystack->character;
584 if (nc&0x80) return 0;
585 if (hc&0x80) return 0;
587 /* tolower depends on the locale and only ASCII case must be insensitive */
588 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
594 return (match_prefix ? nl==0 : nl==0 && hl==0);
598 /*************************************************
599 * Glob pattern search *
600 *************************************************/
604 needle pattern to search ...
605 haystack ... inside the haystack
607 Returns: 0 needle not found in haystack
612 static int eq_glob(const struct String *needle,
613 const struct String *haystack, int ascii_caseless, int match_octet)
615 const uschar *n,*h,*nend,*hend;
619 h=haystack->character;
620 nend=n+needle->length;
621 hend=h+haystack->length;
631 const uschar *npart,*hpart;
633 /* Try to match a non-star part of the needle at the current */
634 /* position in the haystack. */
638 while (npart<nend && *npart!='*') switch (*npart)
642 if (hpart==hend) return 0;
647 /* Match one UTF8 encoded character */
648 if ((*hpart&0xc0)==0xc0)
651 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
662 if (npart==nend) return -1;
667 if (hpart==hend) return 0;
668 /* tolower depends on the locale, but we need ASCII */
672 (*hpart&0x80) || (*npart&0x80) ||
675 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
680 /* string match after a star failed, advance and try again */
694 /* at this point, a part was matched successfully */
695 if (may_advance && npart==nend && hpart<hend)
696 /* needle ends, but haystack does not: if there was a star before, advance and try again */
706 return (h==hend ? 1 : may_advance);
710 /*************************************************
711 * ASCII numeric comparison *
712 *************************************************/
716 a first numeric string
717 b second numeric string
718 relop relational operator
720 Returns: 0 not (a relop b)
724 static int eq_asciinumeric(const struct String *a,
725 const struct String *b, enum RelOp relop)
728 const uschar *as,*aend,*bs,*bend;
732 aend=a->character+a->length;
734 bend=b->character+b->length;
736 while (*as>='0' && *as<='9' && as<aend) ++as;
738 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
741 if (al && bl==0) cmp=-1;
742 else if (al==0 && bl==0) cmp=0;
743 else if (al==0 && bl) cmp=1;
747 if (cmp==0) cmp=memcmp(a->character,b->character,al);
751 case LT: return cmp<0;
752 case LE: return cmp<=0;
753 case EQ: return cmp==0;
754 case GE: return cmp>=0;
755 case GT: return cmp>0;
756 case NE: return cmp!=0;
763 /*************************************************
765 *************************************************/
769 filter points to the Sieve filter including its state
770 needle UTF-8 pattern or string to search ...
771 haystack ... inside the haystack
775 Returns: 0 needle not found in haystack
777 -1 comparator does not offer matchtype
780 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
781 enum Comparator co, enum MatchType mt)
785 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
786 (debug_selector & D_filter) != 0)
788 debug_printf("String comparison (match ");
791 case MATCH_IS: debug_printf(":is"); break;
792 case MATCH_CONTAINS: debug_printf(":contains"); break;
793 case MATCH_MATCHES: debug_printf(":matches"); break;
795 debug_printf(", comparison \"");
798 case COMP_OCTET: debug_printf("i;octet"); break;
799 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
800 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
802 debug_printf("\"):\n");
803 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
804 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
814 if (eq_octet(needle,haystack,0)) r=1;
817 case COMP_EN_ASCII_CASEMAP:
819 if (eq_asciicase(needle,haystack,0)) r=1;
822 case COMP_ASCII_NUMERIC:
824 if (!filter->require_iascii_numeric)
826 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
829 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
843 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
846 case COMP_EN_ASCII_CASEMAP:
848 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
853 filter->errmsg=CUS "comparator does not offer specified matchtype";
865 if ((r=eq_glob(needle,haystack,0,1))==-1)
867 filter->errmsg=CUS "syntactically invalid pattern";
872 case COMP_EN_ASCII_CASEMAP:
874 if ((r=eq_glob(needle,haystack,1,1))==-1)
876 filter->errmsg=CUS "syntactically invalid pattern";
883 filter->errmsg=CUS "comparator does not offer specified matchtype";
890 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
891 (debug_selector & D_filter) != 0)
892 debug_printf(" Result %s\n",r?"true":"false");
897 /*************************************************
898 * Check header field syntax *
899 *************************************************/
902 RFC 2822, section 3.6.8 says:
906 ftext = %d33-57 / ; Any character except
907 %d59-126 ; controls, SP, and
910 That forbids 8-bit header fields. This implementation accepts them, since
911 all of Exim is 8-bit clean, so it adds %d128-%d255.
914 header header field to quote for suitable use in Exim expansions
916 Returns: 0 string is not a valid header field
917 1 string is a value header field
920 static int is_header(const struct String *header)
930 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
941 /*************************************************
942 * Quote special characters string *
943 *************************************************/
947 header header field to quote for suitable use in Exim expansions
950 Returns: quoted string
953 static const uschar *quote(const struct String *header)
968 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
975 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
979 quoted=string_cat(quoted,&size,&ptr,h,1);
985 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
990 /*************************************************
991 * Add address to list of generated addresses *
992 *************************************************/
995 According to RFC 3028, duplicate delivery to the same address must
996 not happen, so the list is first searched for the address.
999 generated list of generated addresses
1000 addr new address to add
1001 file address denotes a file
1006 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1008 address_item *new_addr;
1010 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1012 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1014 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1016 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1022 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1024 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1026 new_addr=deliver_make_addr(addr,TRUE);
1029 setflag(new_addr, af_pfr|af_file);
1032 new_addr->p.errors_address = NULL;
1033 new_addr->next = *generated;
1034 *generated = new_addr;
1038 /*************************************************
1039 * Return decoded header field *
1040 *************************************************/
1043 Unfold the header field as described in RFC 2822 and remove all
1044 leading and trailing white space, then perform MIME decoding and
1045 translate the header field to UTF-8.
1048 value returned value of the field
1049 header name of the header field
1051 Returns: nothing The expanded string is empty
1052 in case there is no such header
1055 static void expand_header(struct String *value, const struct String *header)
1061 value->character=(uschar*)0;
1063 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1064 while (*r==' ' || *r=='\t') ++r;
1072 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1074 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1078 /*************************************************
1079 * Parse remaining hash comment *
1080 *************************************************/
1084 Comment up to terminating CRLF
1087 filter points to the Sieve filter including its state
1093 static int parse_hashcomment(struct Sieve *filter)
1099 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1101 if (*filter->pc=='\n')
1114 filter->errmsg=CUS "missing end of comment";
1119 /*************************************************
1120 * Parse remaining C-style comment *
1121 *************************************************/
1125 Everything up to star slash
1128 filter points to the Sieve filter including its state
1134 static int parse_comment(struct Sieve *filter)
1139 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1146 filter->errmsg=CUS "missing end of comment";
1151 /*************************************************
1152 * Parse optional white space *
1153 *************************************************/
1157 Spaces, tabs, CRLFs, hash comments or C-style comments
1160 filter points to the Sieve filter including its state
1166 static int parse_white(struct Sieve *filter)
1170 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1172 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1174 else if (*filter->pc=='\n')
1184 else if (*filter->pc=='#')
1186 if (parse_hashcomment(filter)==-1) return -1;
1188 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1190 if (parse_comment(filter)==-1) return -1;
1198 #ifdef ENCODED_CHARACTER
1199 /*************************************************
1200 * Decode hex-encoded-character string *
1201 *************************************************/
1204 Encoding definition:
1205 blank = SP / TAB / CRLF
1206 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
1207 hex-pair = 1*2HEXDIG
1210 src points to a hex-pair-seq
1211 end points to its end
1212 dst points to the destination of the decoded octets,
1213 optionally to (uschar*)0 for checking only
1215 Returns: >=0 number of decoded octets
1219 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1223 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1228 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);
1229 if (d==0) return -1;
1232 if (src==end) return decoded;
1233 if (*src==' ' || *src=='\t' || *src=='\n')
1234 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1243 /*************************************************
1244 * Decode unicode-encoded-character string *
1245 *************************************************/
1248 Encoding definition:
1249 blank = SP / TAB / CRLF
1250 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1251 unicode-hex = 1*HEXDIG
1253 It is an error for a script to use a hexadecimal value that isn't in
1254 either the range 0 to D7FF or the range E000 to 10FFFF.
1256 At this time, strings are already scanned, thus the CRLF is converted
1257 to the internally used \n (should RFC_EOL have been used).
1260 src points to a unicode-hex-seq
1261 end points to its end
1262 dst points to the destination of the decoded octets,
1263 optionally to (uschar*)0 for checking only
1265 Returns: >=0 number of decoded octets
1267 -2 semantic error (character range violation)
1270 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1274 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1281 for (hex_seq=src; src<end && *src=='0'; ++src);
1282 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);
1283 if (src==hex_seq) return -1;
1284 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1290 else if (c>=0x80 && c<=0x7ff)
1295 *dst++=128+(c&0x3f);
1299 else if (c>=0x800 && c<=0xffff)
1304 *dst++=128+((c>>6)&0x3f);
1305 *dst++=128+(c&0x3f);
1309 else if (c>=0x10000 && c<=0x1fffff)
1314 *dst++=128+((c>>10)&0x3f);
1315 *dst++=128+((c>>6)&0x3f);
1316 *dst++=128+(c&0x3f);
1320 if (*src==' ' || *src=='\t' || *src=='\n')
1322 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1323 if (src==end) return decoded;
1332 /*************************************************
1333 * Decode encoded-character string *
1334 *************************************************/
1337 Encoding definition:
1338 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1339 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1342 encoded points to an encoded string, returns decoded string
1343 filter points to the Sieve filter including its state
1349 static int string_decode(struct Sieve *filter, struct String *data)
1351 uschar *src,*dst,*end;
1353 src=data->character;
1355 end=data->character+data->length;
1361 strncmpic(src,US "${hex:",6)==0
1362 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1363 && (hex_decode(src+6,brace,(uschar*)0))>=0
1366 dst+=hex_decode(src+6,brace,dst);
1370 strncmpic(src,US "${unicode:",10)==0
1371 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1374 switch (unicode_decode(src+10,brace,(uschar*)0))
1378 filter->errmsg=CUS "unicode character out of range";
1388 dst+=unicode_decode(src+10,brace,dst);
1395 data->length=dst-data->character;
1402 /*************************************************
1403 * Parse an optional string *
1404 *************************************************/
1408 quoted-string = DQUOTE *CHAR DQUOTE
1409 ;; in general, \ CHAR inside a string maps to CHAR
1410 ;; so \" maps to " and \\ maps to \
1411 ;; note that newlines and other characters are all allowed
1414 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1415 *(multi-line-literal / multi-line-dotstuff)
1417 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1418 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1419 ;; A line containing only "." ends the multi-line.
1420 ;; Remove a leading '.' if followed by another '.'.
1421 string = quoted-string / multi-line
1424 filter points to the Sieve filter including its state
1425 id specifies identifier to match
1429 0 identifier not matched
1432 static int parse_string(struct Sieve *filter, struct String *data)
1437 data->character=(uschar*)0;
1438 if (*filter->pc=='"') /* quoted string */
1443 if (*filter->pc=='"') /* end of string */
1445 int foo=data->length;
1448 /* that way, there will be at least one character allocated */
1449 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1450 #ifdef ENCODED_CHARACTER
1451 if (filter->require_encoded_character
1452 && string_decode(filter,data)==-1)
1457 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1459 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1460 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1463 else /* regular character */
1466 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1468 if (*filter->pc=='\n')
1470 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1474 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1478 filter->errmsg=CUS "missing end of string";
1481 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1484 /* skip optional white space followed by hashed comment or CRLF */
1485 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1486 if (*filter->pc=='#')
1488 if (parse_hashcomment(filter)==-1) return -1;
1491 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1493 else if (*filter->pc=='\n')
1505 filter->errmsg=CUS "syntax error";
1511 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1513 if (*filter->pc=='\n') /* end of line */
1516 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1524 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1526 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1529 int foo=data->length;
1531 /* that way, there will be at least one character allocated */
1532 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1539 #ifdef ENCODED_CHARACTER
1540 if (filter->require_encoded_character
1541 && string_decode(filter,data)==-1)
1546 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1548 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1552 else /* regular character */
1554 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1558 filter->errmsg=CUS "missing end of multi line string";
1565 /*************************************************
1566 * Parse a specific identifier *
1567 *************************************************/
1571 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1574 filter points to the Sieve filter including its state
1575 id specifies identifier to match
1578 0 identifier not matched
1581 static int parse_identifier(struct Sieve *filter, const uschar *id)
1583 size_t idlen=Ustrlen(id);
1585 if (strncmpic(US filter->pc,US id,idlen)==0)
1587 uschar next=filter->pc[idlen];
1589 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1597 /*************************************************
1599 *************************************************/
1603 number = 1*DIGIT [QUANTIFIER]
1604 QUANTIFIER = "K" / "M" / "G"
1607 filter points to the Sieve filter including its state
1611 -1 no string list found
1614 static int parse_number(struct Sieve *filter, unsigned long *data)
1618 if (*filter->pc>='0' && *filter->pc<='9')
1623 d=Ustrtoul(filter->pc,&e,10);
1626 filter->errmsg=CUstrerror(ERANGE);
1631 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1632 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1633 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1634 if (d>(ULONG_MAX/u))
1636 filter->errmsg=CUstrerror(ERANGE);
1645 filter->errmsg=CUS "missing number";
1651 /*************************************************
1652 * Parse a string list *
1653 *************************************************/
1657 string-list = "[" string *("," string) "]" / string
1660 filter points to the Sieve filter including its state
1661 data returns string list
1664 -1 no string list found
1667 static int parse_stringlist(struct Sieve *filter, struct String **data)
1669 const uschar *orig=filter->pc;
1672 struct String *d=(struct String*)0;
1675 if (*filter->pc=='[') /* string list */
1680 if (parse_white(filter)==-1) goto error;
1681 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1684 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1685 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1686 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1688 filter->errmsg=CUstrerror(errno);
1691 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1693 dataCapacity=newCapacity;
1695 m=parse_string(filter,&d[dataLength]);
1698 if (dataLength==0) break;
1701 filter->errmsg=CUS "missing string";
1705 else if (m==-1) goto error;
1707 if (parse_white(filter)==-1) goto error;
1708 if (*filter->pc==',') ++filter->pc;
1711 if (*filter->pc==']')
1713 d[dataLength].character=(uschar*)0;
1714 d[dataLength].length=-1;
1721 filter->errmsg=CUS "missing closing bracket";
1725 else /* single string */
1727 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1731 m=parse_string(filter,&d[0]);
1743 d[1].character=(uschar*)0;
1750 filter->errmsg=CUS "missing string list";
1755 /*************************************************
1756 * Parse an optional address part specifier *
1757 *************************************************/
1761 address-part = ":localpart" / ":domain" / ":all"
1762 address-part =/ ":user" / ":detail"
1765 filter points to the Sieve filter including its state
1766 a returns address part specified
1769 0 no comparator found
1773 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1776 if (parse_identifier(filter,CUS ":user")==1)
1778 if (!filter->require_subaddress)
1780 filter->errmsg=CUS "missing previous require \"subaddress\";";
1786 else if (parse_identifier(filter,CUS ":detail")==1)
1788 if (!filter->require_subaddress)
1790 filter->errmsg=CUS "missing previous require \"subaddress\";";
1798 if (parse_identifier(filter,CUS ":localpart")==1)
1800 *a=ADDRPART_LOCALPART;
1803 else if (parse_identifier(filter,CUS ":domain")==1)
1808 else if (parse_identifier(filter,CUS ":all")==1)
1817 /*************************************************
1818 * Parse an optional comparator *
1819 *************************************************/
1823 comparator = ":comparator" <comparator-name: string>
1826 filter points to the Sieve filter including its state
1827 c returns comparator
1830 0 no comparator found
1831 -1 incomplete comparator found
1834 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1836 struct String comparator_name;
1838 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1839 if (parse_white(filter)==-1) return -1;
1840 switch (parse_string(filter,&comparator_name))
1845 filter->errmsg=CUS "missing comparator";
1852 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1857 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1859 *c=COMP_EN_ASCII_CASEMAP;
1862 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1864 *c=COMP_EN_ASCII_CASEMAP;
1867 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1869 *c=COMP_ASCII_NUMERIC;
1874 filter->errmsg=CUS "invalid comparator";
1883 /*************************************************
1884 * Parse an optional match type *
1885 *************************************************/
1889 match-type = ":is" / ":contains" / ":matches"
1892 filter points to the Sieve filter including its state
1893 m returns match type
1896 0 no match type found
1899 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1901 if (parse_identifier(filter,CUS ":is")==1)
1906 else if (parse_identifier(filter,CUS ":contains")==1)
1911 else if (parse_identifier(filter,CUS ":matches")==1)
1920 /*************************************************
1921 * Parse and interpret an optional test list *
1922 *************************************************/
1926 test-list = "(" test *("," test) ")"
1929 filter points to the Sieve filter including its state
1930 n total number of tests
1931 true number of passed tests
1932 exec Execute parsed statements
1935 0 no test list found
1936 -1 syntax or execution error
1939 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1941 if (parse_white(filter)==-1) return -1;
1942 if (*filter->pc=='(')
1951 switch (parse_test(filter,&cond,exec))
1954 case 0: filter->errmsg=CUS "missing test"; return -1;
1955 default: ++*n; if (cond) ++*true; break;
1957 if (parse_white(filter)==-1) return -1;
1958 if (*filter->pc==',') ++filter->pc;
1961 if (*filter->pc==')')
1968 filter->errmsg=CUS "missing closing paren";
1976 /*************************************************
1977 * Parse and interpret an optional test *
1978 *************************************************/
1982 filter points to the Sieve filter including its state
1983 cond returned condition status
1984 exec Execute parsed statements
1988 -1 syntax or execution error
1991 static int parse_test(struct Sieve *filter, int *cond, int exec)
1993 if (parse_white(filter)==-1) return -1;
1994 if (parse_identifier(filter,CUS "address"))
1997 address-test = "address" { [address-part] [comparator] [match-type] }
1998 <header-list: string-list> <key-list: string-list>
2000 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2003 enum AddressPart addressPart=ADDRPART_ALL;
2004 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2005 enum MatchType matchType=MATCH_IS;
2006 struct String *hdr,*h,*key,*k;
2012 if (parse_white(filter)==-1) return -1;
2013 if ((m=parse_addresspart(filter,&addressPart))!=0)
2015 if (m==-1) return -1;
2018 filter->errmsg=CUS "address part already specified";
2023 else if ((m=parse_comparator(filter,&comparator))!=0)
2025 if (m==-1) return -1;
2028 filter->errmsg=CUS "comparator already specified";
2033 else if ((m=parse_matchtype(filter,&matchType))!=0)
2035 if (m==-1) return -1;
2038 filter->errmsg=CUS "match type already specified";
2045 if (parse_white(filter)==-1) return -1;
2046 if ((m=parse_stringlist(filter,&hdr))!=1)
2048 if (m==0) filter->errmsg=CUS "header string list expected";
2051 if (parse_white(filter)==-1) return -1;
2052 if ((m=parse_stringlist(filter,&key))!=1)
2054 if (m==0) filter->errmsg=CUS "key string list expected";
2058 for (h=hdr; h->length!=-1 && !*cond; ++h)
2060 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2064 !eq_asciicase(h,&str_from,0)
2065 && !eq_asciicase(h,&str_to,0)
2066 && !eq_asciicase(h,&str_cc,0)
2067 && !eq_asciicase(h,&str_bcc,0)
2068 && !eq_asciicase(h,&str_sender,0)
2069 && !eq_asciicase(h,&str_resent_from,0)
2070 && !eq_asciicase(h,&str_resent_to,0)
2073 filter->errmsg=CUS "invalid header field";
2078 /* We are only interested in addresses below, so no MIME decoding */
2079 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2080 if (header_value == NULL)
2082 filter->errmsg=CUS "header string expansion failed";
2085 parse_allow_group = TRUE;
2086 while (*header_value && !*cond)
2089 int start, end, domain;
2093 end_addr = parse_find_address_end(header_value, FALSE);
2094 saveend = *end_addr;
2096 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2098 if (extracted_addr) switch (addressPart)
2100 case ADDRPART_ALL: part=extracted_addr; break;
2104 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2105 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2107 case ADDRPART_DETAIL: part=NULL; break;
2111 *end_addr = saveend;
2114 for (k=key; k->length!=-1; ++k)
2116 struct String partStr;
2118 partStr.character=part;
2119 partStr.length=Ustrlen(part);
2122 *cond=compare(filter,k,&partStr,comparator,matchType);
2123 if (*cond==-1) return -1;
2128 if (saveend == 0) break;
2129 header_value = end_addr + 1;
2131 parse_allow_group = FALSE;
2132 parse_found_group = FALSE;
2137 else if (parse_identifier(filter,CUS "allof"))
2140 allof-test = "allof" <tests: test-list>
2145 switch (parse_testlist(filter,&n,&true,exec))
2148 case 0: filter->errmsg=CUS "missing test list"; return -1;
2149 default: *cond=(n==true); return 1;
2152 else if (parse_identifier(filter,CUS "anyof"))
2155 anyof-test = "anyof" <tests: test-list>
2160 switch (parse_testlist(filter,&n,&true,exec))
2163 case 0: filter->errmsg=CUS "missing test list"; return -1;
2164 default: *cond=(true>0); return 1;
2167 else if (parse_identifier(filter,CUS "exists"))
2170 exists-test = "exists" <header-names: string-list>
2173 struct String *hdr,*h;
2176 if (parse_white(filter)==-1) return -1;
2177 if ((m=parse_stringlist(filter,&hdr))!=1)
2179 if (m==0) filter->errmsg=CUS "header string list expected";
2185 for (h=hdr; h->length!=-1 && *cond; ++h)
2189 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2190 if (header_def == NULL)
2192 filter->errmsg=CUS "header string expansion failed";
2195 if (Ustrcmp(header_def,"false")==0) *cond=0;
2200 else if (parse_identifier(filter,CUS "false"))
2203 false-test = "false"
2209 else if (parse_identifier(filter,CUS "header"))
2212 header-test = "header" { [comparator] [match-type] }
2213 <header-names: string-list> <key-list: string-list>
2216 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2217 enum MatchType matchType=MATCH_IS;
2218 struct String *hdr,*h,*key,*k;
2224 if (parse_white(filter)==-1) return -1;
2225 if ((m=parse_comparator(filter,&comparator))!=0)
2227 if (m==-1) return -1;
2230 filter->errmsg=CUS "comparator already specified";
2235 else if ((m=parse_matchtype(filter,&matchType))!=0)
2237 if (m==-1) return -1;
2240 filter->errmsg=CUS "match type already specified";
2247 if (parse_white(filter)==-1) return -1;
2248 if ((m=parse_stringlist(filter,&hdr))!=1)
2250 if (m==0) filter->errmsg=CUS "header string list expected";
2253 if (parse_white(filter)==-1) return -1;
2254 if ((m=parse_stringlist(filter,&key))!=1)
2256 if (m==0) filter->errmsg=CUS "key string list expected";
2260 for (h=hdr; h->length!=-1 && !*cond; ++h)
2264 filter->errmsg=CUS "invalid header field";
2269 struct String header_value;
2272 expand_header(&header_value,h);
2273 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2274 if (header_value.character == NULL || header_def == NULL)
2276 filter->errmsg=CUS "header string expansion failed";
2279 for (k=key; k->length!=-1; ++k)
2281 if (Ustrcmp(header_def,"true")==0)
2283 *cond=compare(filter,k,&header_value,comparator,matchType);
2284 if (*cond==-1) return -1;
2292 else if (parse_identifier(filter,CUS "not"))
2294 if (parse_white(filter)==-1) return -1;
2295 switch (parse_test(filter,cond,exec))
2298 case 0: filter->errmsg=CUS "missing test"; return -1;
2299 default: *cond=!*cond; return 1;
2302 else if (parse_identifier(filter,CUS "size"))
2305 relop = ":over" / ":under"
2306 size-test = "size" relop <limit: number>
2309 unsigned long limit;
2312 if (parse_white(filter)==-1) return -1;
2313 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2314 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2317 filter->errmsg=CUS "missing :over or :under";
2320 if (parse_white(filter)==-1) return -1;
2321 if (parse_number(filter,&limit)==-1) return -1;
2322 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2325 else if (parse_identifier(filter,CUS "true"))
2330 else if (parse_identifier(filter,CUS "envelope"))
2333 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2334 <envelope-part: string-list> <key-list: string-list>
2336 envelope-part is case insensitive "from" or "to"
2337 #ifdef ENVELOPE_AUTH
2338 envelope-part =/ "auth"
2342 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2343 enum AddressPart addressPart=ADDRPART_ALL;
2344 enum MatchType matchType=MATCH_IS;
2345 struct String *env,*e,*key,*k;
2349 if (!filter->require_envelope)
2351 filter->errmsg=CUS "missing previous require \"envelope\";";
2356 if (parse_white(filter)==-1) return -1;
2357 if ((m=parse_comparator(filter,&comparator))!=0)
2359 if (m==-1) return -1;
2362 filter->errmsg=CUS "comparator already specified";
2367 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2369 if (m==-1) return -1;
2372 filter->errmsg=CUS "address part already specified";
2377 else if ((m=parse_matchtype(filter,&matchType))!=0)
2379 if (m==-1) return -1;
2382 filter->errmsg=CUS "match type already specified";
2389 if (parse_white(filter)==-1) return -1;
2390 if ((m=parse_stringlist(filter,&env))!=1)
2392 if (m==0) filter->errmsg=CUS "envelope string list expected";
2395 if (parse_white(filter)==-1) return -1;
2396 if ((m=parse_stringlist(filter,&key))!=1)
2398 if (m==0) filter->errmsg=CUS "key string list expected";
2402 for (e=env; e->length!=-1 && !*cond; ++e)
2404 const uschar *envelopeExpr=CUS 0;
2405 uschar *envelope=US 0;
2407 if (eq_asciicase(e,&str_from,0))
2409 switch (addressPart)
2411 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2415 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2416 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2418 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2422 else if (eq_asciicase(e,&str_to,0))
2424 switch (addressPart)
2426 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2428 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2429 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2431 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2432 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2435 #ifdef ENVELOPE_AUTH
2436 else if (eq_asciicase(e,&str_auth,0))
2438 switch (addressPart)
2440 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2444 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2445 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2447 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2454 filter->errmsg=CUS "invalid envelope string";
2457 if (exec && envelopeExpr)
2459 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2461 filter->errmsg=CUS "header string expansion failed";
2464 for (k=key; k->length!=-1; ++k)
2466 struct String envelopeStr;
2468 envelopeStr.character=envelope;
2469 envelopeStr.length=Ustrlen(envelope);
2470 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2471 if (*cond==-1) return -1;
2479 else if (parse_identifier(filter,CUS "valid_notif_method"))
2482 valid_notif_method = "valid_notif_method"
2483 <notification-uris: string-list>
2486 struct String *uris,*u;
2489 if (!filter->require_enotify)
2491 filter->errmsg=CUS "missing previous require \"enotify\";";
2494 if (parse_white(filter)==-1) return -1;
2495 if ((m=parse_stringlist(filter,&uris))!=1)
2497 if (m==0) filter->errmsg=CUS "URI string list expected";
2503 for (u=uris; u->length!=-1 && *cond; ++u)
2505 string_item *recipient;
2506 struct String header,body;
2510 header.character=(uschar*)0;
2512 body.character=(uschar*)0;
2513 if (parse_mailto_uri(filter,u->character,&recipient,&header,&body)!=1)
2524 /*************************************************
2525 * Parse and interpret an optional block *
2526 *************************************************/
2530 filter points to the Sieve filter including its state
2531 exec Execute parsed statements
2532 generated where to hang newly-generated addresses
2534 Returns: 2 success by stop
2536 0 no block command found
2537 -1 syntax or execution error
2540 static int parse_block(struct Sieve *filter, int exec,
2541 address_item **generated)
2545 if (parse_white(filter)==-1) return -1;
2546 if (*filter->pc=='{')
2549 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2550 if (*filter->pc=='}')
2557 filter->errmsg=CUS "expecting command or closing brace";
2565 /*************************************************
2566 * Match a semicolon *
2567 *************************************************/
2571 filter points to the Sieve filter including its state
2577 static int parse_semicolon(struct Sieve *filter)
2579 if (parse_white(filter)==-1) return -1;
2580 if (*filter->pc==';')
2587 filter->errmsg=CUS "missing semicolon";
2593 /*************************************************
2594 * Parse and interpret a Sieve command *
2595 *************************************************/
2599 filter points to the Sieve filter including its state
2600 exec Execute parsed statements
2601 generated where to hang newly-generated addresses
2603 Returns: 2 success by stop
2605 -1 syntax or execution error
2607 static int parse_commands(struct Sieve *filter, int exec,
2608 address_item **generated)
2612 if (parse_white(filter)==-1) return -1;
2613 if (parse_identifier(filter,CUS "if"))
2616 if-command = "if" test block *( "elsif" test block ) [ else block ]
2619 int cond,m,unsuccessful;
2622 if (parse_white(filter)==-1) return -1;
2623 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2626 filter->errmsg=CUS "missing test";
2629 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2630 (debug_selector & D_filter) != 0)
2632 if (exec) debug_printf("if %s\n",cond?"true":"false");
2634 m=parse_block(filter,exec ? cond : 0, generated);
2635 if (m==-1 || m==2) return m;
2638 filter->errmsg=CUS "missing block";
2641 unsuccessful = !cond;
2642 for (;;) /* elsif test block */
2644 if (parse_white(filter)==-1) return -1;
2645 if (parse_identifier(filter,CUS "elsif"))
2647 if (parse_white(filter)==-1) return -1;
2648 m=parse_test(filter,&cond,exec && unsuccessful);
2649 if (m==-1 || m==2) return m;
2652 filter->errmsg=CUS "missing test";
2655 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2656 (debug_selector & D_filter) != 0)
2658 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2660 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2661 if (m==-1 || m==2) return m;
2664 filter->errmsg=CUS "missing block";
2667 if (exec && unsuccessful && cond) unsuccessful = 0;
2672 if (parse_white(filter)==-1) return -1;
2673 if (parse_identifier(filter,CUS "else"))
2675 m=parse_block(filter,exec && unsuccessful, generated);
2676 if (m==-1 || m==2) return m;
2679 filter->errmsg=CUS "missing block";
2684 else if (parse_identifier(filter,CUS "stop"))
2687 stop-command = "stop" { stop-options } ";"
2691 if (parse_semicolon(filter)==-1) return -1;
2694 filter->pc+=Ustrlen(filter->pc);
2698 else if (parse_identifier(filter,CUS "keep"))
2701 keep-command = "keep" { keep-options } ";"
2705 if (parse_semicolon(filter)==-1) return -1;
2708 add_addr(generated,US"inbox",1,0,0,0);
2712 else if (parse_identifier(filter,CUS "discard"))
2715 discard-command = "discard" { discard-options } ";"
2719 if (parse_semicolon(filter)==-1) return -1;
2720 if (exec) filter->keep=0;
2722 else if (parse_identifier(filter,CUS "redirect"))
2725 redirect-command = "redirect" redirect-options "string" ";"
2727 redirect-options =) ":copy"
2730 struct String recipient;
2736 if (parse_white(filter)==-1) return -1;
2737 if (parse_identifier(filter,CUS ":copy")==1)
2739 if (!filter->require_copy)
2741 filter->errmsg=CUS "missing previous require \"copy\";";
2748 if (parse_white(filter)==-1) return -1;
2749 if ((m=parse_string(filter,&recipient))!=1)
2751 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2754 if (strchr(CCS recipient.character,'@')==(char*)0)
2756 filter->errmsg=CUS "unqualified recipient address";
2761 add_addr(generated,recipient.character,0,0,0,0);
2762 if (!copy) filter->keep = 0;
2764 if (parse_semicolon(filter)==-1) return -1;
2766 else if (parse_identifier(filter,CUS "fileinto"))
2769 fileinto-command = "fileinto" { fileinto-options } string ";"
2771 fileinto-options =) [ ":copy" ]
2774 struct String folder;
2777 unsigned long maxage, maxmessages, maxstorage;
2780 maxage = maxmessages = maxstorage = 0;
2781 if (!filter->require_fileinto)
2783 filter->errmsg=CUS "missing previous require \"fileinto\";";
2788 if (parse_white(filter)==-1) return -1;
2789 if (parse_identifier(filter,CUS ":copy")==1)
2791 if (!filter->require_copy)
2793 filter->errmsg=CUS "missing previous require \"copy\";";
2800 if (parse_white(filter)==-1) return -1;
2801 if ((m=parse_string(filter,&folder))!=1)
2803 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2806 m=0; s=folder.character;
2807 if (folder.length==0) m=1;
2808 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2811 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2816 filter->errmsg=CUS "invalid folder";
2821 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2822 if (!copy) filter->keep = 0;
2824 if (parse_semicolon(filter)==-1) return -1;
2827 else if (parse_identifier(filter,CUS "notify"))
2830 notify-command = "notify" { notify-options } <method: string> ";"
2831 notify-options = [":from" string]
2832 [":importance" <"1" / "2" / "3">]
2833 [":options" 1*(string-list / number)]
2839 struct String importance;
2840 struct String *options;
2841 struct String message;
2842 struct String method;
2843 struct Notification *already;
2844 string_item *recipient;
2845 struct String header;
2847 uschar *envelope_from,*envelope_to;
2849 if (!filter->require_enotify)
2851 filter->errmsg=CUS "missing previous require \"enotify\";";
2854 from.character=(uschar*)0;
2856 importance.character=(uschar*)0;
2857 importance.length=-1;
2858 options=(struct String*)0;
2859 message.character=(uschar*)0;
2863 header.character=(uschar*)0;
2865 body.character=(uschar*)0;
2866 envelope_from=expand_string("$sender_address");
2867 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2870 if (parse_white(filter)==-1) return -1;
2871 if (parse_identifier(filter,CUS ":from")==1)
2873 if (parse_white(filter)==-1) return -1;
2874 if ((m=parse_string(filter,&from))!=1)
2876 if (m==0) filter->errmsg=CUS "from string expected";
2880 else if (parse_identifier(filter,CUS ":importance")==1)
2882 if (parse_white(filter)==-1) return -1;
2883 if ((m=parse_string(filter,&importance))!=1)
2885 if (m==0) filter->errmsg=CUS "importance string expected";
2888 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2890 filter->errmsg=CUS "invalid importance";
2894 else if (parse_identifier(filter,CUS ":options")==1)
2896 if (parse_white(filter)==-1) return -1;
2898 else if (parse_identifier(filter,CUS ":message")==1)
2900 if (parse_white(filter)==-1) return -1;
2901 if ((m=parse_string(filter,&message))!=1)
2903 if (m==0) filter->errmsg=CUS "message string expected";
2909 if (parse_white(filter)==-1) return -1;
2910 if ((m=parse_string(filter,&method))!=1)
2912 if (m==0) filter->errmsg=CUS "missing method string";
2915 if (parse_semicolon(filter)==-1) return -1;
2917 for (already=filter->notified; already; already=already->next)
2919 if (already->method.length==method.length
2920 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
2921 && already->importance.length==importance.length
2922 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
2923 && already->message.length==message.length
2924 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
2927 if (already==(struct Notification*)0)
2928 /* New notification, process it */
2930 if (parse_mailto_uri(filter,method.character,&recipient,&header,&body)!=1)
2932 struct Notification *sent;
2933 sent=store_get(sizeof(struct Notification));
2934 sent->method=method;
2935 sent->importance=importance;
2936 sent->message=message;
2937 sent->next=filter->notified;
2938 filter->notified=sent;
2939 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2941 debug_printf("Notification to `%s'.\n",method.character);
2943 #ifndef COMPILE_SYNTAX_CHECKER
2944 if (exec && filter_test == FTEST_NONE)
2950 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
2954 int buffer_capacity;
2956 f = fdopen(fd, "wb");
2957 for (h = header_list; h != NULL; h = h->next)
2958 if (h->type == htype_received) fprintf(f,"%s",h->text);
2959 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
2960 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
2961 if (header.length>0) fprintf(f,"%s",header.character);
2962 if (message.length==-1)
2964 message.character=US"Notification";
2965 message.length=Ustrlen(message.character);
2967 /* Allocation is larger than neccessary, but enough even for split MIME words */
2968 buffer_capacity=32+4*message.length;
2969 buffer=store_get(buffer_capacity);
2970 fprintf(f,"Subject: %s\n\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
2971 if (body.length>0) fprintf(f,"%s\n",body.character);
2974 (void)child_close(pid, 0);
2981 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2983 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
2989 else if (parse_identifier(filter,CUS "vacation"))
2992 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2993 vacation-options = [":days" number]
2996 [":addresses" string-list]
3003 struct String subject;
3005 struct String *addresses;
3007 string_item *aliases;
3008 struct String handle;
3009 struct String reason;
3011 if (!filter->require_vacation)
3013 filter->errmsg=CUS "missing previous require \"vacation\";";
3018 if (filter->vacation_ran)
3020 filter->errmsg=CUS "trying to execute vacation more than once";
3023 filter->vacation_ran=1;
3025 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3026 subject.character=(uschar*)0;
3028 from.character=(uschar*)0;
3030 addresses=(struct String*)0;
3033 handle.character=(uschar*)0;
3037 if (parse_white(filter)==-1) return -1;
3038 if (parse_identifier(filter,CUS ":days")==1)
3040 if (parse_white(filter)==-1) return -1;
3041 if (parse_number(filter,&days)==-1) return -1;
3042 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3043 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3045 else if (parse_identifier(filter,CUS ":subject")==1)
3047 if (parse_white(filter)==-1) return -1;
3048 if ((m=parse_string(filter,&subject))!=1)
3050 if (m==0) filter->errmsg=CUS "subject string expected";
3054 else if (parse_identifier(filter,CUS ":from")==1)
3056 if (parse_white(filter)==-1) return -1;
3057 if ((m=parse_string(filter,&from))!=1)
3059 if (m==0) filter->errmsg=CUS "from string expected";
3062 if (check_mail_address(filter,&from)!=1)
3065 else if (parse_identifier(filter,CUS ":addresses")==1)
3069 if (parse_white(filter)==-1) return -1;
3070 if ((m=parse_stringlist(filter,&addresses))!=1)
3072 if (m==0) filter->errmsg=CUS "addresses string list expected";
3075 for (a=addresses; a->length!=-1; ++a)
3079 new=store_get(sizeof(string_item));
3080 new->text=store_get(a->length+1);
3081 if (a->length) memcpy(new->text,a->character,a->length);
3082 new->text[a->length]='\0';
3087 else if (parse_identifier(filter,CUS ":mime")==1)
3089 else if (parse_identifier(filter,CUS ":handle")==1)
3091 if (parse_white(filter)==-1) return -1;
3092 if ((m=parse_string(filter,&from))!=1)
3094 if (m==0) filter->errmsg=CUS "handle string expected";
3100 if (parse_white(filter)==-1) return -1;
3101 if ((m=parse_string(filter,&reason))!=1)
3103 if (m==0) filter->errmsg=CUS "missing reason string";
3110 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3113 filter->errmsg=CUS "MIME reason string contains 8bit text";
3117 if (parse_semicolon(filter)==-1) return -1;
3124 int buffer_capacity;
3128 uschar hexdigest[33];
3132 if (filter_personal(aliases,TRUE))
3134 if (filter_test == FTEST_NONE)
3136 /* ensure oncelog directory exists; failure will be detected later */
3138 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3140 /* build oncelog filename */
3142 key.character=(uschar*)0;
3145 if (handle.length==-1)
3147 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3148 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3149 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3150 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3155 md5_end(&base, key.character, key.length, digest);
3156 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3157 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3159 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3161 if (filter_test == FTEST_NONE)
3163 capacity=Ustrlen(filter->vacation_directory);
3165 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3166 once=string_cat(once,&capacity,&start,hexdigest,33);
3169 /* process subject */
3171 if (subject.length==-1)
3173 uschar *subject_def;
3175 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3176 if (Ustrcmp(subject_def,"true")==0)
3178 expand_header(&subject,&str_subject);
3181 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3182 subject.length=start;
3186 subject.character=US"Automated reply";
3187 subject.length=Ustrlen(subject.character);
3191 /* add address to list of generated addresses */
3193 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3194 setflag(addr, af_pfr);
3195 setflag(addr, af_ignore_error);
3196 addr->next = *generated;
3198 addr->reply = store_get(sizeof(reply_item));
3199 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3200 addr->reply->to = string_copy(sender_address);
3201 if (from.length==-1)
3202 addr->reply->from = expand_string(US"$local_part@$domain");
3204 addr->reply->from = from.character;
3205 /* Allocation is larger than neccessary, but enough even for split MIME words */
3206 buffer_capacity=32+4*subject.length;
3207 buffer=store_get(buffer_capacity);
3208 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3209 addr->reply->oncelog=once;
3210 addr->reply->once_repeat=days*86400;
3212 /* build body and MIME headers */
3216 uschar *mime_body,*reason_end;
3217 static const uschar nlnl[]="\r\n\r\n";
3221 mime_body=reason.character,reason_end=reason.character+reason.length;
3222 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3227 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3228 addr->reply->headers[start] = '\0';
3231 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3232 else mime_body=reason_end-1;
3233 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3234 addr->reply->text[start] = '\0';
3238 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3241 start = reason.length;
3242 addr->reply->headers = US"MIME-Version: 1.0\n"
3243 "Content-Type: text/plain;\n"
3244 "\tcharset=\"utf-8\"\n"
3245 "Content-Transfer-Encoding: quoted-printable";
3246 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3250 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3252 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3263 /*************************************************
3264 * Parse and interpret a sieve filter *
3265 *************************************************/
3269 filter points to the Sieve filter including its state
3270 exec Execute parsed statements
3271 generated where to hang newly-generated addresses
3274 -1 syntax or execution error
3277 static int parse_start(struct Sieve *filter, int exec,
3278 address_item **generated)
3280 filter->pc=filter->filter;
3283 filter->require_envelope=0;
3284 filter->require_fileinto=0;
3285 #ifdef ENCODED_CHARACTER
3286 filter->require_encoded_character=0;
3288 #ifdef ENVELOPE_AUTH
3289 filter->require_envelope_auth=0;
3292 filter->require_enotify=0;
3293 filter->notified=(struct Notification*)0;
3296 filter->require_subaddress=0;
3299 filter->require_vacation=0;
3300 filter->vacation_ran=0;
3302 filter->require_copy=0;
3303 filter->require_iascii_numeric=0;
3305 if (parse_white(filter)==-1) return -1;
3307 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3310 struct dirent *oncelog;
3311 struct stat properties;
3314 /* clean up old vacation log databases */
3316 oncelogdir=opendir(CS filter->vacation_directory);
3318 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3320 filter->errmsg=CUS "unable to open vacation directory";
3324 if (oncelogdir != NULL)
3328 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3330 if (strlen(oncelog->d_name)==32)
3332 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3333 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3337 closedir(oncelogdir);
3341 while (parse_identifier(filter,CUS "require"))
3344 require-command = "require" <capabilities: string-list>
3347 struct String *cap,*check;
3350 if (parse_white(filter)==-1) return -1;
3351 if ((m=parse_stringlist(filter,&cap))!=1)
3353 if (m==0) filter->errmsg=CUS "capability string list expected";
3356 for (check=cap; check->character; ++check)
3358 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3359 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3360 #ifdef ENCODED_CHARACTER
3361 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3363 #ifdef ENVELOPE_AUTH
3364 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3367 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3370 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3373 else if (eq_octet(check,&str_vacation,0))
3375 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3377 filter->errmsg=CUS "vacation disabled";
3380 filter->require_vacation=1;
3383 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3384 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3385 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3386 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3387 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3390 filter->errmsg=CUS "unknown capability";
3394 if (parse_semicolon(filter)==-1) return -1;
3396 if (parse_commands(filter,exec,generated)==-1) return -1;
3399 filter->errmsg=CUS "syntax error";
3406 /*************************************************
3407 * Interpret a sieve filter file *
3408 *************************************************/
3412 filter points to the entire file, read into store as a single string
3413 options controls whether various special things are allowed, and requests
3414 special actions (not currently used)
3415 sieve_vacation_directory where to store vacation "once" files
3416 useraddress string expression for :user part of address
3417 subaddress string expression for :subaddress part of address
3418 generated where to hang newly-generated addresses
3419 error where to pass back an error text
3421 Returns: FF_DELIVERED success, a significant action was taken
3422 FF_NOTDELIVERED success, no significant action
3423 FF_DEFER defer requested
3424 FF_FAIL fail requested
3425 FF_FREEZE freeze requested
3426 FF_ERROR there was a problem
3430 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3431 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3437 options = options; /* Keep picky compilers happy */
3440 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3441 sieve.filter=filter;
3443 if (vacation_directory == NULL)
3444 sieve.vacation_directory = NULL;
3447 sieve.vacation_directory=expand_string(vacation_directory);
3448 if (sieve.vacation_directory == NULL)
3450 *error = string_sprintf("failed to expand \"%s\" "
3451 "(sieve_vacation_directory): %s", vacation_directory,
3452 expand_string_message);
3457 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3458 sieve.subaddress = subaddress;
3460 #ifdef COMPILE_SYNTAX_CHECKER
3461 if (parse_start(&sieve,0,generated)==1)
3463 if (parse_start(&sieve,1,generated)==1)
3468 add_addr(generated,US"inbox",1,0,0,0);
3469 msg = string_sprintf("Implicit keep");
3474 msg = string_sprintf("No implicit keep");
3480 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3481 #ifdef COMPILE_SYNTAX_CHECKER
3485 add_addr(generated,US"inbox",1,0,0,0);
3490 #ifndef COMPILE_SYNTAX_CHECKER
3491 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3492 else debug_printf("%s\n", msg);
3495 DEBUG(D_route) debug_printf("Sieve: end of processing\n");