1 /* $Cambridge: exim/src/src/sieve.c,v 1.15 2005/11/15 10:08:25 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 "notify". */
34 /* Define this for the Sieve extension "subaddress". */
37 /* Define this for the Sieve extension "vacation". */
41 #define VACATION_MIN_DAYS 1
42 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
43 #define VACATION_MAX_DAYS 31
45 /* Keep this at 75 to accept only RFC compliant MIME words. */
46 /* Increase it if you want to match headers from buggy MUAs. */
47 #define MIMEWORD_LENGTH 75
62 int require_subaddress;
68 uschar *vacation_directory;
69 const uschar *subaddress;
70 const uschar *useraddress;
72 int require_iascii_numeric;
75 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
76 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
78 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
80 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
82 enum RelOp { LT, LE, EQ, GE, GT, NE };
90 static int parse_test(struct Sieve *filter, int *cond, int exec);
91 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
93 static uschar str_from_c[]="From";
94 static const struct String str_from={ str_from_c, 4 };
95 static uschar str_to_c[]="To";
96 static const struct String str_to={ str_to_c, 2 };
97 static uschar str_cc_c[]="Cc";
98 static const struct String str_cc={ str_cc_c, 2 };
99 static uschar str_bcc_c[]="Bcc";
100 static const struct String str_bcc={ str_bcc_c, 3 };
101 static uschar str_sender_c[]="Sender";
102 static const struct String str_sender={ str_sender_c, 6 };
103 static uschar str_resent_from_c[]="Resent-From";
104 static const struct String str_resent_from={ str_resent_from_c, 11 };
105 static uschar str_resent_to_c[]="Resent-To";
106 static const struct String str_resent_to={ str_resent_to_c, 9 };
107 static uschar str_fileinto_c[]="fileinto";
108 static const struct String str_fileinto={ str_fileinto_c, 8 };
109 static uschar str_envelope_c[]="envelope";
110 static const struct String str_envelope={ str_envelope_c, 8 };
112 static uschar str_notify_c[]="notify";
113 static const struct String str_notify={ str_notify_c, 6 };
116 static uschar str_subaddress_c[]="subaddress";
117 static const struct String str_subaddress={ str_subaddress_c, 10 };
120 static uschar str_vacation_c[]="vacation";
121 static const struct String str_vacation={ str_vacation_c, 8 };
122 static uschar str_subject_c[]="Subject";
123 static const struct String str_subject={ str_subject_c, 7 };
125 static uschar str_copy_c[]="copy";
126 static const struct String str_copy={ str_copy_c, 4 };
127 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
128 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
129 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
130 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
131 static uschar str_ioctet_c[]="i;octet";
132 static const struct String str_ioctet={ str_ioctet_c, 7 };
133 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
134 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
135 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
136 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
137 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
138 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
139 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
140 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
141 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
142 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
145 /*************************************************
146 * Encode to quoted-printable *
147 *************************************************/
154 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
157 const uschar *start,*end;
162 for (pass=0; pass<=1; ++pass)
169 dst->character=store_get(dst->length+1); /* plus one for \0 */
172 for (start=src->character,end=start+src->length; start<end; ++start)
189 || (ch>=62 && ch<=126)
195 && (*(start+1)!='\r' || *(start+2)!='\n')
210 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
238 sprintf(CS new,"=%02X",ch);
245 *new='\0'; /* not included in length, but nice */
250 /*************************************************
251 * Octet-wise string comparison *
252 *************************************************/
256 needle UTF-8 string to search ...
257 haystack ... inside the haystack
258 match_prefix 1 to compare if needle is a prefix of haystack
260 Returns: 0 needle not found in haystack
264 static int eq_octet(const struct String *needle,
265 const struct String *haystack, int match_prefix)
273 h=haystack->character;
277 if (*n&0x80) return 0;
278 if (*h&0x80) return 0;
280 if (*n!=*h) return 0;
286 return (match_prefix ? nl==0 : nl==0 && hl==0);
290 /*************************************************
291 * ASCII case-insensitive string comparison *
292 *************************************************/
296 needle UTF-8 string to search ...
297 haystack ... inside the haystack
298 match_prefix 1 to compare if needle is a prefix of haystack
300 Returns: 0 needle not found in haystack
304 static int eq_asciicase(const struct String *needle,
305 const struct String *haystack, int match_prefix)
314 h=haystack->character;
320 if (nc&0x80) return 0;
321 if (hc&0x80) return 0;
323 /* tolower depends on the locale and only ASCII case must be insensitive */
324 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
330 return (match_prefix ? nl==0 : nl==0 && hl==0);
334 /*************************************************
335 * Glob pattern search *
336 *************************************************/
340 needle pattern to search ...
341 haystack ... inside the haystack
343 Returns: 0 needle not found in haystack
348 static int eq_glob(const struct String *needle,
349 const struct String *haystack, int ascii_caseless)
351 const uschar *n,*h,*nend,*hend;
355 h=haystack->character;
356 nend=n+needle->length;
357 hend=h+haystack->length;
367 const uschar *npart,*hpart;
369 /* Try to match a non-star part of the needle at the current */
370 /* position in the haystack. */
374 while (npart<nend && *npart!='*') switch (*npart)
378 if (hpart==hend) return 0;
379 /* watch out: Do not match one character, but one UTF8 encoded character */
380 if ((*hpart&0xc0)==0xc0)
383 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
393 if (npart==nend) return -1;
398 if (hpart==hend) return 0;
399 /* tolower depends on the locale, but we need ASCII */
403 (*hpart&0x80) || (*npart&0x80) ||
406 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
411 /* string match after a star failed, advance and try again */
425 /* at this point, a part was matched successfully */
426 if (may_advance && npart==nend && hpart<hend)
427 /* needle ends, but haystack does not: if there was a star before, advance and try again */
437 return (h==hend ? 1 : may_advance);
441 /*************************************************
442 * ASCII numeric comparison *
443 *************************************************/
447 a first numeric string
448 b second numeric string
449 relop relational operator
451 Returns: 0 not (a relop b)
455 static int eq_asciinumeric(const struct String *a,
456 const struct String *b, enum RelOp relop)
459 const uschar *as,*aend,*bs,*bend;
463 aend=a->character+a->length;
465 bend=b->character+b->length;
467 while (*as>='0' && *as<='9' && as<aend) ++as;
469 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
472 if (al && bl==0) cmp=-1;
473 else if (al==0 && bl==0) cmp=0;
474 else if (al==0 && bl) cmp=1;
478 if (cmp==0) cmp=memcmp(a->character,b->character,al);
482 case LT: return cmp<0;
483 case LE: return cmp<=0;
484 case EQ: return cmp==0;
485 case GE: return cmp>=0;
486 case GT: return cmp>0;
487 case NE: return cmp!=0;
494 /*************************************************
496 *************************************************/
500 needle UTF-8 pattern or string to search ...
501 haystack ... inside the haystack
505 Returns: 0 needle not found in haystack
507 -1 comparator does not offer matchtype
510 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
511 enum Comparator co, enum MatchType mt)
515 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
516 (debug_selector & D_filter) != 0)
518 debug_printf("String comparison (match ");
521 case MATCH_IS: debug_printf(":is"); break;
522 case MATCH_CONTAINS: debug_printf(":contains"); break;
523 case MATCH_MATCHES: debug_printf(":matches"); break;
525 debug_printf(", comparison \"");
528 case COMP_OCTET: debug_printf("i;octet"); break;
529 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
530 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
532 debug_printf("\"):\n");
533 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
534 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
544 if (eq_octet(needle,haystack,0)) r=1;
547 case COMP_EN_ASCII_CASEMAP:
549 if (eq_asciicase(needle,haystack,0)) r=1;
552 case COMP_ASCII_NUMERIC:
554 if (!filter->require_iascii_numeric)
556 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
559 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
573 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
576 case COMP_EN_ASCII_CASEMAP:
578 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
583 filter->errmsg=CUS "comparator does not offer specified matchtype";
595 if ((r=eq_glob(needle,haystack,0))==-1)
597 filter->errmsg=CUS "syntactically invalid pattern";
602 case COMP_EN_ASCII_CASEMAP:
604 if ((r=eq_glob(needle,haystack,1))==-1)
606 filter->errmsg=CUS "syntactically invalid pattern";
613 filter->errmsg=CUS "comparator does not offer specified matchtype";
620 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
621 (debug_selector & D_filter) != 0)
622 debug_printf(" Result %s\n",r?"true":"false");
627 /*************************************************
628 * Check header field syntax *
629 *************************************************/
632 RFC 2822, section 3.6.8 says:
636 ftext = %d33-57 / ; Any character except
637 %d59-126 ; controls, SP, and
640 That forbids 8-bit header fields. This implementation accepts them, since
641 all of Exim is 8-bit clean, so it adds %d128-%d255.
644 header header field to quote for suitable use in Exim expansions
646 Returns: 0 string is not a valid header field
647 1 string is a value header field
650 static int is_header(const struct String *header)
660 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
671 /*************************************************
672 * Quote special characters string *
673 *************************************************/
677 header header field to quote for suitable use in Exim expansions
680 Returns: quoted string
683 static const uschar *quote(const struct String *header)
698 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
705 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
709 quoted=string_cat(quoted,&size,&ptr,h,1);
715 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
720 /*************************************************
721 * Add address to list of generated addresses *
722 *************************************************/
725 According to RFC 3028, duplicate delivery to the same address must
726 not happen, so the list is first searched for the address.
729 generated list of generated addresses
730 addr new address to add
731 file address denotes a file
736 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
738 address_item *new_addr;
740 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
742 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
744 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
746 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
752 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
754 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
756 new_addr=deliver_make_addr(addr,TRUE);
759 setflag(new_addr, af_pfr|af_file);
762 new_addr->p.errors_address = NULL;
763 new_addr->next = *generated;
764 *generated = new_addr;
768 /*************************************************
769 * Return decoded header field *
770 *************************************************/
774 value returned value of the field
775 header name of the header field
777 Returns: nothing The expanded string is empty
778 in case there is no such header
781 static void expand_header(struct String *value, const struct String *header)
787 value->character=(uschar*)0;
789 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
796 while (*r==' ' || *r=='\t') ++r;
803 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
807 /*************************************************
808 * Parse remaining hash comment *
809 *************************************************/
813 Comment up to terminating CRLF
816 filter points to the Sieve filter including its state
822 static int parse_hashcomment(struct Sieve *filter)
828 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
830 if (*filter->pc=='\n')
843 filter->errmsg=CUS "missing end of comment";
848 /*************************************************
849 * Parse remaining C-style comment *
850 *************************************************/
854 Everything up to star slash
857 filter points to the Sieve filter including its state
863 static int parse_comment(struct Sieve *filter)
868 if (*filter->pc=='*' && *(filter->pc+1)=='/')
875 filter->errmsg=CUS "missing end of comment";
880 /*************************************************
881 * Parse optional white space *
882 *************************************************/
886 Spaces, tabs, CRLFs, hash comments or C-style comments
889 filter points to the Sieve filter including its state
895 static int parse_white(struct Sieve *filter)
899 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
901 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
903 else if (*filter->pc=='\n')
913 else if (*filter->pc=='#')
915 if (parse_hashcomment(filter)==-1) return -1;
917 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
919 if (parse_comment(filter)==-1) return -1;
927 /*************************************************
928 * Parse a optional string *
929 *************************************************/
933 quoted-string = DQUOTE *CHAR DQUOTE
934 ;; in general, \ CHAR inside a string maps to CHAR
935 ;; so \" maps to " and \\ maps to \
936 ;; note that newlines and other characters are all allowed
939 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
940 *(multi-line-literal / multi-line-dotstuff)
942 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
943 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
944 ;; A line containing only "." ends the multi-line.
945 ;; Remove a leading '.' if followed by another '.'.
946 string = quoted-string / multi-line
949 filter points to the Sieve filter including its state
950 id specifies identifier to match
954 0 identifier not matched
957 static int parse_string(struct Sieve *filter, struct String *data)
962 data->character=(uschar*)0;
963 if (*filter->pc=='"') /* quoted string */
968 if (*filter->pc=='"') /* end of string */
970 int foo=data->length;
973 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
976 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
978 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
979 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
982 else /* regular character */
984 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
988 filter->errmsg=CUS "missing end of string";
991 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
994 /* skip optional white space followed by hashed comment or CRLF */
995 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
996 if (*filter->pc=='#')
998 if (parse_hashcomment(filter)==-1) return -1;
1001 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1003 else if (*filter->pc=='\n')
1015 filter->errmsg=CUS "syntax error";
1021 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1023 if (*filter->pc=='\n') /* end of line */
1026 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1034 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1036 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1039 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1048 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1050 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1054 else /* regular character */
1056 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1060 filter->errmsg=CUS "missing end of multi line string";
1067 /*************************************************
1068 * Parse a specific identifier *
1069 *************************************************/
1073 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1076 filter points to the Sieve filter including its state
1077 id specifies identifier to match
1080 0 identifier not matched
1083 static int parse_identifier(struct Sieve *filter, const uschar *id)
1085 size_t idlen=Ustrlen(id);
1087 if (Ustrncmp(filter->pc,id,idlen)==0)
1089 uschar next=filter->pc[idlen];
1091 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1099 /*************************************************
1101 *************************************************/
1105 number = 1*DIGIT [QUANTIFIER]
1106 QUANTIFIER = "K" / "M" / "G"
1109 filter points to the Sieve filter including its state
1113 -1 no string list found
1116 static int parse_number(struct Sieve *filter, unsigned long *data)
1120 if (*filter->pc>='0' && *filter->pc<='9')
1125 d=Ustrtoul(filter->pc,&e,10);
1128 filter->errmsg=CUstrerror(ERANGE);
1133 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1134 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1135 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1136 if (d>(ULONG_MAX/u))
1138 filter->errmsg=CUstrerror(ERANGE);
1147 filter->errmsg=CUS "missing number";
1153 /*************************************************
1154 * Parse a string list *
1155 *************************************************/
1159 string-list = "[" string *("," string) "]" / string
1162 filter points to the Sieve filter including its state
1163 data returns string list
1166 -1 no string list found
1169 static int parse_stringlist(struct Sieve *filter, struct String **data)
1171 const uschar *orig=filter->pc;
1174 struct String *d=(struct String*)0;
1177 if (*filter->pc=='[') /* string list */
1182 if (parse_white(filter)==-1) goto error;
1183 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1186 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1187 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1188 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1190 filter->errmsg=CUstrerror(errno);
1193 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1195 dataCapacity=newCapacity;
1197 m=parse_string(filter,&d[dataLength]);
1200 if (dataLength==0) break;
1203 filter->errmsg=CUS "missing string";
1207 else if (m==-1) goto error;
1209 if (parse_white(filter)==-1) goto error;
1210 if (*filter->pc==',') ++filter->pc;
1213 if (*filter->pc==']')
1215 d[dataLength].character=(uschar*)0;
1216 d[dataLength].length=-1;
1223 filter->errmsg=CUS "missing closing bracket";
1227 else /* single string */
1229 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1233 m=parse_string(filter,&d[0]);
1245 d[1].character=(uschar*)0;
1252 filter->errmsg=CUS "missing string list";
1257 /*************************************************
1258 * Parse an optional address part specifier *
1259 *************************************************/
1263 address-part = ":localpart" / ":domain" / ":all"
1264 address-part =/ ":user" / ":detail"
1267 filter points to the Sieve filter including its state
1268 a returns address part specified
1271 0 no comparator found
1275 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1278 if (parse_identifier(filter,CUS ":user")==1)
1280 if (!filter->require_subaddress)
1282 filter->errmsg=CUS "missing previous require \"subaddress\";";
1288 else if (parse_identifier(filter,CUS ":detail")==1)
1290 if (!filter->require_subaddress)
1292 filter->errmsg=CUS "missing previous require \"subaddress\";";
1300 if (parse_identifier(filter,CUS ":localpart")==1)
1302 *a=ADDRPART_LOCALPART;
1305 else if (parse_identifier(filter,CUS ":domain")==1)
1310 else if (parse_identifier(filter,CUS ":all")==1)
1319 /*************************************************
1320 * Parse an optional comparator *
1321 *************************************************/
1325 comparator = ":comparator" <comparator-name: string>
1328 filter points to the Sieve filter including its state
1329 c returns comparator
1332 0 no comparator found
1333 -1 incomplete comparator found
1336 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1338 struct String comparator_name;
1340 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1341 if (parse_white(filter)==-1) return -1;
1342 switch (parse_string(filter,&comparator_name))
1347 filter->errmsg=CUS "missing comparator";
1354 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1359 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1361 *c=COMP_EN_ASCII_CASEMAP;
1364 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1366 *c=COMP_EN_ASCII_CASEMAP;
1369 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1371 *c=COMP_ASCII_NUMERIC;
1376 filter->errmsg=CUS "invalid comparator";
1385 /*************************************************
1386 * Parse an optional match type *
1387 *************************************************/
1391 match-type = ":is" / ":contains" / ":matches"
1394 filter points to the Sieve filter including its state
1395 m returns match type
1398 0 no match type found
1401 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1403 if (parse_identifier(filter,CUS ":is")==1)
1408 else if (parse_identifier(filter,CUS ":contains")==1)
1413 else if (parse_identifier(filter,CUS ":matches")==1)
1422 /*************************************************
1423 * Parse and interpret an optional test list *
1424 *************************************************/
1428 test-list = "(" test *("," test) ")"
1431 filter points to the Sieve filter including its state
1432 n total number of tests
1433 true number of passed tests
1434 exec Execute parsed statements
1437 0 no test list found
1438 -1 syntax or execution error
1441 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1443 if (parse_white(filter)==-1) return -1;
1444 if (*filter->pc=='(')
1453 switch (parse_test(filter,&cond,exec))
1456 case 0: filter->errmsg=CUS "missing test"; return -1;
1457 default: ++*n; if (cond) ++*true; break;
1459 if (parse_white(filter)==-1) return -1;
1460 if (*filter->pc==',') ++filter->pc;
1463 if (*filter->pc==')')
1470 filter->errmsg=CUS "missing closing paren";
1478 /*************************************************
1479 * Parse and interpret an optional test *
1480 *************************************************/
1484 filter points to the Sieve filter including its state
1485 cond returned condition status
1486 exec Execute parsed statements
1490 -1 syntax or execution error
1493 static int parse_test(struct Sieve *filter, int *cond, int exec)
1495 if (parse_white(filter)==-1) return -1;
1496 if (parse_identifier(filter,CUS "address"))
1499 address-test = "address" { [address-part] [comparator] [match-type] }
1500 <header-list: string-list> <key-list: string-list>
1502 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1505 enum AddressPart addressPart=ADDRPART_ALL;
1506 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1507 enum MatchType matchType=MATCH_IS;
1508 struct String *hdr,*h,*key,*k;
1514 if (parse_white(filter)==-1) return -1;
1515 if ((m=parse_addresspart(filter,&addressPart))!=0)
1517 if (m==-1) return -1;
1520 filter->errmsg=CUS "address part already specified";
1525 else if ((m=parse_comparator(filter,&comparator))!=0)
1527 if (m==-1) return -1;
1530 filter->errmsg=CUS "comparator already specified";
1535 else if ((m=parse_matchtype(filter,&matchType))!=0)
1537 if (m==-1) return -1;
1540 filter->errmsg=CUS "match type already specified";
1547 if (parse_white(filter)==-1) return -1;
1548 if ((m=parse_stringlist(filter,&hdr))!=1)
1550 if (m==0) filter->errmsg=CUS "header string list expected";
1553 if (parse_white(filter)==-1) return -1;
1554 if ((m=parse_stringlist(filter,&key))!=1)
1556 if (m==0) filter->errmsg=CUS "key string list expected";
1560 for (h=hdr; h->length!=-1 && !*cond; ++h)
1562 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1566 !eq_asciicase(h,&str_from,0)
1567 && !eq_asciicase(h,&str_to,0)
1568 && !eq_asciicase(h,&str_cc,0)
1569 && !eq_asciicase(h,&str_bcc,0)
1570 && !eq_asciicase(h,&str_sender,0)
1571 && !eq_asciicase(h,&str_resent_from,0)
1572 && !eq_asciicase(h,&str_resent_to,0)
1575 filter->errmsg=CUS "invalid header field";
1580 /* We are only interested in addresses below, so no MIME decoding */
1581 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1582 if (header_value == NULL)
1584 filter->errmsg=CUS "header string expansion failed";
1587 parse_allow_group = TRUE;
1588 while (*header_value && !*cond)
1591 int start, end, domain;
1595 end_addr = parse_find_address_end(header_value, FALSE);
1596 saveend = *end_addr;
1598 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1600 if (extracted_addr) switch (addressPart)
1602 case ADDRPART_ALL: part=extracted_addr; break;
1606 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1607 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1609 case ADDRPART_DETAIL: part=NULL; break;
1613 *end_addr = saveend;
1616 for (k=key; k->length!=-1; ++k)
1618 struct String partStr;
1620 partStr.character=part;
1621 partStr.length=Ustrlen(part);
1624 *cond=compare(filter,k,&partStr,comparator,matchType);
1625 if (*cond==-1) return -1;
1630 if (saveend == 0) break;
1631 header_value = end_addr + 1;
1637 else if (parse_identifier(filter,CUS "allof"))
1640 allof-test = "allof" <tests: test-list>
1645 switch (parse_testlist(filter,&n,&true,exec))
1648 case 0: filter->errmsg=CUS "missing test list"; return -1;
1649 default: *cond=(n==true); return 1;
1652 else if (parse_identifier(filter,CUS "anyof"))
1655 anyof-test = "anyof" <tests: test-list>
1660 switch (parse_testlist(filter,&n,&true,exec))
1663 case 0: filter->errmsg=CUS "missing test list"; return -1;
1664 default: *cond=(true>0); return 1;
1667 else if (parse_identifier(filter,CUS "exists"))
1670 exists-test = "exists" <header-names: string-list>
1673 struct String *hdr,*h;
1676 if (parse_white(filter)==-1) return -1;
1677 if ((m=parse_stringlist(filter,&hdr))!=1)
1679 if (m==0) filter->errmsg=CUS "header string list expected";
1685 for (h=hdr; h->length!=-1 && *cond; ++h)
1689 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1690 if (header_def == NULL)
1692 filter->errmsg=CUS "header string expansion failed";
1695 if (Ustrcmp(header_def,"false")==0) *cond=0;
1700 else if (parse_identifier(filter,CUS "false"))
1703 false-test = "false"
1709 else if (parse_identifier(filter,CUS "header"))
1712 header-test = "header" { [comparator] [match-type] }
1713 <header-names: string-list> <key-list: string-list>
1716 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1717 enum MatchType matchType=MATCH_IS;
1718 struct String *hdr,*h,*key,*k;
1724 if (parse_white(filter)==-1) return -1;
1725 if ((m=parse_comparator(filter,&comparator))!=0)
1727 if (m==-1) return -1;
1730 filter->errmsg=CUS "comparator already specified";
1735 else if ((m=parse_matchtype(filter,&matchType))!=0)
1737 if (m==-1) return -1;
1740 filter->errmsg=CUS "match type already specified";
1747 if (parse_white(filter)==-1) return -1;
1748 if ((m=parse_stringlist(filter,&hdr))!=1)
1750 if (m==0) filter->errmsg=CUS "header string list expected";
1753 if (parse_white(filter)==-1) return -1;
1754 if ((m=parse_stringlist(filter,&key))!=1)
1756 if (m==0) filter->errmsg=CUS "key string list expected";
1760 for (h=hdr; h->length!=-1 && !*cond; ++h)
1764 filter->errmsg=CUS "invalid header field";
1769 struct String header_value;
1772 expand_header(&header_value,h);
1773 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1774 if (header_value.character == NULL || header_def == NULL)
1776 filter->errmsg=CUS "header string expansion failed";
1779 for (k=key; k->length!=-1; ++k)
1781 if (Ustrcmp(header_def,"true")==0)
1783 *cond=compare(filter,k,&header_value,comparator,matchType);
1784 if (*cond==-1) return -1;
1792 else if (parse_identifier(filter,CUS "not"))
1794 if (parse_white(filter)==-1) return -1;
1795 switch (parse_test(filter,cond,exec))
1798 case 0: filter->errmsg=CUS "missing test"; return -1;
1799 default: *cond=!*cond; return 1;
1802 else if (parse_identifier(filter,CUS "size"))
1805 relop = ":over" / ":under"
1806 size-test = "size" relop <limit: number>
1809 unsigned long limit;
1812 if (parse_white(filter)==-1) return -1;
1813 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
1814 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
1817 filter->errmsg=CUS "missing :over or :under";
1820 if (parse_white(filter)==-1) return -1;
1821 if (parse_number(filter,&limit)==-1) return -1;
1822 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
1825 else if (parse_identifier(filter,CUS "true"))
1830 else if (parse_identifier(filter,CUS "envelope"))
1833 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
1834 <envelope-part: string-list> <key-list: string-list>
1836 envelope-part is case insensitive "from" or "to"
1839 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1840 enum AddressPart addressPart=ADDRPART_ALL;
1841 enum MatchType matchType=MATCH_IS;
1842 struct String *env,*e,*key,*k;
1846 if (!filter->require_envelope)
1848 filter->errmsg=CUS "missing previous require \"envelope\";";
1853 if (parse_white(filter)==-1) return -1;
1854 if ((m=parse_comparator(filter,&comparator))!=0)
1856 if (m==-1) return -1;
1859 filter->errmsg=CUS "comparator already specified";
1864 else if ((m=parse_addresspart(filter,&addressPart))!=0)
1866 if (m==-1) return -1;
1869 filter->errmsg=CUS "address part already specified";
1874 else if ((m=parse_matchtype(filter,&matchType))!=0)
1876 if (m==-1) return -1;
1879 filter->errmsg=CUS "match type already specified";
1886 if (parse_white(filter)==-1) return -1;
1887 if ((m=parse_stringlist(filter,&env))!=1)
1889 if (m==0) filter->errmsg=CUS "envelope string list expected";
1892 if (parse_white(filter)==-1) return -1;
1893 if ((m=parse_stringlist(filter,&key))!=1)
1895 if (m==0) filter->errmsg=CUS "key string list expected";
1899 for (e=env; e->length!=-1 && !*cond; ++e)
1901 const uschar *envelopeExpr=CUS 0;
1902 uschar *envelope=US 0;
1904 if (eq_asciicase(e,&str_from,0))
1906 switch (addressPart)
1908 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
1912 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
1913 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
1915 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
1919 else if (eq_asciicase(e,&str_to,0))
1921 switch (addressPart)
1923 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
1925 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
1926 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
1928 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
1929 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
1934 filter->errmsg=CUS "invalid envelope string";
1937 if (exec && envelopeExpr)
1939 if ((envelope=expand_string(US envelopeExpr)) == NULL)
1941 filter->errmsg=CUS "header string expansion failed";
1944 for (k=key; k->length!=-1; ++k)
1946 struct String envelopeStr;
1948 envelopeStr.character=envelope;
1949 envelopeStr.length=Ustrlen(envelope);
1950 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
1951 if (*cond==-1) return -1;
1962 /*************************************************
1963 * Parse and interpret an optional block *
1964 *************************************************/
1968 filter points to the Sieve filter including its state
1969 exec Execute parsed statements
1970 generated where to hang newly-generated addresses
1972 Returns: 2 success by stop
1974 0 no block command found
1975 -1 syntax or execution error
1978 static int parse_block(struct Sieve *filter, int exec,
1979 address_item **generated)
1983 if (parse_white(filter)==-1) return -1;
1984 if (*filter->pc=='{')
1987 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
1988 if (*filter->pc=='}')
1995 filter->errmsg=CUS "expecting command or closing brace";
2003 /*************************************************
2004 * Match a semicolon *
2005 *************************************************/
2009 filter points to the Sieve filter including its state
2015 static int parse_semicolon(struct Sieve *filter)
2017 if (parse_white(filter)==-1) return -1;
2018 if (*filter->pc==';')
2025 filter->errmsg=CUS "missing semicolon";
2031 /*************************************************
2032 * Parse and interpret a Sieve command *
2033 *************************************************/
2037 filter points to the Sieve filter including its state
2038 exec Execute parsed statements
2039 generated where to hang newly-generated addresses
2041 Returns: 2 success by stop
2043 -1 syntax or execution error
2045 static int parse_commands(struct Sieve *filter, int exec,
2046 address_item **generated)
2050 if (parse_white(filter)==-1) return -1;
2051 if (parse_identifier(filter,CUS "if"))
2054 if-command = "if" test block *( "elsif" test block ) [ else block ]
2057 int cond,m,unsuccessful;
2060 if (parse_white(filter)==-1) return -1;
2061 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2064 filter->errmsg=CUS "missing test";
2067 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2068 (debug_selector & D_filter) != 0)
2070 if (exec) debug_printf("if %s\n",cond?"true":"false");
2072 m=parse_block(filter,exec ? cond : 0, generated);
2073 if (m==-1 || m==2) return m;
2076 filter->errmsg=CUS "missing block";
2079 unsuccessful = !cond;
2080 for (;;) /* elsif test block */
2082 if (parse_white(filter)==-1) return -1;
2083 if (parse_identifier(filter,CUS "elsif"))
2085 if (parse_white(filter)==-1) return -1;
2086 m=parse_test(filter,&cond,exec && unsuccessful);
2087 if (m==-1 || m==2) return m;
2090 filter->errmsg=CUS "missing test";
2093 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2094 (debug_selector & D_filter) != 0)
2096 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2098 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2099 if (m==-1 || m==2) return m;
2102 filter->errmsg=CUS "missing block";
2105 if (exec && unsuccessful && cond) unsuccessful = 0;
2110 if (parse_white(filter)==-1) return -1;
2111 if (parse_identifier(filter,CUS "else"))
2113 m=parse_block(filter,exec && unsuccessful, generated);
2114 if (m==-1 || m==2) return m;
2117 filter->errmsg=CUS "missing block";
2122 else if (parse_identifier(filter,CUS "stop"))
2125 stop-command = "stop" { stop-options } ";"
2129 if (parse_semicolon(filter)==-1) return -1;
2132 filter->pc+=Ustrlen(filter->pc);
2136 else if (parse_identifier(filter,CUS "keep"))
2139 keep-command = "keep" { keep-options } ";"
2143 if (parse_semicolon(filter)==-1) return -1;
2146 add_addr(generated,US"inbox",1,0,0,0);
2150 else if (parse_identifier(filter,CUS "discard"))
2153 discard-command = "discard" { discard-options } ";"
2157 if (parse_semicolon(filter)==-1) return -1;
2158 if (exec) filter->keep=0;
2160 else if (parse_identifier(filter,CUS "redirect"))
2163 redirect-command = "redirect" redirect-options "string" ";"
2165 redirect-options =) ":copy"
2168 struct String recipient;
2174 if (parse_white(filter)==-1) return -1;
2175 if (parse_identifier(filter,CUS ":copy")==1)
2177 if (!filter->require_copy)
2179 filter->errmsg=CUS "missing previous require \"copy\";";
2186 if (parse_white(filter)==-1) return -1;
2187 if ((m=parse_string(filter,&recipient))!=1)
2189 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2192 if (strchr(CCS recipient.character,'@')==(char*)0)
2194 filter->errmsg=CUS "unqualified recipient address";
2199 add_addr(generated,recipient.character,0,0,0,0);
2200 if (!copy) filter->keep = 0;
2202 if (parse_semicolon(filter)==-1) return -1;
2204 else if (parse_identifier(filter,CUS "fileinto"))
2207 fileinto-command = "fileinto" { fileinto-options } string ";"
2209 fileinto-options =) [ ":copy" ]
2212 struct String folder;
2215 unsigned long maxage, maxmessages, maxstorage;
2218 maxage = maxmessages = maxstorage = 0;
2219 if (!filter->require_fileinto)
2221 filter->errmsg=CUS "missing previous require \"fileinto\";";
2226 if (parse_white(filter)==-1) return -1;
2227 if (parse_identifier(filter,CUS ":copy")==1)
2229 if (!filter->require_copy)
2231 filter->errmsg=CUS "missing previous require \"copy\";";
2238 if (parse_white(filter)==-1) return -1;
2239 if ((m=parse_string(filter,&folder))!=1)
2241 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2244 m=0; s=folder.character;
2245 if (folder.length==0) m=1;
2246 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2249 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2254 filter->errmsg=CUS "invalid folder";
2259 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2260 if (!copy) filter->keep = 0;
2262 if (parse_semicolon(filter)==-1) return -1;
2265 else if (parse_identifier(filter,CUS "notify"))
2268 notify-command = "notify" { notify-options } ";"
2269 notify-options = [":method" string]
2274 struct String method;
2275 struct String message;
2277 if (!filter->require_notify)
2279 filter->errmsg=CUS "missing previous require \"notify\";";
2282 method.character=(uschar*)0;
2284 message.character=(uschar*)0;
2288 if (parse_white(filter)==-1) return -1;
2289 if (parse_identifier(filter,CUS ":method")==1)
2291 if (parse_white(filter)==-1) return -1;
2292 if ((m=parse_string(filter,&method))!=1)
2294 if (m==0) filter->errmsg=CUS "method string expected";
2298 else if (parse_identifier(filter,CUS ":message")==1)
2300 if (parse_white(filter)==-1) return -1;
2301 if ((m=parse_string(filter,&message))!=1)
2303 if (m==0) filter->errmsg=CUS "message string expected";
2309 if (parse_semicolon(filter)==-1) return -1;
2313 else if (parse_identifier(filter,CUS "vacation"))
2316 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2317 vacation-options = [":days" number]
2320 [":addresses" string-list]
2327 struct String subject;
2329 struct String *addresses;
2331 string_item *aliases;
2332 struct String handle;
2333 struct String reason;
2335 if (!filter->require_vacation)
2337 filter->errmsg=CUS "missing previous require \"vacation\";";
2342 if (filter->vacation_ran)
2344 filter->errmsg=CUS "trying to execute vacation more than once";
2347 filter->vacation_ran=1;
2349 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2350 subject.character=(uschar*)0;
2352 from.character=(uschar*)0;
2354 addresses=(struct String*)0;
2357 handle.character=(uschar*)0;
2361 if (parse_white(filter)==-1) return -1;
2362 if (parse_identifier(filter,CUS ":days")==1)
2364 if (parse_white(filter)==-1) return -1;
2365 if (parse_number(filter,&days)==-1) return -1;
2366 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2367 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2369 else if (parse_identifier(filter,CUS ":subject")==1)
2371 if (parse_white(filter)==-1) return -1;
2372 if ((m=parse_string(filter,&subject))!=1)
2374 if (m==0) filter->errmsg=CUS "subject string expected";
2378 else if (parse_identifier(filter,CUS ":from")==1)
2380 int start, end, domain;
2383 if (parse_white(filter)==-1) return -1;
2384 if ((m=parse_string(filter,&from))!=1)
2386 if (m==0) filter->errmsg=CUS "from string expected";
2391 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2395 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2396 "Sieve filter: %s", from.character, error);
2402 filter->errmsg=CUS "empty :from address in Sieve filter";
2406 else if (parse_identifier(filter,CUS ":addresses")==1)
2410 if (parse_white(filter)==-1) return -1;
2411 if ((m=parse_stringlist(filter,&addresses))!=1)
2413 if (m==0) filter->errmsg=CUS "addresses string list expected";
2416 for (a=addresses; a->length!=-1; ++a)
2420 new=store_get(sizeof(string_item));
2421 new->text=store_get(a->length+1);
2422 if (a->length) memcpy(new->text,a->character,a->length);
2423 new->text[a->length]='\0';
2428 else if (parse_identifier(filter,CUS ":mime")==1)
2430 else if (parse_identifier(filter,CUS ":handle")==1)
2432 if (parse_white(filter)==-1) return -1;
2433 if ((m=parse_string(filter,&from))!=1)
2435 if (m==0) filter->errmsg=CUS "handle string expected";
2441 if (parse_white(filter)==-1) return -1;
2442 if ((m=parse_string(filter,&reason))!=1)
2444 if (m==0) filter->errmsg=CUS "missing reason string";
2451 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2454 filter->errmsg=CUS "MIME reason string contains 8bit text";
2458 if (parse_semicolon(filter)==-1) return -1;
2465 int buffer_capacity;
2469 uschar hexdigest[33];
2473 if (filter_personal(aliases,TRUE))
2475 if (filter_test == FTEST_NONE)
2477 /* ensure oncelog directory exists; failure will be detected later */
2479 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2481 /* build oncelog filename */
2483 key.character=(uschar*)0;
2486 if (handle.length==-1)
2488 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2489 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2490 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2491 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2496 md5_end(&base, key.character, key.length, digest);
2497 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2498 if (filter_test != FTEST_NONE)
2500 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2504 capacity=Ustrlen(filter->vacation_directory);
2506 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2507 once=string_cat(once,&capacity,&start,hexdigest,33);
2510 /* process subject */
2512 if (subject.length==-1)
2514 expand_header(&subject,&str_subject);
2517 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2518 subject.length=start;
2521 /* add address to list of generated addresses */
2523 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2524 setflag(addr, af_pfr);
2525 setflag(addr, af_ignore_error);
2526 addr->next = *generated;
2528 addr->reply = store_get(sizeof(reply_item));
2529 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2530 addr->reply->to = string_copy(sender_address);
2531 if (from.length==-1)
2532 addr->reply->from = expand_string(US"$local_part@$domain");
2534 addr->reply->from = from.character;
2535 /* Allocation is larger than neccessary, but enough even for split MIME words */
2536 buffer_capacity=16+4*subject.length;
2537 buffer=store_get(buffer_capacity);
2538 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2539 addr->reply->oncelog=once;
2540 addr->reply->once_repeat=days*86400;
2542 /* build body and MIME headers */
2546 uschar *mime_body,*reason_end;
2548 static const uschar nlnl[]="\r\n\r\n";
2550 static const uschar nlnl[]="\n\n";
2555 mime_body=reason.character,reason_end=reason.character+reason.length;
2556 mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
2561 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2562 addr->reply->headers[start] = '\0';
2565 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
2566 else mime_body=reason_end-1;
2567 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2568 addr->reply->text[start] = '\0';
2575 start = reason.length;
2576 addr->reply->headers = US"MIME-Version: 1.0\n"
2577 "Content-Type: text/plain;\n"
2578 "\tcharset=\"utf-8\"\n"
2579 "Content-Transfer-Encoding: quoted-printable";
2580 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2584 else if (filter_test != FTEST_NONE)
2586 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
2597 /*************************************************
2598 * Parse and interpret a sieve filter *
2599 *************************************************/
2603 filter points to the Sieve filter including its state
2604 exec Execute parsed statements
2605 generated where to hang newly-generated addresses
2608 -1 syntax or execution error
2611 static int parse_start(struct Sieve *filter, int exec,
2612 address_item **generated)
2614 filter->pc=filter->filter;
2617 filter->require_envelope=0;
2618 filter->require_fileinto=0;
2620 filter->require_notify=0;
2623 filter->require_subaddress=0;
2626 filter->require_vacation=0;
2627 filter->vacation_ran=0;
2629 filter->require_copy=0;
2630 filter->require_iascii_numeric=0;
2632 if (parse_white(filter)==-1) return -1;
2634 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
2637 struct dirent *oncelog;
2638 struct stat properties;
2641 /* clean up old vacation log databases */
2643 oncelogdir=opendir(CS filter->vacation_directory);
2645 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2647 filter->errmsg=CUS "unable to open vacation directory";
2651 if (oncelogdir != NULL)
2655 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2657 if (strlen(oncelog->d_name)==32)
2659 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2660 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2664 closedir(oncelogdir);
2668 while (parse_identifier(filter,CUS "require"))
2671 require-command = "require" <capabilities: string-list>
2674 struct String *cap,*check;
2677 if (parse_white(filter)==-1) return -1;
2678 if ((m=parse_stringlist(filter,&cap))!=1)
2680 if (m==0) filter->errmsg=CUS "capability string list expected";
2683 for (check=cap; check->character; ++check)
2685 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
2686 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
2688 else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
2691 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
2694 else if (eq_octet(check,&str_vacation,0))
2696 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
2698 filter->errmsg=CUS "vacation disabled";
2701 filter->require_vacation=1;
2704 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
2705 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
2706 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
2707 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
2708 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
2711 filter->errmsg=CUS "unknown capability";
2715 if (parse_semicolon(filter)==-1) return -1;
2717 if (parse_commands(filter,exec,generated)==-1) return -1;
2720 filter->errmsg=CUS "syntax error";
2727 /*************************************************
2728 * Interpret a sieve filter file *
2729 *************************************************/
2733 filter points to the entire file, read into store as a single string
2734 options controls whether various special things are allowed, and requests
2735 special actions (not currently used)
2736 sieve_vacation_directory where to store vacation "once" files
2737 useraddress string expression for :user part of address
2738 subaddress string expression for :subaddress part of address
2739 generated where to hang newly-generated addresses
2740 error where to pass back an error text
2742 Returns: FF_DELIVERED success, a significant action was taken
2743 FF_NOTDELIVERED success, no significant action
2744 FF_DEFER defer requested
2745 FF_FAIL fail requested
2746 FF_FREEZE freeze requested
2747 FF_ERROR there was a problem
2751 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
2752 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
2758 options = options; /* Keep picky compilers happy */
2761 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
2762 sieve.filter=filter;
2764 if (vacation_directory == NULL)
2765 sieve.vacation_directory = NULL;
2768 sieve.vacation_directory=expand_string(vacation_directory);
2769 if (sieve.vacation_directory == NULL)
2771 *error = string_sprintf("failed to expand \"%s\" "
2772 "(sieve_vacation_directory): %s", vacation_directory,
2773 expand_string_message);
2778 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
2779 sieve.subaddress = subaddress;
2781 #ifdef COMPILE_SYNTAX_CHECKER
2782 if (parse_start(&sieve,0,generated)==1)
2784 if (parse_start(&sieve,1,generated)==1)
2789 add_addr(generated,US"inbox",1,0,0,0);
2790 msg = string_sprintf("Implicit keep");
2795 msg = string_sprintf("No implicit keep");
2801 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
2802 #ifdef COMPILE_SYNTAX_CHECKER
2806 add_addr(generated,US"inbox",1,0,0,0);
2811 #ifndef COMPILE_SYNTAX_CHECKER
2812 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
2813 else debug_printf("%s\n", msg);
2816 DEBUG(D_route) debug_printf("Sieve: end of processing\n");