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