Further Sieve patches (tidies and documentation).
[exim.git] / src / src / sieve.c
CommitLineData
024bd3c2 1/* $Cambridge: exim/src/src/sieve.c,v 1.16 2005/11/21 10:09:13 ph10 Exp $ */
059ec3d9
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
e4a89c47 7/* Copyright (c) Michael Haardt 2003-2005 */
059ec3d9
PH
8/* See the file NOTICE for conditions of use and distribution. */
9
10/* This code was contributed by Michael Haardt. */
11
12
13/* Sieve mail filter. */
14
15#include <ctype.h>
16#include <errno.h>
17#include <limits.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "exim.h"
22
23#if HAVE_ICONV
24#include <iconv.h>
25#endif
26
27/* Define this for RFC compliant \r\n end-of-line terminators. */
28/* Undefine it for UNIX-style \n end-of-line terminators (default). */
29#undef RFC_EOL
30
024bd3c2
PH
31/* Define this for development of the Sieve extension "envelope-auth". */
32#undef ENVELOPE_AUTH
33
87fcc8b9
PH
34/* Define this for development of the Sieve extension "notify". */
35#undef NOTIFY
36
37/* Define this for the Sieve extension "subaddress". */
5ea81592 38#define SUBADDRESS
059ec3d9 39
87fcc8b9 40/* Define this for the Sieve extension "vacation". */
059ec3d9
PH
41#define VACATION
42
43/* Must be >= 1 */
44#define VACATION_MIN_DAYS 1
45/* Must be >= VACATION_MIN_DAYS, must be > 7, should be > 30 */
46#define VACATION_MAX_DAYS 31
47
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
51
52struct Sieve
53 {
54 uschar *filter;
55 const uschar *pc;
56 int line;
57 const uschar *errmsg;
58 int keep;
59 int require_envelope;
60 int require_fileinto;
024bd3c2
PH
61#ifdef ENVELOPE_AUTH
62 int require_envelope_auth;
63#endif
87fcc8b9
PH
64#ifdef NOTIFY
65 int require_notify;
66#endif
059ec3d9
PH
67#ifdef SUBADDRESS
68 int require_subaddress;
69#endif
70#ifdef VACATION
71 int require_vacation;
72 int vacation_ran;
73#endif
74 uschar *vacation_directory;
e4a89c47
PH
75 const uschar *subaddress;
76 const uschar *useraddress;
059ec3d9
PH
77 int require_copy;
78 int require_iascii_numeric;
79 };
80
5ea81592 81enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
059ec3d9
PH
82enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
83#ifdef SUBADDRESS
84enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
85#else
86enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
87#endif
88enum RelOp { LT, LE, EQ, GE, GT, NE };
89
90struct String
91 {
92 uschar *character;
93 int length;
94 };
95
96static int parse_test(struct Sieve *filter, int *cond, int exec);
97static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
98
99static uschar str_from_c[]="From";
100static const struct String str_from={ str_from_c, 4 };
101static uschar str_to_c[]="To";
102static const struct String str_to={ str_to_c, 2 };
103static uschar str_cc_c[]="Cc";
104static const struct String str_cc={ str_cc_c, 2 };
105static uschar str_bcc_c[]="Bcc";
106static const struct String str_bcc={ str_bcc_c, 3 };
024bd3c2
PH
107static uschar str_auth_c[]="auth";
108static const struct String str_auth={ str_auth_c, 4 };
059ec3d9
PH
109static uschar str_sender_c[]="Sender";
110static const struct String str_sender={ str_sender_c, 6 };
111static uschar str_resent_from_c[]="Resent-From";
112static const struct String str_resent_from={ str_resent_from_c, 11 };
113static uschar str_resent_to_c[]="Resent-To";
114static const struct String str_resent_to={ str_resent_to_c, 9 };
115static uschar str_fileinto_c[]="fileinto";
116static const struct String str_fileinto={ str_fileinto_c, 8 };
117static uschar str_envelope_c[]="envelope";
118static const struct String str_envelope={ str_envelope_c, 8 };
024bd3c2
PH
119#ifdef ENVELOPE_AUTH
120static uschar str_envelope_auth_c[]="envelope-auth";
121static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
122#endif
87fcc8b9
PH
123#ifdef NOTIFY
124static uschar str_notify_c[]="notify";
125static const struct String str_notify={ str_notify_c, 6 };
126#endif
059ec3d9
PH
127#ifdef SUBADDRESS
128static uschar str_subaddress_c[]="subaddress";
129static const struct String str_subaddress={ str_subaddress_c, 10 };
130#endif
131#ifdef VACATION
132static uschar str_vacation_c[]="vacation";
133static const struct String str_vacation={ str_vacation_c, 8 };
134static uschar str_subject_c[]="Subject";
135static const struct String str_subject={ str_subject_c, 7 };
136#endif
137static uschar str_copy_c[]="copy";
138static const struct String str_copy={ str_copy_c, 4 };
139static uschar str_iascii_casemap_c[]="i;ascii-casemap";
140static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
5ea81592
PH
141static uschar str_enascii_casemap_c[]="en;ascii-casemap";
142static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
059ec3d9
PH
143static uschar str_ioctet_c[]="i;octet";
144static const struct String str_ioctet={ str_ioctet_c, 7 };
145static uschar str_iascii_numeric_c[]="i;ascii-numeric";
146static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
147static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
148static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
5ea81592
PH
149static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
150static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
059ec3d9
PH
151static uschar str_comparator_ioctet_c[]="comparator-i;octet";
152static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
153static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
154static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
155
156
157/*************************************************
158* Encode to quoted-printable *
159*************************************************/
160
161/*
162Arguments:
163 src UTF-8 string
164*/
165
166static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
167{
168int pass;
169const uschar *start,*end;
170uschar *new = NULL;
171uschar ch;
172size_t line;
173
174for (pass=0; pass<=1; ++pass)
175 {
176 line=0;
177 if (pass==0)
178 dst->length=0;
179 else
180 {
181 dst->character=store_get(dst->length+1); /* plus one for \0 */
182 new=dst->character;
183 }
184 for (start=src->character,end=start+src->length; start<end; ++start)
185 {
186 ch=*start;
187 if (line>=73)
188 {
189 if (pass==0)
190 dst->length+=2;
191 else
192 {
193 *new++='=';
194 *new++='\n';
195 }
196 line=0;
197 }
198 if
199 (
200 (ch>=33 && ch<=60)
201 || (ch>=62 && ch<=126)
202 ||
203 (
204 (ch==9 || ch==32)
205#ifdef RFC_EOL
206 && start+2<end
207 && (*(start+1)!='\r' || *(start+2)!='\n')
208#else
209 && start+1<end
210 && *(start+1)!='\n'
211#endif
212 )
213 )
214 {
215 if (pass==0)
216 ++dst->length;
217 else
218 *new++=*start;
219 ++line;
220 }
221#ifdef RFC_EOL
222 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
223 {
224 if (pass==0)
225 {
226 ++dst->length;
227 line=0;
228 ++start;
229 }
230 else
231 *new++='\n';
232 line=0;
233 }
234#else
235 else if (ch=='\n')
236 {
237 if (pass==0)
238 ++dst->length;
239 else
240 *new++=*start;
241 ++line;
242 }
243#endif
244 else
245 {
246 if (pass==0)
247 dst->length+=3;
248 else
249 {
250 sprintf(CS new,"=%02X",ch);
251 new+=3;
252 }
253 line+=3;
254 }
255 }
256 }
257 *new='\0'; /* not included in length, but nice */
258 return dst;
259}
260
261
262/*************************************************
263* Octet-wise string comparison *
264*************************************************/
265
266/*
267Arguments:
268 needle UTF-8 string to search ...
269 haystack ... inside the haystack
270 match_prefix 1 to compare if needle is a prefix of haystack
271
272Returns: 0 needle not found in haystack
273 1 needle found
274*/
275
276static int eq_octet(const struct String *needle,
277 const struct String *haystack, int match_prefix)
278{
279size_t nl,hl;
280const uschar *n,*h;
281
282nl=needle->length;
283n=needle->character;
284hl=haystack->length;
285h=haystack->character;
286while (nl>0 && hl>0)
287 {
288#if !HAVE_ICONV
289 if (*n&0x80) return 0;
290 if (*h&0x80) return 0;
291#endif
292 if (*n!=*h) return 0;
293 ++n;
294 ++h;
295 --nl;
296 --hl;
297 }
298return (match_prefix ? nl==0 : nl==0 && hl==0);
299}
300
301
302/*************************************************
303* ASCII case-insensitive string comparison *
304*************************************************/
305
306/*
307Arguments:
308 needle UTF-8 string to search ...
309 haystack ... inside the haystack
310 match_prefix 1 to compare if needle is a prefix of haystack
311
312Returns: 0 needle not found in haystack
313 1 needle found
314*/
315
316static int eq_asciicase(const struct String *needle,
317 const struct String *haystack, int match_prefix)
318{
319size_t nl,hl;
320const uschar *n,*h;
321uschar nc,hc;
322
323nl=needle->length;
324n=needle->character;
325hl=haystack->length;
326h=haystack->character;
327while (nl>0 && hl>0)
328 {
329 nc=*n;
330 hc=*h;
331#if !HAVE_ICONV
332 if (nc&0x80) return 0;
333 if (hc&0x80) return 0;
334#endif
335 /* tolower depends on the locale and only ASCII case must be insensitive */
1c59d63b 336 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
059ec3d9
PH
337 ++n;
338 ++h;
339 --nl;
340 --hl;
341 }
342return (match_prefix ? nl==0 : nl==0 && hl==0);
343}
344
345
346/*************************************************
1c59d63b 347* Glob pattern search *
059ec3d9
PH
348*************************************************/
349
350/*
351Arguments:
352 needle pattern to search ...
353 haystack ... inside the haystack
354
355Returns: 0 needle not found in haystack
356 1 needle found
1c59d63b 357 -1 pattern error
059ec3d9
PH
358*/
359
1c59d63b
PH
360static int eq_glob(const struct String *needle,
361 const struct String *haystack, int ascii_caseless)
059ec3d9 362{
1c59d63b
PH
363const uschar *n,*h,*nend,*hend;
364int may_advance=0;
059ec3d9 365
1c59d63b
PH
366n=needle->character;
367h=haystack->character;
368nend=n+needle->length;
369hend=h+haystack->length;
370while (n<nend)
059ec3d9 371 {
1c59d63b
PH
372 if (*n=='*')
373 {
374 ++n;
375 may_advance=1;
059ec3d9 376 }
1c59d63b
PH
377 else
378 {
379 const uschar *npart,*hpart;
059ec3d9 380
1c59d63b
PH
381 /* Try to match a non-star part of the needle at the current */
382 /* position in the haystack. */
383 match_part:
384 npart=n;
385 hpart=h;
386 while (npart<nend && *npart!='*') switch (*npart)
387 {
388 case '?':
059ec3d9 389 {
1c59d63b
PH
390 if (hpart==hend) return 0;
391 /* watch out: Do not match one character, but one UTF8 encoded character */
392 if ((*hpart&0xc0)==0xc0)
059ec3d9 393 {
1c59d63b
PH
394 ++hpart;
395 while (hpart<hend && ((*hpart&0xc0)==0x80)) ++hpart;
059ec3d9 396 }
1c59d63b
PH
397 else
398 ++hpart;
399 ++npart;
400 break;
059ec3d9 401 }
1c59d63b 402 case '\\':
059ec3d9 403 {
1c59d63b
PH
404 ++npart;
405 if (npart==nend) return -1;
406 /* FALLTHROUGH */
059ec3d9 407 }
1c59d63b 408 default:
059ec3d9 409 {
1c59d63b
PH
410 if (hpart==hend) return 0;
411 /* tolower depends on the locale, but we need ASCII */
412 if
413 (
414#if !HAVE_ICONV
415 (*hpart&0x80) || (*npart&0x80) ||
416#endif
417 ascii_caseless
418 ? ((*npart>='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart))
419 : *hpart!=*npart
420 )
059ec3d9 421 {
1c59d63b
PH
422 if (may_advance)
423 /* string match after a star failed, advance and try again */
059ec3d9 424 {
1c59d63b
PH
425 ++h;
426 goto match_part;
059ec3d9 427 }
1c59d63b 428 else return 0;
059ec3d9
PH
429 }
430 else
431 {
1c59d63b
PH
432 ++npart;
433 ++hpart;
434 };
059ec3d9 435 }
059ec3d9 436 }
1c59d63b
PH
437 /* at this point, a part was matched successfully */
438 if (may_advance && npart==nend && hpart<hend)
439 /* needle ends, but haystack does not: if there was a star before, advance and try again */
059ec3d9 440 {
1c59d63b
PH
441 ++h;
442 goto match_part;
059ec3d9 443 }
1c59d63b
PH
444 h=hpart;
445 n=npart;
446 may_advance=0;
059ec3d9
PH
447 }
448 }
1c59d63b 449return (h==hend ? 1 : may_advance);
059ec3d9
PH
450}
451
452
453/*************************************************
454* ASCII numeric comparison *
455*************************************************/
456
457/*
458Arguments:
459 a first numeric string
460 b second numeric string
461 relop relational operator
462
463Returns: 0 not (a relop b)
464 1 a relop b
465*/
466
467static int eq_asciinumeric(const struct String *a,
468 const struct String *b, enum RelOp relop)
469{
470size_t al,bl;
471const uschar *as,*aend,*bs,*bend;
472int cmp;
473
474as=a->character;
475aend=a->character+a->length;
476bs=b->character;
477bend=b->character+b->length;
478
479while (*as>='0' && *as<='9' && as<aend) ++as;
480al=as-a->character;
481while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
482bl=bs-b->character;
483
484if (al && bl==0) cmp=-1;
485else if (al==0 && bl==0) cmp=0;
486else if (al==0 && bl) cmp=1;
487else
488 {
489 cmp=al-bl;
490 if (cmp==0) cmp=memcmp(a->character,b->character,al);
491 }
492switch (relop)
493 {
494 case LT: return cmp<0;
495 case LE: return cmp<=0;
496 case EQ: return cmp==0;
497 case GE: return cmp>=0;
498 case GT: return cmp>0;
499 case NE: return cmp!=0;
500 }
501 /*NOTREACHED*/
502 return -1;
503}
504
505
506/*************************************************
507* Compare strings *
508*************************************************/
509
510/*
511Arguments:
512 needle UTF-8 pattern or string to search ...
513 haystack ... inside the haystack
514 co comparator to use
515 mt match type to use
516
517Returns: 0 needle not found in haystack
518 1 needle found
519 -1 comparator does not offer matchtype
520*/
521
522static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
523 enum Comparator co, enum MatchType mt)
524{
525int r=0;
526
f05da2e8 527if ((filter_test != FTEST_NONE && debug_selector != 0) ||
059ec3d9
PH
528 (debug_selector & D_filter) != 0)
529 {
530 debug_printf("String comparison (match ");
531 switch (mt)
532 {
533 case MATCH_IS: debug_printf(":is"); break;
534 case MATCH_CONTAINS: debug_printf(":contains"); break;
535 case MATCH_MATCHES: debug_printf(":matches"); break;
536 }
537 debug_printf(", comparison \"");
538 switch (co)
539 {
540 case COMP_OCTET: debug_printf("i;octet"); break;
5ea81592 541 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
059ec3d9
PH
542 case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break;
543 }
544 debug_printf("\"):\n");
545 debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length);
546 debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length);
547 }
548switch (mt)
549 {
550 case MATCH_IS:
551 {
552 switch (co)
553 {
554 case COMP_OCTET:
555 {
556 if (eq_octet(needle,haystack,0)) r=1;
557 break;
558 }
5ea81592 559 case COMP_EN_ASCII_CASEMAP:
059ec3d9
PH
560 {
561 if (eq_asciicase(needle,haystack,0)) r=1;
562 break;
563 }
564 case COMP_ASCII_NUMERIC:
565 {
566 if (!filter->require_iascii_numeric)
567 {
568 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
569 return -1;
570 }
571 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
572 break;
573 }
574 }
575 break;
576 }
577 case MATCH_CONTAINS:
578 {
579 struct String h;
580
581 switch (co)
582 {
583 case COMP_OCTET:
584 {
585 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_octet(needle,&h,1)) { r=1; break; }
586 break;
587 }
5ea81592 588 case COMP_EN_ASCII_CASEMAP:
059ec3d9
PH
589 {
590 for (h=*haystack; h.length; ++h.character,--h.length) if (eq_asciicase(needle,&h,1)) { r=1; break; }
591 break;
592 }
593 default:
594 {
595 filter->errmsg=CUS "comparator does not offer specified matchtype";
596 return -1;
597 }
598 }
599 break;
600 }
601 case MATCH_MATCHES:
602 {
603 switch (co)
604 {
605 case COMP_OCTET:
606 {
1c59d63b
PH
607 if ((r=eq_glob(needle,haystack,0))==-1)
608 {
609 filter->errmsg=CUS "syntactically invalid pattern";
610 return -1;
611 }
059ec3d9
PH
612 break;
613 }
5ea81592 614 case COMP_EN_ASCII_CASEMAP:
059ec3d9 615 {
1c59d63b
PH
616 if ((r=eq_glob(needle,haystack,1))==-1)
617 {
618 filter->errmsg=CUS "syntactically invalid pattern";
619 return -1;
620 }
059ec3d9
PH
621 break;
622 }
623 default:
624 {
625 filter->errmsg=CUS "comparator does not offer specified matchtype";
626 return -1;
627 }
628 }
629 break;
630 }
631 }
f05da2e8 632if ((filter_test != FTEST_NONE && debug_selector != 0) ||
059ec3d9
PH
633 (debug_selector & D_filter) != 0)
634 debug_printf(" Result %s\n",r?"true":"false");
635return r;
636}
637
638
639/*************************************************
640* Check header field syntax *
641*************************************************/
642
643/*
644RFC 2822, section 3.6.8 says:
645
646 field-name = 1*ftext
647
648 ftext = %d33-57 / ; Any character except
649 %d59-126 ; controls, SP, and
650 ; ":".
651
652That forbids 8-bit header fields. This implementation accepts them, since
653all of Exim is 8-bit clean, so it adds %d128-%d255.
654
655Arguments:
656 header header field to quote for suitable use in Exim expansions
657
658Returns: 0 string is not a valid header field
659 1 string is a value header field
660*/
661
662static int is_header(const struct String *header)
663{
664size_t l;
665const uschar *h;
666
667l=header->length;
668h=header->character;
669if (l==0) return 0;
670while (l)
671 {
672 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
673 else
674 {
675 ++h;
676 --l;
677 }
678 }
679return 1;
680}
681
682
683/*************************************************
684* Quote special characters string *
685*************************************************/
686
687/*
688Arguments:
689 header header field to quote for suitable use in Exim expansions
690 or as debug output
691
692Returns: quoted string
693*/
694
695static const uschar *quote(const struct String *header)
696{
697uschar *quoted=NULL;
698int size=0,ptr=0;
699size_t l;
700const uschar *h;
701
702l=header->length;
703h=header->character;
704while (l)
705 {
706 switch (*h)
707 {
708 case '\0':
709 {
710 quoted=string_cat(quoted,&size,&ptr,CUS "\\0",2);
711 break;
712 }
713 case '$':
714 case '{':
715 case '}':
716 {
717 quoted=string_cat(quoted,&size,&ptr,CUS "\\",1);
718 }
719 default:
720 {
721 quoted=string_cat(quoted,&size,&ptr,h,1);
722 }
723 }
724 ++h;
725 --l;
726 }
727quoted=string_cat(quoted,&size,&ptr,CUS "",1);
728return quoted;
729}
730
731
732/*************************************************
733* Add address to list of generated addresses *
734*************************************************/
735
736/*
737According to RFC 3028, duplicate delivery to the same address must
738not happen, so the list is first searched for the address.
739
740Arguments:
741 generated list of generated addresses
742 addr new address to add
743 file address denotes a file
744
745Returns: nothing
746*/
747
748static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
749{
750address_item *new_addr;
751
752for (new_addr=*generated; new_addr; new_addr=new_addr->next)
753 {
754 if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
755 {
f05da2e8 756 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9
PH
757 {
758 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
759 }
760 return;
761 }
762 }
763
f05da2e8 764if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9
PH
765 {
766 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
767 }
768new_addr=deliver_make_addr(addr,TRUE);
769if (file)
770 {
771 setflag(new_addr, af_pfr|af_file);
772 new_addr->mode = 0;
773 }
774new_addr->p.errors_address = NULL;
775new_addr->next = *generated;
776*generated = new_addr;
777}
778
779
780/*************************************************
781* Return decoded header field *
782*************************************************/
783
784/*
785Arguments:
786 value returned value of the field
787 header name of the header field
788
789Returns: nothing The expanded string is empty
790 in case there is no such header
791*/
792
793static void expand_header(struct String *value, const struct String *header)
794{
795uschar *s,*r,*t;
796uschar *errmsg;
797
798value->length=0;
799value->character=(uschar*)0;
800
801t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
802while (*r==' ') ++r;
803while (*r)
804 {
805 if (*r=='\n')
806 {
807 ++r;
808 while (*r==' ' || *r=='\t') ++r;
809 if (*r) *t++=' ';
810 }
811 else
812 *t++=*r++;
813 }
814*t++='\0';
a0d6ba8a 815value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
059ec3d9
PH
816}
817
818
819/*************************************************
820* Parse remaining hash comment *
821*************************************************/
822
823/*
824Token definition:
825 Comment up to terminating CRLF
826
827Arguments:
828 filter points to the Sieve filter including its state
829
830Returns: 1 success
831 -1 syntax error
832*/
833
834static int parse_hashcomment(struct Sieve *filter)
835{
836++filter->pc;
837while (*filter->pc)
838 {
839#ifdef RFC_EOL
840 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
841#else
842 if (*filter->pc=='\n')
843#endif
844 {
845#ifdef RFC_EOL
846 filter->pc+=2;
847#else
848 ++filter->pc;
849#endif
850 ++filter->line;
851 return 1;
852 }
853 else ++filter->pc;
854 }
855filter->errmsg=CUS "missing end of comment";
856return -1;
857}
858
859
860/*************************************************
861* Parse remaining C-style comment *
862*************************************************/
863
864/*
865Token definition:
866 Everything up to star slash
867
868Arguments:
869 filter points to the Sieve filter including its state
870
871Returns: 1 success
872 -1 syntax error
873*/
874
875static int parse_comment(struct Sieve *filter)
876{
877 filter->pc+=2;
878 while (*filter->pc)
879 {
880 if (*filter->pc=='*' && *(filter->pc+1)=='/')
881 {
882 filter->pc+=2;
883 return 1;
884 }
885 else ++filter->pc;
886 }
887 filter->errmsg=CUS "missing end of comment";
888 return -1;
889}
890
891
892/*************************************************
893* Parse optional white space *
894*************************************************/
895
896/*
897Token definition:
898 Spaces, tabs, CRLFs, hash comments or C-style comments
899
900Arguments:
901 filter points to the Sieve filter including its state
902
903Returns: 1 success
904 -1 syntax error
905*/
906
907static int parse_white(struct Sieve *filter)
908{
909while (*filter->pc)
910 {
911 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
912#ifdef RFC_EOL
913 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
914#else
915 else if (*filter->pc=='\n')
916#endif
917 {
918#ifdef RFC_EOL
919 filter->pc+=2;
920#else
921 ++filter->pc;
922#endif
923 ++filter->line;
924 }
925 else if (*filter->pc=='#')
926 {
927 if (parse_hashcomment(filter)==-1) return -1;
928 }
929 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
930 {
931 if (parse_comment(filter)==-1) return -1;
932 }
933 else break;
934 }
935return 1;
936}
937
938
939/*************************************************
940* Parse a optional string *
941*************************************************/
942
943/*
944Token definition:
945 quoted-string = DQUOTE *CHAR DQUOTE
946 ;; in general, \ CHAR inside a string maps to CHAR
947 ;; so \" maps to " and \\ maps to \
948 ;; note that newlines and other characters are all allowed
949 ;; in strings
950
951 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
952 *(multi-line-literal / multi-line-dotstuff)
953 "." CRLF
954 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
955 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
956 ;; A line containing only "." ends the multi-line.
957 ;; Remove a leading '.' if followed by another '.'.
958 string = quoted-string / multi-line
959
960Arguments:
961 filter points to the Sieve filter including its state
962 id specifies identifier to match
963
964Returns: 1 success
965 -1 syntax error
966 0 identifier not matched
967*/
968
969static int parse_string(struct Sieve *filter, struct String *data)
970{
971int dataCapacity=0;
972
973data->length=0;
974data->character=(uschar*)0;
975if (*filter->pc=='"') /* quoted string */
976 {
977 ++filter->pc;
978 while (*filter->pc)
979 {
980 if (*filter->pc=='"') /* end of string */
981 {
982 int foo=data->length;
983
984 ++filter->pc;
985 data->character=string_cat(data->character,&dataCapacity,&foo,CUS "",1);
986 return 1;
987 }
988 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
989 {
990 if (*(filter->pc+1)=='0') data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
991 else data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc+1,1);
992 filter->pc+=2;
993 }
994 else /* regular character */
995 {
996 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
997 filter->pc++;
998 }
999 }
1000 filter->errmsg=CUS "missing end of string";
1001 return -1;
1002 }
1003else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1004 {
1005 filter->pc+=5;
1006 /* skip optional white space followed by hashed comment or CRLF */
1007 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1008 if (*filter->pc=='#')
1009 {
1010 if (parse_hashcomment(filter)==-1) return -1;
1011 }
1012#ifdef RFC_EOL
1013 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1014#else
1015 else if (*filter->pc=='\n')
1016#endif
1017 {
1018#ifdef RFC_EOL
1019 filter->pc+=2;
1020#else
1021 ++filter->pc;
1022#endif
1023 ++filter->line;
1024 }
1025 else
1026 {
1027 filter->errmsg=CUS "syntax error";
1028 return -1;
1029 }
1030 while (*filter->pc)
1031 {
1032#ifdef RFC_EOL
1033 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1034#else
1035 if (*filter->pc=='\n') /* end of line */
1036#endif
1037 {
1038 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "\r\n",2);
1039#ifdef RFC_EOL
1040 filter->pc+=2;
1041#else
1042 ++filter->pc;
1043#endif
1044 ++filter->line;
1045#ifdef RFC_EOL
1046 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1047#else
1048 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1049#endif
1050 {
1051 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS "",1);
1052#ifdef RFC_EOL
1053 filter->pc+=3;
1054#else
1055 filter->pc+=2;
1056#endif
1057 ++filter->line;
1058 return 1;
1059 }
1060 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1061 {
1062 data->character=string_cat(data->character,&dataCapacity,&data->length,CUS ".",1);
1063 filter->pc+=2;
1064 }
1065 }
1066 else /* regular character */
1067 {
1068 data->character=string_cat(data->character,&dataCapacity,&data->length,filter->pc,1);
1069 filter->pc++;
1070 }
1071 }
1072 filter->errmsg=CUS "missing end of multi line string";
1073 return -1;
1074 }
1075else return 0;
1076}
1077
1078
1079/*************************************************
1080* Parse a specific identifier *
1081*************************************************/
1082
1083/*
1084Token definition:
1085 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1086
1087Arguments:
1088 filter points to the Sieve filter including its state
1089 id specifies identifier to match
1090
1091Returns: 1 success
1092 0 identifier not matched
1093*/
1094
1095static int parse_identifier(struct Sieve *filter, const uschar *id)
1096{
1097 size_t idlen=Ustrlen(id);
1098
1099 if (Ustrncmp(filter->pc,id,idlen)==0)
1100 {
1101 uschar next=filter->pc[idlen];
1102
1103 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1104 filter->pc+=idlen;
1105 return 1;
1106 }
1107 else return 0;
1108}
1109
1110
1111/*************************************************
1112* Parse a number *
1113*************************************************/
1114
1115/*
1116Token definition:
1117 number = 1*DIGIT [QUANTIFIER]
1118 QUANTIFIER = "K" / "M" / "G"
1119
1120Arguments:
1121 filter points to the Sieve filter including its state
1122 data returns value
1123
1124Returns: 1 success
1125 -1 no string list found
1126*/
1127
1128static int parse_number(struct Sieve *filter, unsigned long *data)
1129{
1130unsigned long d,u;
1131
1132if (*filter->pc>='0' && *filter->pc<='9')
1133 {
1134 uschar *e;
1135
1136 errno=0;
1137 d=Ustrtoul(filter->pc,&e,10);
1138 if (errno==ERANGE)
1139 {
1140 filter->errmsg=CUstrerror(ERANGE);
1141 return -1;
1142 }
1143 filter->pc=e;
1144 u=1;
1145 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1146 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1147 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1148 if (d>(ULONG_MAX/u))
1149 {
1150 filter->errmsg=CUstrerror(ERANGE);
1151 return -1;
1152 }
1153 d*=u;
1154 *data=d;
1155 return 1;
1156 }
1157else
1158 {
1159 filter->errmsg=CUS "missing number";
1160 return -1;
1161 }
1162}
1163
1164
1165/*************************************************
1166* Parse a string list *
1167*************************************************/
1168
1169/*
1170Grammar:
1171 string-list = "[" string *("," string) "]" / string
1172
1173Arguments:
1174 filter points to the Sieve filter including its state
1175 data returns string list
1176
1177Returns: 1 success
1178 -1 no string list found
1179*/
1180
1181static int parse_stringlist(struct Sieve *filter, struct String **data)
1182{
1183const uschar *orig=filter->pc;
1184int dataCapacity=0;
1185int dataLength=0;
1186struct String *d=(struct String*)0;
1187int m;
1188
1189if (*filter->pc=='[') /* string list */
1190 {
1191 ++filter->pc;
1192 for (;;)
1193 {
1194 if (parse_white(filter)==-1) goto error;
1195 if ((dataLength+1)>=dataCapacity) /* increase buffer */
1196 {
1197 struct String *new;
1198 int newCapacity; /* Don't amalgamate with next line; some compilers grumble */
1199 newCapacity=dataCapacity?(dataCapacity*=2):(dataCapacity=4);
1200 if ((new=(struct String*)store_get(sizeof(struct String)*newCapacity))==(struct String*)0)
1201 {
1202 filter->errmsg=CUstrerror(errno);
1203 goto error;
1204 }
1205 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
1206 d=new;
1207 dataCapacity=newCapacity;
1208 }
1209 m=parse_string(filter,&d[dataLength]);
1210 if (m==0)
1211 {
1212 if (dataLength==0) break;
1213 else
1214 {
1215 filter->errmsg=CUS "missing string";
1216 goto error;
1217 }
1218 }
1219 else if (m==-1) goto error;
1220 else ++dataLength;
1221 if (parse_white(filter)==-1) goto error;
1222 if (*filter->pc==',') ++filter->pc;
1223 else break;
1224 }
1225 if (*filter->pc==']')
1226 {
1227 d[dataLength].character=(uschar*)0;
1228 d[dataLength].length=-1;
1229 ++filter->pc;
1230 *data=d;
1231 return 1;
1232 }
1233 else
1234 {
1235 filter->errmsg=CUS "missing closing bracket";
1236 goto error;
1237 }
1238 }
1239else /* single string */
1240 {
1241 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1242 {
1243 return -1;
1244 }
1245 m=parse_string(filter,&d[0]);
1246 if (m==-1)
1247 {
1248 return -1;
1249 }
1250 else if (m==0)
1251 {
1252 filter->pc=orig;
1253 return 0;
1254 }
1255 else
1256 {
1257 d[1].character=(uschar*)0;
1258 d[1].length=-1;
1259 *data=d;
1260 return 1;
1261 }
1262 }
1263error:
1264filter->errmsg=CUS "missing string list";
1265return -1;
1266}
1267
1268
1269/*************************************************
1270* Parse an optional address part specifier *
1271*************************************************/
1272
1273/*
1274Grammar:
1275 address-part = ":localpart" / ":domain" / ":all"
1276 address-part =/ ":user" / ":detail"
1277
1278Arguments:
1279 filter points to the Sieve filter including its state
1280 a returns address part specified
1281
1282Returns: 1 success
1283 0 no comparator found
1284 -1 syntax error
1285*/
1286
1287static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1288{
1289#ifdef SUBADDRESS
1290if (parse_identifier(filter,CUS ":user")==1)
1291 {
1292 if (!filter->require_subaddress)
1293 {
1294 filter->errmsg=CUS "missing previous require \"subaddress\";";
1295 return -1;
1296 }
1297 *a=ADDRPART_USER;
1298 return 1;
1299 }
1300else if (parse_identifier(filter,CUS ":detail")==1)
1301 {
1302 if (!filter->require_subaddress)
1303 {
1304 filter->errmsg=CUS "missing previous require \"subaddress\";";
1305 return -1;
1306 }
1307 *a=ADDRPART_DETAIL;
1308 return 1;
1309 }
1310else
1311#endif
1312if (parse_identifier(filter,CUS ":localpart")==1)
1313 {
1314 *a=ADDRPART_LOCALPART;
1315 return 1;
1316 }
1317else if (parse_identifier(filter,CUS ":domain")==1)
1318 {
1319 *a=ADDRPART_DOMAIN;
1320 return 1;
1321 }
1322else if (parse_identifier(filter,CUS ":all")==1)
1323 {
1324 *a=ADDRPART_ALL;
1325 return 1;
1326 }
1327else return 0;
1328}
1329
1330
1331/*************************************************
1332* Parse an optional comparator *
1333*************************************************/
1334
1335/*
1336Grammar:
1337 comparator = ":comparator" <comparator-name: string>
1338
1339Arguments:
1340 filter points to the Sieve filter including its state
1341 c returns comparator
1342
1343Returns: 1 success
1344 0 no comparator found
1345 -1 incomplete comparator found
1346*/
1347
1348static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1349{
1350struct String comparator_name;
1351
1352if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1353if (parse_white(filter)==-1) return -1;
1354switch (parse_string(filter,&comparator_name))
1355 {
1356 case -1: return -1;
1357 case 0:
1358 {
1359 filter->errmsg=CUS "missing comparator";
1360 return -1;
1361 }
1362 default:
1363 {
1364 int match;
1365
1366 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1367 {
1368 *c=COMP_OCTET;
1369 match=1;
1370 }
1371 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1372 {
5ea81592
PH
1373 *c=COMP_EN_ASCII_CASEMAP;
1374 match=1;
1375 }
1376 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1377 {
1378 *c=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
1379 match=1;
1380 }
1381 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1382 {
1383 *c=COMP_ASCII_NUMERIC;
1384 match=1;
1385 }
1386 else
1387 {
1388 filter->errmsg=CUS "invalid comparator";
1389 match=-1;
1390 }
1391 return match;
1392 }
1393 }
1394}
1395
1396
1397/*************************************************
1398* Parse an optional match type *
1399*************************************************/
1400
1401/*
1402Grammar:
1403 match-type = ":is" / ":contains" / ":matches"
1404
1405Arguments:
1406 filter points to the Sieve filter including its state
1407 m returns match type
1408
1409Returns: 1 success
1410 0 no match type found
1411*/
1412
1413static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1414{
1415 if (parse_identifier(filter,CUS ":is")==1)
1416 {
1417 *m=MATCH_IS;
1418 return 1;
1419 }
1420 else if (parse_identifier(filter,CUS ":contains")==1)
1421 {
1422 *m=MATCH_CONTAINS;
1423 return 1;
1424 }
1425 else if (parse_identifier(filter,CUS ":matches")==1)
1426 {
1427 *m=MATCH_MATCHES;
1428 return 1;
1429 }
1430 else return 0;
1431}
1432
1433
1434/*************************************************
1435* Parse and interpret an optional test list *
1436*************************************************/
1437
1438/*
1439Grammar:
1440 test-list = "(" test *("," test) ")"
1441
1442Arguments:
1443 filter points to the Sieve filter including its state
1444 n total number of tests
1445 true number of passed tests
1446 exec Execute parsed statements
1447
1448Returns: 1 success
1449 0 no test list found
1450 -1 syntax or execution error
1451*/
1452
1453static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1454{
1455if (parse_white(filter)==-1) return -1;
1456if (*filter->pc=='(')
1457 {
1458 ++filter->pc;
1459 *n=0;
1460 *true=0;
1461 for (;;)
1462 {
1463 int cond;
1464
1465 switch (parse_test(filter,&cond,exec))
1466 {
1467 case -1: return -1;
1468 case 0: filter->errmsg=CUS "missing test"; return -1;
1469 default: ++*n; if (cond) ++*true; break;
1470 }
1471 if (parse_white(filter)==-1) return -1;
1472 if (*filter->pc==',') ++filter->pc;
1473 else break;
1474 }
1475 if (*filter->pc==')')
1476 {
1477 ++filter->pc;
1478 return 1;
1479 }
1480 else
1481 {
1482 filter->errmsg=CUS "missing closing paren";
1483 return -1;
1484 }
1485 }
1486else return 0;
1487}
1488
1489
1490/*************************************************
1491* Parse and interpret an optional test *
1492*************************************************/
1493
1494/*
1495Arguments:
1496 filter points to the Sieve filter including its state
1497 cond returned condition status
1498 exec Execute parsed statements
1499
1500Returns: 1 success
1501 0 no test found
1502 -1 syntax or execution error
1503*/
1504
1505static int parse_test(struct Sieve *filter, int *cond, int exec)
1506{
1507if (parse_white(filter)==-1) return -1;
1508if (parse_identifier(filter,CUS "address"))
1509 {
1510 /*
1511 address-test = "address" { [address-part] [comparator] [match-type] }
1512 <header-list: string-list> <key-list: string-list>
1513
1514 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
1515 */
1516
1517 enum AddressPart addressPart=ADDRPART_ALL;
5ea81592 1518 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
1519 enum MatchType matchType=MATCH_IS;
1520 struct String *hdr,*h,*key,*k;
1521 int m;
1522 int ap=0,co=0,mt=0;
1523
1524 for (;;)
1525 {
1526 if (parse_white(filter)==-1) return -1;
1527 if ((m=parse_addresspart(filter,&addressPart))!=0)
1528 {
1529 if (m==-1) return -1;
1530 if (ap)
1531 {
1532 filter->errmsg=CUS "address part already specified";
1533 return -1;
1534 }
1535 else ap=1;
1536 }
1537 else if ((m=parse_comparator(filter,&comparator))!=0)
1538 {
1539 if (m==-1) return -1;
1540 if (co)
1541 {
1542 filter->errmsg=CUS "comparator already specified";
1543 return -1;
1544 }
1545 else co=1;
1546 }
1547 else if ((m=parse_matchtype(filter,&matchType))!=0)
1548 {
1549 if (m==-1) return -1;
1550 if (mt)
1551 {
1552 filter->errmsg=CUS "match type already specified";
1553 return -1;
1554 }
1555 else mt=1;
1556 }
1557 else break;
1558 }
1559 if (parse_white(filter)==-1) return -1;
1560 if ((m=parse_stringlist(filter,&hdr))!=1)
1561 {
1562 if (m==0) filter->errmsg=CUS "header string list expected";
1563 return -1;
1564 }
1565 if (parse_white(filter)==-1) return -1;
1566 if ((m=parse_stringlist(filter,&key))!=1)
1567 {
1568 if (m==0) filter->errmsg=CUS "key string list expected";
1569 return -1;
1570 }
1571 *cond=0;
1572 for (h=hdr; h->length!=-1 && !*cond; ++h)
1573 {
1574 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
1575
1576 if
1577 (
1578 !eq_asciicase(h,&str_from,0)
1579 && !eq_asciicase(h,&str_to,0)
1580 && !eq_asciicase(h,&str_cc,0)
1581 && !eq_asciicase(h,&str_bcc,0)
1582 && !eq_asciicase(h,&str_sender,0)
1583 && !eq_asciicase(h,&str_resent_from,0)
1584 && !eq_asciicase(h,&str_resent_to,0)
1585 )
1586 {
1587 filter->errmsg=CUS "invalid header field";
1588 return -1;
1589 }
1590 if (exec)
1591 {
1592 /* We are only interested in addresses below, so no MIME decoding */
1593 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
1594 if (header_value == NULL)
1595 {
1596 filter->errmsg=CUS "header string expansion failed";
1597 return -1;
1598 }
1599 parse_allow_group = TRUE;
1600 while (*header_value && !*cond)
1601 {
1602 uschar *error;
1603 int start, end, domain;
1604 int saveend;
1605 uschar *part=NULL;
1606
1607 end_addr = parse_find_address_end(header_value, FALSE);
1608 saveend = *end_addr;
1609 *end_addr = 0;
1610 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
1611
1612 if (extracted_addr) switch (addressPart)
1613 {
1614 case ADDRPART_ALL: part=extracted_addr; break;
1615#ifdef SUBADDRESS
1616 case ADDRPART_USER:
1617#endif
1618 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
1619 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
1620#ifdef SUBADDRESS
e4a89c47 1621 case ADDRPART_DETAIL: part=NULL; break;
059ec3d9 1622#endif
059ec3d9
PH
1623 }
1624
1625 *end_addr = saveend;
1626 if (part)
1627 {
1628 for (k=key; k->length!=-1; ++k)
1629 {
1630 struct String partStr;
1631
1632 partStr.character=part;
1633 partStr.length=Ustrlen(part);
1634 if (extracted_addr)
1635 {
1636 *cond=compare(filter,k,&partStr,comparator,matchType);
1637 if (*cond==-1) return -1;
1638 if (*cond) break;
1639 }
1640 }
1641 }
1642 if (saveend == 0) break;
1643 header_value = end_addr + 1;
1644 }
1645 }
1646 }
1647 return 1;
1648 }
1649else if (parse_identifier(filter,CUS "allof"))
1650 {
1651 /*
1652 allof-test = "allof" <tests: test-list>
1653 */
1654
1655 int n,true;
1656
1657 switch (parse_testlist(filter,&n,&true,exec))
1658 {
1659 case -1: return -1;
1660 case 0: filter->errmsg=CUS "missing test list"; return -1;
1661 default: *cond=(n==true); return 1;
1662 }
1663 }
1664else if (parse_identifier(filter,CUS "anyof"))
1665 {
1666 /*
1667 anyof-test = "anyof" <tests: test-list>
1668 */
1669
1670 int n,true;
1671
1672 switch (parse_testlist(filter,&n,&true,exec))
1673 {
1674 case -1: return -1;
1675 case 0: filter->errmsg=CUS "missing test list"; return -1;
1676 default: *cond=(true>0); return 1;
1677 }
1678 }
1679else if (parse_identifier(filter,CUS "exists"))
1680 {
1681 /*
1682 exists-test = "exists" <header-names: string-list>
1683 */
1684
1685 struct String *hdr,*h;
1686 int m;
1687
1688 if (parse_white(filter)==-1) return -1;
1689 if ((m=parse_stringlist(filter,&hdr))!=1)
1690 {
1691 if (m==0) filter->errmsg=CUS "header string list expected";
1692 return -1;
1693 }
1694 if (exec)
1695 {
1696 *cond=1;
1697 for (h=hdr; h->length!=-1 && *cond; ++h)
1698 {
1699 uschar *header_def;
1700
1701 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1702 if (header_def == NULL)
1703 {
1704 filter->errmsg=CUS "header string expansion failed";
1705 return -1;
1706 }
1707 if (Ustrcmp(header_def,"false")==0) *cond=0;
1708 }
1709 }
1710 return 1;
1711 }
1712else if (parse_identifier(filter,CUS "false"))
1713 {
1714 /*
1715 false-test = "false"
1716 */
1717
1718 *cond=0;
1719 return 1;
1720 }
1721else if (parse_identifier(filter,CUS "header"))
1722 {
1723 /*
1724 header-test = "header" { [comparator] [match-type] }
1725 <header-names: string-list> <key-list: string-list>
1726 */
1727
5ea81592 1728 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
1729 enum MatchType matchType=MATCH_IS;
1730 struct String *hdr,*h,*key,*k;
1731 int m;
1732 int co=0,mt=0;
1733
1734 for (;;)
1735 {
1736 if (parse_white(filter)==-1) return -1;
1737 if ((m=parse_comparator(filter,&comparator))!=0)
1738 {
1739 if (m==-1) return -1;
1740 if (co)
1741 {
1742 filter->errmsg=CUS "comparator already specified";
1743 return -1;
1744 }
1745 else co=1;
1746 }
1747 else if ((m=parse_matchtype(filter,&matchType))!=0)
1748 {
1749 if (m==-1) return -1;
1750 if (mt)
1751 {
1752 filter->errmsg=CUS "match type already specified";
1753 return -1;
1754 }
1755 else mt=1;
1756 }
1757 else break;
1758 }
1759 if (parse_white(filter)==-1) return -1;
1760 if ((m=parse_stringlist(filter,&hdr))!=1)
1761 {
1762 if (m==0) filter->errmsg=CUS "header string list expected";
1763 return -1;
1764 }
1765 if (parse_white(filter)==-1) return -1;
1766 if ((m=parse_stringlist(filter,&key))!=1)
1767 {
1768 if (m==0) filter->errmsg=CUS "key string list expected";
1769 return -1;
1770 }
1771 *cond=0;
1772 for (h=hdr; h->length!=-1 && !*cond; ++h)
1773 {
1774 if (!is_header(h))
1775 {
1776 filter->errmsg=CUS "invalid header field";
1777 return -1;
1778 }
1779 if (exec)
1780 {
1781 struct String header_value;
1782 uschar *header_def;
1783
1784 expand_header(&header_value,h);
1785 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
1786 if (header_value.character == NULL || header_def == NULL)
1787 {
1788 filter->errmsg=CUS "header string expansion failed";
1789 return -1;
1790 }
1791 for (k=key; k->length!=-1; ++k)
1792 {
1793 if (Ustrcmp(header_def,"true")==0)
1794 {
1795 *cond=compare(filter,k,&header_value,comparator,matchType);
1796 if (*cond==-1) return -1;
1797 if (*cond) break;
1798 }
1799 }
1800 }
1801 }
1802 return 1;
1803 }
1804else if (parse_identifier(filter,CUS "not"))
1805 {
1806 if (parse_white(filter)==-1) return -1;
1807 switch (parse_test(filter,cond,exec))
1808 {
1809 case -1: return -1;
1810 case 0: filter->errmsg=CUS "missing test"; return -1;
1811 default: *cond=!*cond; return 1;
1812 }
1813 }
1814else if (parse_identifier(filter,CUS "size"))
1815 {
1816 /*
1817 relop = ":over" / ":under"
1818 size-test = "size" relop <limit: number>
1819 */
1820
1821 unsigned long limit;
1822 int overNotUnder;
1823
1824 if (parse_white(filter)==-1) return -1;
1825 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
1826 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
1827 else
1828 {
1829 filter->errmsg=CUS "missing :over or :under";
1830 return -1;
1831 }
1832 if (parse_white(filter)==-1) return -1;
1833 if (parse_number(filter,&limit)==-1) return -1;
1834 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
1835 return 1;
1836 }
1837else if (parse_identifier(filter,CUS "true"))
1838 {
1839 *cond=1;
1840 return 1;
1841 }
1842else if (parse_identifier(filter,CUS "envelope"))
1843 {
1844 /*
1845 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
1846 <envelope-part: string-list> <key-list: string-list>
1847
1848 envelope-part is case insensitive "from" or "to"
024bd3c2
PH
1849#ifdef ENVELOPE_AUTH
1850 envelope-part =/ "auth"
1851#endif
059ec3d9
PH
1852 */
1853
5ea81592 1854 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
1855 enum AddressPart addressPart=ADDRPART_ALL;
1856 enum MatchType matchType=MATCH_IS;
1857 struct String *env,*e,*key,*k;
1858 int m;
1859 int co=0,ap=0,mt=0;
1860
1861 if (!filter->require_envelope)
1862 {
1863 filter->errmsg=CUS "missing previous require \"envelope\";";
1864 return -1;
1865 }
1866 for (;;)
1867 {
1868 if (parse_white(filter)==-1) return -1;
1869 if ((m=parse_comparator(filter,&comparator))!=0)
1870 {
1871 if (m==-1) return -1;
1872 if (co)
1873 {
1874 filter->errmsg=CUS "comparator already specified";
1875 return -1;
1876 }
1877 else co=1;
1878 }
1879 else if ((m=parse_addresspart(filter,&addressPart))!=0)
1880 {
1881 if (m==-1) return -1;
1882 if (ap)
1883 {
1884 filter->errmsg=CUS "address part already specified";
1885 return -1;
1886 }
1887 else ap=1;
1888 }
1889 else if ((m=parse_matchtype(filter,&matchType))!=0)
1890 {
1891 if (m==-1) return -1;
1892 if (mt)
1893 {
1894 filter->errmsg=CUS "match type already specified";
1895 return -1;
1896 }
1897 else mt=1;
1898 }
1899 else break;
1900 }
1901 if (parse_white(filter)==-1) return -1;
1902 if ((m=parse_stringlist(filter,&env))!=1)
1903 {
1904 if (m==0) filter->errmsg=CUS "envelope string list expected";
1905 return -1;
1906 }
1907 if (parse_white(filter)==-1) return -1;
1908 if ((m=parse_stringlist(filter,&key))!=1)
1909 {
1910 if (m==0) filter->errmsg=CUS "key string list expected";
1911 return -1;
1912 }
1913 *cond=0;
87fcc8b9 1914 for (e=env; e->length!=-1 && !*cond; ++e)
059ec3d9
PH
1915 {
1916 const uschar *envelopeExpr=CUS 0;
1917 uschar *envelope=US 0;
1918
1919 if (eq_asciicase(e,&str_from,0))
1920 {
1921 switch (addressPart)
1922 {
1923 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
1924#ifdef SUBADDRESS
1925 case ADDRPART_USER:
1926#endif
1927 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
1928 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
1929#ifdef SUBADDRESS
e4a89c47 1930 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
059ec3d9
PH
1931#endif
1932 }
1933 }
1934 else if (eq_asciicase(e,&str_to,0))
1935 {
1936 switch (addressPart)
1937 {
1938 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
1939#ifdef SUBADDRESS
e4a89c47
PH
1940 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
1941 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
059ec3d9
PH
1942#endif
1943 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
1944 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
1945 }
1946 }
024bd3c2
PH
1947#ifdef ENVELOPE_AUTH
1948 else if (eq_asciicase(e,&str_auth,0))
1949 {
1950 switch (addressPart)
1951 {
1952 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
1953#ifdef SUBADDRESS
1954 case ADDRPART_USER:
1955#endif
1956 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
1957 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
1958#ifdef SUBADDRESS
1959 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
1960#endif
1961 }
1962 }
1963#endif
059ec3d9
PH
1964 else
1965 {
1966 filter->errmsg=CUS "invalid envelope string";
1967 return -1;
1968 }
1969 if (exec && envelopeExpr)
1970 {
1971 if ((envelope=expand_string(US envelopeExpr)) == NULL)
1972 {
1973 filter->errmsg=CUS "header string expansion failed";
1974 return -1;
1975 }
1976 for (k=key; k->length!=-1; ++k)
1977 {
1978 struct String envelopeStr;
1979
1980 envelopeStr.character=envelope;
1981 envelopeStr.length=Ustrlen(envelope);
7fe1560f 1982 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
059ec3d9
PH
1983 if (*cond==-1) return -1;
1984 if (*cond) break;
1985 }
1986 }
1987 }
1988 return 1;
1989 }
1990else return 0;
1991}
1992
1993
1994/*************************************************
1995* Parse and interpret an optional block *
1996*************************************************/
1997
1998/*
1999Arguments:
2000 filter points to the Sieve filter including its state
2001 exec Execute parsed statements
2002 generated where to hang newly-generated addresses
2003
2004Returns: 2 success by stop
2005 1 other success
2006 0 no block command found
2007 -1 syntax or execution error
2008*/
2009
2010static int parse_block(struct Sieve *filter, int exec,
2011 address_item **generated)
2012{
2013int r;
2014
2015if (parse_white(filter)==-1) return -1;
2016if (*filter->pc=='{')
2017 {
2018 ++filter->pc;
2019 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2020 if (*filter->pc=='}')
2021 {
2022 ++filter->pc;
2023 return 1;
2024 }
2025 else
2026 {
8e669ac1 2027 filter->errmsg=CUS "expecting command or closing brace";
059ec3d9
PH
2028 return -1;
2029 }
2030 }
2031else return 0;
2032}
2033
2034
2035/*************************************************
2036* Match a semicolon *
2037*************************************************/
2038
2039/*
2040Arguments:
2041 filter points to the Sieve filter including its state
2042
2043Returns: 1 success
2044 -1 syntax error
2045*/
2046
2047static int parse_semicolon(struct Sieve *filter)
2048{
2049 if (parse_white(filter)==-1) return -1;
2050 if (*filter->pc==';')
2051 {
2052 ++filter->pc;
2053 return 1;
2054 }
2055 else
2056 {
2057 filter->errmsg=CUS "missing semicolon";
2058 return -1;
2059 }
2060}
2061
2062
2063/*************************************************
2064* Parse and interpret a Sieve command *
2065*************************************************/
2066
2067/*
2068Arguments:
2069 filter points to the Sieve filter including its state
2070 exec Execute parsed statements
2071 generated where to hang newly-generated addresses
2072
2073Returns: 2 success by stop
2074 1 other success
2075 -1 syntax or execution error
2076*/
2077static int parse_commands(struct Sieve *filter, int exec,
2078 address_item **generated)
2079{
2080while (*filter->pc)
2081 {
2082 if (parse_white(filter)==-1) return -1;
2083 if (parse_identifier(filter,CUS "if"))
2084 {
2085 /*
2086 if-command = "if" test block *( "elsif" test block ) [ else block ]
2087 */
2088
2089 int cond,m,unsuccessful;
2090
2091 /* test block */
2092 if (parse_white(filter)==-1) return -1;
2093 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2094 if (m==0)
2095 {
2096 filter->errmsg=CUS "missing test";
2097 return -1;
2098 }
87fcc8b9
PH
2099 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2100 (debug_selector & D_filter) != 0)
2101 {
2102 if (exec) debug_printf("if %s\n",cond?"true":"false");
2103 }
024bd3c2
PH
2104 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2105 (debug_selector & D_filter) != 0)
2106 {
2107 if (exec) debug_printf("if %s\n",cond?"true":"false");
2108 }
059ec3d9
PH
2109 m=parse_block(filter,exec ? cond : 0, generated);
2110 if (m==-1 || m==2) return m;
2111 if (m==0)
2112 {
2113 filter->errmsg=CUS "missing block";
2114 return -1;
2115 }
2116 unsuccessful = !cond;
2117 for (;;) /* elsif test block */
2118 {
2119 if (parse_white(filter)==-1) return -1;
2120 if (parse_identifier(filter,CUS "elsif"))
2121 {
2122 if (parse_white(filter)==-1) return -1;
2123 m=parse_test(filter,&cond,exec && unsuccessful);
2124 if (m==-1 || m==2) return m;
2125 if (m==0)
2126 {
2127 filter->errmsg=CUS "missing test";
2128 return -1;
2129 }
87fcc8b9
PH
2130 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2131 (debug_selector & D_filter) != 0)
2132 {
2133 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2134 }
024bd3c2
PH
2135 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2136 (debug_selector & D_filter) != 0)
2137 {
2138 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2139 }
059ec3d9
PH
2140 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2141 if (m==-1 || m==2) return m;
2142 if (m==0)
2143 {
2144 filter->errmsg=CUS "missing block";
2145 return -1;
2146 }
2147 if (exec && unsuccessful && cond) unsuccessful = 0;
2148 }
2149 else break;
2150 }
2151 /* else block */
2152 if (parse_white(filter)==-1) return -1;
2153 if (parse_identifier(filter,CUS "else"))
2154 {
2155 m=parse_block(filter,exec && unsuccessful, generated);
2156 if (m==-1 || m==2) return m;
2157 if (m==0)
2158 {
2159 filter->errmsg=CUS "missing block";
2160 return -1;
2161 }
2162 }
2163 }
2164 else if (parse_identifier(filter,CUS "stop"))
2165 {
2166 /*
2167 stop-command = "stop" { stop-options } ";"
2168 stop-options =
2169 */
2170
2171 if (parse_semicolon(filter)==-1) return -1;
2172 if (exec)
2173 {
2174 filter->pc+=Ustrlen(filter->pc);
2175 return 2;
2176 }
2177 }
2178 else if (parse_identifier(filter,CUS "keep"))
2179 {
2180 /*
2181 keep-command = "keep" { keep-options } ";"
2182 keep-options =
2183 */
2184
2185 if (parse_semicolon(filter)==-1) return -1;
2186 if (exec)
2187 {
2188 add_addr(generated,US"inbox",1,0,0,0);
2189 filter->keep = 0;
2190 }
2191 }
2192 else if (parse_identifier(filter,CUS "discard"))
2193 {
2194 /*
2195 discard-command = "discard" { discard-options } ";"
2196 discard-options =
2197 */
2198
2199 if (parse_semicolon(filter)==-1) return -1;
2200 if (exec) filter->keep=0;
2201 }
2202 else if (parse_identifier(filter,CUS "redirect"))
2203 {
2204 /*
2205 redirect-command = "redirect" redirect-options "string" ";"
2206 redirect-options =
2207 redirect-options =) ":copy"
2208 */
2209
2210 struct String recipient;
2211 int m;
2212 int copy=0;
2213
2214 for (;;)
2215 {
2216 if (parse_white(filter)==-1) return -1;
2217 if (parse_identifier(filter,CUS ":copy")==1)
2218 {
2219 if (!filter->require_copy)
2220 {
2221 filter->errmsg=CUS "missing previous require \"copy\";";
2222 return -1;
2223 }
2224 copy=1;
2225 }
2226 else break;
2227 }
2228 if (parse_white(filter)==-1) return -1;
2229 if ((m=parse_string(filter,&recipient))!=1)
2230 {
2231 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2232 return -1;
2233 }
2234 if (strchr(CCS recipient.character,'@')==(char*)0)
2235 {
2236 filter->errmsg=CUS "unqualified recipient address";
2237 return -1;
2238 }
2239 if (exec)
2240 {
2241 add_addr(generated,recipient.character,0,0,0,0);
2242 if (!copy) filter->keep = 0;
2243 }
2244 if (parse_semicolon(filter)==-1) return -1;
2245 }
2246 else if (parse_identifier(filter,CUS "fileinto"))
2247 {
2248 /*
2249 fileinto-command = "fileinto" { fileinto-options } string ";"
2250 fileinto-options =
2251 fileinto-options =) [ ":copy" ]
87fcc8b9 2252 */
059ec3d9
PH
2253
2254 struct String folder;
2255 uschar *s;
2256 int m;
2257 unsigned long maxage, maxmessages, maxstorage;
2258 int copy=0;
2259
2260 maxage = maxmessages = maxstorage = 0;
2261 if (!filter->require_fileinto)
2262 {
2263 filter->errmsg=CUS "missing previous require \"fileinto\";";
2264 return -1;
2265 }
2266 for (;;)
2267 {
2268 if (parse_white(filter)==-1) return -1;
2269 if (parse_identifier(filter,CUS ":copy")==1)
2270 {
2271 if (!filter->require_copy)
2272 {
2273 filter->errmsg=CUS "missing previous require \"copy\";";
2274 return -1;
2275 }
2276 copy=1;
2277 }
2278 else break;
2279 }
2280 if (parse_white(filter)==-1) return -1;
2281 if ((m=parse_string(filter,&folder))!=1)
2282 {
2283 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2284 return -1;
2285 }
2286 m=0; s=folder.character;
2287 if (folder.length==0) m=1;
2288 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2289 else while (*s)
2290 {
2291 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2292 ++s;
2293 }
2294 if (m)
2295 {
2296 filter->errmsg=CUS "invalid folder";
2297 return -1;
2298 }
2299 if (exec)
2300 {
2301 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2302 if (!copy) filter->keep = 0;
2303 }
2304 if (parse_semicolon(filter)==-1) return -1;
2305 }
87fcc8b9
PH
2306#ifdef NOTIFY
2307 else if (parse_identifier(filter,CUS "notify"))
2308 {
2309 /*
2310 notify-command = "notify" { notify-options } ";"
2311 notify-options = [":method" string]
2312 [":message" string]
2313 */
2314
2315 int m;
2316 struct String method;
2317 struct String message;
2318
2319 if (!filter->require_notify)
2320 {
2321 filter->errmsg=CUS "missing previous require \"notify\";";
2322 return -1;
2323 }
2324 method.character=(uschar*)0;
2325 method.length=-1;
2326 message.character=(uschar*)0;
2327 message.length=-1;
2328 for (;;)
2329 {
2330 if (parse_white(filter)==-1) return -1;
2331 if (parse_identifier(filter,CUS ":method")==1)
2332 {
2333 if (parse_white(filter)==-1) return -1;
2334 if ((m=parse_string(filter,&method))!=1)
2335 {
2336 if (m==0) filter->errmsg=CUS "method string expected";
2337 return -1;
2338 }
2339 }
2340 else if (parse_identifier(filter,CUS ":message")==1)
2341 {
2342 if (parse_white(filter)==-1) return -1;
2343 if ((m=parse_string(filter,&message))!=1)
2344 {
2345 if (m==0) filter->errmsg=CUS "message string expected";
2346 return -1;
2347 }
2348 }
2349 else break;
2350 }
2351 if (parse_semicolon(filter)==-1) return -1;
2352 }
2353#endif
059ec3d9
PH
2354#ifdef VACATION
2355 else if (parse_identifier(filter,CUS "vacation"))
2356 {
2357 /*
2358 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2359 vacation-options = [":days" number]
059ec3d9 2360 [":subject" string]
f656d135
PH
2361 [":from" string]
2362 [":addresses" string-list]
059ec3d9 2363 [":mime"]
f656d135 2364 [":handle" string]
059ec3d9
PH
2365 */
2366
2367 int m;
2368 unsigned long days;
059ec3d9 2369 struct String subject;
f656d135
PH
2370 struct String from;
2371 struct String *addresses;
059ec3d9
PH
2372 int reason_is_mime;
2373 string_item *aliases;
f656d135 2374 struct String handle;
059ec3d9
PH
2375 struct String reason;
2376
2377 if (!filter->require_vacation)
2378 {
2379 filter->errmsg=CUS "missing previous require \"vacation\";";
2380 return -1;
2381 }
2382 if (exec)
2383 {
2384 if (filter->vacation_ran)
2385 {
2386 filter->errmsg=CUS "trying to execute vacation more than once";
2387 return -1;
2388 }
2389 filter->vacation_ran=1;
2390 }
2391 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2392 subject.character=(uschar*)0;
2393 subject.length=-1;
f656d135
PH
2394 from.character=(uschar*)0;
2395 from.length=-1;
2396 addresses=(struct String*)0;
059ec3d9
PH
2397 aliases=NULL;
2398 reason_is_mime=0;
f656d135
PH
2399 handle.character=(uschar*)0;
2400 handle.length=-1;
059ec3d9
PH
2401 for (;;)
2402 {
2403 if (parse_white(filter)==-1) return -1;
2404 if (parse_identifier(filter,CUS ":days")==1)
2405 {
2406 if (parse_white(filter)==-1) return -1;
2407 if (parse_number(filter,&days)==-1) return -1;
2408 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2409 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2410 }
f656d135
PH
2411 else if (parse_identifier(filter,CUS ":subject")==1)
2412 {
2413 if (parse_white(filter)==-1) return -1;
2414 if ((m=parse_string(filter,&subject))!=1)
2415 {
2416 if (m==0) filter->errmsg=CUS "subject string expected";
2417 return -1;
2418 }
2419 }
2420 else if (parse_identifier(filter,CUS ":from")==1)
2421 {
2422 int start, end, domain;
2423 uschar *error,*ss;
2424
2425 if (parse_white(filter)==-1) return -1;
2426 if ((m=parse_string(filter,&from))!=1)
2427 {
2428 if (m==0) filter->errmsg=CUS "from string expected";
2429 return -1;
2430 }
2431 if (from.length>0)
2432 {
2433 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2434 FALSE);
2435 if (ss == NULL)
2436 {
2437 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2438 "Sieve filter: %s", from.character, error);
2439 return -1;
2440 }
2441 }
2442 else
2443 {
2444 filter->errmsg=CUS "empty :from address in Sieve filter";
2445 return -1;
2446 }
2447 }
059ec3d9
PH
2448 else if (parse_identifier(filter,CUS ":addresses")==1)
2449 {
2450 struct String *a;
2451
2452 if (parse_white(filter)==-1) return -1;
2453 if ((m=parse_stringlist(filter,&addresses))!=1)
2454 {
2455 if (m==0) filter->errmsg=CUS "addresses string list expected";
2456 return -1;
2457 }
2458 for (a=addresses; a->length!=-1; ++a)
2459 {
2460 string_item *new;
2461
2462 new=store_get(sizeof(string_item));
2463 new->text=store_get(a->length+1);
2464 if (a->length) memcpy(new->text,a->character,a->length);
2465 new->text[a->length]='\0';
2466 new->next=aliases;
2467 aliases=new;
2468 }
2469 }
f656d135
PH
2470 else if (parse_identifier(filter,CUS ":mime")==1)
2471 reason_is_mime=1;
2472 else if (parse_identifier(filter,CUS ":handle")==1)
059ec3d9
PH
2473 {
2474 if (parse_white(filter)==-1) return -1;
f656d135 2475 if ((m=parse_string(filter,&from))!=1)
059ec3d9 2476 {
f656d135 2477 if (m==0) filter->errmsg=CUS "handle string expected";
059ec3d9
PH
2478 return -1;
2479 }
2480 }
059ec3d9
PH
2481 else break;
2482 }
2483 if (parse_white(filter)==-1) return -1;
2484 if ((m=parse_string(filter,&reason))!=1)
2485 {
2486 if (m==0) filter->errmsg=CUS "missing reason string";
2487 return -1;
2488 }
5ea81592
PH
2489 if (reason_is_mime)
2490 {
2491 uschar *s,*end;
2492
2493 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2494 if (s<end)
2495 {
2496 filter->errmsg=CUS "MIME reason string contains 8bit text";
2497 return -1;
2498 }
2499 }
059ec3d9
PH
2500 if (parse_semicolon(filter)==-1) return -1;
2501
2502 if (exec)
2503 {
2504 address_item *addr;
2505 int capacity,start;
2506 uschar *buffer;
2507 int buffer_capacity;
2508 struct String key;
2509 md5 base;
2510 uschar digest[16];
2511 uschar hexdigest[33];
2512 int i;
2513 uschar *once;
2514
2515 if (filter_personal(aliases,TRUE))
2516 {
4929acf0
PH
2517 if (filter_test == FTEST_NONE)
2518 {
2519 /* ensure oncelog directory exists; failure will be detected later */
059ec3d9 2520
4929acf0
PH
2521 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2522 }
059ec3d9
PH
2523 /* build oncelog filename */
2524
2525 key.character=(uschar*)0;
2526 key.length=0;
2527 capacity=0;
f656d135 2528 if (handle.length==-1)
8e669ac1 2529 {
f656d135
PH
2530 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2531 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2532 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2533 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
8e669ac1 2534 }
f656d135
PH
2535 else
2536 key=handle;
059ec3d9
PH
2537 md5_start(&base);
2538 md5_end(&base, key.character, key.length, digest);
2539 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
4929acf0 2540 if (filter_test != FTEST_NONE)
059ec3d9 2541 {
4929acf0 2542 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
059ec3d9 2543 }
4929acf0 2544 else
059ec3d9 2545 {
4929acf0
PH
2546 capacity=Ustrlen(filter->vacation_directory);
2547 start=capacity;
2548 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2549 once=string_cat(once,&capacity,&start,hexdigest,33);
2550 once[start] = '\0';
2551
2552 /* process subject */
2553
2554 if (subject.length==-1)
2555 {
2556 expand_header(&subject,&str_subject);
4929acf0
PH
2557 capacity=6;
2558 start=6;
2559 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2560 subject.length=start;
2561 }
2562
2563 /* add address to list of generated addresses */
2564
2565 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2566 setflag(addr, af_pfr);
2567 setflag(addr, af_ignore_error);
2568 addr->next = *generated;
2569 *generated = addr;
2570 addr->reply = store_get(sizeof(reply_item));
2571 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2572 addr->reply->to = string_copy(sender_address);
f656d135
PH
2573 if (from.length==-1)
2574 addr->reply->from = expand_string(US"$local_part@$domain");
2575 else
2576 addr->reply->from = from.character;
4929acf0
PH
2577 /* Allocation is larger than neccessary, but enough even for split MIME words */
2578 buffer_capacity=16+4*subject.length;
2579 buffer=store_get(buffer_capacity);
2580 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2581 addr->reply->oncelog=once;
2582 addr->reply->once_repeat=days*86400;
2583
2584 /* build body and MIME headers */
2585
2586 if (reason_is_mime)
2587 {
2588 uschar *mime_body,*reason_end;
059ec3d9 2589#ifdef RFC_EOL
4929acf0 2590 static const uschar nlnl[]="\r\n\r\n";
059ec3d9 2591#else
4929acf0 2592 static const uschar nlnl[]="\n\n";
059ec3d9
PH
2593#endif
2594
4929acf0
PH
2595 for
2596 (
2597 mime_body=reason.character,reason_end=reason.character+reason.length;
2598 mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
2599 ++mime_body
2600 );
2601 capacity = 0;
2602 start = 0;
2603 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2604 addr->reply->headers[start] = '\0';
2605 capacity = 0;
2606 start = 0;
2607 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
2608 else mime_body=reason_end-1;
2609 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2610 addr->reply->text[start] = '\0';
2611 }
2612 else
2613 {
2614 struct String qp;
2615
2616 capacity = 0;
2617 start = reason.length;
2618 addr->reply->headers = US"MIME-Version: 1.0\n"
2619 "Content-Type: text/plain;\n"
2620 "\tcharset=\"utf-8\"\n"
2621 "Content-Transfer-Encoding: quoted-printable";
2622 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2623 }
059ec3d9 2624 }
4929acf0
PH
2625 }
2626 else if (filter_test != FTEST_NONE)
059ec3d9 2627 {
4929acf0 2628 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
059ec3d9 2629 }
059ec3d9
PH
2630 }
2631 }
4929acf0 2632 else break;
059ec3d9 2633#endif
059ec3d9
PH
2634 }
2635return 1;
2636}
2637
2638
2639/*************************************************
2640* Parse and interpret a sieve filter *
2641*************************************************/
2642
2643/*
2644Arguments:
2645 filter points to the Sieve filter including its state
2646 exec Execute parsed statements
2647 generated where to hang newly-generated addresses
2648
2649Returns: 1 success
2650 -1 syntax or execution error
2651*/
2652
2653static int parse_start(struct Sieve *filter, int exec,
2654 address_item **generated)
2655{
2656filter->pc=filter->filter;
2657filter->line=1;
2658filter->keep=1;
2659filter->require_envelope=0;
2660filter->require_fileinto=0;
024bd3c2
PH
2661#ifdef ENVELOPE_AUTH
2662filter->require_envelope_auth=0;
2663#endif
87fcc8b9
PH
2664#ifdef NOTIFY
2665filter->require_notify=0;
2666#endif
059ec3d9
PH
2667#ifdef SUBADDRESS
2668filter->require_subaddress=0;
2669#endif
2670#ifdef VACATION
2671filter->require_vacation=0;
2672filter->vacation_ran=0;
2673#endif
2674filter->require_copy=0;
2675filter->require_iascii_numeric=0;
2676
2677if (parse_white(filter)==-1) return -1;
2678
4929acf0 2679if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
059ec3d9
PH
2680 {
2681 DIR *oncelogdir;
2682 struct dirent *oncelog;
2683 struct stat properties;
2684 time_t now;
2685
2686 /* clean up old vacation log databases */
2687
2688 oncelogdir=opendir(CS filter->vacation_directory);
2689
2690 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2691 {
2692 filter->errmsg=CUS "unable to open vacation directory";
2693 return -1;
2694 }
2695
2696 if (oncelogdir != NULL)
2697 {
2698 time(&now);
2699
2700 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2701 {
2702 if (strlen(oncelog->d_name)==32)
2703 {
2704 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2705 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2706 Uunlink(s);
2707 }
2708 }
2709 closedir(oncelogdir);
2710 }
2711 }
2712
2713while (parse_identifier(filter,CUS "require"))
2714 {
2715 /*
2716 require-command = "require" <capabilities: string-list>
2717 */
2718
2719 struct String *cap,*check;
2720 int m;
2721
2722 if (parse_white(filter)==-1) return -1;
2723 if ((m=parse_stringlist(filter,&cap))!=1)
2724 {
2725 if (m==0) filter->errmsg=CUS "capability string list expected";
2726 return -1;
2727 }
2728 for (check=cap; check->character; ++check)
2729 {
5ea81592
PH
2730 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
2731 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
024bd3c2
PH
2732#ifdef ENVELOPE_AUTH
2733 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
2734#endif
87fcc8b9
PH
2735#ifdef NOTIFY
2736 else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
2737#endif
059ec3d9 2738#ifdef SUBADDRESS
5ea81592 2739 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
059ec3d9
PH
2740#endif
2741#ifdef VACATION
5ea81592 2742 else if (eq_octet(check,&str_vacation,0))
059ec3d9 2743 {
4929acf0 2744 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
059ec3d9
PH
2745 {
2746 filter->errmsg=CUS "vacation disabled";
2747 return -1;
2748 }
2749 filter->require_vacation=1;
2750 }
2751#endif
5ea81592
PH
2752 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
2753 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
2754 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
2755 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
2756 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
059ec3d9
PH
2757 else
2758 {
2759 filter->errmsg=CUS "unknown capability";
2760 return -1;
2761 }
2762 }
2763 if (parse_semicolon(filter)==-1) return -1;
2764 }
2765 if (parse_commands(filter,exec,generated)==-1) return -1;
2766 if (*filter->pc)
2767 {
2768 filter->errmsg=CUS "syntax error";
2769 return -1;
2770 }
2771 return 1;
2772}
2773
2774
2775/*************************************************
2776* Interpret a sieve filter file *
2777*************************************************/
2778
2779/*
2780Arguments:
2781 filter points to the entire file, read into store as a single string
2782 options controls whether various special things are allowed, and requests
2783 special actions (not currently used)
2784 sieve_vacation_directory where to store vacation "once" files
e4a89c47
PH
2785 useraddress string expression for :user part of address
2786 subaddress string expression for :subaddress part of address
059ec3d9
PH
2787 generated where to hang newly-generated addresses
2788 error where to pass back an error text
2789
2790Returns: FF_DELIVERED success, a significant action was taken
2791 FF_NOTDELIVERED success, no significant action
2792 FF_DEFER defer requested
2793 FF_FAIL fail requested
2794 FF_FREEZE freeze requested
2795 FF_ERROR there was a problem
2796*/
2797
2798int
2799sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
e4a89c47 2800 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
059ec3d9
PH
2801{
2802struct Sieve sieve;
2803int r;
2804uschar *msg;
2805
2806options = options; /* Keep picky compilers happy */
2807error = error;
2808
2809DEBUG(D_route) debug_printf("Sieve: start of processing\n");
2810sieve.filter=filter;
2811
2812if (vacation_directory == NULL)
2813 sieve.vacation_directory = NULL;
2814else
2815 {
2816 sieve.vacation_directory=expand_string(vacation_directory);
2817 if (sieve.vacation_directory == NULL)
2818 {
2819 *error = string_sprintf("failed to expand \"%s\" "
2820 "(sieve_vacation_directory): %s", vacation_directory,
2821 expand_string_message);
2822 return FF_ERROR;
2823 }
2824 }
2825
e4a89c47
PH
2826sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
2827sieve.subaddress = subaddress;
2828
059ec3d9
PH
2829#ifdef COMPILE_SYNTAX_CHECKER
2830if (parse_start(&sieve,0,generated)==1)
2831#else
2832if (parse_start(&sieve,1,generated)==1)
2833#endif
2834 {
2835 if (sieve.keep)
2836 {
2837 add_addr(generated,US"inbox",1,0,0,0);
1c59d63b 2838 msg = string_sprintf("Implicit keep");
059ec3d9
PH
2839 r = FF_DELIVERED;
2840 }
1c59d63b 2841 else
059ec3d9 2842 {
1c59d63b 2843 msg = string_sprintf("No implicit keep");
059ec3d9
PH
2844 r = FF_DELIVERED;
2845 }
2846 }
2847else
2848 {
2849 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
2850#ifdef COMPILE_SYNTAX_CHECKER
2851 r = FF_ERROR;
2852 *error = msg;
2853#else
2854 add_addr(generated,US"inbox",1,0,0,0);
2855 r = FF_DELIVERED;
2856#endif
2857 }
2858
2859#ifndef COMPILE_SYNTAX_CHECKER
f05da2e8 2860if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
059ec3d9
PH
2861 else debug_printf("%s\n", msg);
2862#endif
2863
2864DEBUG(D_route) debug_printf("Sieve: end of processing\n");
2865return r;
2866}