1 /* $Cambridge: exim/src/src/sieve.c,v 1.18 2006/03/01 10:40:03 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(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(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;
1833 else if (parse_identifier(filter,CUS "allof"))
1836 allof-test = "allof" <tests: test-list>
1841 switch (parse_testlist(filter,&n,&true,exec))
1844 case 0: filter->errmsg=CUS "missing test list"; return -1;
1845 default: *cond=(n==true); return 1;
1848 else if (parse_identifier(filter,CUS "anyof"))
1851 anyof-test = "anyof" <tests: test-list>
1856 switch (parse_testlist(filter,&n,&true,exec))
1859 case 0: filter->errmsg=CUS "missing test list"; return -1;
1860 default: *cond=(true>0); return 1;
1863 else if (parse_identifier(filter,CUS "exists"))
1866 exists-test = "exists" <header-names: string-list>
1869 struct String *hdr,*h;
1872 if (parse_white(filter)==-1) return -1;
1873 if ((m=parse_stringlist(filter,&hdr))!=1)
1875 if (m==0) filter->errmsg=CUS "header string list expected";
1881 for (h=hdr; h->length!=-1 && *cond; ++h)
1885 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1886 if (header_def == NULL)
1888 filter->errmsg=CUS "header string expansion failed";
1891 if (Ustrcmp(header_def,"false")==0) *cond=0;
1896 else if (parse_identifier(filter,CUS "false"))
1899 false-test = "false"
1905 else if (parse_identifier(filter,CUS "header"))
1908 header-test = "header" { [comparator] [match-type] }
1909 <header-names: string-list> <key-list: string-list>
1912 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
1913 enum MatchType matchType=MATCH_IS;
1914 struct String *hdr,*h,*key,*k;
1920 if (parse_white(filter)==-1) return -1;
1921 if ((m=parse_comparator(filter,&comparator))!=0)
1923 if (m==-1) return -1;
1926 filter->errmsg=CUS "comparator already specified";
1931 else if ((m=parse_matchtype(filter,&matchType))!=0)
1933 if (m==-1) return -1;
1936 filter->errmsg=CUS "match type already specified";
1943 if (parse_white(filter)==-1) return -1;
1944 if ((m=parse_stringlist(filter,&hdr))!=1)
1946 if (m==0) filter->errmsg=CUS "header string list expected";
1949 if (parse_white(filter)==-1) return -1;
1950 if ((m=parse_stringlist(filter,&key))!=1)
1952 if (m==0) filter->errmsg=CUS "key string list expected";
1956 for (h=hdr; h->length!=-1 && !*cond; ++h)
1960 filter->errmsg=CUS "invalid header field";
1965 struct String header_value;
1968 expand_header(&header_value,h);
1969 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1970 if (header_value.character == NULL || header_def == NULL)
1972 filter->errmsg=CUS "header string expansion failed";
1975 for (k=key; k->length!=-1; ++k)
1977 if (Ustrcmp(header_def,"true")==0)
1979 *cond=compare(filter,k,&header_value,comparator,matchType);
1980 if (*cond==-1) return -1;
1988 else if (parse_identifier(filter,CUS "not"))
1990 if (parse_white(filter)==-1) return -1;
1991 switch (parse_test(filter,cond,exec))
1994 case 0: filter->errmsg=CUS "missing test"; return -1;
1995 default: *cond=!*cond; return 1;
1998 else if (parse_identifier(filter,CUS "size"))
2001 relop = ":over" / ":under"
2002 size-test = "size" relop <limit: number>
2005 unsigned long limit;
2008 if (parse_white(filter)==-1) return -1;
2009 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2010 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2013 filter->errmsg=CUS "missing :over or :under";
2016 if (parse_white(filter)==-1) return -1;
2017 if (parse_number(filter,&limit)==-1) return -1;
2018 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2021 else if (parse_identifier(filter,CUS "true"))
2026 else if (parse_identifier(filter,CUS "envelope"))
2029 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2030 <envelope-part: string-list> <key-list: string-list>
2032 envelope-part is case insensitive "from" or "to"
2033 #ifdef ENVELOPE_AUTH
2034 envelope-part =/ "auth"
2038 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2039 enum AddressPart addressPart=ADDRPART_ALL;
2040 enum MatchType matchType=MATCH_IS;
2041 struct String *env,*e,*key,*k;
2045 if (!filter->require_envelope)
2047 filter->errmsg=CUS "missing previous require \"envelope\";";
2052 if (parse_white(filter)==-1) return -1;
2053 if ((m=parse_comparator(filter,&comparator))!=0)
2055 if (m==-1) return -1;
2058 filter->errmsg=CUS "comparator already specified";
2063 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2065 if (m==-1) return -1;
2068 filter->errmsg=CUS "address part already specified";
2073 else if ((m=parse_matchtype(filter,&matchType))!=0)
2075 if (m==-1) return -1;
2078 filter->errmsg=CUS "match type already specified";
2085 if (parse_white(filter)==-1) return -1;
2086 if ((m=parse_stringlist(filter,&env))!=1)
2088 if (m==0) filter->errmsg=CUS "envelope string list expected";
2091 if (parse_white(filter)==-1) return -1;
2092 if ((m=parse_stringlist(filter,&key))!=1)
2094 if (m==0) filter->errmsg=CUS "key string list expected";
2098 for (e=env; e->length!=-1 && !*cond; ++e)
2100 const uschar *envelopeExpr=CUS 0;
2101 uschar *envelope=US 0;
2103 if (eq_asciicase(e,&str_from,0))
2105 switch (addressPart)
2107 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2111 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2112 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2114 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2118 else if (eq_asciicase(e,&str_to,0))
2120 switch (addressPart)
2122 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2124 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2125 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
2127 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2128 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2131 #ifdef ENVELOPE_AUTH
2132 else if (eq_asciicase(e,&str_auth,0))
2134 switch (addressPart)
2136 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2140 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2141 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2143 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2150 filter->errmsg=CUS "invalid envelope string";
2153 if (exec && envelopeExpr)
2155 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2157 filter->errmsg=CUS "header string expansion failed";
2160 for (k=key; k->length!=-1; ++k)
2162 struct String envelopeStr;
2164 envelopeStr.character=envelope;
2165 envelopeStr.length=Ustrlen(envelope);
2166 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
2167 if (*cond==-1) return -1;
2178 /*************************************************
2179 * Parse and interpret an optional block *
2180 *************************************************/
2184 filter points to the Sieve filter including its state
2185 exec Execute parsed statements
2186 generated where to hang newly-generated addresses
2188 Returns: 2 success by stop
2190 0 no block command found
2191 -1 syntax or execution error
2194 static int parse_block(struct Sieve *filter, int exec,
2195 address_item **generated)
2199 if (parse_white(filter)==-1) return -1;
2200 if (*filter->pc=='{')
2203 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2204 if (*filter->pc=='}')
2211 filter->errmsg=CUS "expecting command or closing brace";
2219 /*************************************************
2220 * Match a semicolon *
2221 *************************************************/
2225 filter points to the Sieve filter including its state
2231 static int parse_semicolon(struct Sieve *filter)
2233 if (parse_white(filter)==-1) return -1;
2234 if (*filter->pc==';')
2241 filter->errmsg=CUS "missing semicolon";
2247 /*************************************************
2248 * Parse and interpret a Sieve command *
2249 *************************************************/
2253 filter points to the Sieve filter including its state
2254 exec Execute parsed statements
2255 generated where to hang newly-generated addresses
2257 Returns: 2 success by stop
2259 -1 syntax or execution error
2261 static int parse_commands(struct Sieve *filter, int exec,
2262 address_item **generated)
2266 if (parse_white(filter)==-1) return -1;
2267 if (parse_identifier(filter,CUS "if"))
2270 if-command = "if" test block *( "elsif" test block ) [ else block ]
2273 int cond,m,unsuccessful;
2276 if (parse_white(filter)==-1) return -1;
2277 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2280 filter->errmsg=CUS "missing test";
2283 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2284 (debug_selector & D_filter) != 0)
2286 if (exec) debug_printf("if %s\n",cond?"true":"false");
2288 m=parse_block(filter,exec ? cond : 0, generated);
2289 if (m==-1 || m==2) return m;
2292 filter->errmsg=CUS "missing block";
2295 unsuccessful = !cond;
2296 for (;;) /* elsif test block */
2298 if (parse_white(filter)==-1) return -1;
2299 if (parse_identifier(filter,CUS "elsif"))
2301 if (parse_white(filter)==-1) return -1;
2302 m=parse_test(filter,&cond,exec && unsuccessful);
2303 if (m==-1 || m==2) return m;
2306 filter->errmsg=CUS "missing test";
2309 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2310 (debug_selector & D_filter) != 0)
2312 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2314 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2315 if (m==-1 || m==2) return m;
2318 filter->errmsg=CUS "missing block";
2321 if (exec && unsuccessful && cond) unsuccessful = 0;
2326 if (parse_white(filter)==-1) return -1;
2327 if (parse_identifier(filter,CUS "else"))
2329 m=parse_block(filter,exec && unsuccessful, generated);
2330 if (m==-1 || m==2) return m;
2333 filter->errmsg=CUS "missing block";
2338 else if (parse_identifier(filter,CUS "stop"))
2341 stop-command = "stop" { stop-options } ";"
2345 if (parse_semicolon(filter)==-1) return -1;
2348 filter->pc+=Ustrlen(filter->pc);
2352 else if (parse_identifier(filter,CUS "keep"))
2355 keep-command = "keep" { keep-options } ";"
2359 if (parse_semicolon(filter)==-1) return -1;
2362 add_addr(generated,US"inbox",1,0,0,0);
2366 else if (parse_identifier(filter,CUS "discard"))
2369 discard-command = "discard" { discard-options } ";"
2373 if (parse_semicolon(filter)==-1) return -1;
2374 if (exec) filter->keep=0;
2376 else if (parse_identifier(filter,CUS "redirect"))
2379 redirect-command = "redirect" redirect-options "string" ";"
2381 redirect-options =) ":copy"
2384 struct String recipient;
2390 if (parse_white(filter)==-1) return -1;
2391 if (parse_identifier(filter,CUS ":copy")==1)
2393 if (!filter->require_copy)
2395 filter->errmsg=CUS "missing previous require \"copy\";";
2402 if (parse_white(filter)==-1) return -1;
2403 if ((m=parse_string(filter,&recipient))!=1)
2405 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2408 if (strchr(CCS recipient.character,'@')==(char*)0)
2410 filter->errmsg=CUS "unqualified recipient address";
2415 add_addr(generated,recipient.character,0,0,0,0);
2416 if (!copy) filter->keep = 0;
2418 if (parse_semicolon(filter)==-1) return -1;
2420 else if (parse_identifier(filter,CUS "fileinto"))
2423 fileinto-command = "fileinto" { fileinto-options } string ";"
2425 fileinto-options =) [ ":copy" ]
2428 struct String folder;
2431 unsigned long maxage, maxmessages, maxstorage;
2434 maxage = maxmessages = maxstorage = 0;
2435 if (!filter->require_fileinto)
2437 filter->errmsg=CUS "missing previous require \"fileinto\";";
2442 if (parse_white(filter)==-1) return -1;
2443 if (parse_identifier(filter,CUS ":copy")==1)
2445 if (!filter->require_copy)
2447 filter->errmsg=CUS "missing previous require \"copy\";";
2454 if (parse_white(filter)==-1) return -1;
2455 if ((m=parse_string(filter,&folder))!=1)
2457 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2460 m=0; s=folder.character;
2461 if (folder.length==0) m=1;
2462 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2465 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2470 filter->errmsg=CUS "invalid folder";
2475 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2476 if (!copy) filter->keep = 0;
2478 if (parse_semicolon(filter)==-1) return -1;
2481 else if (parse_identifier(filter,CUS "notify"))
2484 notify-command = "notify" { notify-options } ";"
2485 notify-options = [":method" string]
2486 [":priority" string]
2491 struct String method;
2492 struct String priority;
2493 struct String message;
2494 struct Notification *already;
2495 string_item *recipient;
2497 uschar *envelope_from,*envelope_to;
2499 if (!filter->require_notify)
2501 filter->errmsg=CUS "missing previous require \"notify\";";
2504 method.character=(uschar*)0;
2506 priority.character=(uschar*)0;
2508 message.character=(uschar*)0;
2512 body.character=(uschar*)0;
2513 envelope_from=expand_string("$sender_address");
2514 envelope_to=expand_string("$local_part_prefix$local_part$local_part_suffix@$domain");
2517 if (parse_white(filter)==-1) return -1;
2518 if (parse_identifier(filter,CUS ":method")==1)
2520 if (parse_white(filter)==-1) return -1;
2521 if ((m=parse_string(filter,&method))!=1)
2523 if (m==0) filter->errmsg=CUS "method string expected";
2526 if (method.length>7 && strncmp(method.character,"mailto:",7)==0)
2528 if (parse_mailto_uri(filter,method.character+7,&recipient,&body)==-1) return -1;
2531 else if (parse_identifier(filter,CUS ":priority")==1)
2533 if (parse_white(filter)==-1) return -1;
2534 if ((m=parse_string(filter,&priority))!=1)
2536 if (m==0) filter->errmsg=CUS "priority string expected";
2540 else if (parse_identifier(filter,CUS ":message")==1)
2542 if (parse_white(filter)==-1) return -1;
2543 if ((m=parse_string(filter,&message))!=1)
2545 if (m==0) filter->errmsg=CUS "message string expected";
2551 if (parse_semicolon(filter)==-1) return -1;
2553 if (method.length==-1)
2555 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2557 debug_printf("Ignoring method-less notification.\n");
2562 for (already=filter->notified; already; already=already->next)
2564 if (already->method.length==method.length
2565 && (method.length==-1 || strcmp(already->method.character,method.character)==0)
2566 && already->priority.length==priority.length
2567 && (priority.length==-1 || strcmp(already->priority.character,priority.character)==0)
2568 && already->message.length==message.length
2569 && (message.length==-1 || strcmp(already->message.character,message.character)==0))
2572 if (already==(struct Notification*)0)
2573 /* New notification, process it */
2575 struct Notification *sent;
2576 sent=store_get(sizeof(struct Notification));
2577 sent->method=method;
2578 sent->priority=priority;
2579 sent->message=message;
2580 sent->next=filter->notified;
2581 filter->notified=sent;
2582 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2584 debug_printf("Notification to `%s'.\n",method.character);
2592 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
2596 f = fdopen(fd, "wb");
2597 fprintf(f,"From: %s\n",envelope_to);
2598 for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
2599 for (h = header_list; h != NULL; h = h->next)
2600 if (h->type == htype_received) fprintf(f,"%s",h->text);
2601 fprintf(f,"Subject: %s\n",message.length==-1 ? CUS "notification" : message.character);
2603 if (body.length>0) fprintf(f,"%s\n",body.character);
2606 (void)child_close(pid, 0);
2612 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2614 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
2621 else if (parse_identifier(filter,CUS "vacation"))
2624 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2625 vacation-options = [":days" number]
2628 [":addresses" string-list]
2635 struct String subject;
2637 struct String *addresses;
2639 string_item *aliases;
2640 struct String handle;
2641 struct String reason;
2643 if (!filter->require_vacation)
2645 filter->errmsg=CUS "missing previous require \"vacation\";";
2650 if (filter->vacation_ran)
2652 filter->errmsg=CUS "trying to execute vacation more than once";
2655 filter->vacation_ran=1;
2657 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2658 subject.character=(uschar*)0;
2660 from.character=(uschar*)0;
2662 addresses=(struct String*)0;
2665 handle.character=(uschar*)0;
2669 if (parse_white(filter)==-1) return -1;
2670 if (parse_identifier(filter,CUS ":days")==1)
2672 if (parse_white(filter)==-1) return -1;
2673 if (parse_number(filter,&days)==-1) return -1;
2674 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2675 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2677 else if (parse_identifier(filter,CUS ":subject")==1)
2679 if (parse_white(filter)==-1) return -1;
2680 if ((m=parse_string(filter,&subject))!=1)
2682 if (m==0) filter->errmsg=CUS "subject string expected";
2686 else if (parse_identifier(filter,CUS ":from")==1)
2688 int start, end, domain;
2691 if (parse_white(filter)==-1) return -1;
2692 if ((m=parse_string(filter,&from))!=1)
2694 if (m==0) filter->errmsg=CUS "from string expected";
2699 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2703 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2704 "Sieve filter: %s", from.character, error);
2710 filter->errmsg=CUS "empty :from address in Sieve filter";
2714 else if (parse_identifier(filter,CUS ":addresses")==1)
2718 if (parse_white(filter)==-1) return -1;
2719 if ((m=parse_stringlist(filter,&addresses))!=1)
2721 if (m==0) filter->errmsg=CUS "addresses string list expected";
2724 for (a=addresses; a->length!=-1; ++a)
2728 new=store_get(sizeof(string_item));
2729 new->text=store_get(a->length+1);
2730 if (a->length) memcpy(new->text,a->character,a->length);
2731 new->text[a->length]='\0';
2736 else if (parse_identifier(filter,CUS ":mime")==1)
2738 else if (parse_identifier(filter,CUS ":handle")==1)
2740 if (parse_white(filter)==-1) return -1;
2741 if ((m=parse_string(filter,&from))!=1)
2743 if (m==0) filter->errmsg=CUS "handle string expected";
2749 if (parse_white(filter)==-1) return -1;
2750 if ((m=parse_string(filter,&reason))!=1)
2752 if (m==0) filter->errmsg=CUS "missing reason string";
2759 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2762 filter->errmsg=CUS "MIME reason string contains 8bit text";
2766 if (parse_semicolon(filter)==-1) return -1;
2773 int buffer_capacity;
2777 uschar hexdigest[33];
2781 if (filter_personal(aliases,TRUE))
2783 if (filter_test == FTEST_NONE)
2785 /* ensure oncelog directory exists; failure will be detected later */
2787 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2789 /* build oncelog filename */
2791 key.character=(uschar*)0;
2794 if (handle.length==-1)
2796 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2797 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2798 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2799 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
2804 md5_end(&base, key.character, key.length, digest);
2805 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
2806 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2808 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
2810 if (filter_test == FTEST_NONE)
2812 capacity=Ustrlen(filter->vacation_directory);
2814 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2815 once=string_cat(once,&capacity,&start,hexdigest,33);
2818 /* process subject */
2820 if (subject.length==-1)
2822 uschar *subject_def;
2824 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
2825 if (Ustrcmp(subject_def,"true")==0)
2827 expand_header(&subject,&str_subject);
2830 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2831 subject.length=start;
2835 subject.character=US"Automated reply";
2836 subject.length=Ustrlen(subject.character);
2840 /* add address to list of generated addresses */
2842 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2843 setflag(addr, af_pfr);
2844 setflag(addr, af_ignore_error);
2845 addr->next = *generated;
2847 addr->reply = store_get(sizeof(reply_item));
2848 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2849 addr->reply->to = string_copy(sender_address);
2850 if (from.length==-1)
2851 addr->reply->from = expand_string(US"$local_part@$domain");
2853 addr->reply->from = from.character;
2854 /* Allocation is larger than neccessary, but enough even for split MIME words */
2855 buffer_capacity=32+4*subject.length;
2856 buffer=store_get(buffer_capacity);
2857 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2858 addr->reply->oncelog=once;
2859 addr->reply->once_repeat=days*86400;
2861 /* build body and MIME headers */
2865 uschar *mime_body,*reason_end;
2866 static const uschar nlnl[]="\r\n\r\n";
2870 mime_body=reason.character,reason_end=reason.character+reason.length;
2871 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
2876 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2877 addr->reply->headers[start] = '\0';
2880 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
2881 else mime_body=reason_end-1;
2882 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2883 addr->reply->text[start] = '\0';
2890 start = reason.length;
2891 addr->reply->headers = US"MIME-Version: 1.0\n"
2892 "Content-Type: text/plain;\n"
2893 "\tcharset=\"utf-8\"\n"
2894 "Content-Transfer-Encoding: quoted-printable";
2895 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2899 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
2901 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
2912 /*************************************************
2913 * Parse and interpret a sieve filter *
2914 *************************************************/
2918 filter points to the Sieve filter including its state
2919 exec Execute parsed statements
2920 generated where to hang newly-generated addresses
2923 -1 syntax or execution error
2926 static int parse_start(struct Sieve *filter, int exec,
2927 address_item **generated)
2929 filter->pc=filter->filter;
2932 filter->require_envelope=0;
2933 filter->require_fileinto=0;
2934 #ifdef ENVELOPE_AUTH
2935 filter->require_envelope_auth=0;
2938 filter->require_notify=0;
2939 filter->notified=(struct Notification*)0;
2942 filter->require_subaddress=0;
2945 filter->require_vacation=0;
2946 filter->vacation_ran=0;
2948 filter->require_copy=0;
2949 filter->require_iascii_numeric=0;
2951 if (parse_white(filter)==-1) return -1;
2953 if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
2956 struct dirent *oncelog;
2957 struct stat properties;
2960 /* clean up old vacation log databases */
2962 oncelogdir=opendir(CS filter->vacation_directory);
2964 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2966 filter->errmsg=CUS "unable to open vacation directory";
2970 if (oncelogdir != NULL)
2974 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2976 if (strlen(oncelog->d_name)==32)
2978 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2979 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2983 closedir(oncelogdir);
2987 while (parse_identifier(filter,CUS "require"))
2990 require-command = "require" <capabilities: string-list>
2993 struct String *cap,*check;
2996 if (parse_white(filter)==-1) return -1;
2997 if ((m=parse_stringlist(filter,&cap))!=1)
2999 if (m==0) filter->errmsg=CUS "capability string list expected";
3002 for (check=cap; check->character; ++check)
3004 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3005 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
3006 #ifdef ENVELOPE_AUTH
3007 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3010 else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
3013 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
3016 else if (eq_octet(check,&str_vacation,0))
3018 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
3020 filter->errmsg=CUS "vacation disabled";
3023 filter->require_vacation=1;
3026 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3027 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3028 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3029 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3030 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
3033 filter->errmsg=CUS "unknown capability";
3037 if (parse_semicolon(filter)==-1) return -1;
3039 if (parse_commands(filter,exec,generated)==-1) return -1;
3042 filter->errmsg=CUS "syntax error";
3049 /*************************************************
3050 * Interpret a sieve filter file *
3051 *************************************************/
3055 filter points to the entire file, read into store as a single string
3056 options controls whether various special things are allowed, and requests
3057 special actions (not currently used)
3058 sieve_vacation_directory where to store vacation "once" files
3059 useraddress string expression for :user part of address
3060 subaddress string expression for :subaddress part of address
3061 generated where to hang newly-generated addresses
3062 error where to pass back an error text
3064 Returns: FF_DELIVERED success, a significant action was taken
3065 FF_NOTDELIVERED success, no significant action
3066 FF_DEFER defer requested
3067 FF_FAIL fail requested
3068 FF_FREEZE freeze requested
3069 FF_ERROR there was a problem
3073 sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
3074 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
3080 options = options; /* Keep picky compilers happy */
3083 DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3084 sieve.filter=filter;
3086 if (vacation_directory == NULL)
3087 sieve.vacation_directory = NULL;
3090 sieve.vacation_directory=expand_string(vacation_directory);
3091 if (sieve.vacation_directory == NULL)
3093 *error = string_sprintf("failed to expand \"%s\" "
3094 "(sieve_vacation_directory): %s", vacation_directory,
3095 expand_string_message);
3100 sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3101 sieve.subaddress = subaddress;
3103 #ifdef COMPILE_SYNTAX_CHECKER
3104 if (parse_start(&sieve,0,generated)==1)
3106 if (parse_start(&sieve,1,generated)==1)
3111 add_addr(generated,US"inbox",1,0,0,0);
3112 msg = string_sprintf("Implicit keep");
3117 msg = string_sprintf("No implicit keep");
3123 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3124 #ifdef COMPILE_SYNTAX_CHECKER
3128 add_addr(generated,US"inbox",1,0,0,0);
3133 #ifndef COMPILE_SYNTAX_CHECKER
3134 if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
3135 else debug_printf("%s\n", msg);
3138 DEBUG(D_route) debug_printf("Sieve: end of processing\n");