1 /* $Cambridge: exim/src/src/sieve.c,v 1.16 2005/11/21 10:09:13 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2005 */
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 "envelope-auth". */
34 /* Define this for development of the Sieve extension "notify". */
37 /* Define this for the Sieve extension "subaddress". */
40 /* Define this for the Sieve extension "vacation". */
44 #define VACATION_MIN_DAYS 1
45 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
46 #define VACATION_MAX_DAYS 31
48 /* Keep this at 75 to accept only RFC compliant MIME words. */
49 /* Increase it if you want to match headers from buggy MUAs. */
50 #define MIMEWORD_LENGTH 75
62 int require_envelope_auth;
68 int require_subaddress;
74 uschar *vacation_directory;
75 const uschar *subaddress;
76 const uschar *useraddress;
78 int require_iascii_numeric;
81 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
82 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
84 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
86 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
88 enum RelOp { LT, LE, EQ, GE, GT, NE };
96 static int parse_test(struct Sieve *filter, int *cond, int exec);
97 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
99 static uschar str_from_c[]="From";
100 static const struct String str_from={ str_from_c, 4 };
101 static uschar str_to_c[]="To";
102 static const struct String str_to={ str_to_c, 2 };
103 static uschar str_cc_c[]="Cc";
104 static const struct String str_cc={ str_cc_c, 2 };
105 static uschar str_bcc_c[]="Bcc";
106 static const struct String str_bcc={ str_bcc_c, 3 };
107 static uschar str_auth_c[]="auth";
108 static const struct String str_auth={ str_auth_c, 4 };
109 static uschar str_sender_c[]="Sender";
110 static const struct String str_sender={ str_sender_c, 6 };
111 static uschar str_resent_from_c[]="Resent-From";
112 static const struct String str_resent_from={ str_resent_from_c, 11 };
113 static uschar str_resent_to_c[]="Resent-To";
114 static const struct String str_resent_to={ str_resent_to_c, 9 };
115 static uschar str_fileinto_c[]="fileinto";
116 static const struct String str_fileinto={ str_fileinto_c, 8 };
117 static uschar str_envelope_c[]="envelope";
118 static const struct String str_envelope={ str_envelope_c, 8 };
120 static uschar str_envelope_auth_c[]="envelope-auth";
121 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
124 static uschar str_notify_c[]="notify";
125 static const struct String str_notify={ str_notify_c, 6 };
128 static uschar str_subaddress_c[]="subaddress";
129 static const struct String str_subaddress={ str_subaddress_c, 10 };
132 static uschar str_vacation_c[]="vacation";
133 static const struct String str_vacation={ str_vacation_c, 8 };
134 static uschar str_subject_c[]="Subject";
135 static const struct String str_subject={ str_subject_c, 7 };
137 static uschar str_copy_c[]="copy";
138 static const struct String str_copy={ str_copy_c, 4 };
139 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
140 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
141 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
142 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
143 static uschar str_ioctet_c[]="i;octet";
144 static const struct String str_ioctet={ str_ioctet_c, 7 };
145 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
146 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
147 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
148 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
149 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
150 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
151 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
152 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
153 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
154 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
157 /*************************************************
158 * Encode to quoted-printable *
159 *************************************************/
166 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
169 const uschar *start,*end;
174 for (pass=0; pass<=1; ++pass)
181 dst->character=store_get(dst->length+1); /* plus one for \0 */
184 for (start=src->character,end=start+src->length; start<end; ++start)
201 || (ch>=62 && ch<=126)
207 && (*(start+1)!='\r' || *(start+2)!='\n')
222 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
250 sprintf(CS new,"=%02X",ch);
257 *new='\0'; /* not included in length, but nice */
262 /*************************************************
263 * Octet-wise string comparison *
264 *************************************************/
268 needle UTF-8 string to search ...
269 haystack ... inside the haystack
270 match_prefix 1 to compare if needle is a prefix of haystack
272 Returns: 0 needle not found in haystack
276 static int eq_octet(const struct String *needle,
277 const struct String *haystack, int match_prefix)
285 h=haystack->character;
289 if (*n&0x80) return 0;
290 if (*h&0x80) return 0;
292 if (*n!=*h) return 0;
298 return (match_prefix ? nl==0 : nl==0 && hl==0);
302 /*************************************************
303 * ASCII case-insensitive string comparison *
304 *************************************************/
308 needle UTF-8 string to search ...
309 haystack ... inside the haystack
310 match_prefix 1 to compare if needle is a prefix of haystack
312 Returns: 0 needle not found in haystack
316 static int eq_asciicase(const struct String *needle,
317 const struct String *haystack, int match_prefix)
326 h=haystack->character;
332 if (nc&0x80) return 0;
333 if (hc&0x80) return 0;
335 /* tolower depends on the locale and only ASCII case must be insensitive */
336 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
342 return (match_prefix ? nl==0 : nl==0 && hl==0);
346 /*************************************************
347 * Glob pattern search *
348 *************************************************/
352 needle pattern to search ...
353 haystack ... inside the haystack
355 Returns: 0 needle not found in haystack
360 static int eq_glob(const struct String *needle,
361 const struct String *haystack, int ascii_caseless)
363 const uschar *n,*h,*nend,*hend;
367 h=haystack->character;
368 nend=n+needle->length;
369 hend=h+haystack->length;
379 const uschar *npart,*hpart;
381 /* Try to match a non-star part of the needle at the current */
382 /* position in the haystack. */
386 while (npart<nend && *npart!='*') switch (*npart)
390 if (hpart==hend) return 0;
391 /* watch out: Do not match one character, but one UTF8 encoded character */
392 if ((*hpart&0xc0)==0xc0)
395 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
405 if (npart==nend) return -1;
410 if (hpart==hend) return 0;
411 /* tolower depends on the locale, but we need ASCII */
415 (*hpart&0x80) || (*npart&0x80) ||
418 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
423 /* string match after a star failed, advance and try again */
437 /* at this point, a part was matched successfully */
438 if (may_advance && npart==nend && hpart<hend)
439 /* needle ends, but haystack does not: if there was a star before, advance and try again */
449 return (h==hend ? 1 : may_advance);
453 /*************************************************
454 * ASCII numeric comparison *
455 *************************************************/
459 a first numeric string
460 b second numeric string
461 relop relational operator
463 Returns: 0 not (a relop b)
467 static int eq_asciinumeric(const struct String *a,
468 const struct String *b, enum RelOp relop)
471 const uschar *as,*aend,*bs,*bend;
475 aend=a->character+a->length;
477 bend=b->character+b->length;
479 while (*as>='0' && *as<='9' && as<aend) ++as;
481 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
484 if (al && bl==0) cmp=-1;
485 else if (al==0 && bl==0) cmp=0;
486 else if (al==0 && bl) cmp=1;
490 if (cmp==0) cmp=memcmp(a->character,b->character,al);
494 case LT: return cmp<0;
495 case LE: return cmp<=0;
496 case EQ: return cmp==0;
497 case GE: return cmp>=0;
498 case GT: return cmp>0;
499 case NE: return cmp!=0;
506 /*************************************************
508 *************************************************/
512 needle UTF-8 pattern or string to search ...
513 haystack ... inside the haystack
517 Returns: 0 needle not found in haystack
519 -1 comparator does not offer matchtype
522 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
523 enum Comparator co, enum MatchType mt)
527 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
528 (debug_selector & D_filter) != 0)
530 debug_printf("String comparison (match ");
533 case MATCH_IS: debug_printf(":is"); break;
534 case MATCH_CONTAINS: debug_printf(":contains"); break;
535 case MATCH_MATCHES: debug_printf(":matches"); break;
537 debug_printf(", comparison \"");
540 case COMP_OCTET: debug_printf("i;octet"); break;
541 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
542 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
544 debug_printf("\"):\n");
545 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
546 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
556 if (eq_octet(needle,haystack,0)) r=1;
559 case COMP_EN_ASCII_CASEMAP:
561 if (eq_asciicase(needle,haystack,0)) r=1;
564 case COMP_ASCII_NUMERIC:
566 if (!filter->require_iascii_numeric)
568 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
571 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
585 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
588 case COMP_EN_ASCII_CASEMAP:
590 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
595 filter->errmsg=CUS "comparator does not offer specified matchtype";
607 if ((r=eq_glob(needle,haystack,0))==-1)
609 filter->errmsg=CUS "syntactically invalid pattern";
614 case COMP_EN_ASCII_CASEMAP:
616 if ((r=eq_glob(needle,haystack,1))==-1)
618 filter->errmsg=CUS "syntactically invalid pattern";
625 filter->errmsg=CUS "comparator does not offer specified matchtype";
632 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
633 (debug_selector & D_filter) != 0)
634 debug_printf(" Result %s\n",r?"true":"false");
639 /*************************************************
640 * Check header field syntax *
641 *************************************************/
644 RFC 2822, section 3.6.8 says:
648 ftext = %d33-57 / ; Any character except
649 %d59-126 ; controls, SP, and
652 That forbids 8-bit header fields. This implementation accepts them, since
653 all of Exim is 8-bit clean, so it adds %d128-%d255.
656 header header field to quote for suitable use in Exim expansions
658 Returns: 0 string is not a valid header field
659 1 string is a value header field
662 static int is_header(const struct String *header)
672 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
683 /*************************************************
684 * Quote special characters string *
685 *************************************************/
689 header header field to quote for suitable use in Exim expansions
692 Returns: quoted string
695 static const uschar *quote(const struct String *header)
710 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
717 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
721 quoted=string_cat(quoted,&size,&ptr,h,1);
727 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
732 /*************************************************
733 * Add address to list of generated addresses *
734 *************************************************/
737 According to RFC 3028, duplicate delivery to the same address must
738 not happen, so the list is first searched for the address.
741 generated list of generated addresses
742 addr new address to add
743 file address denotes a file
748 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
750 address_item *new_addr;
752 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
754 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
756 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
758 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
764 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
766 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
768 new_addr=deliver_make_addr(addr,TRUE);
771 setflag(new_addr, af_pfr|af_file);
774 new_addr->p.errors_address = NULL;
775 new_addr->next = *generated;
776 *generated = new_addr;
780 /*************************************************
781 * Return decoded header field *
782 *************************************************/
786 value returned value of the field
787 header name of the header field
789 Returns: nothing The expanded string is empty
790 in case there is no such header
793 static void expand_header(struct String *value, const struct String *header)
799 value->character=(uschar*)0;
801 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
808 while (*r==' ' || *r=='\t') ++r;
815 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
819 /*************************************************
820 * Parse remaining hash comment *
821 *************************************************/
825 Comment up to terminating CRLF
828 filter points to the Sieve filter including its state
834 static int parse_hashcomment(struct Sieve *filter)
840 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
842 if (*filter->pc=='\n')
855 filter->errmsg=CUS "missing end of comment";
860 /*************************************************
861 * Parse remaining C-style comment *
862 *************************************************/
866 Everything up to star slash
869 filter points to the Sieve filter including its state
875 static int parse_comment(struct Sieve *filter)
880 if (*filter->pc=='*' && *(filter->pc+1)=='/')
887 filter->errmsg=CUS "missing end of comment";
892 /*************************************************
893 * Parse optional white space *
894 *************************************************/
898 Spaces, tabs, CRLFs, hash comments or C-style comments
901 filter points to the Sieve filter including its state
907 static int parse_white(struct Sieve *filter)
911 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
913 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
915 else if (*filter->pc=='\n')
925 else if (*filter->pc=='#')
927 if (parse_hashcomment(filter)==-1) return -1;
929 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
931 if (parse_comment(filter)==-1) return -1;
939 /*************************************************
940 * Parse a optional string *
941 *************************************************/
945 quoted-string = DQUOTE *CHAR DQUOTE
946 ;; in general, \ CHAR inside a string maps to CHAR
947 ;; so \" maps to " and \\ maps to \
948 ;; note that newlines and other characters are all allowed
951 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
952 *(multi-line-literal / multi-line-dotstuff)
954 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
955 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
956 ;; A line containing only "." ends the multi-line.
957 ;; Remove a leading '.' if followed by another '.'.
958 string = quoted-string / multi-line
961 filter points to the Sieve filter including its state
962 id specifies identifier to match
966 0 identifier not matched
969 static int parse_string(struct Sieve *filter, struct String *data)
974 data->character=(uschar*)0;
975 if (*filter->pc=='"') /* quoted string */
980 if (*filter->pc=='"') /* end of string */
982 int foo=data->length;
985 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
988 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
990 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
991 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
994 else /* regular character */
996 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1000 filter->errmsg=CUS "missing end of string";
1003 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1006 /* skip optional white space followed by hashed comment or CRLF */
1007 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1008 if (*filter->pc=='#')
1010 if (parse_hashcomment(filter)==-1) return -1;
1013 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1015 else if (*filter->pc=='\n')
1027 filter->errmsg=CUS "syntax error";
1033 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1035 if (*filter->pc=='\n') /* end of line */
1038 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1046 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1048 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1051 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1060 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1062 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1066 else /* regular character */
1068 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1072 filter->errmsg=CUS "missing end of multi line string";
1079 /*************************************************
1080 * Parse a specific identifier *
1081 *************************************************/
1085 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1088 filter points to the Sieve filter including its state
1089 id specifies identifier to match
1092 0 identifier not matched
1095 static int parse_identifier(struct Sieve *filter, const uschar *id)
1097 size_t idlen=Ustrlen(id);
1099 if (Ustrncmp(filter->pc,id,idlen)==0)
1101 uschar next=filter->pc[idlen];
1103 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1111 /*************************************************
1113 *************************************************/
1117 number = 1*DIGIT [QUANTIFIER]
1118 QUANTIFIER = "K" / "M" / "G"
1121 filter points to the Sieve filter including its state
1125 -1 no string list found
1128 static int parse_number(struct Sieve *filter, unsigned long *data)
1132 if (*filter->pc>='0' && *filter->pc<='9')
1137 d=Ustrtoul(filter->pc,&e,10);
1140 filter->errmsg=CUstrerror(ERANGE);
1145 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1146 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1147 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1148 if (d>(ULONG_MAX/u))
1150 filter->errmsg=CUstrerror(ERANGE);
1159 filter->errmsg=CUS "missing number";
1165 /*************************************************
1166 * Parse a string list *
1167 *************************************************/
1171 string-list = "[" string *("," string) "]" / string
1174 filter points to the Sieve filter including its state
1175 data returns string list
1178 -1 no string list found
1181 static int parse_stringlist(struct Sieve *filter, struct String **data)
1183 const uschar *orig=filter->pc;
1186 struct String *d=(struct String*)0;
1189 if (*filter->pc=='[') /* string list */
1194 if (parse_white(filter)==-1) goto error;
1195 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1198 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1199 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1200 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1202 filter->errmsg=CUstrerror(errno);
1205 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1207 dataCapacity=newCapacity;
1209 m=parse_string(filter,&d[dataLength]);
1212 if (dataLength==0) break;
1215 filter->errmsg=CUS "missing string";
1219 else if (m==-1) goto error;
1221 if (parse_white(filter)==-1) goto error;
1222 if (*filter->pc==',') ++filter->pc;
1225 if (*filter->pc==']')
1227 d[dataLength].character=(uschar*)0;
1228 d[dataLength].length=-1;
1235 filter->errmsg=CUS "missing closing bracket";
1239 else /* single string */
1241 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1245 m=parse_string(filter,&d[0]);
1257 d[1].character=(uschar*)0;
1264 filter->errmsg=CUS "missing string list";
1269 /*************************************************
1270 * Parse an optional address part specifier *
1271 *************************************************/
1275 address-part = ":localpart" / ":domain" / ":all"
1276 address-part =/ ":user" / ":detail"
1279 filter points to the Sieve filter including its state
1280 a returns address part specified
1283 0 no comparator found
1287 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1290 if (parse_identifier(filter,CUS ":user")==1)
1292 if (!filter->require_subaddress)
1294 filter->errmsg=CUS "missing previous require \"subaddress\";";
1300 else if (parse_identifier(filter,CUS ":detail")==1)
1302 if (!filter->require_subaddress)
1304 filter->errmsg=CUS "missing previous require \"subaddress\";";
1312 if (parse_identifier(filter,CUS ":localpart")==1)
1314 *a=ADDRPART_LOCALPART;
1317 else if (parse_identifier(filter,CUS ":domain")==1)
1322 else if (parse_identifier(filter,CUS ":all")==1)
1331 /*************************************************
1332 * Parse an optional comparator *
1333 *************************************************/
1337 comparator = ":comparator" <comparator-name: string>
1340 filter points to the Sieve filter including its state
1341 c returns comparator
1344 0 no comparator found
1345 -1 incomplete comparator found
1348 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1350 struct String comparator_name;
1352 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1353 if (parse_white(filter)==-1) return -1;
1354 switch (parse_string(filter,&comparator_name))
1359 filter->errmsg=CUS "missing comparator";
1366 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1371 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1373 *c=COMP_EN_ASCII_CASEMAP;
1376 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1378 *c=COMP_EN_ASCII_CASEMAP;
1381 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1383 *c=COMP_ASCII_NUMERIC;
1388 filter->errmsg=CUS "invalid comparator";
1397 /*************************************************
1398 * Parse an optional match type *
1399 *************************************************/
1403 match-type = ":is" / ":contains" / ":matches"
1406 filter points to the Sieve filter including its state
1407 m returns match type
1410 0 no match type found
1413 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1415 if (parse_identifier(filter,CUS ":is")==1)
1420 else if (parse_identifier(filter,CUS ":contains")==1)
1425 else if (parse_identifier(filter,CUS ":matches")==1)
1434 /*************************************************
1435 * Parse and interpret an optional test list *
1436 *************************************************/
1440 test-list = "(" test *("," test) ")"
1443 filter points to the Sieve filter including its state
1444 n total number of tests
1445 true number of passed tests
1446 exec Execute parsed statements
1449 0 no test list found
1450 -1 syntax or execution error
1453 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1455 if (parse_white(filter)==-1) return -1;
1456 if (*filter->pc=='(')
1465 switch (parse_test(filter,&cond,exec))
1468 case 0: filter->errmsg=CUS "missing test"; return -1;
1469 default: ++*n; if (cond) ++*true; break;
1471 if (parse_white(filter)==-1) return -1;
1472 if (*filter->pc==',') ++filter->pc;
1475 if (*filter->pc==')')
1482 filter->errmsg=CUS "missing closing paren";
1490 /*************************************************
1491 * Parse and interpret an optional test *
1492 *************************************************/
1496 filter points to the Sieve filter including its state
1497 cond returned condition status
1498 exec Execute parsed statements
1502 -1 syntax or execution error
1505 static int parse_test(struct Sieve *filter, int *cond, int exec)
1507 if (parse_white(filter)==-1) return -1;
1508 if (parse_identifier(filter,CUS "address"))
1511 address-test = "address" { [address-part] [comparator] [match-type] }
1512 <header-list: string-list> <key-list: string-list>
1514 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1517 enum AddressPart addressPart=ADDRPART_ALL;
1518 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1519 enum MatchType matchType=MATCH_IS;
1520 struct String *hdr,*h,*key,*k;
1526 if (parse_white(filter)==-1) return -1;
1527 if ((m=parse_addresspart(filter,&addressPart))!=0)
1529 if (m==-1) return -1;
1532 filter->errmsg=CUS "address part already specified";
1537 else if ((m=parse_comparator(filter,&comparator))!=0)
1539 if (m==-1) return -1;
1542 filter->errmsg=CUS "comparator already specified";
1547 else if ((m=parse_matchtype(filter,&matchType))!=0)
1549 if (m==-1) return -1;
1552 filter->errmsg=CUS "match type already specified";
1559 if (parse_white(filter)==-1) return -1;
1560 if ((m=parse_stringlist(filter,&hdr))!=1)
1562 if (m==0) filter->errmsg=CUS "header string list expected";
1565 if (parse_white(filter)==-1) return -1;
1566 if ((m=parse_stringlist(filter,&key))!=1)
1568 if (m==0) filter->errmsg=CUS "key string list expected";
1572 for (h=hdr; h->length!=-1 && !*cond; ++h)
1574 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1578 !eq_asciicase(h,&str_from,0)
1579 && !eq_asciicase(h,&str_to,0)
1580 && !eq_asciicase(h,&str_cc,0)
1581 && !eq_asciicase(h,&str_bcc,0)
1582 && !eq_asciicase(h,&str_sender,0)
1583 && !eq_asciicase(h,&str_resent_from,0)
1584 && !eq_asciicase(h,&str_resent_to,0)
1587 filter->errmsg=CUS "invalid header field";
1592 /* We are only interested in addresses below, so no MIME decoding */
1593 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1594 if (header_value == NULL)
1596 filter->errmsg=CUS "header string expansion failed";
1599 parse_allow_group = TRUE;
1600 while (*header_value && !*cond)
1603 int start, end, domain;
1607 end_addr = parse_find_address_end(header_value, FALSE);
1608 saveend = *end_addr;
1610 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1612 if (extracted_addr) switch (addressPart)
1614 case ADDRPART_ALL: part=extracted_addr; break;
1618 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1619 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1621 case ADDRPART_DETAIL: part=NULL; break;
1625 *end_addr = saveend;
1628 for (k=key; k->length!=-1; ++k)
1630 struct String partStr;
1632 partStr.character=part;
1633 partStr.length=Ustrlen(part);
1636 *cond=compare(filter,k,&partStr,comparator,matchType);
1637 if (*cond==-1) return -1;
1642 if (saveend == 0) break;
1643 header_value = end_addr + 1;
1649 else if (parse_identifier(filter,CUS "allof"))
1652 allof-test = "allof" <tests: test-list>
1657 switch (parse_testlist(filter,&n,&true,exec))
1660 case 0: filter->errmsg=CUS "missing test list"; return -1;
1661 default: *cond=(n==true); return 1;
1664 else if (parse_identifier(filter,CUS "anyof"))
1667 anyof-test = "anyof" <tests: test-list>
1672 switch (parse_testlist(filter,&n,&true,exec))
1675 case 0: filter->errmsg=CUS "missing test list"; return -1;
1676 default: *cond=(true>0); return 1;
1679 else if (parse_identifier(filter,CUS "exists"))
1682 exists-test = "exists" <header-names: string-list>
1685 struct String *hdr,*h;
1688 if (parse_white(filter)==-1) return -1;
1689 if ((m=parse_stringlist(filter,&hdr))!=1)
1691 if (m==0) filter->errmsg=CUS "header string list expected";
1697 for (h=hdr; h->length!=-1 && *cond; ++h)
1701 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1702 if (header_def == NULL)
1704 filter->errmsg=CUS "header string expansion failed";
1707 if (Ustrcmp(header_def,"false")==0) *cond=0;
1712 else if (parse_identifier(filter,CUS "false"))
1715 false-test = "false"
1721 else if (parse_identifier(filter,CUS "header"))
1724 header-test = "header" { [comparator] [match-type] }
1725 <header-names: string-list> <key-list: string-list>
1728 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1729 enum MatchType matchType=MATCH_IS;
1730 struct String *hdr,*h,*key,*k;
1736 if (parse_white(filter)==-1) return -1;
1737 if ((m=parse_comparator(filter,&comparator))!=0)
1739 if (m==-1) return -1;
1742 filter->errmsg=CUS "comparator already specified";
1747 else if ((m=parse_matchtype(filter,&matchType))!=0)
1749 if (m==-1) return -1;
1752 filter->errmsg=CUS "match type already specified";
1759 if (parse_white(filter)==-1) return -1;
1760 if ((m=parse_stringlist(filter,&hdr))!=1)
1762 if (m==0) filter->errmsg=CUS "header string list expected";
1765 if (parse_white(filter)==-1) return -1;
1766 if ((m=parse_stringlist(filter,&key))!=1)
1768 if (m==0) filter->errmsg=CUS "key string list expected";
1772 for (h=hdr; h->length!=-1 && !*cond; ++h)
1776 filter->errmsg=CUS "invalid header field";
1781 struct String header_value;
1784 expand_header(&header_value,h);
1785 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1786 if (header_value.character == NULL || header_def == NULL)
1788 filter->errmsg=CUS "header string expansion failed";
1791 for (k=key; k->length!=-1; ++k)
1793 if (Ustrcmp(header_def,"true")==0)
1795 *cond=compare(filter,k,&header_value,comparator,matchType);
1796 if (*cond==-1) return -1;
1804 else if (parse_identifier(filter,CUS "not"))
1806 if (parse_white(filter)==-1) return -1;
1807 switch (parse_test(filter,cond,exec))
1810 case 0: filter->errmsg=CUS "missing test"; return -1;
1811 default: *cond=!*cond; return 1;
1814 else if (parse_identifier(filter,CUS "size"))
1817 relop = ":over" / ":under"
1818 size-test = "size" relop <limit: number>
1821 unsigned long limit;
1824 if (parse_white(filter)==-1) return -1;
1825 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
1826 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
1829 filter->errmsg=CUS "missing :over or :under";
1832 if (parse_white(filter)==-1) return -1;
1833 if (parse_number(filter,&limit)==-1) return -1;
1834 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
1837 else if (parse_identifier(filter,CUS "true"))
1842 else if (parse_identifier(filter,CUS "envelope"))
1845 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
1846 <envelope-part: string-list> <key-list: string-list>
1848 envelope-part is case insensitive "from" or "to"
1849 #ifdef ENVELOPE_AUTH
1850 envelope-part =/ "auth"
1854 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1855 enum AddressPart addressPart=ADDRPART_ALL;
1856 enum MatchType matchType=MATCH_IS;
1857 struct String *env,*e,*key,*k;
1861 if (!filter->require_envelope)
1863 filter->errmsg=CUS "missing previous require \"envelope\";";
1868 if (parse_white(filter)==-1) return -1;
1869 if ((m=parse_comparator(filter,&comparator))!=0)
1871 if (m==-1) return -1;
1874 filter->errmsg=CUS "comparator already specified";
1879 else if ((m=parse_addresspart(filter,&addressPart))!=0)
1881 if (m==-1) return -1;
1884 filter->errmsg=CUS "address part already specified";
1889 else if ((m=parse_matchtype(filter,&matchType))!=0)
1891 if (m==-1) return -1;
1894 filter->errmsg=CUS "match type already specified";
1901 if (parse_white(filter)==-1) return -1;
1902 if ((m=parse_stringlist(filter,&env))!=1)
1904 if (m==0) filter->errmsg=CUS "envelope string list expected";
1907 if (parse_white(filter)==-1) return -1;
1908 if ((m=parse_stringlist(filter,&key))!=1)
1910 if (m==0) filter->errmsg=CUS "key string list expected";
1914 for (e=env; e->length!=-1 && !*cond; ++e)
1916 const uschar *envelopeExpr=CUS 0;
1917 uschar *envelope=US 0;
1919 if (eq_asciicase(e,&str_from,0))
1921 switch (addressPart)
1923 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
1927 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
1928 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
1930 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
1934 else if (eq_asciicase(e,&str_to,0))
1936 switch (addressPart)
1938 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
1940 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
1941 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
1943 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
1944 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
1947 #ifdef ENVELOPE_AUTH
1948 else if (eq_asciicase(e,&str_auth,0))
1950 switch (addressPart)
1952 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
1956 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
1957 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
1959 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
1966 filter->errmsg=CUS "invalid envelope string";
1969 if (exec && envelopeExpr)
1971 if ((envelope=expand_string(US envelopeExpr)) == NULL)
1973 filter->errmsg=CUS "header string expansion failed";
1976 for (k=key; k->length!=-1; ++k)
1978 struct String envelopeStr;
1980 envelopeStr.character=envelope;
1981 envelopeStr.length=Ustrlen(envelope);
1982 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
1983 if (*cond==-1) return -1;
1994 /*************************************************
1995 * Parse and interpret an optional block *
1996 *************************************************/
2000 filter points to the Sieve filter including its state
2001 exec Execute parsed statements
2002 generated where to hang newly-generated addresses
2004 Returns: 2 success by stop
2006 0 no block command found
2007 -1 syntax or execution error
2010 static int parse_block(struct Sieve *filter, int exec,
2011 address_item **generated)
2015 if (parse_white(filter)==-1) return -1;
2016 if (*filter->pc=='{')
2019 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2020 if (*filter->pc=='}')
2027 filter->errmsg=CUS "expecting command or closing brace";
2035 /*************************************************
2036 * Match a semicolon *
2037 *************************************************/
2041 filter points to the Sieve filter including its state
2047 static int parse_semicolon(struct Sieve *filter)
2049 if (parse_white(filter)==-1) return -1;
2050 if (*filter->pc==';')
2057 filter->errmsg=CUS "missing semicolon";
2063 /*************************************************
2064 * Parse and interpret a Sieve command *
2065 *************************************************/
2069 filter points to the Sieve filter including its state
2070 exec Execute parsed statements
2071 generated where to hang newly-generated addresses
2073 Returns: 2 success by stop
2075 -1 syntax or execution error
2077 static int parse_commands(struct Sieve *filter, int exec,
2078 address_item **generated)
2082 if (parse_white(filter)==-1) return -1;
2083 if (parse_identifier(filter,CUS "if"))
2086 if-command = "if" test block *( "elsif" test block ) [ else block ]
2089 int cond,m,unsuccessful;
2092 if (parse_white(filter)==-1) return -1;
2093 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2096 filter->errmsg=CUS "missing test";
2099 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2100 (debug_selector & D_filter) != 0)
2102 if (exec) debug_printf("if %s\n",cond?"true":"false");
2104 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2105 (debug_selector & D_filter) != 0)
2107 if (exec) debug_printf("if %s\n",cond?"true":"false");
2109 m=parse_block(filter,exec ? cond : 0, generated);
2110 if (m==-1 || m==2) return m;
2113 filter->errmsg=CUS "missing block";
2116 unsuccessful = !cond;
2117 for (;;) /* elsif test block */
2119 if (parse_white(filter)==-1) return -1;
2120 if (parse_identifier(filter,CUS "elsif"))
2122 if (parse_white(filter)==-1) return -1;
2123 m=parse_test(filter,&cond,exec && unsuccessful);
2124 if (m==-1 || m==2) return m;
2127 filter->errmsg=CUS "missing test";
2130 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2131 (debug_selector & D_filter) != 0)
2133 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2135 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2136 (debug_selector & D_filter) != 0)
2138 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2140 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2141 if (m==-1 || m==2) return m;
2144 filter->errmsg=CUS "missing block";
2147 if (exec && unsuccessful && cond) unsuccessful = 0;
2152 if (parse_white(filter)==-1) return -1;
2153 if (parse_identifier(filter,CUS "else"))
2155 m=parse_block(filter,exec && unsuccessful, generated);
2156 if (m==-1 || m==2) return m;
2159 filter->errmsg=CUS "missing block";
2164 else if (parse_identifier(filter,CUS "stop"))
2167 stop-command = "stop" { stop-options } ";"
2171 if (parse_semicolon(filter)==-1) return -1;
2174 filter->pc+=Ustrlen(filter->pc);
2178 else if (parse_identifier(filter,CUS "keep"))
2181 keep-command = "keep" { keep-options } ";"
2185 if (parse_semicolon(filter)==-1) return -1;
2188 add_addr(generated,US"inbox",1,0,0,0);
2192 else if (parse_identifier(filter,CUS "discard"))
2195 discard-command = "discard" { discard-options } ";"
2199 if (parse_semicolon(filter)==-1) return -1;
2200 if (exec) filter->keep=0;
2202 else if (parse_identifier(filter,CUS "redirect"))
2205 redirect-command = "redirect" redirect-options "string" ";"
2207 redirect-options =) ":copy"
2210 struct String recipient;
2216 if (parse_white(filter)==-1) return -1;
2217 if (parse_identifier(filter,CUS ":copy")==1)
2219 if (!filter->require_copy)
2221 filter->errmsg=CUS "missing previous require \"copy\";";
2228 if (parse_white(filter)==-1) return -1;
2229 if ((m=parse_string(filter,&recipient))!=1)
2231 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2234 if (strchr(CCS recipient.character,'@')==(char*)0)
2236 filter->errmsg=CUS "unqualified recipient address";
2241 add_addr(generated,recipient.character,0,0,0,0);
2242 if (!copy) filter->keep = 0;
2244 if (parse_semicolon(filter)==-1) return -1;
2246 else if (parse_identifier(filter,CUS "fileinto"))
2249 fileinto-command = "fileinto" { fileinto-options } string ";"
2251 fileinto-options =) [ ":copy" ]
2254 struct String folder;
2257 unsigned long maxage, maxmessages, maxstorage;
2260 maxage = maxmessages = maxstorage = 0;
2261 if (!filter->require_fileinto)
2263 filter->errmsg=CUS "missing previous require \"fileinto\";";
2268 if (parse_white(filter)==-1) return -1;
2269 if (parse_identifier(filter,CUS ":copy")==1)
2271 if (!filter->require_copy)
2273 filter->errmsg=CUS "missing previous require \"copy\";";
2280 if (parse_white(filter)==-1) return -1;
2281 if ((m=parse_string(filter,&folder))!=1)
2283 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2286 m=0; s=folder.character;
2287 if (folder.length==0) m=1;
2288 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2291 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2296 filter->errmsg=CUS "invalid folder";
2301 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2302 if (!copy) filter->keep = 0;
2304 if (parse_semicolon(filter)==-1) return -1;
2307 else if (parse_identifier(filter,CUS "notify"))
2310 notify-command = "notify" { notify-options } ";"
2311 notify-options = [":method" string]
2316 struct String method;
2317 struct String message;
2319 if (!filter->require_notify)
2321 filter->errmsg=CUS "missing previous require \"notify\";";
2324 method.character=(uschar*)0;
2326 message.character=(uschar*)0;
2330 if (parse_white(filter)==-1) return -1;
2331 if (parse_identifier(filter,CUS ":method")==1)
2333 if (parse_white(filter)==-1) return -1;
2334 if ((m=parse_string(filter,&method))!=1)
2336 if (m==0) filter->errmsg=CUS "method string expected";
2340 else if (parse_identifier(filter,CUS ":message")==1)
2342 if (parse_white(filter)==-1) return -1;
2343 if ((m=parse_string(filter,&message))!=1)
2345 if (m==0) filter->errmsg=CUS "message string expected";
2351 if (parse_semicolon(filter)==-1) return -1;
2355 else if (parse_identifier(filter,CUS "vacation"))
2358 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2359 vacation-options = [":days" number]
2362 [":addresses" string-list]
2369 struct String subject;
2371 struct String *addresses;
2373 string_item *aliases;
2374 struct String handle;
2375 struct String reason;
2377 if (!filter->require_vacation)
2379 filter->errmsg=CUS "missing previous require \"vacation\";";
2384 if (filter->vacation_ran)
2386 filter->errmsg=CUS "trying to execute vacation more than once";
2389 filter->vacation_ran=1;
2391 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2392 subject.character=(uschar*)0;
2394 from.character=(uschar*)0;
2396 addresses=(struct String*)0;
2399 handle.character=(uschar*)0;
2403 if (parse_white(filter)==-1) return -1;
2404 if (parse_identifier(filter,CUS ":days")==1)
2406 if (parse_white(filter)==-1) return -1;
2407 if (parse_number(filter,&days)==-1) return -1;
2408 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2409 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2411 else if (parse_identifier(filter,CUS ":subject")==1)
2413 if (parse_white(filter)==-1) return -1;
2414 if ((m=parse_string(filter,&subject))!=1)
2416 if (m==0) filter->errmsg=CUS "subject string expected";
2420 else if (parse_identifier(filter,CUS ":from")==1)
2422 int start, end, domain;
2425 if (parse_white(filter)==-1) return -1;
2426 if ((m=parse_string(filter,&from))!=1)
2428 if (m==0) filter->errmsg=CUS "from string expected";
2433 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2437 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2438 "Sieve filter: %s", from.character, error);
2444 filter->errmsg=CUS "empty :from address in Sieve filter";
2448 else if (parse_identifier(filter,CUS ":addresses")==1)
2452 if (parse_white(filter)==-1) return -1;
2453 if ((m=parse_stringlist(filter,&addresses))!=1)
2455 if (m==0) filter->errmsg=CUS "addresses string list expected";
2458 for (a=addresses; a->length!=-1; ++a)
2462 new=store_get(sizeof(string_item));
2463 new->text=store_get(a->length+1);
2464 if (a->length) memcpy(new->text,a->character,a->length);
2465 new->text[a->length]='\0';
2470 else if (parse_identifier(filter,CUS ":mime")==1)
2472 else if (parse_identifier(filter,CUS ":handle")==1)
2474 if (parse_white(filter)==-1) return -1;
2475 if ((m=parse_string(filter,&from))!=1)
2477 if (m==0) filter->errmsg=CUS "handle string expected";
2483 if (parse_white(filter)==-1) return -1;
2484 if ((m=parse_string(filter,&reason))!=1)
2486 if (m==0) filter->errmsg=CUS "missing reason string";
2493 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2496 filter->errmsg=CUS "MIME reason string contains 8bit text";
2500 if (parse_semicolon(filter)==-1) return -1;
2507 int buffer_capacity;
2511 uschar hexdigest[33];
2515 if (filter_personal(aliases,TRUE))
2517 if (filter_test == FTEST_NONE)
2519 /* ensure oncelog directory exists; failure will be detected later */
2521 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2523 /* build oncelog filename */
2525 key.character=(uschar*)0;
2528 if (handle.length==-1)
2530 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2531 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2532 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2533 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2538 md5_end(&base, key.character, key.length, digest);
2539 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2540 if (filter_test != FTEST_NONE)
2542 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2546 capacity=Ustrlen(filter->vacation_directory);
2548 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2549 once=string_cat(once,&capacity,&start,hexdigest,33);
2552 /* process subject */
2554 if (subject.length==-1)
2556 expand_header(&subject,&str_subject);
2559 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2560 subject.length=start;
2563 /* add address to list of generated addresses */
2565 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2566 setflag(addr, af_pfr);
2567 setflag(addr, af_ignore_error);
2568 addr->next = *generated;
2570 addr->reply = store_get(sizeof(reply_item));
2571 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2572 addr->reply->to = string_copy(sender_address);
2573 if (from.length==-1)
2574 addr->reply->from = expand_string(US"$local_part@$domain");
2576 addr->reply->from = from.character;
2577 /* Allocation is larger than neccessary, but enough even for split MIME words */
2578 buffer_capacity=16+4*subject.length;
2579 buffer=store_get(buffer_capacity);
2580 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2581 addr->reply->oncelog=once;
2582 addr->reply->once_repeat=days*86400;
2584 /* build body and MIME headers */
2588 uschar *mime_body,*reason_end;
2590 static const uschar nlnl[]="\r\n\r\n";
2592 static const uschar nlnl[]="\n\n";
2597 mime_body=reason.character,reason_end=reason.character+reason.length;
2598 mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
2603 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2604 addr->reply->headers[start] = '\0';
2607 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
2608 else mime_body=reason_end-1;
2609 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2610 addr->reply->text[start] = '\0';
2617 start = reason.length;
2618 addr->reply->headers = US"MIME-Version: 1.0\n"
2619 "Content-Type: text/plain;\n"
2620 "\tcharset=\"utf-8\"\n"
2621 "Content-Transfer-Encoding: quoted-printable";
2622 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2626 else if (filter_test != FTEST_NONE)
2628 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
2639 /*************************************************
2640 * Parse and interpret a sieve filter *
2641 *************************************************/
2645 filter points to the Sieve filter including its state
2646 exec Execute parsed statements
2647 generated where to hang newly-generated addresses
2650 -1 syntax or execution error
2653 static int parse_start(struct Sieve *filter, int exec,
2654 address_item **generated)
2656 filter->pc=filter->filter;
2659 filter->require_envelope=0;
2660 filter->require_fileinto=0;
2661 #ifdef ENVELOPE_AUTH
2662 filter->require_envelope_auth=0;
2665 filter->require_notify=0;
2668 filter->require_subaddress=0;
2671 filter->require_vacation=0;
2672 filter->vacation_ran=0;
2674 filter->require_copy=0;
2675 filter->require_iascii_numeric=0;
2677 if (parse_white(filter)==-1) return -1;
2679 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
2682 struct dirent *oncelog;
2683 struct stat properties;
2686 /* clean up old vacation log databases */
2688 oncelogdir=opendir(CS filter->vacation_directory);
2690 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2692 filter->errmsg=CUS "unable to open vacation directory";
2696 if (oncelogdir != NULL)
2700 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2702 if (strlen(oncelog->d_name)==32)
2704 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2705 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2709 closedir(oncelogdir);
2713 while (parse_identifier(filter,CUS "require"))
2716 require-command = "require" <capabilities: string-list>
2719 struct String *cap,*check;
2722 if (parse_white(filter)==-1) return -1;
2723 if ((m=parse_stringlist(filter,&cap))!=1)
2725 if (m==0) filter->errmsg=CUS "capability string list expected";
2728 for (check=cap; check->character; ++check)
2730 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
2731 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
2732 #ifdef ENVELOPE_AUTH
2733 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
2736 else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
2739 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
2742 else if (eq_octet(check,&str_vacation,0))
2744 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
2746 filter->errmsg=CUS "vacation disabled";
2749 filter->require_vacation=1;
2752 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
2753 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
2754 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
2755 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
2756 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
2759 filter->errmsg=CUS "unknown capability";
2763 if (parse_semicolon(filter)==-1) return -1;
2765 if (parse_commands(filter,exec,generated)==-1) return -1;
2768 filter->errmsg=CUS "syntax error";
2775 /*************************************************
2776 * Interpret a sieve filter file *
2777 *************************************************/
2781 filter points to the entire file, read into store as a single string
2782 options controls whether various special things are allowed, and requests
2783 special actions (not currently used)
2784 sieve_vacation_directory where to store vacation "once" files
2785 useraddress string expression for :user part of address
2786 subaddress string expression for :subaddress part of address
2787 generated where to hang newly-generated addresses
2788 error where to pass back an error text
2790 Returns: FF_DELIVERED success, a significant action was taken
2791 FF_NOTDELIVERED success, no significant action
2792 FF_DEFER defer requested
2793 FF_FAIL fail requested
2794 FF_FREEZE freeze requested
2795 FF_ERROR there was a problem
2799 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
2800 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
2806 options = options; /* Keep picky compilers happy */
2809 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
2810 sieve.filter=filter;
2812 if (vacation_directory == NULL)
2813 sieve.vacation_directory = NULL;
2816 sieve.vacation_directory=expand_string(vacation_directory);
2817 if (sieve.vacation_directory == NULL)
2819 *error = string_sprintf("failed to expand \"%s\" "
2820 "(sieve_vacation_directory): %s", vacation_directory,
2821 expand_string_message);
2826 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
2827 sieve.subaddress = subaddress;
2829 #ifdef COMPILE_SYNTAX_CHECKER
2830 if (parse_start(&sieve,0,generated)==1)
2832 if (parse_start(&sieve,1,generated)==1)
2837 add_addr(generated,US"inbox",1,0,0,0);
2838 msg = string_sprintf("Implicit keep");
2843 msg = string_sprintf("No implicit keep");
2849 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
2850 #ifdef COMPILE_SYNTAX_CHECKER
2854 add_addr(generated,US"inbox",1,0,0,0);
2859 #ifndef COMPILE_SYNTAX_CHECKER
2860 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
2861 else debug_printf("%s\n", msg);
2864 DEBUG(D_route) debug_printf("Sieve: end of processing\n");