1 /* $Cambridge: exim/src/src/sieve.c,v 1.24 2007/02/07 14:41:13 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Michael Haardt 2003-2006 */
8 /* See the file NOTICE for conditions of use and distribution. */
10 /* This code was contributed by Michael Haardt. */
13 /* Sieve mail filter. */
27 /* Define this for RFC compliant \r\n end-of-line terminators. */
28 /* Undefine it for UNIX-style \n end-of-line terminators (default). */
31 /* Define this for development of the Sieve extension "envelope-auth". */
34 /* Define this for development of the Sieve extension "enotify". */
37 /* Define this for the Sieve extension "subaddress". */
40 /* Define this for the Sieve extension "vacation". */
44 #define VACATION_MIN_DAYS 1
45 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
46 #define VACATION_MAX_DAYS 31
48 /* Keep this at 75 to accept only RFC compliant MIME words. */
49 /* Increase it if you want to match headers from buggy MUAs. */
50 #define MIMEWORD_LENGTH 75
62 int require_envelope_auth;
66 struct Notification *notified;
69 int require_subaddress;
75 uschar *vacation_directory;
76 const uschar *subaddress;
77 const uschar *useraddress;
79 int require_iascii_numeric;
82 enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
83 enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
85 enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
87 enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
89 enum RelOp { LT, LE, EQ, GE, GT, NE };
100 struct String importance;
101 struct String message;
102 struct Notification *next;
105 static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
106 static int parse_test(struct Sieve *filter, int *cond, int exec);
107 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
109 static uschar str_from_c[]="From";
110 static const struct String str_from={ str_from_c, 4 };
111 static uschar str_to_c[]="To";
112 static const struct String str_to={ str_to_c, 2 };
113 static uschar str_cc_c[]="Cc";
114 static const struct String str_cc={ str_cc_c, 2 };
115 static uschar str_bcc_c[]="Bcc";
116 static const struct String str_bcc={ str_bcc_c, 3 };
117 static uschar str_auth_c[]="auth";
118 static const struct String str_auth={ str_auth_c, 4 };
119 static uschar str_sender_c[]="Sender";
120 static const struct String str_sender={ str_sender_c, 6 };
121 static uschar str_resent_from_c[]="Resent-From";
122 static const struct String str_resent_from={ str_resent_from_c, 11 };
123 static uschar str_resent_to_c[]="Resent-To";
124 static const struct String str_resent_to={ str_resent_to_c, 9 };
125 static uschar str_fileinto_c[]="fileinto";
126 static const struct String str_fileinto={ str_fileinto_c, 8 };
127 static uschar str_envelope_c[]="envelope";
128 static const struct String str_envelope={ str_envelope_c, 8 };
130 static uschar str_envelope_auth_c[]="envelope-auth";
131 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
134 static uschar str_enotify_c[]="enotify";
135 static const struct String str_enotify={ str_enotify_c, 7 };
138 static uschar str_subaddress_c[]="subaddress";
139 static const struct String str_subaddress={ str_subaddress_c, 10 };
142 static uschar str_vacation_c[]="vacation";
143 static const struct String str_vacation={ str_vacation_c, 8 };
144 static uschar str_subject_c[]="Subject";
145 static const struct String str_subject={ str_subject_c, 7 };
147 static uschar str_copy_c[]="copy";
148 static const struct String str_copy={ str_copy_c, 4 };
149 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
150 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
151 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
152 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
153 static uschar str_ioctet_c[]="i;octet";
154 static const struct String str_ioctet={ str_ioctet_c, 7 };
155 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
156 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
157 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
158 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
159 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
160 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
161 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
162 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
163 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
164 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
167 /*************************************************
168 * Encode to quoted-printable *
169 *************************************************/
180 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
183 const uschar *start,*end;
188 for (pass=0; pass<=1; ++pass)
195 dst->character=store_get(dst->length+1); /* plus one for \0 */
198 for (start=src->character,end=start+src->length; start<end; ++start)
215 || (ch>=62 && ch<=126)
220 && (*(start+1)!='\r' || *(start+2)!='\n')
230 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
248 sprintf(CS new,"=%02X",ch);
255 *new='\0'; /* not included in length, but nice */
260 /*************************************************
261 * Check mail address for correct syntax *
262 *************************************************/
265 Check mail address for being syntactically correct.
268 filter points to the Sieve filter including its state
269 address String containing one address
272 1 Mail address is syntactically OK
276 int check_mail_address(struct Sieve *filter, const struct String *address)
278 int start, end, domain;
281 if (address->length>0)
283 ss = parse_extract_address(address->character, &error, &start, &end, &domain,
287 filter->errmsg=string_sprintf("malformed address \"%s\" (%s)",
288 address->character, error);
296 filter->errmsg=CUS "empty address";
302 /*************************************************
303 * Decode URI encoded string *
304 *************************************************/
308 str URI encoded string
311 0 Decoding successful
316 static int uri_decode(struct String *str)
320 if (str->length==0) return 0;
321 for (s=str->character,t=s,e=s+str->length; s<e; )
325 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
327 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
328 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
337 str->length=t-str->character;
342 /*************************************************
344 *************************************************/
349 mailtoURI = "mailto:" [ to ] [ headers ]
350 to = [ addr-spec *("%2C" addr-spec ) ]
351 headers = "?" header *( "&" header )
352 header = hname "=" hvalue
357 filter points to the Sieve filter including its state
358 uri URI, excluding scheme
363 1 URI is syntactically OK
368 static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *body)
371 struct String to,hname,hvalue;
375 if (Ustrncmp(uri,"mailto:",7))
377 filter->errmsg=US "Unknown URI scheme";
381 if (*uri && *uri!='?')
385 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
389 to.character=(uschar*)0;
391 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
392 to.character[to.length]='\0';
393 if (uri_decode(&to)==-1)
395 filter->errmsg=US"Invalid URI encoding";
398 new=store_get(sizeof(string_item));
399 new->text=store_get(to.length+1);
400 if (to.length) memcpy(new->text,to.character,to.length);
401 new->text[to.length]='\0';
402 new->next=*recipient;
407 filter->errmsg=US"Missing addr-spec in URI";
410 if (*uri=='%') uri+=3;
419 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
423 hname.character=(uschar*)0;
425 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
426 hname.character[hname.length]='\0';
427 if (uri_decode(&hname)==-1)
429 filter->errmsg=US"Invalid URI encoding";
438 filter->errmsg=US"Missing equal after hname";
442 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
446 hvalue.character=(uschar*)0;
448 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
449 hvalue.character[hvalue.length]='\0';
450 if (uri_decode(&hvalue)==-1)
452 filter->errmsg=US"Invalid URI encoding";
456 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
458 new=store_get(sizeof(string_item));
459 new->text=store_get(hvalue.length+1);
460 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
461 new->text[hvalue.length]='\0';
462 new->next=*recipient;
465 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
469 static struct String ignore[]=
475 static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]);
478 for (i=ignore; i<end && !eq_asciicase(&hname,i,0); ++i);
481 if (header->length==-1) header->length=0;
482 capacity=header->length;
483 header->character=string_cat(header->character,&capacity,&header->length,hname.character,hname.length);
484 header->character=string_cat(header->character,&capacity,&header->length,CUS ": ",2);
485 header->character=string_cat(header->character,&capacity,&header->length,hvalue.character,hvalue.length);
486 header->character=string_cat(header->character,&capacity,&header->length,CUS "\n",1);
487 header->character[header->length]='\0';
490 if (*uri=='&') ++uri;
496 filter->errmsg=US"Syntactically invalid URI";
504 /*************************************************
505 * Octet-wise string comparison *
506 *************************************************/
510 needle UTF-8 string to search ...
511 haystack ... inside the haystack
512 match_prefix 1 to compare if needle is a prefix of haystack
514 Returns: 0 needle not found in haystack
518 static int eq_octet(const struct String *needle,
519 const struct String *haystack, int match_prefix)
527 h=haystack->character;
531 if (*n&0x80) return 0;
532 if (*h&0x80) return 0;
534 if (*n!=*h) return 0;
540 return (match_prefix ? nl==0 : nl==0 && hl==0);
544 /*************************************************
545 * ASCII case-insensitive string comparison *
546 *************************************************/
550 needle UTF-8 string to search ...
551 haystack ... inside the haystack
552 match_prefix 1 to compare if needle is a prefix of haystack
554 Returns: 0 needle not found in haystack
558 static int eq_asciicase(const struct String *needle,
559 const struct String *haystack, int match_prefix)
568 h=haystack->character;
574 if (nc&0x80) return 0;
575 if (hc&0x80) return 0;
577 /* tolower depends on the locale and only ASCII case must be insensitive */
578 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
584 return (match_prefix ? nl==0 : nl==0 && hl==0);
588 /*************************************************
589 * Glob pattern search *
590 *************************************************/
594 needle pattern to search ...
595 haystack ... inside the haystack
597 Returns: 0 needle not found in haystack
602 static int eq_glob(const struct String *needle,
603 const struct String *haystack, int ascii_caseless, int match_octet)
605 const uschar *n,*h,*nend,*hend;
609 h=haystack->character;
610 nend=n+needle->length;
611 hend=h+haystack->length;
621 const uschar *npart,*hpart;
623 /* Try to match a non-star part of the needle at the current */
624 /* position in the haystack. */
628 while (npart<nend && *npart!='*') switch (*npart)
632 if (hpart==hend) return 0;
637 /* Match one UTF8 encoded character */
638 if ((*hpart&0xc0)==0xc0)
641 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
652 if (npart==nend) return -1;
657 if (hpart==hend) return 0;
658 /* tolower depends on the locale, but we need ASCII */
662 (*hpart&0x80) || (*npart&0x80) ||
665 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
670 /* string match after a star failed, advance and try again */
684 /* at this point, a part was matched successfully */
685 if (may_advance && npart==nend && hpart<hend)
686 /* needle ends, but haystack does not: if there was a star before, advance and try again */
696 return (h==hend ? 1 : may_advance);
700 /*************************************************
701 * ASCII numeric comparison *
702 *************************************************/
706 a first numeric string
707 b second numeric string
708 relop relational operator
710 Returns: 0 not (a relop b)
714 static int eq_asciinumeric(const struct String *a,
715 const struct String *b, enum RelOp relop)
718 const uschar *as,*aend,*bs,*bend;
722 aend=a->character+a->length;
724 bend=b->character+b->length;
726 while (*as>='0' && *as<='9' && as<aend) ++as;
728 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
731 if (al && bl==0) cmp=-1;
732 else if (al==0 && bl==0) cmp=0;
733 else if (al==0 && bl) cmp=1;
737 if (cmp==0) cmp=memcmp(a->character,b->character,al);
741 case LT: return cmp<0;
742 case LE: return cmp<=0;
743 case EQ: return cmp==0;
744 case GE: return cmp>=0;
745 case GT: return cmp>0;
746 case NE: return cmp!=0;
753 /*************************************************
755 *************************************************/
759 filter points to the Sieve filter including its state
760 needle UTF-8 pattern or string to search ...
761 haystack ... inside the haystack
765 Returns: 0 needle not found in haystack
767 -1 comparator does not offer matchtype
770 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
771 enum Comparator co, enum MatchType mt)
775 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
776 (debug_selector & D_filter) != 0)
778 debug_printf("String comparison (match ");
781 case MATCH_IS: debug_printf(":is"); break;
782 case MATCH_CONTAINS: debug_printf(":contains"); break;
783 case MATCH_MATCHES: debug_printf(":matches"); break;
785 debug_printf(", comparison \"");
788 case COMP_OCTET: debug_printf("i;octet"); break;
789 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
790 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
792 debug_printf("\"):\n");
793 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
794 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
804 if (eq_octet(needle,haystack,0)) r=1;
807 case COMP_EN_ASCII_CASEMAP:
809 if (eq_asciicase(needle,haystack,0)) r=1;
812 case COMP_ASCII_NUMERIC:
814 if (!filter->require_iascii_numeric)
816 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
819 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
833 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
836 case COMP_EN_ASCII_CASEMAP:
838 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
843 filter->errmsg=CUS "comparator does not offer specified matchtype";
855 if ((r=eq_glob(needle,haystack,0,1))==-1)
857 filter->errmsg=CUS "syntactically invalid pattern";
862 case COMP_EN_ASCII_CASEMAP:
864 if ((r=eq_glob(needle,haystack,1,1))==-1)
866 filter->errmsg=CUS "syntactically invalid pattern";
873 filter->errmsg=CUS "comparator does not offer specified matchtype";
880 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
881 (debug_selector & D_filter) != 0)
882 debug_printf(" Result %s\n",r?"true":"false");
887 /*************************************************
888 * Check header field syntax *
889 *************************************************/
892 RFC 2822, section 3.6.8 says:
896 ftext = %d33-57 / ; Any character except
897 %d59-126 ; controls, SP, and
900 That forbids 8-bit header fields. This implementation accepts them, since
901 all of Exim is 8-bit clean, so it adds %d128-%d255.
904 header header field to quote for suitable use in Exim expansions
906 Returns: 0 string is not a valid header field
907 1 string is a value header field
910 static int is_header(const struct String *header)
920 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
931 /*************************************************
932 * Quote special characters string *
933 *************************************************/
937 header header field to quote for suitable use in Exim expansions
940 Returns: quoted string
943 static const uschar *quote(const struct String *header)
958 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
965 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
969 quoted=string_cat(quoted,&size,&ptr,h,1);
975 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
980 /*************************************************
981 * Add address to list of generated addresses *
982 *************************************************/
985 According to RFC 3028, duplicate delivery to the same address must
986 not happen, so the list is first searched for the address.
989 generated list of generated addresses
990 addr new address to add
991 file address denotes a file
996 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
998 address_item *new_addr;
1000 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
1002 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
1004 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1006 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1012 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
1014 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1016 new_addr=deliver_make_addr(addr,TRUE);
1019 setflag(new_addr, af_pfr|af_file);
1022 new_addr->p.errors_address = NULL;
1023 new_addr->next = *generated;
1024 *generated = new_addr;
1028 /*************************************************
1029 * Return decoded header field *
1030 *************************************************/
1033 Unfold the header field as described in RFC 2822 and remove all
1034 leading and trailing white space, then perform MIME decoding and
1035 translate the header field to UTF-8.
1038 value returned value of the field
1039 header name of the header field
1041 Returns: nothing The expanded string is empty
1042 in case there is no such header
1045 static void expand_header(struct String *value, const struct String *header)
1051 value->character=(uschar*)0;
1053 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
1054 while (*r==' ' || *r=='\t') ++r;
1062 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1064 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
1068 /*************************************************
1069 * Parse remaining hash comment *
1070 *************************************************/
1074 Comment up to terminating CRLF
1077 filter points to the Sieve filter including its state
1083 static int parse_hashcomment(struct Sieve *filter)
1089 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1091 if (*filter->pc=='\n')
1104 filter->errmsg=CUS "missing end of comment";
1109 /*************************************************
1110 * Parse remaining C-style comment *
1111 *************************************************/
1115 Everything up to star slash
1118 filter points to the Sieve filter including its state
1124 static int parse_comment(struct Sieve *filter)
1129 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1136 filter->errmsg=CUS "missing end of comment";
1141 /*************************************************
1142 * Parse optional white space *
1143 *************************************************/
1147 Spaces, tabs, CRLFs, hash comments or C-style comments
1150 filter points to the Sieve filter including its state
1156 static int parse_white(struct Sieve *filter)
1160 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1162 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1164 else if (*filter->pc=='\n')
1174 else if (*filter->pc=='#')
1176 if (parse_hashcomment(filter)==-1) return -1;
1178 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1180 if (parse_comment(filter)==-1) return -1;
1188 /*************************************************
1189 * Parse a optional string *
1190 *************************************************/
1194 quoted-string = DQUOTE *CHAR DQUOTE
1195 ;; in general, \ CHAR inside a string maps to CHAR
1196 ;; so \" maps to " and \\ maps to \
1197 ;; note that newlines and other characters are all allowed
1200 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1201 *(multi-line-literal / multi-line-dotstuff)
1203 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1204 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1205 ;; A line containing only "." ends the multi-line.
1206 ;; Remove a leading '.' if followed by another '.'.
1207 string = quoted-string / multi-line
1210 filter points to the Sieve filter including its state
1211 id specifies identifier to match
1215 0 identifier not matched
1218 static int parse_string(struct Sieve *filter, struct String *data)
1223 data->character=(uschar*)0;
1224 if (*filter->pc=='"') /* quoted string */
1229 if (*filter->pc=='"') /* end of string */
1231 int foo=data->length;
1234 /* that way, there will be at least one character allocated */
1235 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1238 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1240 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1241 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1244 else /* regular character */
1247 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1249 if (*filter->pc=='\n')
1251 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1255 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1259 filter->errmsg=CUS "missing end of string";
1262 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1265 /* skip optional white space followed by hashed comment or CRLF */
1266 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1267 if (*filter->pc=='#')
1269 if (parse_hashcomment(filter)==-1) return -1;
1272 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1274 else if (*filter->pc=='\n')
1286 filter->errmsg=CUS "syntax error";
1292 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1294 if (*filter->pc=='\n') /* end of line */
1297 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1305 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1307 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1310 int foo=data->length;
1312 /* that way, there will be at least one character allocated */
1313 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1322 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1324 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1328 else /* regular character */
1330 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1334 filter->errmsg=CUS "missing end of multi line string";
1341 /*************************************************
1342 * Parse a specific identifier *
1343 *************************************************/
1347 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1350 filter points to the Sieve filter including its state
1351 id specifies identifier to match
1354 0 identifier not matched
1357 static int parse_identifier(struct Sieve *filter, const uschar *id)
1359 size_t idlen=Ustrlen(id);
1361 if (Ustrncmp(filter->pc,id,idlen)==0)
1363 uschar next=filter->pc[idlen];
1365 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1373 /*************************************************
1375 *************************************************/
1379 number = 1*DIGIT [QUANTIFIER]
1380 QUANTIFIER = "K" / "M" / "G"
1383 filter points to the Sieve filter including its state
1387 -1 no string list found
1390 static int parse_number(struct Sieve *filter, unsigned long *data)
1394 if (*filter->pc>='0' && *filter->pc<='9')
1399 d=Ustrtoul(filter->pc,&e,10);
1402 filter->errmsg=CUstrerror(ERANGE);
1407 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1408 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1409 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1410 if (d>(ULONG_MAX/u))
1412 filter->errmsg=CUstrerror(ERANGE);
1421 filter->errmsg=CUS "missing number";
1427 /*************************************************
1428 * Parse a string list *
1429 *************************************************/
1433 string-list = "[" string *("," string) "]" / string
1436 filter points to the Sieve filter including its state
1437 data returns string list
1440 -1 no string list found
1443 static int parse_stringlist(struct Sieve *filter, struct String **data)
1445 const uschar *orig=filter->pc;
1448 struct String *d=(struct String*)0;
1451 if (*filter->pc=='[') /* string list */
1456 if (parse_white(filter)==-1) goto error;
1457 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1460 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1461 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1462 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1464 filter->errmsg=CUstrerror(errno);
1467 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1469 dataCapacity=newCapacity;
1471 m=parse_string(filter,&d[dataLength]);
1474 if (dataLength==0) break;
1477 filter->errmsg=CUS "missing string";
1481 else if (m==-1) goto error;
1483 if (parse_white(filter)==-1) goto error;
1484 if (*filter->pc==',') ++filter->pc;
1487 if (*filter->pc==']')
1489 d[dataLength].character=(uschar*)0;
1490 d[dataLength].length=-1;
1497 filter->errmsg=CUS "missing closing bracket";
1501 else /* single string */
1503 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1507 m=parse_string(filter,&d[0]);
1519 d[1].character=(uschar*)0;
1526 filter->errmsg=CUS "missing string list";
1531 /*************************************************
1532 * Parse an optional address part specifier *
1533 *************************************************/
1537 address-part = ":localpart" / ":domain" / ":all"
1538 address-part =/ ":user" / ":detail"
1541 filter points to the Sieve filter including its state
1542 a returns address part specified
1545 0 no comparator found
1549 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1552 if (parse_identifier(filter,CUS ":user")==1)
1554 if (!filter->require_subaddress)
1556 filter->errmsg=CUS "missing previous require \"subaddress\";";
1562 else if (parse_identifier(filter,CUS ":detail")==1)
1564 if (!filter->require_subaddress)
1566 filter->errmsg=CUS "missing previous require \"subaddress\";";
1574 if (parse_identifier(filter,CUS ":localpart")==1)
1576 *a=ADDRPART_LOCALPART;
1579 else if (parse_identifier(filter,CUS ":domain")==1)
1584 else if (parse_identifier(filter,CUS ":all")==1)
1593 /*************************************************
1594 * Parse an optional comparator *
1595 *************************************************/
1599 comparator = ":comparator" <comparator-name: string>
1602 filter points to the Sieve filter including its state
1603 c returns comparator
1606 0 no comparator found
1607 -1 incomplete comparator found
1610 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1612 struct String comparator_name;
1614 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1615 if (parse_white(filter)==-1) return -1;
1616 switch (parse_string(filter,&comparator_name))
1621 filter->errmsg=CUS "missing comparator";
1628 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1633 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1635 *c=COMP_EN_ASCII_CASEMAP;
1638 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1640 *c=COMP_EN_ASCII_CASEMAP;
1643 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1645 *c=COMP_ASCII_NUMERIC;
1650 filter->errmsg=CUS "invalid comparator";
1659 /*************************************************
1660 * Parse an optional match type *
1661 *************************************************/
1665 match-type = ":is" / ":contains" / ":matches"
1668 filter points to the Sieve filter including its state
1669 m returns match type
1672 0 no match type found
1675 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1677 if (parse_identifier(filter,CUS ":is")==1)
1682 else if (parse_identifier(filter,CUS ":contains")==1)
1687 else if (parse_identifier(filter,CUS ":matches")==1)
1696 /*************************************************
1697 * Parse and interpret an optional test list *
1698 *************************************************/
1702 test-list = "(" test *("," test) ")"
1705 filter points to the Sieve filter including its state
1706 n total number of tests
1707 true number of passed tests
1708 exec Execute parsed statements
1711 0 no test list found
1712 -1 syntax or execution error
1715 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1717 if (parse_white(filter)==-1) return -1;
1718 if (*filter->pc=='(')
1727 switch (parse_test(filter,&cond,exec))
1730 case 0: filter->errmsg=CUS "missing test"; return -1;
1731 default: ++*n; if (cond) ++*true; break;
1733 if (parse_white(filter)==-1) return -1;
1734 if (*filter->pc==',') ++filter->pc;
1737 if (*filter->pc==')')
1744 filter->errmsg=CUS "missing closing paren";
1752 /*************************************************
1753 * Parse and interpret an optional test *
1754 *************************************************/
1758 filter points to the Sieve filter including its state
1759 cond returned condition status
1760 exec Execute parsed statements
1764 -1 syntax or execution error
1767 static int parse_test(struct Sieve *filter, int *cond, int exec)
1769 if (parse_white(filter)==-1) return -1;
1770 if (parse_identifier(filter,CUS "address"))
1773 address-test = "address" { [address-part] [comparator] [match-type] }
1774 <header-list: string-list> <key-list: string-list>
1776 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1779 enum AddressPart addressPart=ADDRPART_ALL;
1780 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1781 enum MatchType matchType=MATCH_IS;
1782 struct String *hdr,*h,*key,*k;
1788 if (parse_white(filter)==-1) return -1;
1789 if ((m=parse_addresspart(filter,&addressPart))!=0)
1791 if (m==-1) return -1;
1794 filter->errmsg=CUS "address part already specified";
1799 else if ((m=parse_comparator(filter,&comparator))!=0)
1801 if (m==-1) return -1;
1804 filter->errmsg=CUS "comparator already specified";
1809 else if ((m=parse_matchtype(filter,&matchType))!=0)
1811 if (m==-1) return -1;
1814 filter->errmsg=CUS "match type already specified";
1821 if (parse_white(filter)==-1) return -1;
1822 if ((m=parse_stringlist(filter,&hdr))!=1)
1824 if (m==0) filter->errmsg=CUS "header string list expected";
1827 if (parse_white(filter)==-1) return -1;
1828 if ((m=parse_stringlist(filter,&key))!=1)
1830 if (m==0) filter->errmsg=CUS "key string list expected";
1834 for (h=hdr; h->length!=-1 && !*cond; ++h)
1836 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1840 !eq_asciicase(h,&str_from,0)
1841 && !eq_asciicase(h,&str_to,0)
1842 && !eq_asciicase(h,&str_cc,0)
1843 && !eq_asciicase(h,&str_bcc,0)
1844 && !eq_asciicase(h,&str_sender,0)
1845 && !eq_asciicase(h,&str_resent_from,0)
1846 && !eq_asciicase(h,&str_resent_to,0)
1849 filter->errmsg=CUS "invalid header field";
1854 /* We are only interested in addresses below, so no MIME decoding */
1855 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1856 if (header_value == NULL)
1858 filter->errmsg=CUS "header string expansion failed";
1861 parse_allow_group = TRUE;
1862 while (*header_value && !*cond)
1865 int start, end, domain;
1869 end_addr = parse_find_address_end(header_value, FALSE);
1870 saveend = *end_addr;
1872 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1874 if (extracted_addr) switch (addressPart)
1876 case ADDRPART_ALL: part=extracted_addr; break;
1880 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1881 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1883 case ADDRPART_DETAIL: part=NULL; break;
1887 *end_addr = saveend;
1890 for (k=key; k->length!=-1; ++k)
1892 struct String partStr;
1894 partStr.character=part;
1895 partStr.length=Ustrlen(part);
1898 *cond=compare(filter,k,&partStr,comparator,matchType);
1899 if (*cond==-1) return -1;
1904 if (saveend == 0) break;
1905 header_value = end_addr + 1;
1907 parse_allow_group = FALSE;
1908 parse_found_group = FALSE;
1913 else if (parse_identifier(filter,CUS "allof"))
1916 allof-test = "allof" <tests: test-list>
1921 switch (parse_testlist(filter,&n,&true,exec))
1924 case 0: filter->errmsg=CUS "missing test list"; return -1;
1925 default: *cond=(n==true); return 1;
1928 else if (parse_identifier(filter,CUS "anyof"))
1931 anyof-test = "anyof" <tests: test-list>
1936 switch (parse_testlist(filter,&n,&true,exec))
1939 case 0: filter->errmsg=CUS "missing test list"; return -1;
1940 default: *cond=(true>0); return 1;
1943 else if (parse_identifier(filter,CUS "exists"))
1946 exists-test = "exists" <header-names: string-list>
1949 struct String *hdr,*h;
1952 if (parse_white(filter)==-1) return -1;
1953 if ((m=parse_stringlist(filter,&hdr))!=1)
1955 if (m==0) filter->errmsg=CUS "header string list expected";
1961 for (h=hdr; h->length!=-1 && *cond; ++h)
1965 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1966 if (header_def == NULL)
1968 filter->errmsg=CUS "header string expansion failed";
1971 if (Ustrcmp(header_def,"false")==0) *cond=0;
1976 else if (parse_identifier(filter,CUS "false"))
1979 false-test = "false"
1985 else if (parse_identifier(filter,CUS "header"))
1988 header-test = "header" { [comparator] [match-type] }
1989 <header-names: string-list> <key-list: string-list>
1992 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1993 enum MatchType matchType=MATCH_IS;
1994 struct String *hdr,*h,*key,*k;
2000 if (parse_white(filter)==-1) return -1;
2001 if ((m=parse_comparator(filter,&comparator))!=0)
2003 if (m==-1) return -1;
2006 filter->errmsg=CUS "comparator already specified";
2011 else if ((m=parse_matchtype(filter,&matchType))!=0)
2013 if (m==-1) return -1;
2016 filter->errmsg=CUS "match type already specified";
2023 if (parse_white(filter)==-1) return -1;
2024 if ((m=parse_stringlist(filter,&hdr))!=1)
2026 if (m==0) filter->errmsg=CUS "header string list expected";
2029 if (parse_white(filter)==-1) return -1;
2030 if ((m=parse_stringlist(filter,&key))!=1)
2032 if (m==0) filter->errmsg=CUS "key string list expected";
2036 for (h=hdr; h->length!=-1 && !*cond; ++h)
2040 filter->errmsg=CUS "invalid header field";
2045 struct String header_value;
2048 expand_header(&header_value,h);
2049 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2050 if (header_value.character == NULL || header_def == NULL)
2052 filter->errmsg=CUS "header string expansion failed";
2055 for (k=key; k->length!=-1; ++k)
2057 if (Ustrcmp(header_def,"true")==0)
2059 *cond=compare(filter,k,&header_value,comparator,matchType);
2060 if (*cond==-1) return -1;
2068 else if (parse_identifier(filter,CUS "not"))
2070 if (parse_white(filter)==-1) return -1;
2071 switch (parse_test(filter,cond,exec))
2074 case 0: filter->errmsg=CUS "missing test"; return -1;
2075 default: *cond=!*cond; return 1;
2078 else if (parse_identifier(filter,CUS "size"))
2081 relop = ":over" / ":under"
2082 size-test = "size" relop <limit: number>
2085 unsigned long limit;
2088 if (parse_white(filter)==-1) return -1;
2089 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2090 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2093 filter->errmsg=CUS "missing :over or :under";
2096 if (parse_white(filter)==-1) return -1;
2097 if (parse_number(filter,&limit)==-1) return -1;
2098 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2101 else if (parse_identifier(filter,CUS "true"))
2106 else if (parse_identifier(filter,CUS "envelope"))
2109 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2110 <envelope-part: string-list> <key-list: string-list>
2112 envelope-part is case insensitive "from" or "to"
2113 #ifdef ENVELOPE_AUTH
2114 envelope-part =/ "auth"
2118 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2119 enum AddressPart addressPart=ADDRPART_ALL;
2120 enum MatchType matchType=MATCH_IS;
2121 struct String *env,*e,*key,*k;
2125 if (!filter->require_envelope)
2127 filter->errmsg=CUS "missing previous require \"envelope\";";
2132 if (parse_white(filter)==-1) return -1;
2133 if ((m=parse_comparator(filter,&comparator))!=0)
2135 if (m==-1) return -1;
2138 filter->errmsg=CUS "comparator already specified";
2143 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2145 if (m==-1) return -1;
2148 filter->errmsg=CUS "address part already specified";
2153 else if ((m=parse_matchtype(filter,&matchType))!=0)
2155 if (m==-1) return -1;
2158 filter->errmsg=CUS "match type already specified";
2165 if (parse_white(filter)==-1) return -1;
2166 if ((m=parse_stringlist(filter,&env))!=1)
2168 if (m==0) filter->errmsg=CUS "envelope string list expected";
2171 if (parse_white(filter)==-1) return -1;
2172 if ((m=parse_stringlist(filter,&key))!=1)
2174 if (m==0) filter->errmsg=CUS "key string list expected";
2178 for (e=env; e->length!=-1 && !*cond; ++e)
2180 const uschar *envelopeExpr=CUS 0;
2181 uschar *envelope=US 0;
2183 if (eq_asciicase(e,&str_from,0))
2185 switch (addressPart)
2187 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2191 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2192 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2194 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2198 else if (eq_asciicase(e,&str_to,0))
2200 switch (addressPart)
2202 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2204 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2205 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2207 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2208 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2211 #ifdef ENVELOPE_AUTH
2212 else if (eq_asciicase(e,&str_auth,0))
2214 switch (addressPart)
2216 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2220 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2221 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2223 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2230 filter->errmsg=CUS "invalid envelope string";
2233 if (exec && envelopeExpr)
2235 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2237 filter->errmsg=CUS "header string expansion failed";
2240 for (k=key; k->length!=-1; ++k)
2242 struct String envelopeStr;
2244 envelopeStr.character=envelope;
2245 envelopeStr.length=Ustrlen(envelope);
2246 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2247 if (*cond==-1) return -1;
2255 else if (parse_identifier(filter,CUS "valid_notif_method"))
2258 valid_notif_method = "valid_notif_method"
2259 <notification-uris: string-list>
2262 struct String *uris,*u;
2265 if (!filter->require_enotify)
2267 filter->errmsg=CUS "missing previous require \"enotify\";";
2270 if (parse_white(filter)==-1) return -1;
2271 if ((m=parse_stringlist(filter,&uris))!=1)
2273 if (m==0) filter->errmsg=CUS "URI string list expected";
2279 for (u=uris; u->length!=-1 && *cond; ++u)
2281 string_item *recipient;
2282 struct String header,body;
2286 header.character=(uschar*)0;
2288 body.character=(uschar*)0;
2289 if (parse_mailto_uri(filter,u->character,&recipient,&header,&body)!=1)
2300 /*************************************************
2301 * Parse and interpret an optional block *
2302 *************************************************/
2306 filter points to the Sieve filter including its state
2307 exec Execute parsed statements
2308 generated where to hang newly-generated addresses
2310 Returns: 2 success by stop
2312 0 no block command found
2313 -1 syntax or execution error
2316 static int parse_block(struct Sieve *filter, int exec,
2317 address_item **generated)
2321 if (parse_white(filter)==-1) return -1;
2322 if (*filter->pc=='{')
2325 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2326 if (*filter->pc=='}')
2333 filter->errmsg=CUS "expecting command or closing brace";
2341 /*************************************************
2342 * Match a semicolon *
2343 *************************************************/
2347 filter points to the Sieve filter including its state
2353 static int parse_semicolon(struct Sieve *filter)
2355 if (parse_white(filter)==-1) return -1;
2356 if (*filter->pc==';')
2363 filter->errmsg=CUS "missing semicolon";
2369 /*************************************************
2370 * Parse and interpret a Sieve command *
2371 *************************************************/
2375 filter points to the Sieve filter including its state
2376 exec Execute parsed statements
2377 generated where to hang newly-generated addresses
2379 Returns: 2 success by stop
2381 -1 syntax or execution error
2383 static int parse_commands(struct Sieve *filter, int exec,
2384 address_item **generated)
2388 if (parse_white(filter)==-1) return -1;
2389 if (parse_identifier(filter,CUS "if"))
2392 if-command = "if" test block *( "elsif" test block ) [ else block ]
2395 int cond,m,unsuccessful;
2398 if (parse_white(filter)==-1) return -1;
2399 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2402 filter->errmsg=CUS "missing test";
2405 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2406 (debug_selector & D_filter) != 0)
2408 if (exec) debug_printf("if %s\n",cond?"true":"false");
2410 m=parse_block(filter,exec ? cond : 0, generated);
2411 if (m==-1 || m==2) return m;
2414 filter->errmsg=CUS "missing block";
2417 unsuccessful = !cond;
2418 for (;;) /* elsif test block */
2420 if (parse_white(filter)==-1) return -1;
2421 if (parse_identifier(filter,CUS "elsif"))
2423 if (parse_white(filter)==-1) return -1;
2424 m=parse_test(filter,&cond,exec && unsuccessful);
2425 if (m==-1 || m==2) return m;
2428 filter->errmsg=CUS "missing test";
2431 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2432 (debug_selector & D_filter) != 0)
2434 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2436 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2437 if (m==-1 || m==2) return m;
2440 filter->errmsg=CUS "missing block";
2443 if (exec && unsuccessful && cond) unsuccessful = 0;
2448 if (parse_white(filter)==-1) return -1;
2449 if (parse_identifier(filter,CUS "else"))
2451 m=parse_block(filter,exec && unsuccessful, generated);
2452 if (m==-1 || m==2) return m;
2455 filter->errmsg=CUS "missing block";
2460 else if (parse_identifier(filter,CUS "stop"))
2463 stop-command = "stop" { stop-options } ";"
2467 if (parse_semicolon(filter)==-1) return -1;
2470 filter->pc+=Ustrlen(filter->pc);
2474 else if (parse_identifier(filter,CUS "keep"))
2477 keep-command = "keep" { keep-options } ";"
2481 if (parse_semicolon(filter)==-1) return -1;
2484 add_addr(generated,US"inbox",1,0,0,0);
2488 else if (parse_identifier(filter,CUS "discard"))
2491 discard-command = "discard" { discard-options } ";"
2495 if (parse_semicolon(filter)==-1) return -1;
2496 if (exec) filter->keep=0;
2498 else if (parse_identifier(filter,CUS "redirect"))
2501 redirect-command = "redirect" redirect-options "string" ";"
2503 redirect-options =) ":copy"
2506 struct String recipient;
2512 if (parse_white(filter)==-1) return -1;
2513 if (parse_identifier(filter,CUS ":copy")==1)
2515 if (!filter->require_copy)
2517 filter->errmsg=CUS "missing previous require \"copy\";";
2524 if (parse_white(filter)==-1) return -1;
2525 if ((m=parse_string(filter,&recipient))!=1)
2527 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2530 if (strchr(CCS recipient.character,'@')==(char*)0)
2532 filter->errmsg=CUS "unqualified recipient address";
2537 add_addr(generated,recipient.character,0,0,0,0);
2538 if (!copy) filter->keep = 0;
2540 if (parse_semicolon(filter)==-1) return -1;
2542 else if (parse_identifier(filter,CUS "fileinto"))
2545 fileinto-command = "fileinto" { fileinto-options } string ";"
2547 fileinto-options =) [ ":copy" ]
2550 struct String folder;
2553 unsigned long maxage, maxmessages, maxstorage;
2556 maxage = maxmessages = maxstorage = 0;
2557 if (!filter->require_fileinto)
2559 filter->errmsg=CUS "missing previous require \"fileinto\";";
2564 if (parse_white(filter)==-1) return -1;
2565 if (parse_identifier(filter,CUS ":copy")==1)
2567 if (!filter->require_copy)
2569 filter->errmsg=CUS "missing previous require \"copy\";";
2576 if (parse_white(filter)==-1) return -1;
2577 if ((m=parse_string(filter,&folder))!=1)
2579 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2582 m=0; s=folder.character;
2583 if (folder.length==0) m=1;
2584 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2587 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2592 filter->errmsg=CUS "invalid folder";
2597 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2598 if (!copy) filter->keep = 0;
2600 if (parse_semicolon(filter)==-1) return -1;
2603 else if (parse_identifier(filter,CUS "notify"))
2606 notify-command = "notify" { notify-options } <method: string> ";"
2607 notify-options = [":from" string]
2608 [":importance" <"1" / "2" / "3">]
2609 [":options" 1*(string-list / number)]
2615 struct String importance;
2616 struct String *options;
2617 struct String message;
2618 struct String method;
2619 struct Notification *already;
2620 string_item *recipient;
2621 struct String header;
2623 uschar *envelope_from,*envelope_to;
2625 if (!filter->require_enotify)
2627 filter->errmsg=CUS "missing previous require \"enotify\";";
2630 from.character=(uschar*)0;
2632 importance.character=(uschar*)0;
2633 importance.length=-1;
2634 options=(struct String*)0;
2635 message.character=(uschar*)0;
2639 header.character=(uschar*)0;
2641 body.character=(uschar*)0;
2642 envelope_from=expand_string("$sender_address");
2643 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2646 if (parse_white(filter)==-1) return -1;
2647 if (parse_identifier(filter,CUS ":from")==1)
2649 if (parse_white(filter)==-1) return -1;
2650 if ((m=parse_string(filter,&from))!=1)
2652 if (m==0) filter->errmsg=CUS "from string expected";
2656 else if (parse_identifier(filter,CUS ":importance")==1)
2658 if (parse_white(filter)==-1) return -1;
2659 if ((m=parse_string(filter,&importance))!=1)
2661 if (m==0) filter->errmsg=CUS "importance string expected";
2664 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
2666 filter->errmsg=CUS "invalid importance";
2670 else if (parse_identifier(filter,CUS ":options")==1)
2672 if (parse_white(filter)==-1) return -1;
2674 else if (parse_identifier(filter,CUS ":message")==1)
2676 if (parse_white(filter)==-1) return -1;
2677 if ((m=parse_string(filter,&message))!=1)
2679 if (m==0) filter->errmsg=CUS "message string expected";
2685 if (parse_white(filter)==-1) return -1;
2686 if ((m=parse_string(filter,&method))!=1)
2688 if (m==0) filter->errmsg=CUS "missing method string";
2691 if (parse_semicolon(filter)==-1) return -1;
2693 for (already=filter->notified; already; already=already->next)
2695 if (already->method.length==method.length
2696 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
2697 && already->importance.length==importance.length
2698 && (importance.length==-1 || strcmp(already->importance.character,importance.character)==0)
2699 && already->message.length==message.length
2700 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
2703 if (already==(struct Notification*)0)
2704 /* New notification, process it */
2706 if (parse_mailto_uri(filter,method.character,&recipient,&header,&body)!=1)
2708 struct Notification *sent;
2709 sent=store_get(sizeof(struct Notification));
2710 sent->method=method;
2711 sent->importance=importance;
2712 sent->message=message;
2713 sent->next=filter->notified;
2714 filter->notified=sent;
2715 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2717 debug_printf("Notification to `%s'.\n",method.character);
2719 #ifndef COMPILE_SYNTAX_CHECKER
2726 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
2730 f = fdopen(fd, "wb");
2731 for (h = header_list; h != NULL; h = h->next)
2732 if (h->type == htype_received) fprintf(f,"%s",h->text);
2733 fprintf(f,"From: %s\n",from.length==-1 ? envelope_to : from.character);
2734 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
2735 if (header.length>0) fprintf(f,"%s",header.character);
2736 fprintf(f,"Subject: %s\n",message.length==-1 ? CUS "notification" : message.character);
2738 if (body.length>0) fprintf(f,"%s\n",body.character);
2741 (void)child_close(pid, 0);
2748 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2750 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
2756 else if (parse_identifier(filter,CUS "vacation"))
2759 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2760 vacation-options = [":days" number]
2763 [":addresses" string-list]
2770 struct String subject;
2772 struct String *addresses;
2774 string_item *aliases;
2775 struct String handle;
2776 struct String reason;
2778 if (!filter->require_vacation)
2780 filter->errmsg=CUS "missing previous require \"vacation\";";
2785 if (filter->vacation_ran)
2787 filter->errmsg=CUS "trying to execute vacation more than once";
2790 filter->vacation_ran=1;
2792 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2793 subject.character=(uschar*)0;
2795 from.character=(uschar*)0;
2797 addresses=(struct String*)0;
2800 handle.character=(uschar*)0;
2804 if (parse_white(filter)==-1) return -1;
2805 if (parse_identifier(filter,CUS ":days")==1)
2807 if (parse_white(filter)==-1) return -1;
2808 if (parse_number(filter,&days)==-1) return -1;
2809 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2810 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2812 else if (parse_identifier(filter,CUS ":subject")==1)
2814 if (parse_white(filter)==-1) return -1;
2815 if ((m=parse_string(filter,&subject))!=1)
2817 if (m==0) filter->errmsg=CUS "subject string expected";
2821 else if (parse_identifier(filter,CUS ":from")==1)
2823 if (parse_white(filter)==-1) return -1;
2824 if ((m=parse_string(filter,&from))!=1)
2826 if (m==0) filter->errmsg=CUS "from string expected";
2829 if (check_mail_address(filter,&from)!=1)
2832 else if (parse_identifier(filter,CUS ":addresses")==1)
2836 if (parse_white(filter)==-1) return -1;
2837 if ((m=parse_stringlist(filter,&addresses))!=1)
2839 if (m==0) filter->errmsg=CUS "addresses string list expected";
2842 for (a=addresses; a->length!=-1; ++a)
2846 new=store_get(sizeof(string_item));
2847 new->text=store_get(a->length+1);
2848 if (a->length) memcpy(new->text,a->character,a->length);
2849 new->text[a->length]='\0';
2854 else if (parse_identifier(filter,CUS ":mime")==1)
2856 else if (parse_identifier(filter,CUS ":handle")==1)
2858 if (parse_white(filter)==-1) return -1;
2859 if ((m=parse_string(filter,&from))!=1)
2861 if (m==0) filter->errmsg=CUS "handle string expected";
2867 if (parse_white(filter)==-1) return -1;
2868 if ((m=parse_string(filter,&reason))!=1)
2870 if (m==0) filter->errmsg=CUS "missing reason string";
2877 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2880 filter->errmsg=CUS "MIME reason string contains 8bit text";
2884 if (parse_semicolon(filter)==-1) return -1;
2891 int buffer_capacity;
2895 uschar hexdigest[33];
2899 if (filter_personal(aliases,TRUE))
2901 if (filter_test == FTEST_NONE)
2903 /* ensure oncelog directory exists; failure will be detected later */
2905 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2907 /* build oncelog filename */
2909 key.character=(uschar*)0;
2912 if (handle.length==-1)
2914 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2915 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2916 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2917 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2922 md5_end(&base, key.character, key.length, digest);
2923 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2924 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2926 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2928 if (filter_test == FTEST_NONE)
2930 capacity=Ustrlen(filter->vacation_directory);
2932 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2933 once=string_cat(once,&capacity,&start,hexdigest,33);
2936 /* process subject */
2938 if (subject.length==-1)
2940 uschar *subject_def;
2942 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
2943 if (Ustrcmp(subject_def,"true")==0)
2945 expand_header(&subject,&str_subject);
2948 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2949 subject.length=start;
2953 subject.character=US"Automated reply";
2954 subject.length=Ustrlen(subject.character);
2958 /* add address to list of generated addresses */
2960 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2961 setflag(addr, af_pfr);
2962 setflag(addr, af_ignore_error);
2963 addr->next = *generated;
2965 addr->reply = store_get(sizeof(reply_item));
2966 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2967 addr->reply->to = string_copy(sender_address);
2968 if (from.length==-1)
2969 addr->reply->from = expand_string(US"$local_part@$domain");
2971 addr->reply->from = from.character;
2972 /* Allocation is larger than neccessary, but enough even for split MIME words */
2973 buffer_capacity=32+4*subject.length;
2974 buffer=store_get(buffer_capacity);
2975 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
2976 addr->reply->oncelog=once;
2977 addr->reply->once_repeat=days*86400;
2979 /* build body and MIME headers */
2983 uschar *mime_body,*reason_end;
2984 static const uschar nlnl[]="\r\n\r\n";
2988 mime_body=reason.character,reason_end=reason.character+reason.length;
2989 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
2994 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2995 addr->reply->headers[start] = '\0';
2998 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
2999 else mime_body=reason_end-1;
3000 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
3001 addr->reply->text[start] = '\0';
3005 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
3008 start = reason.length;
3009 addr->reply->headers = US"MIME-Version: 1.0\n"
3010 "Content-Type: text/plain;\n"
3011 "\tcharset=\"utf-8\"\n"
3012 "Content-Transfer-Encoding: quoted-printable";
3013 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3017 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3019 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
3030 /*************************************************
3031 * Parse and interpret a sieve filter *
3032 *************************************************/
3036 filter points to the Sieve filter including its state
3037 exec Execute parsed statements
3038 generated where to hang newly-generated addresses
3041 -1 syntax or execution error
3044 static int parse_start(struct Sieve *filter, int exec,
3045 address_item **generated)
3047 filter->pc=filter->filter;
3050 filter->require_envelope=0;
3051 filter->require_fileinto=0;
3052 #ifdef ENVELOPE_AUTH
3053 filter->require_envelope_auth=0;
3056 filter->require_enotify=0;
3057 filter->notified=(struct Notification*)0;
3060 filter->require_subaddress=0;
3063 filter->require_vacation=0;
3064 filter->vacation_ran=0;
3066 filter->require_copy=0;
3067 filter->require_iascii_numeric=0;
3069 if (parse_white(filter)==-1) return -1;
3071 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
3074 struct dirent *oncelog;
3075 struct stat properties;
3078 /* clean up old vacation log databases */
3080 oncelogdir=opendir(CS filter->vacation_directory);
3082 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3084 filter->errmsg=CUS "unable to open vacation directory";
3088 if (oncelogdir != NULL)
3092 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3094 if (strlen(oncelog->d_name)==32)
3096 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3097 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3101 closedir(oncelogdir);
3105 while (parse_identifier(filter,CUS "require"))
3108 require-command = "require" <capabilities: string-list>
3111 struct String *cap,*check;
3114 if (parse_white(filter)==-1) return -1;
3115 if ((m=parse_stringlist(filter,&cap))!=1)
3117 if (m==0) filter->errmsg=CUS "capability string list expected";
3120 for (check=cap; check->character; ++check)
3122 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3123 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3124 #ifdef ENVELOPE_AUTH
3125 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3128 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
3131 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3134 else if (eq_octet(check,&str_vacation,0))
3136 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3138 filter->errmsg=CUS "vacation disabled";
3141 filter->require_vacation=1;
3144 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3145 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3146 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3147 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3148 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3151 filter->errmsg=CUS "unknown capability";
3155 if (parse_semicolon(filter)==-1) return -1;
3157 if (parse_commands(filter,exec,generated)==-1) return -1;
3160 filter->errmsg=CUS "syntax error";
3167 /*************************************************
3168 * Interpret a sieve filter file *
3169 *************************************************/
3173 filter points to the entire file, read into store as a single string
3174 options controls whether various special things are allowed, and requests
3175 special actions (not currently used)
3176 sieve_vacation_directory where to store vacation "once" files
3177 useraddress string expression for :user part of address
3178 subaddress string expression for :subaddress part of address
3179 generated where to hang newly-generated addresses
3180 error where to pass back an error text
3182 Returns: FF_DELIVERED success, a significant action was taken
3183 FF_NOTDELIVERED success, no significant action
3184 FF_DEFER defer requested
3185 FF_FAIL fail requested
3186 FF_FREEZE freeze requested
3187 FF_ERROR there was a problem
3191 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3192 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3198 options = options; /* Keep picky compilers happy */
3201 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3202 sieve.filter=filter;
3204 if (vacation_directory == NULL)
3205 sieve.vacation_directory = NULL;
3208 sieve.vacation_directory=expand_string(vacation_directory);
3209 if (sieve.vacation_directory == NULL)
3211 *error = string_sprintf("failed to expand \"%s\" "
3212 "(sieve_vacation_directory): %s", vacation_directory,
3213 expand_string_message);
3218 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3219 sieve.subaddress = subaddress;
3221 #ifdef COMPILE_SYNTAX_CHECKER
3222 if (parse_start(&sieve,0,generated)==1)
3224 if (parse_start(&sieve,1,generated)==1)
3229 add_addr(generated,US"inbox",1,0,0,0);
3230 msg = string_sprintf("Implicit keep");
3235 msg = string_sprintf("No implicit keep");
3241 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3242 #ifdef COMPILE_SYNTAX_CHECKER
3246 add_addr(generated,US"inbox",1,0,0,0);
3251 #ifndef COMPILE_SYNTAX_CHECKER
3252 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3253 else debug_printf("%s\n", msg);
3256 DEBUG(D_route) debug_printf("Sieve: end of processing\n");