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