1 /* $Cambridge: exim/src/src/sieve.c,v 1.12 2005/06/17 10:47:05 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 subaddress Sieve extension. */
34 /* Define this for the vacation Sieve extension. */
38 #define VACATION_MIN_DAYS 1
39 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
40 #define VACATION_MAX_DAYS 31
42 /* Keep this at 75 to accept only RFC compliant MIME words. */
43 /* Increase it if you want to match headers from buggy MUAs. */
44 #define MIMEWORD_LENGTH 75
56 int require_subaddress;
62 uschar *vacation_directory;
63 const uschar *subaddress;
64 const uschar *useraddress;
66 int require_iascii_numeric;
69 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
70 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
72 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
74 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
76 enum RelOp { LT, LE, EQ, GE, GT, NE };
84 static int parse_test(struct Sieve *filter, int *cond, int exec);
85 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
87 static uschar str_from_c[]="From";
88 static const struct String str_from={ str_from_c, 4 };
89 static uschar str_to_c[]="To";
90 static const struct String str_to={ str_to_c, 2 };
91 static uschar str_cc_c[]="Cc";
92 static const struct String str_cc={ str_cc_c, 2 };
93 static uschar str_bcc_c[]="Bcc";
94 static const struct String str_bcc={ str_bcc_c, 3 };
95 static uschar str_sender_c[]="Sender";
96 static const struct String str_sender={ str_sender_c, 6 };
97 static uschar str_resent_from_c[]="Resent-From";
98 static const struct String str_resent_from={ str_resent_from_c, 11 };
99 static uschar str_resent_to_c[]="Resent-To";
100 static const struct String str_resent_to={ str_resent_to_c, 9 };
101 static uschar str_fileinto_c[]="fileinto";
102 static const struct String str_fileinto={ str_fileinto_c, 8 };
103 static uschar str_envelope_c[]="envelope";
104 static const struct String str_envelope={ str_envelope_c, 8 };
106 static uschar str_subaddress_c[]="subaddress";
107 static const struct String str_subaddress={ str_subaddress_c, 10 };
110 static uschar str_vacation_c[]="vacation";
111 static const struct String str_vacation={ str_vacation_c, 8 };
112 static uschar str_subject_c[]="Subject";
113 static const struct String str_subject={ str_subject_c, 7 };
115 static uschar str_copy_c[]="copy";
116 static const struct String str_copy={ str_copy_c, 4 };
117 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
118 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
119 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
120 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
121 static uschar str_ioctet_c[]="i;octet";
122 static const struct String str_ioctet={ str_ioctet_c, 7 };
123 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
124 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
125 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
126 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
127 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
128 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
129 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
130 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
131 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
132 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
135 /*************************************************
136 * Encode to quoted-printable *
137 *************************************************/
144 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
147 const uschar *start,*end;
152 for (pass=0; pass<=1; ++pass)
159 dst->character=store_get(dst->length+1); /* plus one for \0 */
162 for (start=src->character,end=start+src->length; start<end; ++start)
179 || (ch>=62 && ch<=126)
185 && (*(start+1)!='\r' || *(start+2)!='\n')
200 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
228 sprintf(CS new,"=%02X",ch);
235 *new='\0'; /* not included in length, but nice */
240 /*************************************************
241 * Octet-wise string comparison *
242 *************************************************/
246 needle UTF-8 string to search ...
247 haystack ... inside the haystack
248 match_prefix 1 to compare if needle is a prefix of haystack
250 Returns: 0 needle not found in haystack
254 static int eq_octet(const struct String *needle,
255 const struct String *haystack, int match_prefix)
263 h=haystack->character;
267 if (*n&0x80) return 0;
268 if (*h&0x80) return 0;
270 if (*n!=*h) return 0;
276 return (match_prefix ? nl==0 : nl==0 && hl==0);
280 /*************************************************
281 * ASCII case-insensitive string comparison *
282 *************************************************/
286 needle UTF-8 string to search ...
287 haystack ... inside the haystack
288 match_prefix 1 to compare if needle is a prefix of haystack
290 Returns: 0 needle not found in haystack
294 static int eq_asciicase(const struct String *needle,
295 const struct String *haystack, int match_prefix)
304 h=haystack->character;
310 if (nc&0x80) return 0;
311 if (hc&0x80) return 0;
313 /* tolower depends on the locale and only ASCII case must be insensitive */
314 if ((nc&0x80) || (hc&0x80)) { if (nc!=hc) return 0; }
315 else if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
321 return (match_prefix ? nl==0 : nl==0 && hl==0);
325 /*************************************************
326 * Octet-wise glob pattern search *
327 *************************************************/
331 needle pattern to search ...
332 haystack ... inside the haystack
334 Returns: 0 needle not found in haystack
338 static int eq_octetglob(const struct String *needle,
339 const struct String *haystack)
347 switch (n.character[0])
355 /* The greedy match is not yet well tested. Some day we may */
356 /* need to refer to the matched parts, so the code is already */
357 /* prepared for that. */
360 currentLength=h.length;
361 h.character+=h.length;
363 while (h.length<=currentLength)
365 if (eq_octetglob(&n,&h)) return 1;
366 else /* go back one octet */
377 if (eq_octetglob(&n,&h)) return 1;
378 else /* advance one octet */
411 (h.character[0]&0x80) || (n.character[0]&0x80) ||
413 h.character[0]!=n.character[0]
425 return (h.length==0);
429 /*************************************************
430 * ASCII case-insensitive glob pattern search *
431 *************************************************/
435 needle UTF-8 pattern to search ...
436 haystack ... inside the haystack
438 Returns: 0 needle not found in haystack
442 static int eq_asciicaseglob(const struct String *needle,
443 const struct String *haystack)
451 switch (n.character[0])
459 /* The greedy match is not yet well tested. Some day we may */
460 /* need to refer to the matched parts, so the code is already */
461 /* prepared for that. */
464 currentLength=h.length;
465 h.character+=h.length;
467 while (h.length<=currentLength)
469 if (eq_asciicaseglob(&n,&h)) return 1;
470 else /* go back one UTF-8 character */
472 if (h.length==currentLength) return 0;
475 if (h.character[0]&0x80)
477 while (h.length<currentLength && (*(h.character-1)&0x80))
489 if (eq_asciicaseglob(&n,&h)) return 1;
490 else /* advance one UTF-8 character */
492 if (h.character[0]&0x80)
494 while (h.length && (h.character[0]&0x80))
516 /* advance one UTF-8 character */
517 if (h.character[0]&0x80)
519 while (h.length && (h.character[0]&0x80))
544 if (h.length==0) return 0;
548 if ((hc&0x80) || (nc&0x80)) return 0;
550 /* tolower depends on the locale and only ASCII case must be insensitive */
551 if ((nc&0x80) || (hc&0x80)) { if (nc!=hc) return 0; }
552 else if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
560 return (h.length==0);
564 /*************************************************
565 * ASCII numeric comparison *
566 *************************************************/
570 a first numeric string
571 b second numeric string
572 relop relational operator
574 Returns: 0 not (a relop b)
578 static int eq_asciinumeric(const struct String *a,
579 const struct String *b, enum RelOp relop)
582 const uschar *as,*aend,*bs,*bend;
586 aend=a->character+a->length;
588 bend=b->character+b->length;
590 while (*as>='0' && *as<='9' && as<aend) ++as;
592 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
595 if (al && bl==0) cmp=-1;
596 else if (al==0 && bl==0) cmp=0;
597 else if (al==0 && bl) cmp=1;
601 if (cmp==0) cmp=memcmp(a->character,b->character,al);
605 case LT: return cmp<0;
606 case LE: return cmp<=0;
607 case EQ: return cmp==0;
608 case GE: return cmp>=0;
609 case GT: return cmp>0;
610 case NE: return cmp!=0;
617 /*************************************************
619 *************************************************/
623 needle UTF-8 pattern or string to search ...
624 haystack ... inside the haystack
628 Returns: 0 needle not found in haystack
630 -1 comparator does not offer matchtype
633 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
634 enum Comparator co, enum MatchType mt)
638 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
639 (debug_selector & D_filter) != 0)
641 debug_printf("String comparison (match ");
644 case MATCH_IS: debug_printf(":is"); break;
645 case MATCH_CONTAINS: debug_printf(":contains"); break;
646 case MATCH_MATCHES: debug_printf(":matches"); break;
648 debug_printf(", comparison \"");
651 case COMP_OCTET: debug_printf("i;octet"); break;
652 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
653 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
655 debug_printf("\"):\n");
656 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
657 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
667 if (eq_octet(needle,haystack,0)) r=1;
670 case COMP_EN_ASCII_CASEMAP:
672 if (eq_asciicase(needle,haystack,0)) r=1;
675 case COMP_ASCII_NUMERIC:
677 if (!filter->require_iascii_numeric)
679 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
682 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
696 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
699 case COMP_EN_ASCII_CASEMAP:
701 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
706 filter->errmsg=CUS "comparator does not offer specified matchtype";
718 if (eq_octetglob(needle,haystack)) r=1;
721 case COMP_EN_ASCII_CASEMAP:
723 if (eq_asciicaseglob(needle,haystack)) r=1;
728 filter->errmsg=CUS "comparator does not offer specified matchtype";
735 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
736 (debug_selector & D_filter) != 0)
737 debug_printf(" Result %s\n",r?"true":"false");
742 /*************************************************
743 * Check header field syntax *
744 *************************************************/
747 RFC 2822, section 3.6.8 says:
751 ftext = %d33-57 / ; Any character except
752 %d59-126 ; controls, SP, and
755 That forbids 8-bit header fields. This implementation accepts them, since
756 all of Exim is 8-bit clean, so it adds %d128-%d255.
759 header header field to quote for suitable use in Exim expansions
761 Returns: 0 string is not a valid header field
762 1 string is a value header field
765 static int is_header(const struct String *header)
775 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
786 /*************************************************
787 * Quote special characters string *
788 *************************************************/
792 header header field to quote for suitable use in Exim expansions
795 Returns: quoted string
798 static const uschar *quote(const struct String *header)
813 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
820 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
824 quoted=string_cat(quoted,&size,&ptr,h,1);
830 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
835 /*************************************************
836 * Add address to list of generated addresses *
837 *************************************************/
840 According to RFC 3028, duplicate delivery to the same address must
841 not happen, so the list is first searched for the address.
844 generated list of generated addresses
845 addr new address to add
846 file address denotes a file
851 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
853 address_item *new_addr;
855 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
857 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
859 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
861 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
867 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
869 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
871 new_addr=deliver_make_addr(addr,TRUE);
874 setflag(new_addr, af_pfr|af_file);
877 new_addr->p.errors_address = NULL;
878 new_addr->next = *generated;
879 *generated = new_addr;
883 /*************************************************
884 * Return decoded header field *
885 *************************************************/
889 value returned value of the field
890 header name of the header field
892 Returns: nothing The expanded string is empty
893 in case there is no such header
896 static void expand_header(struct String *value, const struct String *header)
902 value->character=(uschar*)0;
904 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
911 while (*r==' ' || *r=='\t') ++r;
918 value->character=rfc2047_decode(s,TRUE,US"utf-8",'\0',&value->length,&errmsg);
922 /*************************************************
923 * Parse remaining hash comment *
924 *************************************************/
928 Comment up to terminating CRLF
931 filter points to the Sieve filter including its state
937 static int parse_hashcomment(struct Sieve *filter)
943 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
945 if (*filter->pc=='\n')
958 filter->errmsg=CUS "missing end of comment";
963 /*************************************************
964 * Parse remaining C-style comment *
965 *************************************************/
969 Everything up to star slash
972 filter points to the Sieve filter including its state
978 static int parse_comment(struct Sieve *filter)
983 if (*filter->pc=='*' && *(filter->pc+1)=='/')
990 filter->errmsg=CUS "missing end of comment";
995 /*************************************************
996 * Parse optional white space *
997 *************************************************/
1001 Spaces, tabs, CRLFs, hash comments or C-style comments
1004 filter points to the Sieve filter including its state
1010 static int parse_white(struct Sieve *filter)
1014 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1016 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1018 else if (*filter->pc=='\n')
1028 else if (*filter->pc=='#')
1030 if (parse_hashcomment(filter)==-1) return -1;
1032 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1034 if (parse_comment(filter)==-1) return -1;
1042 /*************************************************
1043 * Parse a optional string *
1044 *************************************************/
1048 quoted-string = DQUOTE *CHAR DQUOTE
1049 ;; in general, \ CHAR inside a string maps to CHAR
1050 ;; so \" maps to " and \\ maps to \
1051 ;; note that newlines and other characters are all allowed
1054 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1055 *(multi-line-literal / multi-line-dotstuff)
1057 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1058 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1059 ;; A line containing only "." ends the multi-line.
1060 ;; Remove a leading '.' if followed by another '.'.
1061 string = quoted-string / multi-line
1064 filter points to the Sieve filter including its state
1065 id specifies identifier to match
1069 0 identifier not matched
1072 static int parse_string(struct Sieve *filter, struct String *data)
1077 data->character=(uschar*)0;
1078 if (*filter->pc=='"') /* quoted string */
1083 if (*filter->pc=='"') /* end of string */
1085 int foo=data->length;
1088 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1091 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1093 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1094 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1097 else /* regular character */
1099 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1103 filter->errmsg=CUS "missing end of string";
1106 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1109 /* skip optional white space followed by hashed comment or CRLF */
1110 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1111 if (*filter->pc=='#')
1113 if (parse_hashcomment(filter)==-1) return -1;
1116 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1118 else if (*filter->pc=='\n')
1130 filter->errmsg=CUS "syntax error";
1136 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1138 if (*filter->pc=='\n') /* end of line */
1141 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1149 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1151 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1154 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1163 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1165 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1169 else /* regular character */
1171 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1175 filter->errmsg=CUS "missing end of multi line string";
1182 /*************************************************
1183 * Parse a specific identifier *
1184 *************************************************/
1188 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1191 filter points to the Sieve filter including its state
1192 id specifies identifier to match
1195 0 identifier not matched
1198 static int parse_identifier(struct Sieve *filter, const uschar *id)
1200 size_t idlen=Ustrlen(id);
1202 if (Ustrncmp(filter->pc,id,idlen)==0)
1204 uschar next=filter->pc[idlen];
1206 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1214 /*************************************************
1216 *************************************************/
1220 number = 1*DIGIT [QUANTIFIER]
1221 QUANTIFIER = "K" / "M" / "G"
1224 filter points to the Sieve filter including its state
1228 -1 no string list found
1231 static int parse_number(struct Sieve *filter, unsigned long *data)
1235 if (*filter->pc>='0' && *filter->pc<='9')
1240 d=Ustrtoul(filter->pc,&e,10);
1243 filter->errmsg=CUstrerror(ERANGE);
1248 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1249 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1250 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1251 if (d>(ULONG_MAX/u))
1253 filter->errmsg=CUstrerror(ERANGE);
1262 filter->errmsg=CUS "missing number";
1268 /*************************************************
1269 * Parse a string list *
1270 *************************************************/
1274 string-list = "[" string *("," string) "]" / string
1277 filter points to the Sieve filter including its state
1278 data returns string list
1281 -1 no string list found
1284 static int parse_stringlist(struct Sieve *filter, struct String **data)
1286 const uschar *orig=filter->pc;
1289 struct String *d=(struct String*)0;
1292 if (*filter->pc=='[') /* string list */
1297 if (parse_white(filter)==-1) goto error;
1298 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1301 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1302 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1303 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1305 filter->errmsg=CUstrerror(errno);
1308 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1310 dataCapacity=newCapacity;
1312 m=parse_string(filter,&d[dataLength]);
1315 if (dataLength==0) break;
1318 filter->errmsg=CUS "missing string";
1322 else if (m==-1) goto error;
1324 if (parse_white(filter)==-1) goto error;
1325 if (*filter->pc==',') ++filter->pc;
1328 if (*filter->pc==']')
1330 d[dataLength].character=(uschar*)0;
1331 d[dataLength].length=-1;
1338 filter->errmsg=CUS "missing closing bracket";
1342 else /* single string */
1344 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1348 m=parse_string(filter,&d[0]);
1360 d[1].character=(uschar*)0;
1367 filter->errmsg=CUS "missing string list";
1372 /*************************************************
1373 * Parse an optional address part specifier *
1374 *************************************************/
1378 address-part = ":localpart" / ":domain" / ":all"
1379 address-part =/ ":user" / ":detail"
1382 filter points to the Sieve filter including its state
1383 a returns address part specified
1386 0 no comparator found
1390 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1393 if (parse_identifier(filter,CUS ":user")==1)
1395 if (!filter->require_subaddress)
1397 filter->errmsg=CUS "missing previous require \"subaddress\";";
1403 else if (parse_identifier(filter,CUS ":detail")==1)
1405 if (!filter->require_subaddress)
1407 filter->errmsg=CUS "missing previous require \"subaddress\";";
1415 if (parse_identifier(filter,CUS ":localpart")==1)
1417 *a=ADDRPART_LOCALPART;
1420 else if (parse_identifier(filter,CUS ":domain")==1)
1425 else if (parse_identifier(filter,CUS ":all")==1)
1434 /*************************************************
1435 * Parse an optional comparator *
1436 *************************************************/
1440 comparator = ":comparator" <comparator-name: string>
1443 filter points to the Sieve filter including its state
1444 c returns comparator
1447 0 no comparator found
1448 -1 incomplete comparator found
1451 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1453 struct String comparator_name;
1455 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1456 if (parse_white(filter)==-1) return -1;
1457 switch (parse_string(filter,&comparator_name))
1462 filter->errmsg=CUS "missing comparator";
1469 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1474 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1476 *c=COMP_EN_ASCII_CASEMAP;
1479 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1481 *c=COMP_EN_ASCII_CASEMAP;
1484 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1486 *c=COMP_ASCII_NUMERIC;
1491 filter->errmsg=CUS "invalid comparator";
1500 /*************************************************
1501 * Parse an optional match type *
1502 *************************************************/
1506 match-type = ":is" / ":contains" / ":matches"
1509 filter points to the Sieve filter including its state
1510 m returns match type
1513 0 no match type found
1516 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1518 if (parse_identifier(filter,CUS ":is")==1)
1523 else if (parse_identifier(filter,CUS ":contains")==1)
1528 else if (parse_identifier(filter,CUS ":matches")==1)
1537 /*************************************************
1538 * Parse and interpret an optional test list *
1539 *************************************************/
1543 test-list = "(" test *("," test) ")"
1546 filter points to the Sieve filter including its state
1547 n total number of tests
1548 true number of passed tests
1549 exec Execute parsed statements
1552 0 no test list found
1553 -1 syntax or execution error
1556 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1558 if (parse_white(filter)==-1) return -1;
1559 if (*filter->pc=='(')
1568 switch (parse_test(filter,&cond,exec))
1571 case 0: filter->errmsg=CUS "missing test"; return -1;
1572 default: ++*n; if (cond) ++*true; break;
1574 if (parse_white(filter)==-1) return -1;
1575 if (*filter->pc==',') ++filter->pc;
1578 if (*filter->pc==')')
1585 filter->errmsg=CUS "missing closing paren";
1593 /*************************************************
1594 * Parse and interpret an optional test *
1595 *************************************************/
1599 filter points to the Sieve filter including its state
1600 cond returned condition status
1601 exec Execute parsed statements
1605 -1 syntax or execution error
1608 static int parse_test(struct Sieve *filter, int *cond, int exec)
1610 if (parse_white(filter)==-1) return -1;
1611 if (parse_identifier(filter,CUS "address"))
1614 address-test = "address" { [address-part] [comparator] [match-type] }
1615 <header-list: string-list> <key-list: string-list>
1617 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1620 enum AddressPart addressPart=ADDRPART_ALL;
1621 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1622 enum MatchType matchType=MATCH_IS;
1623 struct String *hdr,*h,*key,*k;
1629 if (parse_white(filter)==-1) return -1;
1630 if ((m=parse_addresspart(filter,&addressPart))!=0)
1632 if (m==-1) return -1;
1635 filter->errmsg=CUS "address part already specified";
1640 else if ((m=parse_comparator(filter,&comparator))!=0)
1642 if (m==-1) return -1;
1645 filter->errmsg=CUS "comparator already specified";
1650 else if ((m=parse_matchtype(filter,&matchType))!=0)
1652 if (m==-1) return -1;
1655 filter->errmsg=CUS "match type already specified";
1662 if (parse_white(filter)==-1) return -1;
1663 if ((m=parse_stringlist(filter,&hdr))!=1)
1665 if (m==0) filter->errmsg=CUS "header string list expected";
1668 if (parse_white(filter)==-1) return -1;
1669 if ((m=parse_stringlist(filter,&key))!=1)
1671 if (m==0) filter->errmsg=CUS "key string list expected";
1675 for (h=hdr; h->length!=-1 && !*cond; ++h)
1677 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1681 !eq_asciicase(h,&str_from,0)
1682 && !eq_asciicase(h,&str_to,0)
1683 && !eq_asciicase(h,&str_cc,0)
1684 && !eq_asciicase(h,&str_bcc,0)
1685 && !eq_asciicase(h,&str_sender,0)
1686 && !eq_asciicase(h,&str_resent_from,0)
1687 && !eq_asciicase(h,&str_resent_to,0)
1690 filter->errmsg=CUS "invalid header field";
1695 /* We are only interested in addresses below, so no MIME decoding */
1696 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1697 if (header_value == NULL)
1699 filter->errmsg=CUS "header string expansion failed";
1702 parse_allow_group = TRUE;
1703 while (*header_value && !*cond)
1706 int start, end, domain;
1710 end_addr = parse_find_address_end(header_value, FALSE);
1711 saveend = *end_addr;
1713 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1715 if (extracted_addr) switch (addressPart)
1717 case ADDRPART_ALL: part=extracted_addr; break;
1721 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1722 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1724 case ADDRPART_DETAIL: part=NULL; break;
1728 *end_addr = saveend;
1731 for (k=key; k->length!=-1; ++k)
1733 struct String partStr;
1735 partStr.character=part;
1736 partStr.length=Ustrlen(part);
1739 *cond=compare(filter,k,&partStr,comparator,matchType);
1740 if (*cond==-1) return -1;
1745 if (saveend == 0) break;
1746 header_value = end_addr + 1;
1752 else if (parse_identifier(filter,CUS "allof"))
1755 allof-test = "allof" <tests: test-list>
1760 switch (parse_testlist(filter,&n,&true,exec))
1763 case 0: filter->errmsg=CUS "missing test list"; return -1;
1764 default: *cond=(n==true); return 1;
1767 else if (parse_identifier(filter,CUS "anyof"))
1770 anyof-test = "anyof" <tests: test-list>
1775 switch (parse_testlist(filter,&n,&true,exec))
1778 case 0: filter->errmsg=CUS "missing test list"; return -1;
1779 default: *cond=(true>0); return 1;
1782 else if (parse_identifier(filter,CUS "exists"))
1785 exists-test = "exists" <header-names: string-list>
1788 struct String *hdr,*h;
1791 if (parse_white(filter)==-1) return -1;
1792 if ((m=parse_stringlist(filter,&hdr))!=1)
1794 if (m==0) filter->errmsg=CUS "header string list expected";
1800 for (h=hdr; h->length!=-1 && *cond; ++h)
1804 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1805 if (header_def == NULL)
1807 filter->errmsg=CUS "header string expansion failed";
1810 if (Ustrcmp(header_def,"false")==0) *cond=0;
1815 else if (parse_identifier(filter,CUS "false"))
1818 false-test = "false"
1824 else if (parse_identifier(filter,CUS "header"))
1827 header-test = "header" { [comparator] [match-type] }
1828 <header-names: string-list> <key-list: string-list>
1831 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1832 enum MatchType matchType=MATCH_IS;
1833 struct String *hdr,*h,*key,*k;
1839 if (parse_white(filter)==-1) return -1;
1840 if ((m=parse_comparator(filter,&comparator))!=0)
1842 if (m==-1) return -1;
1845 filter->errmsg=CUS "comparator already specified";
1850 else if ((m=parse_matchtype(filter,&matchType))!=0)
1852 if (m==-1) return -1;
1855 filter->errmsg=CUS "match type already specified";
1862 if (parse_white(filter)==-1) return -1;
1863 if ((m=parse_stringlist(filter,&hdr))!=1)
1865 if (m==0) filter->errmsg=CUS "header string list expected";
1868 if (parse_white(filter)==-1) return -1;
1869 if ((m=parse_stringlist(filter,&key))!=1)
1871 if (m==0) filter->errmsg=CUS "key string list expected";
1875 for (h=hdr; h->length!=-1 && !*cond; ++h)
1879 filter->errmsg=CUS "invalid header field";
1884 struct String header_value;
1887 expand_header(&header_value,h);
1888 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1889 if (header_value.character == NULL || header_def == NULL)
1891 filter->errmsg=CUS "header string expansion failed";
1894 for (k=key; k->length!=-1; ++k)
1896 if (Ustrcmp(header_def,"true")==0)
1898 *cond=compare(filter,k,&header_value,comparator,matchType);
1899 if (*cond==-1) return -1;
1907 else if (parse_identifier(filter,CUS "not"))
1909 if (parse_white(filter)==-1) return -1;
1910 switch (parse_test(filter,cond,exec))
1913 case 0: filter->errmsg=CUS "missing test"; return -1;
1914 default: *cond=!*cond; return 1;
1917 else if (parse_identifier(filter,CUS "size"))
1920 relop = ":over" / ":under"
1921 size-test = "size" relop <limit: number>
1924 unsigned long limit;
1927 if (parse_white(filter)==-1) return -1;
1928 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
1929 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
1932 filter->errmsg=CUS "missing :over or :under";
1935 if (parse_white(filter)==-1) return -1;
1936 if (parse_number(filter,&limit)==-1) return -1;
1937 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
1940 else if (parse_identifier(filter,CUS "true"))
1945 else if (parse_identifier(filter,CUS "envelope"))
1948 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
1949 <envelope-part: string-list> <key-list: string-list>
1951 envelope-part is case insensitive "from" or "to"
1954 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1955 enum AddressPart addressPart=ADDRPART_ALL;
1956 enum MatchType matchType=MATCH_IS;
1957 struct String *env,*e,*key,*k;
1961 if (!filter->require_envelope)
1963 filter->errmsg=CUS "missing previous require \"envelope\";";
1968 if (parse_white(filter)==-1) return -1;
1969 if ((m=parse_comparator(filter,&comparator))!=0)
1971 if (m==-1) return -1;
1974 filter->errmsg=CUS "comparator already specified";
1979 else if ((m=parse_addresspart(filter,&addressPart))!=0)
1981 if (m==-1) return -1;
1984 filter->errmsg=CUS "address part already specified";
1989 else if ((m=parse_matchtype(filter,&matchType))!=0)
1991 if (m==-1) return -1;
1994 filter->errmsg=CUS "match type already specified";
2001 if (parse_white(filter)==-1) return -1;
2002 if ((m=parse_stringlist(filter,&env))!=1)
2004 if (m==0) filter->errmsg=CUS "envelope string list expected";
2007 if (parse_white(filter)==-1) return -1;
2008 if ((m=parse_stringlist(filter,&key))!=1)
2010 if (m==0) filter->errmsg=CUS "key string list expected";
2014 for (e=env; e->character; ++e)
2016 const uschar *envelopeExpr=CUS 0;
2017 uschar *envelope=US 0;
2019 if (eq_asciicase(e,&str_from,0))
2021 switch (addressPart)
2023 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2027 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2028 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2030 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2034 else if (eq_asciicase(e,&str_to,0))
2036 switch (addressPart)
2038 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2040 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2041 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2043 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2044 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2049 filter->errmsg=CUS "invalid envelope string";
2052 if (exec && envelopeExpr)
2054 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2056 filter->errmsg=CUS "header string expansion failed";
2059 for (k=key; k->length!=-1; ++k)
2061 struct String envelopeStr;
2063 envelopeStr.character=envelope;
2064 envelopeStr.length=Ustrlen(envelope);
2065 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2066 if (*cond==-1) return -1;
2077 /*************************************************
2078 * Parse and interpret an optional block *
2079 *************************************************/
2083 filter points to the Sieve filter including its state
2084 exec Execute parsed statements
2085 generated where to hang newly-generated addresses
2087 Returns: 2 success by stop
2089 0 no block command found
2090 -1 syntax or execution error
2093 static int parse_block(struct Sieve *filter, int exec,
2094 address_item **generated)
2098 if (parse_white(filter)==-1) return -1;
2099 if (*filter->pc=='{')
2102 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2103 if (*filter->pc=='}')
2110 filter->errmsg=CUS "expecting command or closing brace";
2118 /*************************************************
2119 * Match a semicolon *
2120 *************************************************/
2124 filter points to the Sieve filter including its state
2130 static int parse_semicolon(struct Sieve *filter)
2132 if (parse_white(filter)==-1) return -1;
2133 if (*filter->pc==';')
2140 filter->errmsg=CUS "missing semicolon";
2146 /*************************************************
2147 * Parse and interpret a Sieve command *
2148 *************************************************/
2152 filter points to the Sieve filter including its state
2153 exec Execute parsed statements
2154 generated where to hang newly-generated addresses
2156 Returns: 2 success by stop
2158 -1 syntax or execution error
2160 static int parse_commands(struct Sieve *filter, int exec,
2161 address_item **generated)
2165 if (parse_white(filter)==-1) return -1;
2166 if (parse_identifier(filter,CUS "if"))
2169 if-command = "if" test block *( "elsif" test block ) [ else block ]
2172 int cond,m,unsuccessful;
2175 if (parse_white(filter)==-1) return -1;
2176 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2179 filter->errmsg=CUS "missing test";
2182 m=parse_block(filter,exec ? cond : 0, generated);
2183 if (m==-1 || m==2) return m;
2186 filter->errmsg=CUS "missing block";
2189 unsuccessful = !cond;
2190 for (;;) /* elsif test block */
2192 if (parse_white(filter)==-1) return -1;
2193 if (parse_identifier(filter,CUS "elsif"))
2195 if (parse_white(filter)==-1) return -1;
2196 m=parse_test(filter,&cond,exec && unsuccessful);
2197 if (m==-1 || m==2) return m;
2200 filter->errmsg=CUS "missing test";
2203 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2204 if (m==-1 || m==2) return m;
2207 filter->errmsg=CUS "missing block";
2210 if (exec && unsuccessful && cond) unsuccessful = 0;
2215 if (parse_white(filter)==-1) return -1;
2216 if (parse_identifier(filter,CUS "else"))
2218 m=parse_block(filter,exec && unsuccessful, generated);
2219 if (m==-1 || m==2) return m;
2222 filter->errmsg=CUS "missing block";
2227 else if (parse_identifier(filter,CUS "stop"))
2230 stop-command = "stop" { stop-options } ";"
2234 if (parse_semicolon(filter)==-1) return -1;
2237 filter->pc+=Ustrlen(filter->pc);
2241 else if (parse_identifier(filter,CUS "keep"))
2244 keep-command = "keep" { keep-options } ";"
2248 if (parse_semicolon(filter)==-1) return -1;
2251 add_addr(generated,US"inbox",1,0,0,0);
2255 else if (parse_identifier(filter,CUS "discard"))
2258 discard-command = "discard" { discard-options } ";"
2262 if (parse_semicolon(filter)==-1) return -1;
2263 if (exec) filter->keep=0;
2265 else if (parse_identifier(filter,CUS "redirect"))
2268 redirect-command = "redirect" redirect-options "string" ";"
2270 redirect-options =) ":copy"
2273 struct String recipient;
2279 if (parse_white(filter)==-1) return -1;
2280 if (parse_identifier(filter,CUS ":copy")==1)
2282 if (!filter->require_copy)
2284 filter->errmsg=CUS "missing previous require \"copy\";";
2291 if (parse_white(filter)==-1) return -1;
2292 if ((m=parse_string(filter,&recipient))!=1)
2294 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2297 if (strchr(CCS recipient.character,'@')==(char*)0)
2299 filter->errmsg=CUS "unqualified recipient address";
2304 add_addr(generated,recipient.character,0,0,0,0);
2305 if (!copy) filter->keep = 0;
2307 if (parse_semicolon(filter)==-1) return -1;
2309 else if (parse_identifier(filter,CUS "fileinto"))
2312 fileinto-command = "fileinto" { fileinto-options } string ";"
2314 fileinto-options =) [ ":copy" ]
2317 struct String folder;
2320 unsigned long maxage, maxmessages, maxstorage;
2323 maxage = maxmessages = maxstorage = 0;
2324 if (!filter->require_fileinto)
2326 filter->errmsg=CUS "missing previous require \"fileinto\";";
2331 if (parse_white(filter)==-1) return -1;
2332 if (parse_identifier(filter,CUS ":copy")==1)
2334 if (!filter->require_copy)
2336 filter->errmsg=CUS "missing previous require \"copy\";";
2343 if (parse_white(filter)==-1) return -1;
2344 if ((m=parse_string(filter,&folder))!=1)
2346 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2349 m=0; s=folder.character;
2350 if (folder.length==0) m=1;
2351 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2354 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2359 filter->errmsg=CUS "invalid folder";
2364 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2365 if (!copy) filter->keep = 0;
2367 if (parse_semicolon(filter)==-1) return -1;
2370 else if (parse_identifier(filter,CUS "vacation"))
2373 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2374 vacation-options = [":days" number]
2377 [":addresses" string-list]
2384 struct String subject;
2386 struct String *addresses;
2388 string_item *aliases;
2389 struct String handle;
2390 struct String reason;
2392 if (!filter->require_vacation)
2394 filter->errmsg=CUS "missing previous require \"vacation\";";
2399 if (filter->vacation_ran)
2401 filter->errmsg=CUS "trying to execute vacation more than once";
2404 filter->vacation_ran=1;
2406 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2407 subject.character=(uschar*)0;
2409 from.character=(uschar*)0;
2411 addresses=(struct String*)0;
2414 handle.character=(uschar*)0;
2418 if (parse_white(filter)==-1) return -1;
2419 if (parse_identifier(filter,CUS ":days")==1)
2421 if (parse_white(filter)==-1) return -1;
2422 if (parse_number(filter,&days)==-1) return -1;
2423 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2424 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2426 else if (parse_identifier(filter,CUS ":subject")==1)
2428 if (parse_white(filter)==-1) return -1;
2429 if ((m=parse_string(filter,&subject))!=1)
2431 if (m==0) filter->errmsg=CUS "subject string expected";
2435 else if (parse_identifier(filter,CUS ":from")==1)
2437 int start, end, domain;
2440 if (parse_white(filter)==-1) return -1;
2441 if ((m=parse_string(filter,&from))!=1)
2443 if (m==0) filter->errmsg=CUS "from string expected";
2448 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2452 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2453 "Sieve filter: %s", from.character, error);
2459 filter->errmsg=CUS "empty :from address in Sieve filter";
2463 else if (parse_identifier(filter,CUS ":addresses")==1)
2467 if (parse_white(filter)==-1) return -1;
2468 if ((m=parse_stringlist(filter,&addresses))!=1)
2470 if (m==0) filter->errmsg=CUS "addresses string list expected";
2473 for (a=addresses; a->length!=-1; ++a)
2477 new=store_get(sizeof(string_item));
2478 new->text=store_get(a->length+1);
2479 if (a->length) memcpy(new->text,a->character,a->length);
2480 new->text[a->length]='\0';
2485 else if (parse_identifier(filter,CUS ":mime")==1)
2487 else if (parse_identifier(filter,CUS ":handle")==1)
2489 if (parse_white(filter)==-1) return -1;
2490 if ((m=parse_string(filter,&from))!=1)
2492 if (m==0) filter->errmsg=CUS "handle string expected";
2498 if (parse_white(filter)==-1) return -1;
2499 if ((m=parse_string(filter,&reason))!=1)
2501 if (m==0) filter->errmsg=CUS "missing reason string";
2508 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2511 filter->errmsg=CUS "MIME reason string contains 8bit text";
2515 if (parse_semicolon(filter)==-1) return -1;
2522 int buffer_capacity;
2526 uschar hexdigest[33];
2530 if (filter_personal(aliases,TRUE))
2532 if (filter_test == FTEST_NONE)
2534 /* ensure oncelog directory exists; failure will be detected later */
2536 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2538 /* build oncelog filename */
2540 key.character=(uschar*)0;
2543 if (handle.length==-1)
2545 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2546 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2547 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2548 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2553 md5_end(&base, key.character, key.length, digest);
2554 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2555 if (filter_test != FTEST_NONE)
2557 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2561 capacity=Ustrlen(filter->vacation_directory);
2563 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2564 once=string_cat(once,&capacity,&start,hexdigest,33);
2567 /* process subject */
2569 if (subject.length==-1)
2571 expand_header(&subject,&str_subject);
2574 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2575 subject.length=start;
2578 /* add address to list of generated addresses */
2580 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2581 setflag(addr, af_pfr);
2582 setflag(addr, af_ignore_error);
2583 addr->next = *generated;
2585 addr->reply = store_get(sizeof(reply_item));
2586 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2587 addr->reply->to = string_copy(sender_address);
2588 if (from.length==-1)
2589 addr->reply->from = expand_string(US"$local_part@$domain");
2591 addr->reply->from = from.character;
2592 /* Allocation is larger than neccessary, but enough even for split MIME words */
2593 buffer_capacity=16+4*subject.length;
2594 buffer=store_get(buffer_capacity);
2595 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2596 addr->reply->oncelog=once;
2597 addr->reply->once_repeat=days*86400;
2599 /* build body and MIME headers */
2603 uschar *mime_body,*reason_end;
2605 static const uschar nlnl[]="\r\n\r\n";
2607 static const uschar nlnl[]="\n\n";
2612 mime_body=reason.character,reason_end=reason.character+reason.length;
2613 mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
2618 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2619 addr->reply->headers[start] = '\0';
2622 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
2623 else mime_body=reason_end-1;
2624 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2625 addr->reply->text[start] = '\0';
2632 start = reason.length;
2633 addr->reply->headers = US"MIME-Version: 1.0\n"
2634 "Content-Type: text/plain;\n"
2635 "\tcharset=\"utf-8\"\n"
2636 "Content-Transfer-Encoding: quoted-printable";
2637 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2641 else if (filter_test != FTEST_NONE)
2643 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
2654 /*************************************************
2655 * Parse and interpret a sieve filter *
2656 *************************************************/
2660 filter points to the Sieve filter including its state
2661 exec Execute parsed statements
2662 generated where to hang newly-generated addresses
2665 -1 syntax or execution error
2668 static int parse_start(struct Sieve *filter, int exec,
2669 address_item **generated)
2671 filter->pc=filter->filter;
2674 filter->require_envelope=0;
2675 filter->require_fileinto=0;
2677 filter->require_subaddress=0;
2680 filter->require_vacation=0;
2681 filter->vacation_ran=0;
2683 filter->require_copy=0;
2684 filter->require_iascii_numeric=0;
2686 if (parse_white(filter)==-1) return -1;
2688 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
2691 struct dirent *oncelog;
2692 struct stat properties;
2695 /* clean up old vacation log databases */
2697 oncelogdir=opendir(CS filter->vacation_directory);
2699 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2701 filter->errmsg=CUS "unable to open vacation directory";
2705 if (oncelogdir != NULL)
2709 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2711 if (strlen(oncelog->d_name)==32)
2713 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2714 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2718 closedir(oncelogdir);
2722 while (parse_identifier(filter,CUS "require"))
2725 require-command = "require" <capabilities: string-list>
2728 struct String *cap,*check;
2731 if (parse_white(filter)==-1) return -1;
2732 if ((m=parse_stringlist(filter,&cap))!=1)
2734 if (m==0) filter->errmsg=CUS "capability string list expected";
2737 for (check=cap; check->character; ++check)
2739 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
2740 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
2742 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
2745 else if (eq_octet(check,&str_vacation,0))
2747 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
2749 filter->errmsg=CUS "vacation disabled";
2752 filter->require_vacation=1;
2755 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
2756 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
2757 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
2758 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
2759 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
2762 filter->errmsg=CUS "unknown capability";
2766 if (parse_semicolon(filter)==-1) return -1;
2768 if (parse_commands(filter,exec,generated)==-1) return -1;
2771 filter->errmsg=CUS "syntax error";
2778 /*************************************************
2779 * Interpret a sieve filter file *
2780 *************************************************/
2784 filter points to the entire file, read into store as a single string
2785 options controls whether various special things are allowed, and requests
2786 special actions (not currently used)
2787 sieve_vacation_directory where to store vacation "once" files
2788 useraddress string expression for :user part of address
2789 subaddress string expression for :subaddress part of address
2790 generated where to hang newly-generated addresses
2791 error where to pass back an error text
2793 Returns: FF_DELIVERED success, a significant action was taken
2794 FF_NOTDELIVERED success, no significant action
2795 FF_DEFER defer requested
2796 FF_FAIL fail requested
2797 FF_FREEZE freeze requested
2798 FF_ERROR there was a problem
2802 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
2803 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
2809 options = options; /* Keep picky compilers happy */
2812 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
2813 sieve.filter=filter;
2815 if (vacation_directory == NULL)
2816 sieve.vacation_directory = NULL;
2819 sieve.vacation_directory=expand_string(vacation_directory);
2820 if (sieve.vacation_directory == NULL)
2822 *error = string_sprintf("failed to expand \"%s\" "
2823 "(sieve_vacation_directory): %s", vacation_directory,
2824 expand_string_message);
2829 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
2830 sieve.subaddress = subaddress;
2832 #ifdef COMPILE_SYNTAX_CHECKER
2833 if (parse_start(&sieve,0,generated)==1)
2835 if (parse_start(&sieve,1,generated)==1)
2840 add_addr(generated,US"inbox",1,0,0,0);
2841 msg = string_sprintf("Keep");
2846 msg = string_sprintf("No keep");
2852 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
2853 #ifdef COMPILE_SYNTAX_CHECKER
2857 add_addr(generated,US"inbox",1,0,0,0);
2862 #ifndef COMPILE_SYNTAX_CHECKER
2863 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
2864 else debug_printf("%s\n", msg);
2867 DEBUG(D_route) debug_printf("Sieve: end of processing\n");