1 /* $Cambridge: exim/src/src/sieve.c,v 1.27 2007/04/12 09:00:52 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 encoded-character string *
1201 *************************************************/
1204 Encoding definition:
1205 hex-pair-seq = hex-pair *(WSP hex-pair)
1206 hex-pair = 1*2HEXDIG
1209 src points to a hex-pair-seq
1210 end points to its end
1211 dst points to the destination of the decoded octets,
1212 optionally to (uschar*)0 for checking only
1214 Returns: >=0 number of decoded octets
1218 static int hex_decode(uschar *src, uschar *end, uschar *dst)
1226 if (*src==' ' || *src=='\t') ++src;
1227 else if ((src+1)<end && isxdigit(h=tolower(*src)) && isxdigit(l=tolower(*(src+1))))
1229 h=(h>='0' && h<='9') ? h-'0' : 10+(h-'a');
1230 l=(l>='0' && l<='9') ? l-'0' : 10+(l-'a');
1231 if (dst) *dst++=(h<<4)|l;
1241 /*************************************************
1242 * Decode encoded-character string *
1243 *************************************************/
1246 Encoding definition:
1247 unicode-hex-seq = unicode-hex *(WSP unicode-hex)
1248 unicode-hex = 1*6HEXDIG
1250 It is an error for a script to use a hexadecimal value that isn't in
1251 either the range 0 to D7FF or the range E000 to 10FFFF.
1254 src points to a unicode-hex-seq
1255 end points to its end
1256 dst points to the destination of the decoded octets,
1257 optionally to (uschar*)0 for checking only
1259 Returns: >=0 number of decoded octets
1261 -2 semantic error (character range violation)
1264 static int unicode_decode(uschar *src, uschar *end, uschar *dst)
1270 int c,n5,n4,n3,n2,n1,n0;
1272 if (*src==' ' || *src=='\t') ++src;
1275 && isxdigit(n5=tolower(*src))
1276 && isxdigit(n4=tolower(*(src+1)))
1277 && isxdigit(n3=tolower(*(src+2)))
1278 && isxdigit(n2=tolower(*(src+3)))
1279 && isxdigit(n1=tolower(*(src+4)))
1280 && isxdigit(n0=tolower(*(src+5)))
1283 n5=(n5>='0' && n5<='9') ? n5-'0' : 10+(n5-'a');
1284 n4=(n4>='0' && n4<='9') ? n4-'0' : 10+(n4-'a');
1285 n3=(n3>='0' && n3<='9') ? n3-'0' : 10+(n3-'a');
1286 n2=(n2>='0' && n2<='9') ? n2-'0' : 10+(n2-'a');
1287 n1=(n1>='0' && n1<='9') ? n1-'0' : 10+(n1-'a');
1288 n0=(n0>='0' && n0<='9') ? n0-'0' : 10+(n0-'a');
1289 c=(n5<<24)|(n4<<16)|(n3<<12)|(n2<<8)|(n1<<4)|n0;
1290 if (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff))) return -2;
1296 else if (c>=0x80 && c<=0x7ff)
1301 *dst++=128+(c&0x3f);
1305 else if (c>=0x800 && c<=0xffff)
1310 *dst++=128+((c>>6)&0x3f);
1311 *dst++=128+(c&0x3f);
1315 else if (c>=0x10000 && c<=0x1fffff)
1320 *dst++=128+((c>>10)&0x3f);
1321 *dst++=128+((c>>6)&0x3f);
1322 *dst++=128+(c&0x3f);
1334 /*************************************************
1335 * Decode encoded-character string *
1336 *************************************************/
1339 Encoding definition:
1340 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1341 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1344 encoded points to an encoded string, returns decoded string
1345 filter points to the Sieve filter including its state
1351 static int string_decode(struct Sieve *filter, struct String *data)
1353 uschar *src,*dst,*end;
1355 src=data->character;
1357 end=data->character+data->length;
1363 Ustrncmp(src,CUS "${hex:",6)==0
1364 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1365 && (hex_decode(src+6,brace,(uschar*)0))>=0
1368 dst+=hex_decode(src+6,brace,dst);
1372 Ustrncmp(src,CUS "${unicode:",10)==0
1373 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1376 switch (unicode_decode(src+10,brace,(uschar*)0))
1380 filter->errmsg=CUS "unicode character out of range";
1390 dst+=unicode_decode(src+10,brace,dst);
1397 data->length=dst-data->character;
1404 /*************************************************
1405 * Parse an optional string *
1406 *************************************************/
1410 quoted-string = DQUOTE *CHAR DQUOTE
1411 ;; in general, \ CHAR inside a string maps to CHAR
1412 ;; so \" maps to " and \\ maps to \
1413 ;; note that newlines and other characters are all allowed
1416 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1417 *(multi-line-literal / multi-line-dotstuff)
1419 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1420 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1421 ;; A line containing only "." ends the multi-line.
1422 ;; Remove a leading '.' if followed by another '.'.
1423 string = quoted-string / multi-line
1426 filter points to the Sieve filter including its state
1427 id specifies identifier to match
1431 0 identifier not matched
1434 static int parse_string(struct Sieve *filter, struct String *data)
1439 data->character=(uschar*)0;
1440 if (*filter->pc=='"') /* quoted string */
1445 if (*filter->pc=='"') /* end of string */
1447 int foo=data->length;
1450 /* that way, there will be at least one character allocated */
1451 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1452 #ifdef ENCODED_CHARACTER
1453 if (filter->require_encoded_character
1454 && string_decode(filter,data)==-1)
1459 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1461 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1462 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1465 else /* regular character */
1468 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1470 if (*filter->pc=='\n')
1472 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1476 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1480 filter->errmsg=CUS "missing end of string";
1483 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1486 /* skip optional white space followed by hashed comment or CRLF */
1487 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1488 if (*filter->pc=='#')
1490 if (parse_hashcomment(filter)==-1) return -1;
1493 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1495 else if (*filter->pc=='\n')
1507 filter->errmsg=CUS "syntax error";
1513 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1515 if (*filter->pc=='\n') /* end of line */
1518 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1526 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1528 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1531 int foo=data->length;
1533 /* that way, there will be at least one character allocated */
1534 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1541 #ifdef ENCODED_CHARACTER
1542 if (filter->require_encoded_character
1543 && string_decode(filter,data)==-1)
1548 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1550 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1554 else /* regular character */
1556 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1560 filter->errmsg=CUS "missing end of multi line string";
1567 /*************************************************
1568 * Parse a specific identifier *
1569 *************************************************/
1573 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1576 filter points to the Sieve filter including its state
1577 id specifies identifier to match
1580 0 identifier not matched
1583 static int parse_identifier(struct Sieve *filter, const uschar *id)
1585 size_t idlen=Ustrlen(id);
1587 if (Ustrncmp(filter->pc,id,idlen)==0)
1589 uschar next=filter->pc[idlen];
1591 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1599 /*************************************************
1601 *************************************************/
1605 number = 1*DIGIT [QUANTIFIER]
1606 QUANTIFIER = "K" / "M" / "G"
1609 filter points to the Sieve filter including its state
1613 -1 no string list found
1616 static int parse_number(struct Sieve *filter, unsigned long *data)
1620 if (*filter->pc>='0' && *filter->pc<='9')
1625 d=Ustrtoul(filter->pc,&e,10);
1628 filter->errmsg=CUstrerror(ERANGE);
1633 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1634 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1635 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1636 if (d>(ULONG_MAX/u))
1638 filter->errmsg=CUstrerror(ERANGE);
1647 filter->errmsg=CUS "missing number";
1653 /*************************************************
1654 * Parse a string list *
1655 *************************************************/
1659 string-list = "[" string *("," string) "]" / string
1662 filter points to the Sieve filter including its state
1663 data returns string list
1666 -1 no string list found
1669 static int parse_stringlist(struct Sieve *filter, struct String **data)
1671 const uschar *orig=filter->pc;
1674 struct String *d=(struct String*)0;
1677 if (*filter->pc=='[') /* string list */
1682 if (parse_white(filter)==-1) goto error;
1683 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1686 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1687 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1688 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1690 filter->errmsg=CUstrerror(errno);
1693 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1695 dataCapacity=newCapacity;
1697 m=parse_string(filter,&d[dataLength]);
1700 if (dataLength==0) break;
1703 filter->errmsg=CUS "missing string";
1707 else if (m==-1) goto error;
1709 if (parse_white(filter)==-1) goto error;
1710 if (*filter->pc==',') ++filter->pc;
1713 if (*filter->pc==']')
1715 d[dataLength].character=(uschar*)0;
1716 d[dataLength].length=-1;
1723 filter->errmsg=CUS "missing closing bracket";
1727 else /* single string */
1729 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1733 m=parse_string(filter,&d[0]);
1745 d[1].character=(uschar*)0;
1752 filter->errmsg=CUS "missing string list";
1757 /*************************************************
1758 * Parse an optional address part specifier *
1759 *************************************************/
1763 address-part = ":localpart" / ":domain" / ":all"
1764 address-part =/ ":user" / ":detail"
1767 filter points to the Sieve filter including its state
1768 a returns address part specified
1771 0 no comparator found
1775 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1778 if (parse_identifier(filter,CUS ":user")==1)
1780 if (!filter->require_subaddress)
1782 filter->errmsg=CUS "missing previous require \"subaddress\";";
1788 else if (parse_identifier(filter,CUS ":detail")==1)
1790 if (!filter->require_subaddress)
1792 filter->errmsg=CUS "missing previous require \"subaddress\";";
1800 if (parse_identifier(filter,CUS ":localpart")==1)
1802 *a=ADDRPART_LOCALPART;
1805 else if (parse_identifier(filter,CUS ":domain")==1)
1810 else if (parse_identifier(filter,CUS ":all")==1)
1819 /*************************************************
1820 * Parse an optional comparator *
1821 *************************************************/
1825 comparator = ":comparator" <comparator-name: string>
1828 filter points to the Sieve filter including its state
1829 c returns comparator
1832 0 no comparator found
1833 -1 incomplete comparator found
1836 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1838 struct String comparator_name;
1840 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1841 if (parse_white(filter)==-1) return -1;
1842 switch (parse_string(filter,&comparator_name))
1847 filter->errmsg=CUS "missing comparator";
1854 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1859 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1861 *c=COMP_EN_ASCII_CASEMAP;
1864 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1866 *c=COMP_EN_ASCII_CASEMAP;
1869 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1871 *c=COMP_ASCII_NUMERIC;
1876 filter->errmsg=CUS "invalid comparator";
1885 /*************************************************
1886 * Parse an optional match type *
1887 *************************************************/
1891 match-type = ":is" / ":contains" / ":matches"
1894 filter points to the Sieve filter including its state
1895 m returns match type
1898 0 no match type found
1901 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1903 if (parse_identifier(filter,CUS ":is")==1)
1908 else if (parse_identifier(filter,CUS ":contains")==1)
1913 else if (parse_identifier(filter,CUS ":matches")==1)
1922 /*************************************************
1923 * Parse and interpret an optional test list *
1924 *************************************************/
1928 test-list = "(" test *("," test) ")"
1931 filter points to the Sieve filter including its state
1932 n total number of tests
1933 true number of passed tests
1934 exec Execute parsed statements
1937 0 no test list found
1938 -1 syntax or execution error
1941 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1943 if (parse_white(filter)==-1) return -1;
1944 if (*filter->pc=='(')
1953 switch (parse_test(filter,&cond,exec))
1956 case 0: filter->errmsg=CUS "missing test"; return -1;
1957 default: ++*n; if (cond) ++*true; break;
1959 if (parse_white(filter)==-1) return -1;
1960 if (*filter->pc==',') ++filter->pc;
1963 if (*filter->pc==')')
1970 filter->errmsg=CUS "missing closing paren";
1978 /*************************************************
1979 * Parse and interpret an optional test *
1980 *************************************************/
1984 filter points to the Sieve filter including its state
1985 cond returned condition status
1986 exec Execute parsed statements
1990 -1 syntax or execution error
1993 static int parse_test(struct Sieve *filter, int *cond, int exec)
1995 if (parse_white(filter)==-1) return -1;
1996 if (parse_identifier(filter,CUS "address"))
1999 address-test = "address" { [address-part] [comparator] [match-type] }
2000 <header-list: string-list> <key-list: string-list>
2002 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2005 enum AddressPart addressPart=ADDRPART_ALL;
2006 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2007 enum MatchType matchType=MATCH_IS;
2008 struct String *hdr,*h,*key,*k;
2014 if (parse_white(filter)==-1) return -1;
2015 if ((m=parse_addresspart(filter,&addressPart))!=0)
2017 if (m==-1) return -1;
2020 filter->errmsg=CUS "address part already specified";
2025 else if ((m=parse_comparator(filter,&comparator))!=0)
2027 if (m==-1) return -1;
2030 filter->errmsg=CUS "comparator already specified";
2035 else if ((m=parse_matchtype(filter,&matchType))!=0)
2037 if (m==-1) return -1;
2040 filter->errmsg=CUS "match type already specified";
2047 if (parse_white(filter)==-1) return -1;
2048 if ((m=parse_stringlist(filter,&hdr))!=1)
2050 if (m==0) filter->errmsg=CUS "header string list expected";
2053 if (parse_white(filter)==-1) return -1;
2054 if ((m=parse_stringlist(filter,&key))!=1)
2056 if (m==0) filter->errmsg=CUS "key string list expected";
2060 for (h=hdr; h->length!=-1 && !*cond; ++h)
2062 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2066 !eq_asciicase(h,&str_from,0)
2067 && !eq_asciicase(h,&str_to,0)
2068 && !eq_asciicase(h,&str_cc,0)
2069 && !eq_asciicase(h,&str_bcc,0)
2070 && !eq_asciicase(h,&str_sender,0)
2071 && !eq_asciicase(h,&str_resent_from,0)
2072 && !eq_asciicase(h,&str_resent_to,0)
2075 filter->errmsg=CUS "invalid header field";
2080 /* We are only interested in addresses below, so no MIME decoding */
2081 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2082 if (header_value == NULL)
2084 filter->errmsg=CUS "header string expansion failed";
2087 parse_allow_group = TRUE;
2088 while (*header_value && !*cond)
2091 int start, end, domain;
2095 end_addr = parse_find_address_end(header_value, FALSE);
2096 saveend = *end_addr;
2098 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2100 if (extracted_addr) switch (addressPart)
2102 case ADDRPART_ALL: part=extracted_addr; break;
2106 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2107 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2109 case ADDRPART_DETAIL: part=NULL; break;
2113 *end_addr = saveend;
2116 for (k=key; k->length!=-1; ++k)
2118 struct String partStr;
2120 partStr.character=part;
2121 partStr.length=Ustrlen(part);
2124 *cond=compare(filter,k,&partStr,comparator,matchType);
2125 if (*cond==-1) return -1;
2130 if (saveend == 0) break;
2131 header_value = end_addr + 1;
2133 parse_allow_group = FALSE;
2134 parse_found_group = FALSE;
2139 else if (parse_identifier(filter,CUS "allof"))
2142 allof-test = "allof" <tests: test-list>
2147 switch (parse_testlist(filter,&n,&true,exec))
2150 case 0: filter->errmsg=CUS "missing test list"; return -1;
2151 default: *cond=(n==true); return 1;
2154 else if (parse_identifier(filter,CUS "anyof"))
2157 anyof-test = "anyof" <tests: test-list>
2162 switch (parse_testlist(filter,&n,&true,exec))
2165 case 0: filter->errmsg=CUS "missing test list"; return -1;
2166 default: *cond=(true>0); return 1;
2169 else if (parse_identifier(filter,CUS "exists"))
2172 exists-test = "exists" <header-names: string-list>
2175 struct String *hdr,*h;
2178 if (parse_white(filter)==-1) return -1;
2179 if ((m=parse_stringlist(filter,&hdr))!=1)
2181 if (m==0) filter->errmsg=CUS "header string list expected";
2187 for (h=hdr; h->length!=-1 && *cond; ++h)
2191 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2192 if (header_def == NULL)
2194 filter->errmsg=CUS "header string expansion failed";
2197 if (Ustrcmp(header_def,"false")==0) *cond=0;
2202 else if (parse_identifier(filter,CUS "false"))
2205 false-test = "false"
2211 else if (parse_identifier(filter,CUS "header"))
2214 header-test = "header" { [comparator] [match-type] }
2215 <header-names: string-list> <key-list: string-list>
2218 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2219 enum MatchType matchType=MATCH_IS;
2220 struct String *hdr,*h,*key,*k;
2226 if (parse_white(filter)==-1) return -1;
2227 if ((m=parse_comparator(filter,&comparator))!=0)
2229 if (m==-1) return -1;
2232 filter->errmsg=CUS "comparator already specified";
2237 else if ((m=parse_matchtype(filter,&matchType))!=0)
2239 if (m==-1) return -1;
2242 filter->errmsg=CUS "match type already specified";
2249 if (parse_white(filter)==-1) return -1;
2250 if ((m=parse_stringlist(filter,&hdr))!=1)
2252 if (m==0) filter->errmsg=CUS "header string list expected";
2255 if (parse_white(filter)==-1) return -1;
2256 if ((m=parse_stringlist(filter,&key))!=1)
2258 if (m==0) filter->errmsg=CUS "key string list expected";
2262 for (h=hdr; h->length!=-1 && !*cond; ++h)
2266 filter->errmsg=CUS "invalid header field";
2271 struct String header_value;
2274 expand_header(&header_value,h);
2275 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2276 if (header_value.character == NULL || header_def == NULL)
2278 filter->errmsg=CUS "header string expansion failed";
2281 for (k=key; k->length!=-1; ++k)
2283 if (Ustrcmp(header_def,"true")==0)
2285 *cond=compare(filter,k,&header_value,comparator,matchType);
2286 if (*cond==-1) return -1;
2294 else if (parse_identifier(filter,CUS "not"))
2296 if (parse_white(filter)==-1) return -1;
2297 switch (parse_test(filter,cond,exec))
2300 case 0: filter->errmsg=CUS "missing test"; return -1;
2301 default: *cond=!*cond; return 1;
2304 else if (parse_identifier(filter,CUS "size"))
2307 relop = ":over" / ":under"
2308 size-test = "size" relop <limit: number>
2311 unsigned long limit;
2314 if (parse_white(filter)==-1) return -1;
2315 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2316 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2319 filter->errmsg=CUS "missing :over or :under";
2322 if (parse_white(filter)==-1) return -1;
2323 if (parse_number(filter,&limit)==-1) return -1;
2324 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2327 else if (parse_identifier(filter,CUS "true"))
2332 else if (parse_identifier(filter,CUS "envelope"))
2335 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2336 <envelope-part: string-list> <key-list: string-list>
2338 envelope-part is case insensitive "from" or "to"
2339 #ifdef ENVELOPE_AUTH
2340 envelope-part =/ "auth"
2344 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2345 enum AddressPart addressPart=ADDRPART_ALL;
2346 enum MatchType matchType=MATCH_IS;
2347 struct String *env,*e,*key,*k;
2351 if (!filter->require_envelope)
2353 filter->errmsg=CUS "missing previous require \"envelope\";";
2358 if (parse_white(filter)==-1) return -1;
2359 if ((m=parse_comparator(filter,&comparator))!=0)
2361 if (m==-1) return -1;
2364 filter->errmsg=CUS "comparator already specified";
2369 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2371 if (m==-1) return -1;
2374 filter->errmsg=CUS "address part already specified";
2379 else if ((m=parse_matchtype(filter,&matchType))!=0)
2381 if (m==-1) return -1;
2384 filter->errmsg=CUS "match type already specified";
2391 if (parse_white(filter)==-1) return -1;
2392 if ((m=parse_stringlist(filter,&env))!=1)
2394 if (m==0) filter->errmsg=CUS "envelope string list expected";
2397 if (parse_white(filter)==-1) return -1;
2398 if ((m=parse_stringlist(filter,&key))!=1)
2400 if (m==0) filter->errmsg=CUS "key string list expected";
2404 for (e=env; e->length!=-1 && !*cond; ++e)
2406 const uschar *envelopeExpr=CUS 0;
2407 uschar *envelope=US 0;
2409 if (eq_asciicase(e,&str_from,0))
2411 switch (addressPart)
2413 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2417 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2418 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2420 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2424 else if (eq_asciicase(e,&str_to,0))
2426 switch (addressPart)
2428 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2430 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2431 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2433 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2434 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2437 #ifdef ENVELOPE_AUTH
2438 else if (eq_asciicase(e,&str_auth,0))
2440 switch (addressPart)
2442 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2446 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2447 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2449 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2456 filter->errmsg=CUS "invalid envelope string";
2459 if (exec && envelopeExpr)
2461 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2463 filter->errmsg=CUS "header string expansion failed";
2466 for (k=key; k->length!=-1; ++k)
2468 struct String envelopeStr;
2470 envelopeStr.character=envelope;
2471 envelopeStr.length=Ustrlen(envelope);
2472 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2473 if (*cond==-1) return -1;
2481 else if (parse_identifier(filter,CUS "valid_notif_method"))
2484 valid_notif_method = "valid_notif_method"
2485 <notification-uris: string-list>
2488 struct String *uris,*u;
2491 if (!filter->require_enotify)
2493 filter->errmsg=CUS "missing previous require \"enotify\";";
2496 if (parse_white(filter)==-1) return -1;
2497 if ((m=parse_stringlist(filter,&uris))!=1)
2499 if (m==0) filter->errmsg=CUS "URI string list expected";
2505 for (u=uris; u->length!=-1 && *cond; ++u)
2507 string_item *recipient;
2508 struct String header,body;
2512 header.character=(uschar*)0;
2514 body.character=(uschar*)0;
2515 if (parse_mailto_uri(filter,u->character,&recipient,&header,&body)!=1)
2526 /*************************************************
2527 * Parse and interpret an optional block *
2528 *************************************************/
2532 filter points to the Sieve filter including its state
2533 exec Execute parsed statements
2534 generated where to hang newly-generated addresses
2536 Returns: 2 success by stop
2538 0 no block command found
2539 -1 syntax or execution error
2542 static int parse_block(struct Sieve *filter, int exec,
2543 address_item **generated)
2547 if (parse_white(filter)==-1) return -1;
2548 if (*filter->pc=='{')
2551 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2552 if (*filter->pc=='}')
2559 filter->errmsg=CUS "expecting command or closing brace";
2567 /*************************************************
2568 * Match a semicolon *
2569 *************************************************/
2573 filter points to the Sieve filter including its state
2579 static int parse_semicolon(struct Sieve *filter)
2581 if (parse_white(filter)==-1) return -1;
2582 if (*filter->pc==';')
2589 filter->errmsg=CUS "missing semicolon";
2595 /*************************************************
2596 * Parse and interpret a Sieve command *
2597 *************************************************/
2601 filter points to the Sieve filter including its state
2602 exec Execute parsed statements
2603 generated where to hang newly-generated addresses
2605 Returns: 2 success by stop
2607 -1 syntax or execution error
2609 static int parse_commands(struct Sieve *filter, int exec,
2610 address_item **generated)
2614 if (parse_white(filter)==-1) return -1;
2615 if (parse_identifier(filter,CUS "if"))
2618 if-command = "if" test block *( "elsif" test block ) [ else block ]
2621 int cond,m,unsuccessful;
2624 if (parse_white(filter)==-1) return -1;
2625 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2628 filter->errmsg=CUS "missing test";
2631 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2632 (debug_selector & D_filter) != 0)
2634 if (exec) debug_printf("if %s\n",cond?"true":"false");
2636 m=parse_block(filter,exec ? cond : 0, generated);
2637 if (m==-1 || m==2) return m;
2640 filter->errmsg=CUS "missing block";
2643 unsuccessful = !cond;
2644 for (;;) /* elsif test block */
2646 if (parse_white(filter)==-1) return -1;
2647 if (parse_identifier(filter,CUS "elsif"))
2649 if (parse_white(filter)==-1) return -1;
2650 m=parse_test(filter,&cond,exec && unsuccessful);
2651 if (m==-1 || m==2) return m;
2654 filter->errmsg=CUS "missing test";
2657 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2658 (debug_selector & D_filter) != 0)
2660 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2662 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2663 if (m==-1 || m==2) return m;
2666 filter->errmsg=CUS "missing block";
2669 if (exec && unsuccessful && cond) unsuccessful = 0;
2674 if (parse_white(filter)==-1) return -1;
2675 if (parse_identifier(filter,CUS "else"))
2677 m=parse_block(filter,exec && unsuccessful, generated);
2678 if (m==-1 || m==2) return m;
2681 filter->errmsg=CUS "missing block";
2686 else if (parse_identifier(filter,CUS "stop"))
2689 stop-command = "stop" { stop-options } ";"
2693 if (parse_semicolon(filter)==-1) return -1;
2696 filter->pc+=Ustrlen(filter->pc);
2700 else if (parse_identifier(filter,CUS "keep"))
2703 keep-command = "keep" { keep-options } ";"
2707 if (parse_semicolon(filter)==-1) return -1;
2710 add_addr(generated,US"inbox",1,0,0,0);
2714 else if (parse_identifier(filter,CUS "discard"))
2717 discard-command = "discard" { discard-options } ";"
2721 if (parse_semicolon(filter)==-1) return -1;
2722 if (exec) filter->keep=0;
2724 else if (parse_identifier(filter,CUS "redirect"))
2727 redirect-command = "redirect" redirect-options "string" ";"
2729 redirect-options =) ":copy"
2732 struct String recipient;
2738 if (parse_white(filter)==-1) return -1;
2739 if (parse_identifier(filter,CUS ":copy")==1)
2741 if (!filter->require_copy)
2743 filter->errmsg=CUS "missing previous require \"copy\";";
2750 if (parse_white(filter)==-1) return -1;
2751 if ((m=parse_string(filter,&recipient))!=1)
2753 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2756 if (strchr(CCS recipient.character,'@')==(char*)0)
2758 filter->errmsg=CUS "unqualified recipient address";
2763 add_addr(generated,recipient.character,0,0,0,0);
2764 if (!copy) filter->keep = 0;
2766 if (parse_semicolon(filter)==-1) return -1;
2768 else if (parse_identifier(filter,CUS "fileinto"))
2771 fileinto-command = "fileinto" { fileinto-options } string ";"
2773 fileinto-options =) [ ":copy" ]
2776 struct String folder;
2779 unsigned long maxage, maxmessages, maxstorage;
2782 maxage = maxmessages = maxstorage = 0;
2783 if (!filter->require_fileinto)
2785 filter->errmsg=CUS "missing previous require \"fileinto\";";
2790 if (parse_white(filter)==-1) return -1;
2791 if (parse_identifier(filter,CUS ":copy")==1)
2793 if (!filter->require_copy)
2795 filter->errmsg=CUS "missing previous require \"copy\";";
2802 if (parse_white(filter)==-1) return -1;
2803 if ((m=parse_string(filter,&folder))!=1)
2805 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2808 m=0; s=folder.character;
2809 if (folder.length==0) m=1;
2810 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2813 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2818 filter->errmsg=CUS "invalid folder";
2823 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2824 if (!copy) filter->keep = 0;
2826 if (parse_semicolon(filter)==-1) return -1;
2829 else if (parse_identifier(filter,CUS "notify"))
2832 notify-command = "notify" { notify-options } <method: string> ";"
2833 notify-options = [":from" string]
2834 [":importance" <"1" / "2" / "3">]
2835 [":options" 1*(string-list / number)]
2841 struct String importance;
2842 struct String *options;
2843 struct String message;
2844 struct String method;
2845 struct Notification *already;
2846 string_item *recipient;
2847 struct String header;
2849 uschar *envelope_from,*envelope_to;
2851 if (!filter->require_enotify)
2853 filter->errmsg=CUS "missing previous require \"enotify\";";
2856 from.character=(uschar*)0;
2858 importance.character=(uschar*)0;
2859 importance.length=-1;
2860 options=(struct String*)0;
2861 message.character=(uschar*)0;
2865 header.character=(uschar*)0;
2867 body.character=(uschar*)0;
2868 envelope_from=expand_string("$sender_address");
2869 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2872 if (parse_white(filter)==-1) return -1;
2873 if (parse_identifier(filter,CUS ":from")==1)
2875 if (parse_white(filter)==-1) return -1;
2876 if ((m=parse_string(filter,&from))!=1)
2878 if (m==0) filter->errmsg=CUS "from string expected";
2882 else if (parse_identifier(filter,CUS ":importance")==1)
2884 if (parse_white(filter)==-1) return -1;
2885 if ((m=parse_string(filter,&importance))!=1)
2887 if (m==0) filter->errmsg=CUS "importance string expected";
2890 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2892 filter->errmsg=CUS "invalid importance";
2896 else if (parse_identifier(filter,CUS ":options")==1)
2898 if (parse_white(filter)==-1) return -1;
2900 else if (parse_identifier(filter,CUS ":message")==1)
2902 if (parse_white(filter)==-1) return -1;
2903 if ((m=parse_string(filter,&message))!=1)
2905 if (m==0) filter->errmsg=CUS "message string expected";
2911 if (parse_white(filter)==-1) return -1;
2912 if ((m=parse_string(filter,&method))!=1)
2914 if (m==0) filter->errmsg=CUS "missing method string";
2917 if (parse_semicolon(filter)==-1) return -1;
2919 for (already=filter->notified; already; already=already->next)
2921 if (already->method.length==method.length
2922 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
2923 && already->importance.length==importance.length
2924 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
2925 && already->message.length==message.length
2926 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
2929 if (already==(struct Notification*)0)
2930 /* New notification, process it */
2932 if (parse_mailto_uri(filter,method.character,&recipient,&header,&body)!=1)
2934 struct Notification *sent;
2935 sent=store_get(sizeof(struct Notification));
2936 sent->method=method;
2937 sent->importance=importance;
2938 sent->message=message;
2939 sent->next=filter->notified;
2940 filter->notified=sent;
2941 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2943 debug_printf("Notification to `%s'.\n",method.character);
2945 #ifndef COMPILE_SYNTAX_CHECKER
2946 if (exec && filter_test == FTEST_NONE)
2952 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
2956 int buffer_capacity;
2958 f = fdopen(fd, "wb");
2959 for (h = header_list; h != NULL; h = h->next)
2960 if (h->type == htype_received) fprintf(f,"%s",h->text);
2961 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
2962 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
2963 if (header.length>0) fprintf(f,"%s",header.character);
2964 if (message.length==-1)
2966 message.character=US"Notification";
2967 message.length=Ustrlen(message.character);
2969 /* Allocation is larger than neccessary, but enough even for split MIME words */
2970 buffer_capacity=32+4*message.length;
2971 buffer=store_get(buffer_capacity);
2972 fprintf(f,"Subject: %s\n\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
2973 if (body.length>0) fprintf(f,"%s\n",body.character);
2976 (void)child_close(pid, 0);
2983 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2985 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
2991 else if (parse_identifier(filter,CUS "vacation"))
2994 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2995 vacation-options = [":days" number]
2998 [":addresses" string-list]
3005 struct String subject;
3007 struct String *addresses;
3009 string_item *aliases;
3010 struct String handle;
3011 struct String reason;
3013 if (!filter->require_vacation)
3015 filter->errmsg=CUS "missing previous require \"vacation\";";
3020 if (filter->vacation_ran)
3022 filter->errmsg=CUS "trying to execute vacation more than once";
3025 filter->vacation_ran=1;
3027 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3028 subject.character=(uschar*)0;
3030 from.character=(uschar*)0;
3032 addresses=(struct String*)0;
3035 handle.character=(uschar*)0;
3039 if (parse_white(filter)==-1) return -1;
3040 if (parse_identifier(filter,CUS ":days")==1)
3042 if (parse_white(filter)==-1) return -1;
3043 if (parse_number(filter,&days)==-1) return -1;
3044 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3045 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3047 else if (parse_identifier(filter,CUS ":subject")==1)
3049 if (parse_white(filter)==-1) return -1;
3050 if ((m=parse_string(filter,&subject))!=1)
3052 if (m==0) filter->errmsg=CUS "subject string expected";
3056 else if (parse_identifier(filter,CUS ":from")==1)
3058 if (parse_white(filter)==-1) return -1;
3059 if ((m=parse_string(filter,&from))!=1)
3061 if (m==0) filter->errmsg=CUS "from string expected";
3064 if (check_mail_address(filter,&from)!=1)
3067 else if (parse_identifier(filter,CUS ":addresses")==1)
3071 if (parse_white(filter)==-1) return -1;
3072 if ((m=parse_stringlist(filter,&addresses))!=1)
3074 if (m==0) filter->errmsg=CUS "addresses string list expected";
3077 for (a=addresses; a->length!=-1; ++a)
3081 new=store_get(sizeof(string_item));
3082 new->text=store_get(a->length+1);
3083 if (a->length) memcpy(new->text,a->character,a->length);
3084 new->text[a->length]='\0';
3089 else if (parse_identifier(filter,CUS ":mime")==1)
3091 else if (parse_identifier(filter,CUS ":handle")==1)
3093 if (parse_white(filter)==-1) return -1;
3094 if ((m=parse_string(filter,&from))!=1)
3096 if (m==0) filter->errmsg=CUS "handle string expected";
3102 if (parse_white(filter)==-1) return -1;
3103 if ((m=parse_string(filter,&reason))!=1)
3105 if (m==0) filter->errmsg=CUS "missing reason string";
3112 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
3115 filter->errmsg=CUS "MIME reason string contains 8bit text";
3119 if (parse_semicolon(filter)==-1) return -1;
3126 int buffer_capacity;
3130 uschar hexdigest[33];
3134 if (filter_personal(aliases,TRUE))
3136 if (filter_test == FTEST_NONE)
3138 /* ensure oncelog directory exists; failure will be detected later */
3140 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3142 /* build oncelog filename */
3144 key.character=(uschar*)0;
3147 if (handle.length==-1)
3149 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
3150 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
3151 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
3152 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
3157 md5_end(&base, key.character, key.length, digest);
3158 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
3159 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3161 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
3163 if (filter_test == FTEST_NONE)
3165 capacity=Ustrlen(filter->vacation_directory);
3167 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
3168 once=string_cat(once,&capacity,&start,hexdigest,33);
3171 /* process subject */
3173 if (subject.length==-1)
3175 uschar *subject_def;
3177 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3178 if (Ustrcmp(subject_def,"true")==0)
3180 expand_header(&subject,&str_subject);
3183 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
3184 subject.length=start;
3188 subject.character=US"Automated reply";
3189 subject.length=Ustrlen(subject.character);
3193 /* add address to list of generated addresses */
3195 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3196 setflag(addr, af_pfr);
3197 setflag(addr, af_ignore_error);
3198 addr->next = *generated;
3200 addr->reply = store_get(sizeof(reply_item));
3201 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3202 addr->reply->to = string_copy(sender_address);
3203 if (from.length==-1)
3204 addr->reply->from = expand_string(US"$local_part@$domain");
3206 addr->reply->from = from.character;
3207 /* Allocation is larger than neccessary, but enough even for split MIME words */
3208 buffer_capacity=32+4*subject.length;
3209 buffer=store_get(buffer_capacity);
3210 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
3211 addr->reply->oncelog=once;
3212 addr->reply->once_repeat=days*86400;
3214 /* build body and MIME headers */
3218 uschar *mime_body,*reason_end;
3219 static const uschar nlnl[]="\r\n\r\n";
3223 mime_body=reason.character,reason_end=reason.character+reason.length;
3224 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
3229 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
3230 addr->reply->headers[start] = '\0';
3233 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
3234 else mime_body=reason_end-1;
3235 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3236 addr->reply->text[start] = '\0';
3240 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3243 start = reason.length;
3244 addr->reply->headers = US"MIME-Version: 1.0\n"
3245 "Content-Type: text/plain;\n"
3246 "\tcharset=\"utf-8\"\n"
3247 "Content-Transfer-Encoding: quoted-printable";
3248 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3252 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3254 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3265 /*************************************************
3266 * Parse and interpret a sieve filter *
3267 *************************************************/
3271 filter points to the Sieve filter including its state
3272 exec Execute parsed statements
3273 generated where to hang newly-generated addresses
3276 -1 syntax or execution error
3279 static int parse_start(struct Sieve *filter, int exec,
3280 address_item **generated)
3282 filter->pc=filter->filter;
3285 filter->require_envelope=0;
3286 filter->require_fileinto=0;
3287 #ifdef ENCODED_CHARACTER
3288 filter->require_encoded_character=0;
3290 #ifdef ENVELOPE_AUTH
3291 filter->require_envelope_auth=0;
3294 filter->require_enotify=0;
3295 filter->notified=(struct Notification*)0;
3298 filter->require_subaddress=0;
3301 filter->require_vacation=0;
3302 filter->vacation_ran=0;
3304 filter->require_copy=0;
3305 filter->require_iascii_numeric=0;
3307 if (parse_white(filter)==-1) return -1;
3309 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3312 struct dirent *oncelog;
3313 struct stat properties;
3316 /* clean up old vacation log databases */
3318 oncelogdir=opendir(CS filter->vacation_directory);
3320 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3322 filter->errmsg=CUS "unable to open vacation directory";
3326 if (oncelogdir != NULL)
3330 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3332 if (strlen(oncelog->d_name)==32)
3334 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3335 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3339 closedir(oncelogdir);
3343 while (parse_identifier(filter,CUS "require"))
3346 require-command = "require" <capabilities: string-list>
3349 struct String *cap,*check;
3352 if (parse_white(filter)==-1) return -1;
3353 if ((m=parse_stringlist(filter,&cap))!=1)
3355 if (m==0) filter->errmsg=CUS "capability string list expected";
3358 for (check=cap; check->character; ++check)
3360 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3361 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3362 #ifdef ENCODED_CHARACTER
3363 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3365 #ifdef ENVELOPE_AUTH
3366 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3369 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3372 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3375 else if (eq_octet(check,&str_vacation,0))
3377 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3379 filter->errmsg=CUS "vacation disabled";
3382 filter->require_vacation=1;
3385 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3386 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3387 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3388 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3389 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3392 filter->errmsg=CUS "unknown capability";
3396 if (parse_semicolon(filter)==-1) return -1;
3398 if (parse_commands(filter,exec,generated)==-1) return -1;
3401 filter->errmsg=CUS "syntax error";
3408 /*************************************************
3409 * Interpret a sieve filter file *
3410 *************************************************/
3414 filter points to the entire file, read into store as a single string
3415 options controls whether various special things are allowed, and requests
3416 special actions (not currently used)
3417 sieve_vacation_directory where to store vacation "once" files
3418 useraddress string expression for :user part of address
3419 subaddress string expression for :subaddress part of address
3420 generated where to hang newly-generated addresses
3421 error where to pass back an error text
3423 Returns: FF_DELIVERED success, a significant action was taken
3424 FF_NOTDELIVERED success, no significant action
3425 FF_DEFER defer requested
3426 FF_FAIL fail requested
3427 FF_FREEZE freeze requested
3428 FF_ERROR there was a problem
3432 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3433 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3439 options = options; /* Keep picky compilers happy */
3442 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3443 sieve.filter=filter;
3445 if (vacation_directory == NULL)
3446 sieve.vacation_directory = NULL;
3449 sieve.vacation_directory=expand_string(vacation_directory);
3450 if (sieve.vacation_directory == NULL)
3452 *error = string_sprintf("failed to expand \"%s\" "
3453 "(sieve_vacation_directory): %s", vacation_directory,
3454 expand_string_message);
3459 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3460 sieve.subaddress = subaddress;
3462 #ifdef COMPILE_SYNTAX_CHECKER
3463 if (parse_start(&sieve,0,generated)==1)
3465 if (parse_start(&sieve,1,generated)==1)
3470 add_addr(generated,US"inbox",1,0,0,0);
3471 msg = string_sprintf("Implicit keep");
3476 msg = string_sprintf("No implicit keep");
3482 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3483 #ifdef COMPILE_SYNTAX_CHECKER
3487 add_addr(generated,US"inbox",1,0,0,0);
3492 #ifndef COMPILE_SYNTAX_CHECKER
3493 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3494 else debug_printf("%s\n", msg);
3497 DEBUG(D_route) debug_printf("Sieve: end of processing\n");