${prvs was broken on systems where time_t is long long.
[exim.git] / src / src / sieve.c
CommitLineData
4bc97d56 1/* $Cambridge: exim/src/src/sieve.c,v 1.17 2005/11/28 09:47:20 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 }
059ec3d9
PH
2104 m=parse_block(filter,exec ? cond : 0, generated);
2105 if (m==-1 || m==2) return m;
2106 if (m==0)
2107 {
2108 filter->errmsg=CUS "missing block";
2109 return -1;
2110 }
2111 unsuccessful = !cond;
2112 for (;;) /* elsif test block */
2113 {
2114 if (parse_white(filter)==-1) return -1;
2115 if (parse_identifier(filter,CUS "elsif"))
2116 {
2117 if (parse_white(filter)==-1) return -1;
2118 m=parse_test(filter,&cond,exec && unsuccessful);
2119 if (m==-1 || m==2) return m;
2120 if (m==0)
2121 {
2122 filter->errmsg=CUS "missing test";
2123 return -1;
2124 }
87fcc8b9
PH
2125 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2126 (debug_selector & D_filter) != 0)
2127 {
2128 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2129 }
059ec3d9
PH
2130 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2131 if (m==-1 || m==2) return m;
2132 if (m==0)
2133 {
2134 filter->errmsg=CUS "missing block";
2135 return -1;
2136 }
2137 if (exec && unsuccessful && cond) unsuccessful = 0;
2138 }
2139 else break;
2140 }
2141 /* else block */
2142 if (parse_white(filter)==-1) return -1;
2143 if (parse_identifier(filter,CUS "else"))
2144 {
2145 m=parse_block(filter,exec && unsuccessful, generated);
2146 if (m==-1 || m==2) return m;
2147 if (m==0)
2148 {
2149 filter->errmsg=CUS "missing block";
2150 return -1;
2151 }
2152 }
2153 }
2154 else if (parse_identifier(filter,CUS "stop"))
2155 {
2156 /*
2157 stop-command = "stop" { stop-options } ";"
2158 stop-options =
2159 */
2160
2161 if (parse_semicolon(filter)==-1) return -1;
2162 if (exec)
2163 {
2164 filter->pc+=Ustrlen(filter->pc);
2165 return 2;
2166 }
2167 }
2168 else if (parse_identifier(filter,CUS "keep"))
2169 {
2170 /*
2171 keep-command = "keep" { keep-options } ";"
2172 keep-options =
2173 */
2174
2175 if (parse_semicolon(filter)==-1) return -1;
2176 if (exec)
2177 {
2178 add_addr(generated,US"inbox",1,0,0,0);
2179 filter->keep = 0;
2180 }
2181 }
2182 else if (parse_identifier(filter,CUS "discard"))
2183 {
2184 /*
2185 discard-command = "discard" { discard-options } ";"
2186 discard-options =
2187 */
2188
2189 if (parse_semicolon(filter)==-1) return -1;
2190 if (exec) filter->keep=0;
2191 }
2192 else if (parse_identifier(filter,CUS "redirect"))
2193 {
2194 /*
2195 redirect-command = "redirect" redirect-options "string" ";"
2196 redirect-options =
2197 redirect-options =) ":copy"
2198 */
2199
2200 struct String recipient;
2201 int m;
2202 int copy=0;
2203
2204 for (;;)
2205 {
2206 if (parse_white(filter)==-1) return -1;
2207 if (parse_identifier(filter,CUS ":copy")==1)
2208 {
2209 if (!filter->require_copy)
2210 {
2211 filter->errmsg=CUS "missing previous require \"copy\";";
2212 return -1;
2213 }
2214 copy=1;
2215 }
2216 else break;
2217 }
2218 if (parse_white(filter)==-1) return -1;
2219 if ((m=parse_string(filter,&recipient))!=1)
2220 {
2221 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2222 return -1;
2223 }
2224 if (strchr(CCS recipient.character,'@')==(char*)0)
2225 {
2226 filter->errmsg=CUS "unqualified recipient address";
2227 return -1;
2228 }
2229 if (exec)
2230 {
2231 add_addr(generated,recipient.character,0,0,0,0);
2232 if (!copy) filter->keep = 0;
2233 }
2234 if (parse_semicolon(filter)==-1) return -1;
2235 }
2236 else if (parse_identifier(filter,CUS "fileinto"))
2237 {
2238 /*
2239 fileinto-command = "fileinto" { fileinto-options } string ";"
2240 fileinto-options =
2241 fileinto-options =) [ ":copy" ]
87fcc8b9 2242 */
059ec3d9
PH
2243
2244 struct String folder;
2245 uschar *s;
2246 int m;
2247 unsigned long maxage, maxmessages, maxstorage;
2248 int copy=0;
2249
2250 maxage = maxmessages = maxstorage = 0;
2251 if (!filter->require_fileinto)
2252 {
2253 filter->errmsg=CUS "missing previous require \"fileinto\";";
2254 return -1;
2255 }
2256 for (;;)
2257 {
2258 if (parse_white(filter)==-1) return -1;
2259 if (parse_identifier(filter,CUS ":copy")==1)
2260 {
2261 if (!filter->require_copy)
2262 {
2263 filter->errmsg=CUS "missing previous require \"copy\";";
2264 return -1;
2265 }
2266 copy=1;
2267 }
2268 else break;
2269 }
2270 if (parse_white(filter)==-1) return -1;
2271 if ((m=parse_string(filter,&folder))!=1)
2272 {
2273 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2274 return -1;
2275 }
2276 m=0; s=folder.character;
2277 if (folder.length==0) m=1;
2278 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2279 else while (*s)
2280 {
2281 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2282 ++s;
2283 }
2284 if (m)
2285 {
2286 filter->errmsg=CUS "invalid folder";
2287 return -1;
2288 }
2289 if (exec)
2290 {
2291 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2292 if (!copy) filter->keep = 0;
2293 }
2294 if (parse_semicolon(filter)==-1) return -1;
2295 }
87fcc8b9
PH
2296#ifdef NOTIFY
2297 else if (parse_identifier(filter,CUS "notify"))
2298 {
2299 /*
2300 notify-command = "notify" { notify-options } ";"
2301 notify-options = [":method" string]
2302 [":message" string]
2303 */
2304
2305 int m;
2306 struct String method;
2307 struct String message;
2308
2309 if (!filter->require_notify)
2310 {
2311 filter->errmsg=CUS "missing previous require \"notify\";";
2312 return -1;
2313 }
2314 method.character=(uschar*)0;
2315 method.length=-1;
2316 message.character=(uschar*)0;
2317 message.length=-1;
2318 for (;;)
2319 {
2320 if (parse_white(filter)==-1) return -1;
2321 if (parse_identifier(filter,CUS ":method")==1)
2322 {
2323 if (parse_white(filter)==-1) return -1;
2324 if ((m=parse_string(filter,&method))!=1)
2325 {
2326 if (m==0) filter->errmsg=CUS "method string expected";
2327 return -1;
2328 }
2329 }
2330 else if (parse_identifier(filter,CUS ":message")==1)
2331 {
2332 if (parse_white(filter)==-1) return -1;
2333 if ((m=parse_string(filter,&message))!=1)
2334 {
2335 if (m==0) filter->errmsg=CUS "message string expected";
2336 return -1;
2337 }
2338 }
2339 else break;
2340 }
2341 if (parse_semicolon(filter)==-1) return -1;
2342 }
2343#endif
059ec3d9
PH
2344#ifdef VACATION
2345 else if (parse_identifier(filter,CUS "vacation"))
2346 {
2347 /*
2348 vacation-command = "vacation" { vacation-options } <reason: string> ";"
2349 vacation-options = [":days" number]
059ec3d9 2350 [":subject" string]
f656d135
PH
2351 [":from" string]
2352 [":addresses" string-list]
059ec3d9 2353 [":mime"]
f656d135 2354 [":handle" string]
059ec3d9
PH
2355 */
2356
2357 int m;
2358 unsigned long days;
059ec3d9 2359 struct String subject;
f656d135
PH
2360 struct String from;
2361 struct String *addresses;
059ec3d9
PH
2362 int reason_is_mime;
2363 string_item *aliases;
f656d135 2364 struct String handle;
059ec3d9
PH
2365 struct String reason;
2366
2367 if (!filter->require_vacation)
2368 {
2369 filter->errmsg=CUS "missing previous require \"vacation\";";
2370 return -1;
2371 }
2372 if (exec)
2373 {
2374 if (filter->vacation_ran)
2375 {
2376 filter->errmsg=CUS "trying to execute vacation more than once";
2377 return -1;
2378 }
2379 filter->vacation_ran=1;
2380 }
2381 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
2382 subject.character=(uschar*)0;
2383 subject.length=-1;
f656d135
PH
2384 from.character=(uschar*)0;
2385 from.length=-1;
2386 addresses=(struct String*)0;
059ec3d9
PH
2387 aliases=NULL;
2388 reason_is_mime=0;
f656d135
PH
2389 handle.character=(uschar*)0;
2390 handle.length=-1;
059ec3d9
PH
2391 for (;;)
2392 {
2393 if (parse_white(filter)==-1) return -1;
2394 if (parse_identifier(filter,CUS ":days")==1)
2395 {
2396 if (parse_white(filter)==-1) return -1;
2397 if (parse_number(filter,&days)==-1) return -1;
2398 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
2399 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
2400 }
f656d135
PH
2401 else if (parse_identifier(filter,CUS ":subject")==1)
2402 {
2403 if (parse_white(filter)==-1) return -1;
2404 if ((m=parse_string(filter,&subject))!=1)
2405 {
2406 if (m==0) filter->errmsg=CUS "subject string expected";
2407 return -1;
2408 }
2409 }
2410 else if (parse_identifier(filter,CUS ":from")==1)
2411 {
2412 int start, end, domain;
2413 uschar *error,*ss;
2414
2415 if (parse_white(filter)==-1) return -1;
2416 if ((m=parse_string(filter,&from))!=1)
2417 {
2418 if (m==0) filter->errmsg=CUS "from string expected";
2419 return -1;
2420 }
2421 if (from.length>0)
2422 {
2423 ss = parse_extract_address(from.character, &error, &start, &end, &domain,
2424 FALSE);
2425 if (ss == NULL)
2426 {
2427 filter->errmsg=string_sprintf("malformed address \"%s\" in "
2428 "Sieve filter: %s", from.character, error);
2429 return -1;
2430 }
2431 }
2432 else
2433 {
2434 filter->errmsg=CUS "empty :from address in Sieve filter";
2435 return -1;
2436 }
2437 }
059ec3d9
PH
2438 else if (parse_identifier(filter,CUS ":addresses")==1)
2439 {
2440 struct String *a;
2441
2442 if (parse_white(filter)==-1) return -1;
2443 if ((m=parse_stringlist(filter,&addresses))!=1)
2444 {
2445 if (m==0) filter->errmsg=CUS "addresses string list expected";
2446 return -1;
2447 }
2448 for (a=addresses; a->length!=-1; ++a)
2449 {
2450 string_item *new;
2451
2452 new=store_get(sizeof(string_item));
2453 new->text=store_get(a->length+1);
2454 if (a->length) memcpy(new->text,a->character,a->length);
2455 new->text[a->length]='\0';
2456 new->next=aliases;
2457 aliases=new;
2458 }
2459 }
f656d135
PH
2460 else if (parse_identifier(filter,CUS ":mime")==1)
2461 reason_is_mime=1;
2462 else if (parse_identifier(filter,CUS ":handle")==1)
059ec3d9
PH
2463 {
2464 if (parse_white(filter)==-1) return -1;
f656d135 2465 if ((m=parse_string(filter,&from))!=1)
059ec3d9 2466 {
f656d135 2467 if (m==0) filter->errmsg=CUS "handle string expected";
059ec3d9
PH
2468 return -1;
2469 }
2470 }
059ec3d9
PH
2471 else break;
2472 }
2473 if (parse_white(filter)==-1) return -1;
2474 if ((m=parse_string(filter,&reason))!=1)
2475 {
2476 if (m==0) filter->errmsg=CUS "missing reason string";
2477 return -1;
2478 }
5ea81592
PH
2479 if (reason_is_mime)
2480 {
2481 uschar *s,*end;
2482
2483 for (s=reason.character,end=reason.character+reason.length; s<end && (*s&0x80)==0; ++s);
2484 if (s<end)
2485 {
2486 filter->errmsg=CUS "MIME reason string contains 8bit text";
2487 return -1;
2488 }
2489 }
059ec3d9
PH
2490 if (parse_semicolon(filter)==-1) return -1;
2491
2492 if (exec)
2493 {
2494 address_item *addr;
2495 int capacity,start;
2496 uschar *buffer;
2497 int buffer_capacity;
2498 struct String key;
2499 md5 base;
2500 uschar digest[16];
2501 uschar hexdigest[33];
2502 int i;
2503 uschar *once;
2504
2505 if (filter_personal(aliases,TRUE))
2506 {
4929acf0
PH
2507 if (filter_test == FTEST_NONE)
2508 {
2509 /* ensure oncelog directory exists; failure will be detected later */
059ec3d9 2510
4929acf0
PH
2511 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2512 }
059ec3d9
PH
2513 /* build oncelog filename */
2514
2515 key.character=(uschar*)0;
2516 key.length=0;
2517 capacity=0;
f656d135 2518 if (handle.length==-1)
8e669ac1 2519 {
f656d135
PH
2520 if (subject.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,subject.character,subject.length);
2521 if (from.length!=-1) key.character=string_cat(key.character,&capacity,&key.length,from.character,from.length);
2522 key.character=string_cat(key.character,&capacity,&key.length,reason_is_mime?US"1":US"0",1);
2523 key.character=string_cat(key.character,&capacity,&key.length,reason.character,reason.length);
8e669ac1 2524 }
f656d135
PH
2525 else
2526 key=handle;
059ec3d9
PH
2527 md5_start(&base);
2528 md5_end(&base, key.character, key.length, digest);
2529 for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
4929acf0 2530 if (filter_test != FTEST_NONE)
059ec3d9 2531 {
4929acf0 2532 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
059ec3d9 2533 }
4929acf0 2534 else
059ec3d9 2535 {
4929acf0
PH
2536 capacity=Ustrlen(filter->vacation_directory);
2537 start=capacity;
2538 once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
2539 once=string_cat(once,&capacity,&start,hexdigest,33);
2540 once[start] = '\0';
2541
2542 /* process subject */
2543
2544 if (subject.length==-1)
2545 {
2546 expand_header(&subject,&str_subject);
4929acf0
PH
2547 capacity=6;
2548 start=6;
2549 subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
2550 subject.length=start;
2551 }
2552
2553 /* add address to list of generated addresses */
2554
2555 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
2556 setflag(addr, af_pfr);
2557 setflag(addr, af_ignore_error);
2558 addr->next = *generated;
2559 *generated = addr;
2560 addr->reply = store_get(sizeof(reply_item));
2561 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
2562 addr->reply->to = string_copy(sender_address);
f656d135
PH
2563 if (from.length==-1)
2564 addr->reply->from = expand_string(US"$local_part@$domain");
2565 else
2566 addr->reply->from = from.character;
4929acf0
PH
2567 /* Allocation is larger than neccessary, but enough even for split MIME words */
2568 buffer_capacity=16+4*subject.length;
2569 buffer=store_get(buffer_capacity);
2570 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
2571 addr->reply->oncelog=once;
2572 addr->reply->once_repeat=days*86400;
2573
2574 /* build body and MIME headers */
2575
2576 if (reason_is_mime)
2577 {
2578 uschar *mime_body,*reason_end;
059ec3d9 2579#ifdef RFC_EOL
4929acf0 2580 static const uschar nlnl[]="\r\n\r\n";
059ec3d9 2581#else
4929acf0 2582 static const uschar nlnl[]="\n\n";
059ec3d9
PH
2583#endif
2584
4929acf0
PH
2585 for
2586 (
2587 mime_body=reason.character,reason_end=reason.character+reason.length;
2588 mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
2589 ++mime_body
2590 );
2591 capacity = 0;
2592 start = 0;
2593 addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
2594 addr->reply->headers[start] = '\0';
2595 capacity = 0;
2596 start = 0;
2597 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
2598 else mime_body=reason_end-1;
2599 addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
2600 addr->reply->text[start] = '\0';
2601 }
2602 else
2603 {
2604 struct String qp;
2605
2606 capacity = 0;
2607 start = reason.length;
2608 addr->reply->headers = US"MIME-Version: 1.0\n"
2609 "Content-Type: text/plain;\n"
2610 "\tcharset=\"utf-8\"\n"
2611 "Content-Transfer-Encoding: quoted-printable";
2612 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
2613 }
059ec3d9 2614 }
4929acf0
PH
2615 }
2616 else if (filter_test != FTEST_NONE)
059ec3d9 2617 {
4929acf0 2618 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
059ec3d9 2619 }
059ec3d9
PH
2620 }
2621 }
4929acf0 2622 else break;
059ec3d9 2623#endif
059ec3d9
PH
2624 }
2625return 1;
2626}
2627
2628
2629/*************************************************
2630* Parse and interpret a sieve filter *
2631*************************************************/
2632
2633/*
2634Arguments:
2635 filter points to the Sieve filter including its state
2636 exec Execute parsed statements
2637 generated where to hang newly-generated addresses
2638
2639Returns: 1 success
2640 -1 syntax or execution error
2641*/
2642
2643static int parse_start(struct Sieve *filter, int exec,
2644 address_item **generated)
2645{
2646filter->pc=filter->filter;
2647filter->line=1;
2648filter->keep=1;
2649filter->require_envelope=0;
2650filter->require_fileinto=0;
024bd3c2
PH
2651#ifdef ENVELOPE_AUTH
2652filter->require_envelope_auth=0;
2653#endif
87fcc8b9
PH
2654#ifdef NOTIFY
2655filter->require_notify=0;
2656#endif
059ec3d9
PH
2657#ifdef SUBADDRESS
2658filter->require_subaddress=0;
2659#endif
2660#ifdef VACATION
2661filter->require_vacation=0;
2662filter->vacation_ran=0;
2663#endif
2664filter->require_copy=0;
2665filter->require_iascii_numeric=0;
2666
2667if (parse_white(filter)==-1) return -1;
2668
4929acf0 2669if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
059ec3d9
PH
2670 {
2671 DIR *oncelogdir;
2672 struct dirent *oncelog;
2673 struct stat properties;
2674 time_t now;
2675
2676 /* clean up old vacation log databases */
2677
2678 oncelogdir=opendir(CS filter->vacation_directory);
2679
2680 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
2681 {
2682 filter->errmsg=CUS "unable to open vacation directory";
2683 return -1;
2684 }
2685
2686 if (oncelogdir != NULL)
2687 {
2688 time(&now);
2689
2690 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
2691 {
2692 if (strlen(oncelog->d_name)==32)
2693 {
2694 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
2695 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
2696 Uunlink(s);
2697 }
2698 }
2699 closedir(oncelogdir);
2700 }
2701 }
2702
2703while (parse_identifier(filter,CUS "require"))
2704 {
2705 /*
2706 require-command = "require" <capabilities: string-list>
2707 */
2708
2709 struct String *cap,*check;
2710 int m;
2711
2712 if (parse_white(filter)==-1) return -1;
2713 if ((m=parse_stringlist(filter,&cap))!=1)
2714 {
2715 if (m==0) filter->errmsg=CUS "capability string list expected";
2716 return -1;
2717 }
2718 for (check=cap; check->character; ++check)
2719 {
5ea81592
PH
2720 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
2721 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
024bd3c2
PH
2722#ifdef ENVELOPE_AUTH
2723 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
2724#endif
87fcc8b9
PH
2725#ifdef NOTIFY
2726 else if (eq_octet(check,&str_notify,0)) filter->require_notify=1;
2727#endif
059ec3d9 2728#ifdef SUBADDRESS
5ea81592 2729 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
059ec3d9
PH
2730#endif
2731#ifdef VACATION
5ea81592 2732 else if (eq_octet(check,&str_vacation,0))
059ec3d9 2733 {
4929acf0 2734 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
059ec3d9
PH
2735 {
2736 filter->errmsg=CUS "vacation disabled";
2737 return -1;
2738 }
2739 filter->require_vacation=1;
2740 }
2741#endif
5ea81592
PH
2742 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
2743 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
2744 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
2745 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
2746 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
059ec3d9
PH
2747 else
2748 {
2749 filter->errmsg=CUS "unknown capability";
2750 return -1;
2751 }
2752 }
2753 if (parse_semicolon(filter)==-1) return -1;
2754 }
2755 if (parse_commands(filter,exec,generated)==-1) return -1;
2756 if (*filter->pc)
2757 {
2758 filter->errmsg=CUS "syntax error";
2759 return -1;
2760 }
2761 return 1;
2762}
2763
2764
2765/*************************************************
2766* Interpret a sieve filter file *
2767*************************************************/
2768
2769/*
2770Arguments:
2771 filter points to the entire file, read into store as a single string
2772 options controls whether various special things are allowed, and requests
2773 special actions (not currently used)
2774 sieve_vacation_directory where to store vacation "once" files
e4a89c47
PH
2775 useraddress string expression for :user part of address
2776 subaddress string expression for :subaddress part of address
059ec3d9
PH
2777 generated where to hang newly-generated addresses
2778 error where to pass back an error text
2779
2780Returns: FF_DELIVERED success, a significant action was taken
2781 FF_NOTDELIVERED success, no significant action
2782 FF_DEFER defer requested
2783 FF_FAIL fail requested
2784 FF_FREEZE freeze requested
2785 FF_ERROR there was a problem
2786*/
2787
2788int
2789sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
e4a89c47 2790 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
059ec3d9
PH
2791{
2792struct Sieve sieve;
2793int r;
2794uschar *msg;
2795
2796options = options; /* Keep picky compilers happy */
2797error = error;
2798
2799DEBUG(D_route) debug_printf("Sieve: start of processing\n");
2800sieve.filter=filter;
2801
2802if (vacation_directory == NULL)
2803 sieve.vacation_directory = NULL;
2804else
2805 {
2806 sieve.vacation_directory=expand_string(vacation_directory);
2807 if (sieve.vacation_directory == NULL)
2808 {
2809 *error = string_sprintf("failed to expand \"%s\" "
2810 "(sieve_vacation_directory): %s", vacation_directory,
2811 expand_string_message);
2812 return FF_ERROR;
2813 }
2814 }
2815
e4a89c47
PH
2816sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
2817sieve.subaddress = subaddress;
2818
059ec3d9
PH
2819#ifdef COMPILE_SYNTAX_CHECKER
2820if (parse_start(&sieve,0,generated)==1)
2821#else
2822if (parse_start(&sieve,1,generated)==1)
2823#endif
2824 {
2825 if (sieve.keep)
2826 {
2827 add_addr(generated,US"inbox",1,0,0,0);
1c59d63b 2828 msg = string_sprintf("Implicit keep");
059ec3d9
PH
2829 r = FF_DELIVERED;
2830 }
1c59d63b 2831 else
059ec3d9 2832 {
1c59d63b 2833 msg = string_sprintf("No implicit keep");
059ec3d9
PH
2834 r = FF_DELIVERED;
2835 }
2836 }
2837else
2838 {
2839 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
2840#ifdef COMPILE_SYNTAX_CHECKER
2841 r = FF_ERROR;
2842 *error = msg;
2843#else
2844 add_addr(generated,US"inbox",1,0,0,0);
2845 r = FF_DELIVERED;
2846#endif
2847 }
2848
2849#ifndef COMPILE_SYNTAX_CHECKER
f05da2e8 2850if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
059ec3d9
PH
2851 else debug_printf("%s\n", msg);
2852#endif
2853
2854DEBUG(D_route) debug_printf("Sieve: end of processing\n");
2855return r;
2856}