1 /* $Cambridge: exim/src/src/sieve.c,v 1.23 2006/10/10 15:36:50 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 "notify". */
37 /* Define this for the Sieve extension "subaddress". */
40 /* Define this for the Sieve extension "vacation". */
44 #define VACATION_MIN_DAYS 1
45 /* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
46 #define VACATION_MAX_DAYS 31
48 /* Keep this at 75 to accept only RFC compliant MIME words. */
49 /* Increase it if you want to match headers from buggy MUAs. */
50 #define MIMEWORD_LENGTH 75
62 int require_envelope_auth;
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 priority;
101 struct String message;
102 struct Notification *next;
105 static int parse_test(struct Sieve *filter, int *cond, int exec);
106 static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
108 static uschar str_from_c[]="From";
109 static const struct String str_from={ str_from_c, 4 };
110 static uschar str_to_c[]="To";
111 static const struct String str_to={ str_to_c, 2 };
112 static uschar str_cc_c[]="Cc";
113 static const struct String str_cc={ str_cc_c, 2 };
114 static uschar str_bcc_c[]="Bcc";
115 static const struct String str_bcc={ str_bcc_c, 3 };
116 static uschar str_auth_c[]="auth";
117 static const struct String str_auth={ str_auth_c, 4 };
118 static uschar str_sender_c[]="Sender";
119 static const struct String str_sender={ str_sender_c, 6 };
120 static uschar str_resent_from_c[]="Resent-From";
121 static const struct String str_resent_from={ str_resent_from_c, 11 };
122 static uschar str_resent_to_c[]="Resent-To";
123 static const struct String str_resent_to={ str_resent_to_c, 9 };
124 static uschar str_fileinto_c[]="fileinto";
125 static const struct String str_fileinto={ str_fileinto_c, 8 };
126 static uschar str_envelope_c[]="envelope";
127 static const struct String str_envelope={ str_envelope_c, 8 };
129 static uschar str_envelope_auth_c[]="envelope-auth";
130 static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
133 static uschar str_notify_c[]="notify";
134 static const struct String str_notify={ str_notify_c, 6 };
137 static uschar str_subaddress_c[]="subaddress";
138 static const struct String str_subaddress={ str_subaddress_c, 10 };
141 static uschar str_vacation_c[]="vacation";
142 static const struct String str_vacation={ str_vacation_c, 8 };
143 static uschar str_subject_c[]="Subject";
144 static const struct String str_subject={ str_subject_c, 7 };
146 static uschar str_copy_c[]="copy";
147 static const struct String str_copy={ str_copy_c, 4 };
148 static uschar str_iascii_casemap_c[]="i;ascii-casemap";
149 static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
150 static uschar str_enascii_casemap_c[]="en;ascii-casemap";
151 static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
152 static uschar str_ioctet_c[]="i;octet";
153 static const struct String str_ioctet={ str_ioctet_c, 7 };
154 static uschar str_iascii_numeric_c[]="i;ascii-numeric";
155 static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
156 static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
157 static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
158 static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
159 static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
160 static uschar str_comparator_ioctet_c[]="comparator-i;octet";
161 static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
162 static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
163 static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
166 /*************************************************
167 * Encode to quoted-printable *
168 *************************************************/
179 static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
182 const uschar *start,*end;
187 for (pass=0; pass<=1; ++pass)
194 dst->character=store_get(dst->length+1); /* plus one for \0 */
197 for (start=src->character,end=start+src->length; start<end; ++start)
214 || (ch>=62 && ch<=126)
219 && (*(start+1)!='\r' || *(start+2)!='\n')
229 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
247 sprintf(CS new,"=%02X",ch);
254 *new='\0'; /* not included in length, but nice */
259 /*************************************************
260 * Decode URI encoded string *
261 *************************************************/
265 str URI encoded string
268 0 Decoding successful
272 static int uri_decode(struct String *str)
276 if (str->length==0) return 0;
277 for (s=str->character,t=s,e=s+str->length; s<e; )
281 if (s+2<e && isxdigit(*(s+1)) && isxdigit(*(s+2)))
283 *t++=((isdigit(*(s+1)) ? *(s+1)-'0' : tolower(*(s+1))-'a'+10)<<4)
284 | (isdigit(*(s+2)) ? *(s+2)-'0' : tolower(*(s+2))-'a'+10);
293 str->length=t-str->character;
298 /*************************************************
300 *************************************************/
305 mailtoURI = "mailto:" [ to ] [ headers ]
306 to = [ addr-spec *("%2C" addr-spec ) ]
307 headers = "?" header *( "&" header )
308 header = hname "=" hvalue
313 filter points to the Sieve filter including its state
314 uri URI, excluding scheme
317 1 URI is syntactically OK
321 int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *body)
324 struct String to,hname,hvalue;
328 if (*uri && *uri!='?')
332 for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri);
336 to.character=(uschar*)0;
338 to.character=string_cat(to.character,&capacity,&to.length,start,uri-start);
339 to.character[to.length]='\0';
340 if (uri_decode(&to)==-1)
342 filter->errmsg=US"Invalid URI encoding";
345 new=store_get(sizeof(string_item));
346 new->text=store_get(to.length+1);
347 if (to.length) memcpy(new->text,to.character,to.length);
348 new->text[to.length]='\0';
349 new->next=*recipient;
354 filter->errmsg=US"Missing addr-spec in URI";
357 if (*uri=='%') uri+=3;
366 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
370 hname.character=(uschar*)0;
372 hname.character=string_cat(hname.character,&capacity,&hname.length,start,uri-start);
373 hname.character[hname.length]='\0';
374 if (uri_decode(&hname)==-1)
376 filter->errmsg=US"Invalid URI encoding";
385 filter->errmsg=US"Missing equal after hname";
389 for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri);
393 hvalue.character=(uschar*)0;
395 hvalue.character=string_cat(hvalue.character,&capacity,&hvalue.length,start,uri-start);
396 hvalue.character[hvalue.length]='\0';
397 if (uri_decode(&hvalue)==-1)
399 filter->errmsg=US"Invalid URI encoding";
403 if (hname.length==2 && strcmp(CS hname.character,"to")==0)
405 new=store_get(sizeof(string_item));
406 new->text=store_get(hvalue.length+1);
407 if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
408 new->text[hvalue.length]='\0';
409 new->next=*recipient;
412 else if (hname.length==4 && strcmp(CS hname.character,"body")==0)
414 if (*uri=='&') ++uri;
420 filter->errmsg=US"Syntactically invalid URI";
426 /*************************************************
427 * Octet-wise string comparison *
428 *************************************************/
432 needle UTF-8 string to search ...
433 haystack ... inside the haystack
434 match_prefix 1 to compare if needle is a prefix of haystack
436 Returns: 0 needle not found in haystack
440 static int eq_octet(const struct String *needle,
441 const struct String *haystack, int match_prefix)
449 h=haystack->character;
453 if (*n&0x80) return 0;
454 if (*h&0x80) return 0;
456 if (*n!=*h) return 0;
462 return (match_prefix ? nl==0 : nl==0 && hl==0);
466 /*************************************************
467 * ASCII case-insensitive string comparison *
468 *************************************************/
472 needle UTF-8 string to search ...
473 haystack ... inside the haystack
474 match_prefix 1 to compare if needle is a prefix of haystack
476 Returns: 0 needle not found in haystack
480 static int eq_asciicase(const struct String *needle,
481 const struct String *haystack, int match_prefix)
490 h=haystack->character;
496 if (nc&0x80) return 0;
497 if (hc&0x80) return 0;
499 /* tolower depends on the locale and only ASCII case must be insensitive */
500 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
506 return (match_prefix ? nl==0 : nl==0 && hl==0);
510 /*************************************************
511 * Glob pattern search *
512 *************************************************/
516 needle pattern to search ...
517 haystack ... inside the haystack
519 Returns: 0 needle not found in haystack
524 static int eq_glob(const struct String *needle,
525 const struct String *haystack, int ascii_caseless, int match_octet)
527 const uschar *n,*h,*nend,*hend;
531 h=haystack->character;
532 nend=n+needle->length;
533 hend=h+haystack->length;
543 const uschar *npart,*hpart;
545 /* Try to match a non-star part of the needle at the current */
546 /* position in the haystack. */
550 while (npart<nend && *npart!='*') switch (*npart)
554 if (hpart==hend) return 0;
559 /* Match one UTF8 encoded character */
560 if ((*hpart&0xc0)==0xc0)
563 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
574 if (npart==nend) return -1;
579 if (hpart==hend) return 0;
580 /* tolower depends on the locale, but we need ASCII */
584 (*hpart&0x80) || (*npart&0x80) ||
587 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
592 /* string match after a star failed, advance and try again */
606 /* at this point, a part was matched successfully */
607 if (may_advance && npart==nend && hpart<hend)
608 /* needle ends, but haystack does not: if there was a star before, advance and try again */
618 return (h==hend ? 1 : may_advance);
622 /*************************************************
623 * ASCII numeric comparison *
624 *************************************************/
628 a first numeric string
629 b second numeric string
630 relop relational operator
632 Returns: 0 not (a relop b)
636 static int eq_asciinumeric(const struct String *a,
637 const struct String *b, enum RelOp relop)
640 const uschar *as,*aend,*bs,*bend;
644 aend=a->character+a->length;
646 bend=b->character+b->length;
648 while (*as>='0' && *as<='9' && as<aend) ++as;
650 while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
653 if (al && bl==0) cmp=-1;
654 else if (al==0 && bl==0) cmp=0;
655 else if (al==0 && bl) cmp=1;
659 if (cmp==0) cmp=memcmp(a->character,b->character,al);
663 case LT: return cmp<0;
664 case LE: return cmp<=0;
665 case EQ: return cmp==0;
666 case GE: return cmp>=0;
667 case GT: return cmp>0;
668 case NE: return cmp!=0;
675 /*************************************************
677 *************************************************/
681 filter points to the Sieve filter including its state
682 needle UTF-8 pattern or string to search ...
683 haystack ... inside the haystack
687 Returns: 0 needle not found in haystack
689 -1 comparator does not offer matchtype
692 static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
693 enum Comparator co, enum MatchType mt)
697 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
698 (debug_selector & D_filter) != 0)
700 debug_printf("String comparison (match ");
703 case MATCH_IS: debug_printf(":is"); break;
704 case MATCH_CONTAINS: debug_printf(":contains"); break;
705 case MATCH_MATCHES: debug_printf(":matches"); break;
707 debug_printf(", comparison \"");
710 case COMP_OCTET: debug_printf("i;octet"); break;
711 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
712 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
714 debug_printf("\"):\n");
715 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
716 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
726 if (eq_octet(needle,haystack,0)) r=1;
729 case COMP_EN_ASCII_CASEMAP:
731 if (eq_asciicase(needle,haystack,0)) r=1;
734 case COMP_ASCII_NUMERIC:
736 if (!filter->require_iascii_numeric)
738 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
741 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
755 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
758 case COMP_EN_ASCII_CASEMAP:
760 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
765 filter->errmsg=CUS "comparator does not offer specified matchtype";
777 if ((r=eq_glob(needle,haystack,0,1))==-1)
779 filter->errmsg=CUS "syntactically invalid pattern";
784 case COMP_EN_ASCII_CASEMAP:
786 if ((r=eq_glob(needle,haystack,1,1))==-1)
788 filter->errmsg=CUS "syntactically invalid pattern";
795 filter->errmsg=CUS "comparator does not offer specified matchtype";
802 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
803 (debug_selector & D_filter) != 0)
804 debug_printf(" Result %s\n",r?"true":"false");
809 /*************************************************
810 * Check header field syntax *
811 *************************************************/
814 RFC 2822, section 3.6.8 says:
818 ftext = %d33-57 / ; Any character except
819 %d59-126 ; controls, SP, and
822 That forbids 8-bit header fields. This implementation accepts them, since
823 all of Exim is 8-bit clean, so it adds %d128-%d255.
826 header header field to quote for suitable use in Exim expansions
828 Returns: 0 string is not a valid header field
829 1 string is a value header field
832 static int is_header(const struct String *header)
842 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
853 /*************************************************
854 * Quote special characters string *
855 *************************************************/
859 header header field to quote for suitable use in Exim expansions
862 Returns: quoted string
865 static const uschar *quote(const struct String *header)
880 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
887 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
891 quoted=string_cat(quoted,&size,&ptr,h,1);
897 quoted=string_cat(quoted,&size,&ptr,CUS "",1);
902 /*************************************************
903 * Add address to list of generated addresses *
904 *************************************************/
907 According to RFC 3028, duplicate delivery to the same address must
908 not happen, so the list is first searched for the address.
911 generated list of generated addresses
912 addr new address to add
913 file address denotes a file
918 static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
920 address_item *new_addr;
922 for (new_addr=*generated; new_addr; new_addr=new_addr->next)
924 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
926 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
928 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
934 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
936 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
938 new_addr=deliver_make_addr(addr,TRUE);
941 setflag(new_addr, af_pfr|af_file);
944 new_addr->p.errors_address = NULL;
945 new_addr->next = *generated;
946 *generated = new_addr;
950 /*************************************************
951 * Return decoded header field *
952 *************************************************/
955 Unfold the header field as described in RFC 2822 and remove all
956 leading and trailing white space, then perform MIME decoding and
957 translate the header field to UTF-8.
960 value returned value of the field
961 header name of the header field
963 Returns: nothing The expanded string is empty
964 in case there is no such header
967 static void expand_header(struct String *value, const struct String *header)
973 value->character=(uschar*)0;
975 t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
976 while (*r==' ' || *r=='\t') ++r;
984 while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
986 value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
990 /*************************************************
991 * Parse remaining hash comment *
992 *************************************************/
996 Comment up to terminating CRLF
999 filter points to the Sieve filter including its state
1005 static int parse_hashcomment(struct Sieve *filter)
1011 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1013 if (*filter->pc=='\n')
1026 filter->errmsg=CUS "missing end of comment";
1031 /*************************************************
1032 * Parse remaining C-style comment *
1033 *************************************************/
1037 Everything up to star slash
1040 filter points to the Sieve filter including its state
1046 static int parse_comment(struct Sieve *filter)
1051 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1058 filter->errmsg=CUS "missing end of comment";
1063 /*************************************************
1064 * Parse optional white space *
1065 *************************************************/
1069 Spaces, tabs, CRLFs, hash comments or C-style comments
1072 filter points to the Sieve filter including its state
1078 static int parse_white(struct Sieve *filter)
1082 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1084 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1086 else if (*filter->pc=='\n')
1096 else if (*filter->pc=='#')
1098 if (parse_hashcomment(filter)==-1) return -1;
1100 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1102 if (parse_comment(filter)==-1) return -1;
1110 /*************************************************
1111 * Parse a optional string *
1112 *************************************************/
1116 quoted-string = DQUOTE *CHAR DQUOTE
1117 ;; in general, \ CHAR inside a string maps to CHAR
1118 ;; so \" maps to " and \\ maps to \
1119 ;; note that newlines and other characters are all allowed
1122 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1123 *(multi-line-literal / multi-line-dotstuff)
1125 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1126 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1127 ;; A line containing only "." ends the multi-line.
1128 ;; Remove a leading '.' if followed by another '.'.
1129 string = quoted-string / multi-line
1132 filter points to the Sieve filter including its state
1133 id specifies identifier to match
1137 0 identifier not matched
1140 static int parse_string(struct Sieve *filter, struct String *data)
1145 data->character=(uschar*)0;
1146 if (*filter->pc=='"') /* quoted string */
1151 if (*filter->pc=='"') /* end of string */
1153 int foo=data->length;
1156 /* that way, there will be at least one character allocated */
1157 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
1160 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1162 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1163 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
1166 else /* regular character */
1169 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1171 if (*filter->pc=='\n')
1173 data->character=string_cat(data->character,&dataCapacity,&data->length,US"\r",1);
1177 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1181 filter->errmsg=CUS "missing end of string";
1184 else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1187 /* skip optional white space followed by hashed comment or CRLF */
1188 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1189 if (*filter->pc=='#')
1191 if (parse_hashcomment(filter)==-1) return -1;
1194 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1196 else if (*filter->pc=='\n')
1208 filter->errmsg=CUS "syntax error";
1214 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1216 if (*filter->pc=='\n') /* end of line */
1219 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1227 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1229 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1232 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);
1244 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1246 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1250 else /* regular character */
1252 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1256 filter->errmsg=CUS "missing end of multi line string";
1263 /*************************************************
1264 * Parse a specific identifier *
1265 *************************************************/
1269 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1272 filter points to the Sieve filter including its state
1273 id specifies identifier to match
1276 0 identifier not matched
1279 static int parse_identifier(struct Sieve *filter, const uschar *id)
1281 size_t idlen=Ustrlen(id);
1283 if (Ustrncmp(filter->pc,id,idlen)==0)
1285 uschar next=filter->pc[idlen];
1287 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1295 /*************************************************
1297 *************************************************/
1301 number = 1*DIGIT [QUANTIFIER]
1302 QUANTIFIER = "K" / "M" / "G"
1305 filter points to the Sieve filter including its state
1309 -1 no string list found
1312 static int parse_number(struct Sieve *filter, unsigned long *data)
1316 if (*filter->pc>='0' && *filter->pc<='9')
1321 d=Ustrtoul(filter->pc,&e,10);
1324 filter->errmsg=CUstrerror(ERANGE);
1329 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1330 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1331 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1332 if (d>(ULONG_MAX/u))
1334 filter->errmsg=CUstrerror(ERANGE);
1343 filter->errmsg=CUS "missing number";
1349 /*************************************************
1350 * Parse a string list *
1351 *************************************************/
1355 string-list = "[" string *("," string) "]" / string
1358 filter points to the Sieve filter including its state
1359 data returns string list
1362 -1 no string list found
1365 static int parse_stringlist(struct Sieve *filter, struct String **data)
1367 const uschar *orig=filter->pc;
1370 struct String *d=(struct String*)0;
1373 if (*filter->pc=='[') /* string list */
1378 if (parse_white(filter)==-1) goto error;
1379 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1382 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1383 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1384 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1386 filter->errmsg=CUstrerror(errno);
1389 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1391 dataCapacity=newCapacity;
1393 m=parse_string(filter,&d[dataLength]);
1396 if (dataLength==0) break;
1399 filter->errmsg=CUS "missing string";
1403 else if (m==-1) goto error;
1405 if (parse_white(filter)==-1) goto error;
1406 if (*filter->pc==',') ++filter->pc;
1409 if (*filter->pc==']')
1411 d[dataLength].character=(uschar*)0;
1412 d[dataLength].length=-1;
1419 filter->errmsg=CUS "missing closing bracket";
1423 else /* single string */
1425 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1429 m=parse_string(filter,&d[0]);
1441 d[1].character=(uschar*)0;
1448 filter->errmsg=CUS "missing string list";
1453 /*************************************************
1454 * Parse an optional address part specifier *
1455 *************************************************/
1459 address-part = ":localpart" / ":domain" / ":all"
1460 address-part =/ ":user" / ":detail"
1463 filter points to the Sieve filter including its state
1464 a returns address part specified
1467 0 no comparator found
1471 static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1474 if (parse_identifier(filter,CUS ":user")==1)
1476 if (!filter->require_subaddress)
1478 filter->errmsg=CUS "missing previous require \"subaddress\";";
1484 else if (parse_identifier(filter,CUS ":detail")==1)
1486 if (!filter->require_subaddress)
1488 filter->errmsg=CUS "missing previous require \"subaddress\";";
1496 if (parse_identifier(filter,CUS ":localpart")==1)
1498 *a=ADDRPART_LOCALPART;
1501 else if (parse_identifier(filter,CUS ":domain")==1)
1506 else if (parse_identifier(filter,CUS ":all")==1)
1515 /*************************************************
1516 * Parse an optional comparator *
1517 *************************************************/
1521 comparator = ":comparator" <comparator-name: string>
1524 filter points to the Sieve filter including its state
1525 c returns comparator
1528 0 no comparator found
1529 -1 incomplete comparator found
1532 static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1534 struct String comparator_name;
1536 if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1537 if (parse_white(filter)==-1) return -1;
1538 switch (parse_string(filter,&comparator_name))
1543 filter->errmsg=CUS "missing comparator";
1550 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1555 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1557 *c=COMP_EN_ASCII_CASEMAP;
1560 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1562 *c=COMP_EN_ASCII_CASEMAP;
1565 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1567 *c=COMP_ASCII_NUMERIC;
1572 filter->errmsg=CUS "invalid comparator";
1581 /*************************************************
1582 * Parse an optional match type *
1583 *************************************************/
1587 match-type = ":is" / ":contains" / ":matches"
1590 filter points to the Sieve filter including its state
1591 m returns match type
1594 0 no match type found
1597 static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1599 if (parse_identifier(filter,CUS ":is")==1)
1604 else if (parse_identifier(filter,CUS ":contains")==1)
1609 else if (parse_identifier(filter,CUS ":matches")==1)
1618 /*************************************************
1619 * Parse and interpret an optional test list *
1620 *************************************************/
1624 test-list = "(" test *("," test) ")"
1627 filter points to the Sieve filter including its state
1628 n total number of tests
1629 true number of passed tests
1630 exec Execute parsed statements
1633 0 no test list found
1634 -1 syntax or execution error
1637 static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1639 if (parse_white(filter)==-1) return -1;
1640 if (*filter->pc=='(')
1649 switch (parse_test(filter,&cond,exec))
1652 case 0: filter->errmsg=CUS "missing test"; return -1;
1653 default: ++*n; if (cond) ++*true; break;
1655 if (parse_white(filter)==-1) return -1;
1656 if (*filter->pc==',') ++filter->pc;
1659 if (*filter->pc==')')
1666 filter->errmsg=CUS "missing closing paren";
1674 /*************************************************
1675 * Parse and interpret an optional test *
1676 *************************************************/
1680 filter points to the Sieve filter including its state
1681 cond returned condition status
1682 exec Execute parsed statements
1686 -1 syntax or execution error
1689 static int parse_test(struct Sieve *filter, int *cond, int exec)
1691 if (parse_white(filter)==-1) return -1;
1692 if (parse_identifier(filter,CUS "address"))
1695 address-test = "address" { [address-part] [comparator] [match-type] }
1696 <header-list: string-list> <key-list: string-list>
1698 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1701 enum AddressPart addressPart=ADDRPART_ALL;
1702 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1703 enum MatchType matchType=MATCH_IS;
1704 struct String *hdr,*h,*key,*k;
1710 if (parse_white(filter)==-1) return -1;
1711 if ((m=parse_addresspart(filter,&addressPart))!=0)
1713 if (m==-1) return -1;
1716 filter->errmsg=CUS "address part already specified";
1721 else if ((m=parse_comparator(filter,&comparator))!=0)
1723 if (m==-1) return -1;
1726 filter->errmsg=CUS "comparator already specified";
1731 else if ((m=parse_matchtype(filter,&matchType))!=0)
1733 if (m==-1) return -1;
1736 filter->errmsg=CUS "match type already specified";
1743 if (parse_white(filter)==-1) return -1;
1744 if ((m=parse_stringlist(filter,&hdr))!=1)
1746 if (m==0) filter->errmsg=CUS "header string list expected";
1749 if (parse_white(filter)==-1) return -1;
1750 if ((m=parse_stringlist(filter,&key))!=1)
1752 if (m==0) filter->errmsg=CUS "key string list expected";
1756 for (h=hdr; h->length!=-1 && !*cond; ++h)
1758 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1762 !eq_asciicase(h,&str_from,0)
1763 && !eq_asciicase(h,&str_to,0)
1764 && !eq_asciicase(h,&str_cc,0)
1765 && !eq_asciicase(h,&str_bcc,0)
1766 && !eq_asciicase(h,&str_sender,0)
1767 && !eq_asciicase(h,&str_resent_from,0)
1768 && !eq_asciicase(h,&str_resent_to,0)
1771 filter->errmsg=CUS "invalid header field";
1776 /* We are only interested in addresses below, so no MIME decoding */
1777 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1778 if (header_value == NULL)
1780 filter->errmsg=CUS "header string expansion failed";
1783 parse_allow_group = TRUE;
1784 while (*header_value && !*cond)
1787 int start, end, domain;
1791 end_addr = parse_find_address_end(header_value, FALSE);
1792 saveend = *end_addr;
1794 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1796 if (extracted_addr) switch (addressPart)
1798 case ADDRPART_ALL: part=extracted_addr; break;
1802 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1803 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1805 case ADDRPART_DETAIL: part=NULL; break;
1809 *end_addr = saveend;
1812 for (k=key; k->length!=-1; ++k)
1814 struct String partStr;
1816 partStr.character=part;
1817 partStr.length=Ustrlen(part);
1820 *cond=compare(filter,k,&partStr,comparator,matchType);
1821 if (*cond==-1) return -1;
1826 if (saveend == 0) break;
1827 header_value = end_addr + 1;
1829 parse_allow_group = FALSE;
1830 parse_found_group = FALSE;
1835 else if (parse_identifier(filter,CUS "allof"))
1838 allof-test = "allof" <tests: test-list>
1843 switch (parse_testlist(filter,&n,&true,exec))
1846 case 0: filter->errmsg=CUS "missing test list"; return -1;
1847 default: *cond=(n==true); return 1;
1850 else if (parse_identifier(filter,CUS "anyof"))
1853 anyof-test = "anyof" <tests: test-list>
1858 switch (parse_testlist(filter,&n,&true,exec))
1861 case 0: filter->errmsg=CUS "missing test list"; return -1;
1862 default: *cond=(true>0); return 1;
1865 else if (parse_identifier(filter,CUS "exists"))
1868 exists-test = "exists" <header-names: string-list>
1871 struct String *hdr,*h;
1874 if (parse_white(filter)==-1) return -1;
1875 if ((m=parse_stringlist(filter,&hdr))!=1)
1877 if (m==0) filter->errmsg=CUS "header string list expected";
1883 for (h=hdr; h->length!=-1 && *cond; ++h)
1887 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1888 if (header_def == NULL)
1890 filter->errmsg=CUS "header string expansion failed";
1893 if (Ustrcmp(header_def,"false")==0) *cond=0;
1898 else if (parse_identifier(filter,CUS "false"))
1901 false-test = "false"
1907 else if (parse_identifier(filter,CUS "header"))
1910 header-test = "header" { [comparator] [match-type] }
1911 <header-names: string-list> <key-list: string-list>
1914 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1915 enum MatchType matchType=MATCH_IS;
1916 struct String *hdr,*h,*key,*k;
1922 if (parse_white(filter)==-1) return -1;
1923 if ((m=parse_comparator(filter,&comparator))!=0)
1925 if (m==-1) return -1;
1928 filter->errmsg=CUS "comparator already specified";
1933 else if ((m=parse_matchtype(filter,&matchType))!=0)
1935 if (m==-1) return -1;
1938 filter->errmsg=CUS "match type already specified";
1945 if (parse_white(filter)==-1) return -1;
1946 if ((m=parse_stringlist(filter,&hdr))!=1)
1948 if (m==0) filter->errmsg=CUS "header string list expected";
1951 if (parse_white(filter)==-1) return -1;
1952 if ((m=parse_stringlist(filter,&key))!=1)
1954 if (m==0) filter->errmsg=CUS "key string list expected";
1958 for (h=hdr; h->length!=-1 && !*cond; ++h)
1962 filter->errmsg=CUS "invalid header field";
1967 struct String header_value;
1970 expand_header(&header_value,h);
1971 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1972 if (header_value.character == NULL || header_def == NULL)
1974 filter->errmsg=CUS "header string expansion failed";
1977 for (k=key; k->length!=-1; ++k)
1979 if (Ustrcmp(header_def,"true")==0)
1981 *cond=compare(filter,k,&header_value,comparator,matchType);
1982 if (*cond==-1) return -1;
1990 else if (parse_identifier(filter,CUS "not"))
1992 if (parse_white(filter)==-1) return -1;
1993 switch (parse_test(filter,cond,exec))
1996 case 0: filter->errmsg=CUS "missing test"; return -1;
1997 default: *cond=!*cond; return 1;
2000 else if (parse_identifier(filter,CUS "size"))
2003 relop = ":over" / ":under"
2004 size-test = "size" relop <limit: number>
2007 unsigned long limit;
2010 if (parse_white(filter)==-1) return -1;
2011 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2012 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2015 filter->errmsg=CUS "missing :over or :under";
2018 if (parse_white(filter)==-1) return -1;
2019 if (parse_number(filter,&limit)==-1) return -1;
2020 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2023 else if (parse_identifier(filter,CUS "true"))
2028 else if (parse_identifier(filter,CUS "envelope"))
2031 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2032 <envelope-part: string-list> <key-list: string-list>
2034 envelope-part is case insensitive "from" or "to"
2035 #ifdef ENVELOPE_AUTH
2036 envelope-part =/ "auth"
2040 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2041 enum AddressPart addressPart=ADDRPART_ALL;
2042 enum MatchType matchType=MATCH_IS;
2043 struct String *env,*e,*key,*k;
2047 if (!filter->require_envelope)
2049 filter->errmsg=CUS "missing previous require \"envelope\";";
2054 if (parse_white(filter)==-1) return -1;
2055 if ((m=parse_comparator(filter,&comparator))!=0)
2057 if (m==-1) return -1;
2060 filter->errmsg=CUS "comparator already specified";
2065 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2067 if (m==-1) return -1;
2070 filter->errmsg=CUS "address part already specified";
2075 else if ((m=parse_matchtype(filter,&matchType))!=0)
2077 if (m==-1) return -1;
2080 filter->errmsg=CUS "match type already specified";
2087 if (parse_white(filter)==-1) return -1;
2088 if ((m=parse_stringlist(filter,&env))!=1)
2090 if (m==0) filter->errmsg=CUS "envelope string list expected";
2093 if (parse_white(filter)==-1) return -1;
2094 if ((m=parse_stringlist(filter,&key))!=1)
2096 if (m==0) filter->errmsg=CUS "key string list expected";
2100 for (e=env; e->length!=-1 && !*cond; ++e)
2102 const uschar *envelopeExpr=CUS 0;
2103 uschar *envelope=US 0;
2105 if (eq_asciicase(e,&str_from,0))
2107 switch (addressPart)
2109 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2113 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2114 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2116 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2120 else if (eq_asciicase(e,&str_to,0))
2122 switch (addressPart)
2124 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2126 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2127 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2129 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2130 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2133 #ifdef ENVELOPE_AUTH
2134 else if (eq_asciicase(e,&str_auth,0))
2136 switch (addressPart)
2138 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2142 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2143 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2145 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2152 filter->errmsg=CUS "invalid envelope string";
2155 if (exec && envelopeExpr)
2157 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2159 filter->errmsg=CUS "header string expansion failed";
2162 for (k=key; k->length!=-1; ++k)
2164 struct String envelopeStr;
2166 envelopeStr.character=envelope;
2167 envelopeStr.length=Ustrlen(envelope);
2168 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2169 if (*cond==-1) return -1;
2180 /*************************************************
2181 * Parse and interpret an optional block *
2182 *************************************************/
2186 filter points to the Sieve filter including its state
2187 exec Execute parsed statements
2188 generated where to hang newly-generated addresses
2190 Returns: 2 success by stop
2192 0 no block command found
2193 -1 syntax or execution error
2196 static int parse_block(struct Sieve *filter, int exec,
2197 address_item **generated)
2201 if (parse_white(filter)==-1) return -1;
2202 if (*filter->pc=='{')
2205 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2206 if (*filter->pc=='}')
2213 filter->errmsg=CUS "expecting command or closing brace";
2221 /*************************************************
2222 * Match a semicolon *
2223 *************************************************/
2227 filter points to the Sieve filter including its state
2233 static int parse_semicolon(struct Sieve *filter)
2235 if (parse_white(filter)==-1) return -1;
2236 if (*filter->pc==';')
2243 filter->errmsg=CUS "missing semicolon";
2249 /*************************************************
2250 * Parse and interpret a Sieve command *
2251 *************************************************/
2255 filter points to the Sieve filter including its state
2256 exec Execute parsed statements
2257 generated where to hang newly-generated addresses
2259 Returns: 2 success by stop
2261 -1 syntax or execution error
2263 static int parse_commands(struct Sieve *filter, int exec,
2264 address_item **generated)
2268 if (parse_white(filter)==-1) return -1;
2269 if (parse_identifier(filter,CUS "if"))
2272 if-command = "if" test block *( "elsif" test block ) [ else block ]
2275 int cond,m,unsuccessful;
2278 if (parse_white(filter)==-1) return -1;
2279 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2282 filter->errmsg=CUS "missing test";
2285 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2286 (debug_selector & D_filter) != 0)
2288 if (exec) debug_printf("if %s\n",cond?"true":"false");
2290 m=parse_block(filter,exec ? cond : 0, generated);
2291 if (m==-1 || m==2) return m;
2294 filter->errmsg=CUS "missing block";
2297 unsuccessful = !cond;
2298 for (;;) /* elsif test block */
2300 if (parse_white(filter)==-1) return -1;
2301 if (parse_identifier(filter,CUS "elsif"))
2303 if (parse_white(filter)==-1) return -1;
2304 m=parse_test(filter,&cond,exec && unsuccessful);
2305 if (m==-1 || m==2) return m;
2308 filter->errmsg=CUS "missing test";
2311 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2312 (debug_selector & D_filter) != 0)
2314 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2316 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2317 if (m==-1 || m==2) return m;
2320 filter->errmsg=CUS "missing block";
2323 if (exec && unsuccessful && cond) unsuccessful = 0;
2328 if (parse_white(filter)==-1) return -1;
2329 if (parse_identifier(filter,CUS "else"))
2331 m=parse_block(filter,exec && unsuccessful, generated);
2332 if (m==-1 || m==2) return m;
2335 filter->errmsg=CUS "missing block";
2340 else if (parse_identifier(filter,CUS "stop"))
2343 stop-command = "stop" { stop-options } ";"
2347 if (parse_semicolon(filter)==-1) return -1;
2350 filter->pc+=Ustrlen(filter->pc);
2354 else if (parse_identifier(filter,CUS "keep"))
2357 keep-command = "keep" { keep-options } ";"
2361 if (parse_semicolon(filter)==-1) return -1;
2364 add_addr(generated,US"inbox",1,0,0,0);
2368 else if (parse_identifier(filter,CUS "discard"))
2371 discard-command = "discard" { discard-options } ";"
2375 if (parse_semicolon(filter)==-1) return -1;
2376 if (exec) filter->keep=0;
2378 else if (parse_identifier(filter,CUS "redirect"))
2381 redirect-command = "redirect" redirect-options "string" ";"
2383 redirect-options =) ":copy"
2386 struct String recipient;
2392 if (parse_white(filter)==-1) return -1;
2393 if (parse_identifier(filter,CUS ":copy")==1)
2395 if (!filter->require_copy)
2397 filter->errmsg=CUS "missing previous require \"copy\";";
2404 if (parse_white(filter)==-1) return -1;
2405 if ((m=parse_string(filter,&recipient))!=1)
2407 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2410 if (strchr(CCS recipient.character,'@')==(char*)0)
2412 filter->errmsg=CUS "unqualified recipient address";
2417 add_addr(generated,recipient.character,0,0,0,0);
2418 if (!copy) filter->keep = 0;
2420 if (parse_semicolon(filter)==-1) return -1;
2422 else if (parse_identifier(filter,CUS "fileinto"))
2425 fileinto-command = "fileinto" { fileinto-options } string ";"
2427 fileinto-options =) [ ":copy" ]
2430 struct String folder;
2433 unsigned long maxage, maxmessages, maxstorage;
2436 maxage = maxmessages = maxstorage = 0;
2437 if (!filter->require_fileinto)
2439 filter->errmsg=CUS "missing previous require \"fileinto\";";
2444 if (parse_white(filter)==-1) return -1;
2445 if (parse_identifier(filter,CUS ":copy")==1)
2447 if (!filter->require_copy)
2449 filter->errmsg=CUS "missing previous require \"copy\";";
2456 if (parse_white(filter)==-1) return -1;
2457 if ((m=parse_string(filter,&folder))!=1)
2459 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2462 m=0; s=folder.character;
2463 if (folder.length==0) m=1;
2464 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2467 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2472 filter->errmsg=CUS "invalid folder";
2477 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2478 if (!copy) filter->keep = 0;
2480 if (parse_semicolon(filter)==-1) return -1;
2483 else if (parse_identifier(filter,CUS "notify"))
2486 notify-command = "notify" { notify-options } ";"
2487 notify-options = [":method" string]
2488 [":priority" string]
2493 struct String method;
2494 struct String priority;
2495 struct String message;
2496 struct Notification *already;
2497 string_item *recipient;
2499 uschar *envelope_from,*envelope_to;
2501 if (!filter->require_notify)
2503 filter->errmsg=CUS "missing previous require \"notify\";";
2506 method.character=(uschar*)0;
2508 priority.character=(uschar*)0;
2510 message.character=(uschar*)0;
2514 body.character=(uschar*)0;
2515 envelope_from=expand_string("$sender_address");
2516 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2519 if (parse_white(filter)==-1) return -1;
2520 if (parse_identifier(filter,CUS ":method")==1)
2522 if (parse_white(filter)==-1) return -1;
2523 if ((m=parse_string(filter,&method))!=1)
2525 if (m==0) filter->errmsg=CUS "method string expected";
2528 if (method.length>7 && strncmp(method.character,"mailto:",7)==0)
2530 if (parse_mailto_uri(filter,method.character+7,&recipient,&body)==-1) return -1;
2533 else if (parse_identifier(filter,CUS ":priority")==1)
2535 if (parse_white(filter)==-1) return -1;
2536 if ((m=parse_string(filter,&priority))!=1)
2538 if (m==0) filter->errmsg=CUS "priority string expected";
2542 else if (parse_identifier(filter,CUS ":message")==1)
2544 if (parse_white(filter)==-1) return -1;
2545 if ((m=parse_string(filter,&message))!=1)
2547 if (m==0) filter->errmsg=CUS "message string expected";
2553 if (parse_semicolon(filter)==-1) return -1;
2555 if (method.length==-1)
2557 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2559 debug_printf("Ignoring method-less notification.\n");
2564 for (already=filter->notified; already; already=already->next)
2566 if (already->method.length==method.length
2567 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
2568 && already->priority.length==priority.length
2569 && (priority.length==-1 || strcmp(already->priority.character,priority.character)==0)
2570 && already->message.length==message.length
2571 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
2574 if (already==(struct Notification*)0)
2575 /* New notification, process it */
2577 struct Notification *sent;
2578 sent=store_get(sizeof(struct Notification));
2579 sent->method=method;
2580 sent->priority=priority;
2581 sent->message=message;
2582 sent->next=filter->notified;
2583 filter->notified=sent;
2584 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2586 debug_printf("Notification to `%s'.\n",method.character);
2588 #ifndef COMPILE_SYNTAX_CHECKER
2595 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
2599 f = fdopen(fd, "wb");
2600 fprintf(f,"From: %s\n",envelope_to);
2601 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
2602 for (h = header_list; h != NULL; h = h->next)
2603 if (h->type == htype_received) fprintf(f,"%s",h->text);
2604 fprintf(f,"Subject: %s\n",message.length==-1 ? CUS "notification" : message.character);
2606 if (body.length>0) fprintf(f,"%s\n",body.character);
2609 (void)child_close(pid, 0);
2616 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2618 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
2625 else if (parse_identifier(filter,CUS "vacation"))
2628 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2629 vacation-options = [":days" number]
2632 [":addresses" string-list]
2639 struct String subject;
2641 struct String *addresses;
2643 string_item *aliases;
2644 struct String handle;
2645 struct String reason;
2647 if (!filter->require_vacation)
2649 filter->errmsg=CUS "missing previous require \"vacation\";";
2654 if (filter->vacation_ran)
2656 filter->errmsg=CUS "trying to execute vacation more than once";
2659 filter->vacation_ran=1;
2661 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2662 subject.character=(uschar*)0;
2664 from.character=(uschar*)0;
2666 addresses=(struct String*)0;
2669 handle.character=(uschar*)0;
2673 if (parse_white(filter)==-1) return -1;
2674 if (parse_identifier(filter,CUS ":days")==1)
2676 if (parse_white(filter)==-1) return -1;
2677 if (parse_number(filter,&days)==-1) return -1;
2678 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2679 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2681 else if (parse_identifier(filter,CUS ":subject")==1)
2683 if (parse_white(filter)==-1) return -1;
2684 if ((m=parse_string(filter,&subject))!=1)
2686 if (m==0) filter->errmsg=CUS "subject string expected";
2690 else if (parse_identifier(filter,CUS ":from")==1)
2692 int start, end, domain;
2695 if (parse_white(filter)==-1) return -1;
2696 if ((m=parse_string(filter,&from))!=1)
2698 if (m==0) filter->errmsg=CUS "from string expected";
2703 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2707 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2708 "Sieve filter: %s", from.character, error);
2714 filter->errmsg=CUS "empty :from address in Sieve filter";
2718 else if (parse_identifier(filter,CUS ":addresses")==1)
2722 if (parse_white(filter)==-1) return -1;
2723 if ((m=parse_stringlist(filter,&addresses))!=1)
2725 if (m==0) filter->errmsg=CUS "addresses string list expected";
2728 for (a=addresses; a->length!=-1; ++a)
2732 new=store_get(sizeof(string_item));
2733 new->text=store_get(a->length+1);
2734 if (a->length) memcpy(new->text,a->character,a->length);
2735 new->text[a->length]='\0';
2740 else if (parse_identifier(filter,CUS ":mime")==1)
2742 else if (parse_identifier(filter,CUS ":handle")==1)
2744 if (parse_white(filter)==-1) return -1;
2745 if ((m=parse_string(filter,&from))!=1)
2747 if (m==0) filter->errmsg=CUS "handle string expected";
2753 if (parse_white(filter)==-1) return -1;
2754 if ((m=parse_string(filter,&reason))!=1)
2756 if (m==0) filter->errmsg=CUS "missing reason string";
2763 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2766 filter->errmsg=CUS "MIME reason string contains 8bit text";
2770 if (parse_semicolon(filter)==-1) return -1;
2777 int buffer_capacity;
2781 uschar hexdigest[33];
2785 if (filter_personal(aliases,TRUE))
2787 if (filter_test == FTEST_NONE)
2789 /* ensure oncelog directory exists; failure will be detected later */
2791 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2793 /* build oncelog filename */
2795 key.character=(uschar*)0;
2798 if (handle.length==-1)
2800 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2801 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2802 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2803 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2808 md5_end(&base, key.character, key.length, digest);
2809 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2810 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2812 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2814 if (filter_test == FTEST_NONE)
2816 capacity=Ustrlen(filter->vacation_directory);
2818 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2819 once=string_cat(once,&capacity,&start,hexdigest,33);
2822 /* process subject */
2824 if (subject.length==-1)
2826 uschar *subject_def;
2828 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
2829 if (Ustrcmp(subject_def,"true")==0)
2831 expand_header(&subject,&str_subject);
2834 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2835 subject.length=start;
2839 subject.character=US"Automated reply";
2840 subject.length=Ustrlen(subject.character);
2844 /* add address to list of generated addresses */
2846 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2847 setflag(addr, af_pfr);
2848 setflag(addr, af_ignore_error);
2849 addr->next = *generated;
2851 addr->reply = store_get(sizeof(reply_item));
2852 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2853 addr->reply->to = string_copy(sender_address);
2854 if (from.length==-1)
2855 addr->reply->from = expand_string(US"$local_part@$domain");
2857 addr->reply->from = from.character;
2858 /* Allocation is larger than neccessary, but enough even for split MIME words */
2859 buffer_capacity=32+4*subject.length;
2860 buffer=store_get(buffer_capacity);
2861 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
2862 addr->reply->oncelog=once;
2863 addr->reply->once_repeat=days*86400;
2865 /* build body and MIME headers */
2869 uschar *mime_body,*reason_end;
2870 static const uschar nlnl[]="\r\n\r\n";
2874 mime_body=reason.character,reason_end=reason.character+reason.length;
2875 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
2880 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2881 addr->reply->headers[start] = '\0';
2884 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
2885 else mime_body=reason_end-1;
2886 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2887 addr->reply->text[start] = '\0';
2891 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
2894 start = reason.length;
2895 addr->reply->headers = US"MIME-Version: 1.0\n"
2896 "Content-Type: text/plain;\n"
2897 "\tcharset=\"utf-8\"\n"
2898 "Content-Transfer-Encoding: quoted-printable";
2899 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2903 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2905 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
2916 /*************************************************
2917 * Parse and interpret a sieve filter *
2918 *************************************************/
2922 filter points to the Sieve filter including its state
2923 exec Execute parsed statements
2924 generated where to hang newly-generated addresses
2927 -1 syntax or execution error
2930 static int parse_start(struct Sieve *filter, int exec,
2931 address_item **generated)
2933 filter->pc=filter->filter;
2936 filter->require_envelope=0;
2937 filter->require_fileinto=0;
2938 #ifdef ENVELOPE_AUTH
2939 filter->require_envelope_auth=0;
2942 filter->require_notify=0;
2943 filter->notified=(struct Notification*)0;
2946 filter->require_subaddress=0;
2949 filter->require_vacation=0;
2950 filter->vacation_ran=0;
2952 filter->require_copy=0;
2953 filter->require_iascii_numeric=0;
2955 if (parse_white(filter)==-1) return -1;
2957 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
2960 struct dirent *oncelog;
2961 struct stat properties;
2964 /* clean up old vacation log databases */
2966 oncelogdir=opendir(CS filter->vacation_directory);
2968 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2970 filter->errmsg=CUS "unable to open vacation directory";
2974 if (oncelogdir != NULL)
2978 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2980 if (strlen(oncelog->d_name)==32)
2982 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2983 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2987 closedir(oncelogdir);
2991 while (parse_identifier(filter,CUS "require"))
2994 require-command = "require" <capabilities: string-list>
2997 struct String *cap,*check;
3000 if (parse_white(filter)==-1) return -1;
3001 if ((m=parse_stringlist(filter,&cap))!=1)
3003 if (m==0) filter->errmsg=CUS "capability string list expected";
3006 for (check=cap; check->character; ++check)
3008 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3009 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3010 #ifdef ENVELOPE_AUTH
3011 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3014 else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
3017 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3020 else if (eq_octet(check,&str_vacation,0))
3022 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3024 filter->errmsg=CUS "vacation disabled";
3027 filter->require_vacation=1;
3030 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3031 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3032 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3033 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3034 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3037 filter->errmsg=CUS "unknown capability";
3041 if (parse_semicolon(filter)==-1) return -1;
3043 if (parse_commands(filter,exec,generated)==-1) return -1;
3046 filter->errmsg=CUS "syntax error";
3053 /*************************************************
3054 * Interpret a sieve filter file *
3055 *************************************************/
3059 filter points to the entire file, read into store as a single string
3060 options controls whether various special things are allowed, and requests
3061 special actions (not currently used)
3062 sieve_vacation_directory where to store vacation "once" files
3063 useraddress string expression for :user part of address
3064 subaddress string expression for :subaddress part of address
3065 generated where to hang newly-generated addresses
3066 error where to pass back an error text
3068 Returns: FF_DELIVERED success, a significant action was taken
3069 FF_NOTDELIVERED success, no significant action
3070 FF_DEFER defer requested
3071 FF_FAIL fail requested
3072 FF_FREEZE freeze requested
3073 FF_ERROR there was a problem
3077 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3078 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3084 options = options; /* Keep picky compilers happy */
3087 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3088 sieve.filter=filter;
3090 if (vacation_directory == NULL)
3091 sieve.vacation_directory = NULL;
3094 sieve.vacation_directory=expand_string(vacation_directory);
3095 if (sieve.vacation_directory == NULL)
3097 *error = string_sprintf("failed to expand \"%s\" "
3098 "(sieve_vacation_directory): %s", vacation_directory,
3099 expand_string_message);
3104 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3105 sieve.subaddress = subaddress;
3107 #ifdef COMPILE_SYNTAX_CHECKER
3108 if (parse_start(&sieve,0,generated)==1)
3110 if (parse_start(&sieve,1,generated)==1)
3115 add_addr(generated,US"inbox",1,0,0,0);
3116 msg = string_sprintf("Implicit keep");
3121 msg = string_sprintf("No implicit keep");
3127 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3128 #ifdef COMPILE_SYNTAX_CHECKER
3132 add_addr(generated,US"inbox",1,0,0,0);
3137 #ifndef COMPILE_SYNTAX_CHECKER
3138 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3139 else debug_printf("%s\n", msg);
3142 DEBUG(D_route) debug_printf("Sieve: end of processing\n");