Updated documentation for 4.67.
[exim.git] / src / src / sieve.c
CommitLineData
84024b72 1/* $Cambridge: exim/src/src/sieve.c,v 1.26 2007/03/21 15:15:12 ph10 Exp $ */
059ec3d9
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
50c99ba6 7/* Copyright (c) Michael Haardt 2003-2006 */
059ec3d9
PH
8/* See the file NOTICE for conditions of use and distribution. */
9
10/* This code was contributed by Michael Haardt. */
11
12
13/* Sieve mail filter. */
14
15#include <ctype.h>
16#include <errno.h>
17#include <limits.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "exim.h"
22
23#if HAVE_ICONV
24#include <iconv.h>
25#endif
26
27/* Define this for RFC compliant \r\n end-of-line terminators. */
28/* Undefine it for UNIX-style \n end-of-line terminators (default). */
29#undef RFC_EOL
30
84024b72
PH
31/* Define this for development of the Sieve extension "encoded-character". */
32#undef ENCODED_CHARACTER
33
024bd3c2
PH
34/* Define this for development of the Sieve extension "envelope-auth". */
35#undef ENVELOPE_AUTH
36
7c5214ec
PH
37/* Define this for development of the Sieve extension "enotify". */
38#undef ENOTIFY
87fcc8b9
PH
39
40/* Define this for the Sieve extension "subaddress". */
5ea81592 41#define SUBADDRESS
059ec3d9 42
87fcc8b9 43/* Define this for the Sieve extension "vacation". */
059ec3d9
PH
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
55struct 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;
84024b72
PH
64#ifdef ENCODED_CHARACTER
65 int require_encoded_character;
66#endif
024bd3c2
PH
67#ifdef ENVELOPE_AUTH
68 int require_envelope_auth;
69#endif
7c5214ec
PH
70#ifdef ENOTIFY
71 int require_enotify;
50c99ba6 72 struct Notification *notified;
87fcc8b9 73#endif
059ec3d9
PH
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;
e4a89c47
PH
82 const uschar *subaddress;
83 const uschar *useraddress;
059ec3d9
PH
84 int require_copy;
85 int require_iascii_numeric;
86 };
87
5ea81592 88enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC };
059ec3d9
PH
89enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES };
90#ifdef SUBADDRESS
91enum AddressPart { ADDRPART_USER, ADDRPART_DETAIL, ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
92#else
93enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL };
94#endif
95enum RelOp { LT, LE, EQ, GE, GT, NE };
96
97struct String
98 {
99 uschar *character;
100 int length;
101 };
102
50c99ba6
PH
103struct Notification
104 {
105 struct String method;
7c5214ec 106 struct String importance;
50c99ba6
PH
107 struct String message;
108 struct Notification *next;
109 };
110
7c5214ec 111static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix);
059ec3d9
PH
112static int parse_test(struct Sieve *filter, int *cond, int exec);
113static int parse_commands(struct Sieve *filter, int exec, address_item **generated);
114
115static uschar str_from_c[]="From";
116static const struct String str_from={ str_from_c, 4 };
117static uschar str_to_c[]="To";
118static const struct String str_to={ str_to_c, 2 };
119static uschar str_cc_c[]="Cc";
120static const struct String str_cc={ str_cc_c, 2 };
121static uschar str_bcc_c[]="Bcc";
122static const struct String str_bcc={ str_bcc_c, 3 };
024bd3c2
PH
123static uschar str_auth_c[]="auth";
124static const struct String str_auth={ str_auth_c, 4 };
059ec3d9
PH
125static uschar str_sender_c[]="Sender";
126static const struct String str_sender={ str_sender_c, 6 };
127static uschar str_resent_from_c[]="Resent-From";
128static const struct String str_resent_from={ str_resent_from_c, 11 };
129static uschar str_resent_to_c[]="Resent-To";
130static const struct String str_resent_to={ str_resent_to_c, 9 };
131static uschar str_fileinto_c[]="fileinto";
132static const struct String str_fileinto={ str_fileinto_c, 8 };
133static uschar str_envelope_c[]="envelope";
134static const struct String str_envelope={ str_envelope_c, 8 };
84024b72
PH
135#ifdef ENCODED_CHARACTER
136static uschar str_encoded_character_c[]="encoded-character";
137static const struct String str_encoded_character={ str_encoded_character_c, 17 };
138#endif
024bd3c2
PH
139#ifdef ENVELOPE_AUTH
140static uschar str_envelope_auth_c[]="envelope-auth";
141static const struct String str_envelope_auth={ str_envelope_auth_c, 13 };
142#endif
7c5214ec
PH
143#ifdef ENOTIFY
144static uschar str_enotify_c[]="enotify";
145static const struct String str_enotify={ str_enotify_c, 7 };
87fcc8b9 146#endif
059ec3d9
PH
147#ifdef SUBADDRESS
148static uschar str_subaddress_c[]="subaddress";
149static const struct String str_subaddress={ str_subaddress_c, 10 };
150#endif
151#ifdef VACATION
152static uschar str_vacation_c[]="vacation";
153static const struct String str_vacation={ str_vacation_c, 8 };
154static uschar str_subject_c[]="Subject";
155static const struct String str_subject={ str_subject_c, 7 };
156#endif
157static uschar str_copy_c[]="copy";
158static const struct String str_copy={ str_copy_c, 4 };
159static uschar str_iascii_casemap_c[]="i;ascii-casemap";
160static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 };
5ea81592
PH
161static uschar str_enascii_casemap_c[]="en;ascii-casemap";
162static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 };
059ec3d9
PH
163static uschar str_ioctet_c[]="i;octet";
164static const struct String str_ioctet={ str_ioctet_c, 7 };
165static uschar str_iascii_numeric_c[]="i;ascii-numeric";
166static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 };
167static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap";
168static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 };
5ea81592
PH
169static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap";
170static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 };
059ec3d9
PH
171static uschar str_comparator_ioctet_c[]="comparator-i;octet";
172static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 };
173static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric";
174static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 };
175
176
177/*************************************************
178* Encode to quoted-printable *
179*************************************************/
180
181/*
182Arguments:
183 src UTF-8 string
50c99ba6
PH
184 dst US-ASCII string
185
186Returns
187 dst
059ec3d9
PH
188*/
189
190static struct String *quoted_printable_encode(const struct String *src, struct String *dst)
191{
192int pass;
193const uschar *start,*end;
194uschar *new = NULL;
195uschar ch;
196size_t line;
197
198for (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)
059ec3d9
PH
229 && start+2<end
230 && (*(start+1)!='\r' || *(start+2)!='\n')
059ec3d9
PH
231 )
232 )
233 {
234 if (pass==0)
235 ++dst->length;
236 else
237 *new++=*start;
238 ++line;
239 }
059ec3d9
PH
240 else if (ch=='\r' && start+1<end && *(start+1)=='\n')
241 {
242 if (pass==0)
243 {
244 ++dst->length;
245 line=0;
059ec3d9
PH
246 }
247 else
248 *new++='\n';
249 line=0;
50c99ba6 250 ++start;
059ec3d9 251 }
059ec3d9
PH
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
7c5214ec
PH
270/*************************************************
271* Check mail address for correct syntax *
272*************************************************/
273
274/*
275Check mail address for being syntactically correct.
276
277Arguments:
278 filter points to the Sieve filter including its state
279 address String containing one address
280
281Returns
282 1 Mail address is syntactically OK
283 -1 syntax error
284*/
285
286int check_mail_address(struct Sieve *filter, const struct String *address)
287{
288int start, end, domain;
289uschar *error,*ss;
290
291if (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 }
304else
305 {
306 filter->errmsg=CUS "empty address";
307 return -1;
308 }
309}
310
311
50c99ba6
PH
312/*************************************************
313* Decode URI encoded string *
314*************************************************/
315
316/*
317Arguments:
318 str URI encoded string
319
320Returns
321 0 Decoding successful
322 -1 Encoding error
323*/
324
7c5214ec 325#ifdef ENOTIFY
50c99ba6
PH
326static int uri_decode(struct String *str)
327{
328uschar *s,*t,*e;
329
330if (str->length==0) return 0;
331for (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';
347str->length=t-str->character;
348return 0;
349}
350
351
352/*************************************************
353* Parse mailto URI *
354*************************************************/
355
356/*
357Parse 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
366Arguments:
367 filter points to the Sieve filter including its state
368 uri URI, excluding scheme
7c5214ec
PH
369 recipient
370 body
50c99ba6
PH
371
372Returns
373 1 URI is syntactically OK
7c5214ec 374 0 Unknown URI scheme
50c99ba6
PH
375 -1 syntax error
376*/
377
7c5214ec 378static int parse_mailto_uri(struct Sieve *filter, const uschar *uri, string_item **recipient, struct String *header, struct String *body)
50c99ba6
PH
379{
380const uschar *start;
381struct String to,hname,hvalue;
382int capacity;
383string_item *new;
384
7c5214ec
PH
385if (Ustrncmp(uri,"mailto:",7))
386 {
387 filter->errmsg=US "Unknown URI scheme";
388 return 0;
389 }
390uri+=7;
50c99ba6
PH
391if (*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 }
423if (*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 }
7c5214ec 466 if (hname.length==2 && strcmpic(hname.character, US"to")==0)
50c99ba6
PH
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 }
7c5214ec 475 else if (hname.length==4 && strcmpic(hname.character, US"body")==0)
50c99ba6 476 *body=hvalue;
7c5214ec
PH
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 }
50c99ba6
PH
500 if (*uri=='&') ++uri;
501 else break;
502 }
503 }
504if (*uri)
505 {
506 filter->errmsg=US"Syntactically invalid URI";
507 return -1;
508 }
509return 1;
510}
7c5214ec
PH
511#endif
512
50c99ba6 513
059ec3d9
PH
514/*************************************************
515* Octet-wise string comparison *
516*************************************************/
517
518/*
519Arguments:
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
524Returns: 0 needle not found in haystack
525 1 needle found
526*/
527
528static int eq_octet(const struct String *needle,
529 const struct String *haystack, int match_prefix)
530{
531size_t nl,hl;
532const uschar *n,*h;
533
534nl=needle->length;
535n=needle->character;
536hl=haystack->length;
537h=haystack->character;
538while (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 }
550return (match_prefix ? nl==0 : nl==0 && hl==0);
551}
552
553
554/*************************************************
555* ASCII case-insensitive string comparison *
556*************************************************/
557
558/*
559Arguments:
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
564Returns: 0 needle not found in haystack
565 1 needle found
566*/
567
568static int eq_asciicase(const struct String *needle,
569 const struct String *haystack, int match_prefix)
570{
571size_t nl,hl;
572const uschar *n,*h;
573uschar nc,hc;
574
575nl=needle->length;
576n=needle->character;
577hl=haystack->length;
578h=haystack->character;
579while (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 */
1c59d63b 588 if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0;
059ec3d9
PH
589 ++n;
590 ++h;
591 --nl;
592 --hl;
593 }
594return (match_prefix ? nl==0 : nl==0 && hl==0);
595}
596
597
598/*************************************************
1c59d63b 599* Glob pattern search *
059ec3d9
PH
600*************************************************/
601
602/*
603Arguments:
604 needle pattern to search ...
605 haystack ... inside the haystack
606
607Returns: 0 needle not found in haystack
608 1 needle found
1c59d63b 609 -1 pattern error
059ec3d9
PH
610*/
611
1c59d63b 612static int eq_glob(const struct String *needle,
50c99ba6 613 const struct String *haystack, int ascii_caseless, int match_octet)
059ec3d9 614{
1c59d63b
PH
615const uschar *n,*h,*nend,*hend;
616int may_advance=0;
059ec3d9 617
1c59d63b
PH
618n=needle->character;
619h=haystack->character;
620nend=n+needle->length;
621hend=h+haystack->length;
622while (n<nend)
059ec3d9 623 {
1c59d63b
PH
624 if (*n=='*')
625 {
626 ++n;
627 may_advance=1;
059ec3d9 628 }
1c59d63b
PH
629 else
630 {
631 const uschar *npart,*hpart;
059ec3d9 632
1c59d63b
PH
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 '?':
059ec3d9 641 {
1c59d63b 642 if (hpart==hend) return 0;
50c99ba6 643 if (match_octet)
1c59d63b 644 ++hpart;
1c59d63b 645 else
50c99ba6
PH
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 }
1c59d63b
PH
656 ++npart;
657 break;
059ec3d9 658 }
1c59d63b 659 case '\\':
059ec3d9 660 {
1c59d63b
PH
661 ++npart;
662 if (npart==nend) return -1;
663 /* FALLTHROUGH */
059ec3d9 664 }
1c59d63b 665 default:
059ec3d9 666 {
1c59d63b
PH
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 )
059ec3d9 678 {
1c59d63b
PH
679 if (may_advance)
680 /* string match after a star failed, advance and try again */
059ec3d9 681 {
1c59d63b
PH
682 ++h;
683 goto match_part;
059ec3d9 684 }
1c59d63b 685 else return 0;
059ec3d9
PH
686 }
687 else
688 {
1c59d63b
PH
689 ++npart;
690 ++hpart;
691 };
059ec3d9 692 }
059ec3d9 693 }
1c59d63b
PH
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 */
059ec3d9 697 {
1c59d63b
PH
698 ++h;
699 goto match_part;
059ec3d9 700 }
1c59d63b
PH
701 h=hpart;
702 n=npart;
703 may_advance=0;
059ec3d9
PH
704 }
705 }
1c59d63b 706return (h==hend ? 1 : may_advance);
059ec3d9
PH
707}
708
709
710/*************************************************
711* ASCII numeric comparison *
712*************************************************/
713
714/*
715Arguments:
716 a first numeric string
717 b second numeric string
718 relop relational operator
719
720Returns: 0 not (a relop b)
721 1 a relop b
722*/
723
724static int eq_asciinumeric(const struct String *a,
725 const struct String *b, enum RelOp relop)
726{
727size_t al,bl;
728const uschar *as,*aend,*bs,*bend;
729int cmp;
730
731as=a->character;
732aend=a->character+a->length;
733bs=b->character;
734bend=b->character+b->length;
735
736while (*as>='0' && *as<='9' && as<aend) ++as;
737al=as-a->character;
738while (*bs>='0' && *bs<='9' && bs<bend) ++bs;
739bl=bs-b->character;
740
741if (al && bl==0) cmp=-1;
742else if (al==0 && bl==0) cmp=0;
743else if (al==0 && bl) cmp=1;
744else
745 {
746 cmp=al-bl;
747 if (cmp==0) cmp=memcmp(a->character,b->character,al);
748 }
749switch (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/*
768Arguments:
50c99ba6 769 filter points to the Sieve filter including its state
059ec3d9
PH
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
775Returns: 0 needle not found in haystack
776 1 needle found
777 -1 comparator does not offer matchtype
778*/
779
780static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack,
781 enum Comparator co, enum MatchType mt)
782{
783int r=0;
784
f05da2e8 785if ((filter_test != FTEST_NONE && debug_selector != 0) ||
059ec3d9
PH
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;
5ea81592 799 case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break;
059ec3d9
PH
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 }
806switch (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 }
5ea81592 817 case COMP_EN_ASCII_CASEMAP:
059ec3d9
PH
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 }
5ea81592 846 case COMP_EN_ASCII_CASEMAP:
059ec3d9
PH
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 {
50c99ba6 865 if ((r=eq_glob(needle,haystack,0,1))==-1)
1c59d63b
PH
866 {
867 filter->errmsg=CUS "syntactically invalid pattern";
868 return -1;
869 }
059ec3d9
PH
870 break;
871 }
5ea81592 872 case COMP_EN_ASCII_CASEMAP:
059ec3d9 873 {
50c99ba6 874 if ((r=eq_glob(needle,haystack,1,1))==-1)
1c59d63b
PH
875 {
876 filter->errmsg=CUS "syntactically invalid pattern";
877 return -1;
878 }
059ec3d9
PH
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 }
f05da2e8 890if ((filter_test != FTEST_NONE && debug_selector != 0) ||
059ec3d9
PH
891 (debug_selector & D_filter) != 0)
892 debug_printf(" Result %s\n",r?"true":"false");
893return r;
894}
895
896
897/*************************************************
898* Check header field syntax *
899*************************************************/
900
901/*
902RFC 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
910That forbids 8-bit header fields. This implementation accepts them, since
911all of Exim is 8-bit clean, so it adds %d128-%d255.
912
913Arguments:
914 header header field to quote for suitable use in Exim expansions
915
916Returns: 0 string is not a valid header field
917 1 string is a value header field
918*/
919
920static int is_header(const struct String *header)
921{
922size_t l;
923const uschar *h;
924
925l=header->length;
926h=header->character;
927if (l==0) return 0;
928while (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 }
937return 1;
938}
939
940
941/*************************************************
942* Quote special characters string *
943*************************************************/
944
945/*
946Arguments:
947 header header field to quote for suitable use in Exim expansions
948 or as debug output
949
950Returns: quoted string
951*/
952
953static const uschar *quote(const struct String *header)
954{
955uschar *quoted=NULL;
956int size=0,ptr=0;
957size_t l;
958const uschar *h;
959
960l=header->length;
961h=header->character;
962while (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 }
985quoted=string_cat(quoted,&size,&ptr,CUS "",1);
986return quoted;
987}
988
989
990/*************************************************
991* Add address to list of generated addresses *
992*************************************************/
993
994/*
995According to RFC 3028, duplicate delivery to the same address must
996not happen, so the list is first searched for the address.
997
998Arguments:
999 generated list of generated addresses
1000 addr new address to add
1001 file address denotes a file
1002
1003Returns: nothing
1004*/
1005
1006static void add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
1007{
1008address_item *new_addr;
1009
1010for (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 {
f05da2e8 1014 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9
PH
1015 {
1016 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
1017 }
1018 return;
1019 }
1020 }
1021
f05da2e8 1022if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9
PH
1023 {
1024 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
1025 }
1026new_addr=deliver_make_addr(addr,TRUE);
1027if (file)
1028 {
1029 setflag(new_addr, af_pfr|af_file);
1030 new_addr->mode = 0;
1031 }
1032new_addr->p.errors_address = NULL;
1033new_addr->next = *generated;
1034*generated = new_addr;
1035}
1036
1037
1038/*************************************************
1039* Return decoded header field *
1040*************************************************/
1041
1042/*
50c99ba6
PH
1043Unfold the header field as described in RFC 2822 and remove all
1044leading and trailing white space, then perform MIME decoding and
1045translate the header field to UTF-8.
1046
059ec3d9
PH
1047Arguments:
1048 value returned value of the field
1049 header name of the header field
1050
1051Returns: nothing The expanded string is empty
1052 in case there is no such header
1053*/
1054
1055static void expand_header(struct String *value, const struct String *header)
1056{
1057uschar *s,*r,*t;
1058uschar *errmsg;
1059
1060value->length=0;
1061value->character=(uschar*)0;
1062
1063t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
50c99ba6 1064while (*r==' ' || *r=='\t') ++r;
059ec3d9
PH
1065while (*r)
1066 {
1067 if (*r=='\n')
059ec3d9 1068 ++r;
059ec3d9
PH
1069 else
1070 *t++=*r++;
1071 }
50c99ba6
PH
1072while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1073*t='\0';
a0d6ba8a 1074value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
059ec3d9
PH
1075}
1076
1077
1078/*************************************************
1079* Parse remaining hash comment *
1080*************************************************/
1081
1082/*
1083Token definition:
1084 Comment up to terminating CRLF
1085
1086Arguments:
1087 filter points to the Sieve filter including its state
1088
1089Returns: 1 success
1090 -1 syntax error
1091*/
1092
1093static int parse_hashcomment(struct Sieve *filter)
1094{
1095++filter->pc;
1096while (*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 }
1114filter->errmsg=CUS "missing end of comment";
1115return -1;
1116}
1117
1118
1119/*************************************************
1120* Parse remaining C-style comment *
1121*************************************************/
1122
1123/*
1124Token definition:
1125 Everything up to star slash
1126
1127Arguments:
1128 filter points to the Sieve filter including its state
1129
1130Returns: 1 success
1131 -1 syntax error
1132*/
1133
1134static 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/*
1156Token definition:
1157 Spaces, tabs, CRLFs, hash comments or C-style comments
1158
1159Arguments:
1160 filter points to the Sieve filter including its state
1161
1162Returns: 1 success
1163 -1 syntax error
1164*/
1165
1166static int parse_white(struct Sieve *filter)
1167{
1168while (*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 }
1194return 1;
1195}
1196
1197
1198/*************************************************
1199* Parse a optional string *
1200*************************************************/
1201
1202/*
1203Token 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
1219Arguments:
1220 filter points to the Sieve filter including its state
1221 id specifies identifier to match
1222
1223Returns: 1 success
1224 -1 syntax error
1225 0 identifier not matched
1226*/
1227
1228static int parse_string(struct Sieve *filter, struct String *data)
1229{
1230int dataCapacity=0;
1231
1232data->length=0;
1233data->character=(uschar*)0;
1234if (*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;
50c99ba6 1244 /* that way, there will be at least one character allocated */
059ec3d9
PH
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 {
50c99ba6
PH
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
059ec3d9
PH
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 }
1272else 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 {
50c99ba6
PH
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);
059ec3d9
PH
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 }
1347else return 0;
1348}
1349
1350
1351/*************************************************
1352* Parse a specific identifier *
1353*************************************************/
1354
1355/*
1356Token definition:
1357 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1358
1359Arguments:
1360 filter points to the Sieve filter including its state
1361 id specifies identifier to match
1362
1363Returns: 1 success
1364 0 identifier not matched
1365*/
1366
1367static 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/*
1388Token definition:
1389 number = 1*DIGIT [QUANTIFIER]
1390 QUANTIFIER = "K" / "M" / "G"
1391
1392Arguments:
1393 filter points to the Sieve filter including its state
1394 data returns value
1395
1396Returns: 1 success
1397 -1 no string list found
1398*/
1399
1400static int parse_number(struct Sieve *filter, unsigned long *data)
1401{
1402unsigned long d,u;
1403
1404if (*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 }
1429else
1430 {
1431 filter->errmsg=CUS "missing number";
1432 return -1;
1433 }
1434}
1435
1436
1437/*************************************************
1438* Parse a string list *
1439*************************************************/
1440
1441/*
1442Grammar:
1443 string-list = "[" string *("," string) "]" / string
1444
1445Arguments:
1446 filter points to the Sieve filter including its state
1447 data returns string list
1448
1449Returns: 1 success
1450 -1 no string list found
1451*/
1452
1453static int parse_stringlist(struct Sieve *filter, struct String **data)
1454{
1455const uschar *orig=filter->pc;
1456int dataCapacity=0;
1457int dataLength=0;
1458struct String *d=(struct String*)0;
1459int m;
1460
1461if (*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 }
1511else /* 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 }
1535error:
1536filter->errmsg=CUS "missing string list";
1537return -1;
1538}
1539
1540
1541/*************************************************
1542* Parse an optional address part specifier *
1543*************************************************/
1544
1545/*
1546Grammar:
1547 address-part = ":localpart" / ":domain" / ":all"
1548 address-part =/ ":user" / ":detail"
1549
1550Arguments:
1551 filter points to the Sieve filter including its state
1552 a returns address part specified
1553
1554Returns: 1 success
1555 0 no comparator found
1556 -1 syntax error
1557*/
1558
1559static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1560{
1561#ifdef SUBADDRESS
1562if (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 }
1572else 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 }
1582else
1583#endif
1584if (parse_identifier(filter,CUS ":localpart")==1)
1585 {
1586 *a=ADDRPART_LOCALPART;
1587 return 1;
1588 }
1589else if (parse_identifier(filter,CUS ":domain")==1)
1590 {
1591 *a=ADDRPART_DOMAIN;
1592 return 1;
1593 }
1594else if (parse_identifier(filter,CUS ":all")==1)
1595 {
1596 *a=ADDRPART_ALL;
1597 return 1;
1598 }
1599else return 0;
1600}
1601
1602
1603/*************************************************
1604* Parse an optional comparator *
1605*************************************************/
1606
1607/*
1608Grammar:
1609 comparator = ":comparator" <comparator-name: string>
1610
1611Arguments:
1612 filter points to the Sieve filter including its state
1613 c returns comparator
1614
1615Returns: 1 success
1616 0 no comparator found
1617 -1 incomplete comparator found
1618*/
1619
1620static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1621{
1622struct String comparator_name;
1623
1624if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1625if (parse_white(filter)==-1) return -1;
1626switch (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 {
5ea81592
PH
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;
059ec3d9
PH
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/*
1674Grammar:
1675 match-type = ":is" / ":contains" / ":matches"
1676
1677Arguments:
1678 filter points to the Sieve filter including its state
1679 m returns match type
1680
1681Returns: 1 success
1682 0 no match type found
1683*/
1684
1685static 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/*
1711Grammar:
1712 test-list = "(" test *("," test) ")"
1713
1714Arguments:
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
1720Returns: 1 success
1721 0 no test list found
1722 -1 syntax or execution error
1723*/
1724
1725static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
1726{
1727if (parse_white(filter)==-1) return -1;
1728if (*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 }
1758else return 0;
1759}
1760
1761
1762/*************************************************
1763* Parse and interpret an optional test *
1764*************************************************/
1765
1766/*
1767Arguments:
1768 filter points to the Sieve filter including its state
1769 cond returned condition status
1770 exec Execute parsed statements
1771
1772Returns: 1 success
1773 0 no test found
1774 -1 syntax or execution error
1775*/
1776
1777static int parse_test(struct Sieve *filter, int *cond, int exec)
1778{
1779if (parse_white(filter)==-1) return -1;
1780if (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;
5ea81592 1790 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
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
e4a89c47 1893 case ADDRPART_DETAIL: part=NULL; break;
059ec3d9 1894#endif
059ec3d9
PH
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 }
1eccaa59
PH
1917 parse_allow_group = FALSE;
1918 parse_found_group = FALSE;
059ec3d9
PH
1919 }
1920 }
1921 return 1;
1922 }
1923else 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 }
1938else 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 }
1953else 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 }
1986else if (parse_identifier(filter,CUS "false"))
1987 {
1988 /*
1989 false-test = "false"
1990 */
1991
1992 *cond=0;
1993 return 1;
1994 }
1995else 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
5ea81592 2002 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
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 }
2078else 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 }
2088else 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 }
2111else if (parse_identifier(filter,CUS "true"))
2112 {
2113 *cond=1;
2114 return 1;
2115 }
2116else 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"
024bd3c2
PH
2123#ifdef ENVELOPE_AUTH
2124 envelope-part =/ "auth"
2125#endif
059ec3d9
PH
2126 */
2127
5ea81592 2128 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
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;
87fcc8b9 2188 for (e=env; e->length!=-1 && !*cond; ++e)
059ec3d9
PH
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
e4a89c47 2204 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
059ec3d9
PH
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
e4a89c47
PH
2214 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2215 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
059ec3d9
PH
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 }
024bd3c2
PH
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
059ec3d9
PH
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);
7fe1560f 2256 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
059ec3d9
PH
2257 if (*cond==-1) return -1;
2258 if (*cond) break;
2259 }
2260 }
2261 }
2262 return 1;
2263 }
7c5214ec
PH
2264#ifdef ENOTIFY
2265else 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
059ec3d9
PH
2306else return 0;
2307}
2308
2309
2310/*************************************************
2311* Parse and interpret an optional block *
2312*************************************************/
2313
2314/*
2315Arguments:
2316 filter points to the Sieve filter including its state
2317 exec Execute parsed statements
2318 generated where to hang newly-generated addresses
2319
2320Returns: 2 success by stop
2321 1 other success
2322 0 no block command found
2323 -1 syntax or execution error
2324*/
2325
2326static int parse_block(struct Sieve *filter, int exec,
2327 address_item **generated)
2328{
2329int r;
2330
2331if (parse_white(filter)==-1) return -1;
2332if (*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 {
8e669ac1 2343 filter->errmsg=CUS "expecting command or closing brace";
059ec3d9
PH
2344 return -1;
2345 }
2346 }
2347else return 0;
2348}
2349
2350
2351/*************************************************
2352* Match a semicolon *
2353*************************************************/
2354
2355/*
2356Arguments:
2357 filter points to the Sieve filter including its state
2358
2359Returns: 1 success
2360 -1 syntax error
2361*/
2362
2363static 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/*
2384Arguments:
2385 filter points to the Sieve filter including its state
2386 exec Execute parsed statements
2387 generated where to hang newly-generated addresses
2388
2389Returns: 2 success by stop
2390 1 other success
2391 -1 syntax or execution error
2392*/
2393static int parse_commands(struct Sieve *filter, int exec,
2394 address_item **generated)
2395{
2396while (*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 }
87fcc8b9
PH
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 }
059ec3d9
PH
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 }
87fcc8b9
PH
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 }
059ec3d9
PH
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" ]
87fcc8b9 2558 */
059ec3d9
PH
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 }
7c5214ec 2612#ifdef ENOTIFY
87fcc8b9
PH
2613 else if (parse_identifier(filter,CUS "notify"))
2614 {
2615 /*
7c5214ec
PH
2616 notify-command = "notify" { notify-options } <method: string> ";"
2617 notify-options = [":from" string]
2618 [":importance" <"1" / "2" / "3">]
2619 [":options" 1*(string-list / number)]
87fcc8b9
PH
2620 [":message" string]
2621 */
2622
2623 int m;
7c5214ec
PH
2624 struct String from;
2625 struct String importance;
2626 struct String *options;
87fcc8b9 2627 struct String message;
7c5214ec 2628 struct String method;
50c99ba6
PH
2629 struct Notification *already;
2630 string_item *recipient;
7c5214ec 2631 struct String header;
50c99ba6
PH
2632 struct String body;
2633 uschar *envelope_from,*envelope_to;
87fcc8b9 2634
7c5214ec 2635 if (!filter->require_enotify)
87fcc8b9 2636 {
7c5214ec 2637 filter->errmsg=CUS "missing previous require \"enotify\";";
87fcc8b9
PH
2638 return -1;
2639 }
7c5214ec
PH
2640 from.character=(uschar*)0;
2641 from.length=-1;
2642 importance.character=(uschar*)0;
2643 importance.length=-1;
2644 options=(struct String*)0;
87fcc8b9
PH
2645 message.character=(uschar*)0;
2646 message.length=-1;
50c99ba6 2647 recipient=NULL;
7c5214ec
PH
2648 header.length=-1;
2649 header.character=(uschar*)0;
50c99ba6
PH
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");
87fcc8b9
PH
2654 for (;;)
2655 {
2656 if (parse_white(filter)==-1) return -1;
7c5214ec 2657 if (parse_identifier(filter,CUS ":from")==1)
87fcc8b9
PH
2658 {
2659 if (parse_white(filter)==-1) return -1;
7c5214ec 2660 if ((m=parse_string(filter,&from))!=1)
87fcc8b9 2661 {
7c5214ec 2662 if (m==0) filter->errmsg=CUS "from string expected";
87fcc8b9
PH
2663 return -1;
2664 }
50c99ba6 2665 }
7c5214ec 2666 else if (parse_identifier(filter,CUS ":importance")==1)
50c99ba6
PH
2667 {
2668 if (parse_white(filter)==-1) return -1;
7c5214ec 2669 if ((m=parse_string(filter,&importance))!=1)
50c99ba6 2670 {
7c5214ec
PH
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";
50c99ba6
PH
2677 return -1;
2678 }
87fcc8b9 2679 }
7c5214ec
PH
2680 else if (parse_identifier(filter,CUS ":options")==1)
2681 {
2682 if (parse_white(filter)==-1) return -1;
2683 }
87fcc8b9
PH
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 }
7c5214ec
PH
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 }
87fcc8b9 2701 if (parse_semicolon(filter)==-1) return -1;
50c99ba6 2702
7c5214ec 2703 for (already=filter->notified; already; already=already->next)
50c99ba6 2704 {
7c5214ec
PH
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;
50c99ba6 2712 }
7c5214ec
PH
2713 if (already==(struct Notification*)0)
2714 /* New notification, process it */
50c99ba6 2715 {
7c5214ec
PH
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)
50c99ba6 2726 {
7c5214ec 2727 debug_printf("Notification to `%s'.\n",method.character);
50c99ba6 2728 }
bfad5236 2729#ifndef COMPILE_SYNTAX_CHECKER
d2ee6114 2730 if (exec && filter_test == FTEST_NONE)
7c5214ec
PH
2731 {
2732 string_item *p;
2733 header_line *h;
2734 int pid,fd;
50c99ba6 2735
7c5214ec
PH
2736 if ((pid = child_open_exim2(&fd,envelope_to,envelope_to))>=1)
2737 {
2738 FILE *f;
d2ee6114
PH
2739 uschar *buffer;
2740 int buffer_capacity;
7c5214ec
PH
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);
d2ee6114
PH
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));
7c5214ec
PH
2757 if (body.length>0) fprintf(f,"%s\n",body.character);
2758 fflush(f);
2759 (void)fclose(f);
2760 (void)child_close(pid, 0);
50c99ba6
PH
2761 }
2762 }
7c5214ec
PH
2763#endif
2764 }
2765 else
2766 {
2767 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
50c99ba6 2768 {
7c5214ec 2769 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
50c99ba6
PH
2770 }
2771 }
87fcc8b9
PH
2772 }
2773#endif
059ec3d9
PH
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]
059ec3d9 2780 [":subject" string]
f656d135
PH
2781 [":from" string]
2782 [":addresses" string-list]
059ec3d9 2783 [":mime"]
f656d135 2784 [":handle" string]
059ec3d9
PH
2785 */
2786
2787 int m;
2788 unsigned long days;
059ec3d9 2789 struct String subject;
f656d135
PH
2790 struct String from;
2791 struct String *addresses;
059ec3d9
PH
2792 int reason_is_mime;
2793 string_item *aliases;
f656d135 2794 struct String handle;
059ec3d9
PH
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;
f656d135
PH
2814 from.character=(uschar*)0;
2815 from.length=-1;
2816 addresses=(struct String*)0;
059ec3d9
PH
2817 aliases=NULL;
2818 reason_is_mime=0;
f656d135
PH
2819 handle.character=(uschar*)0;
2820 handle.length=-1;
059ec3d9
PH
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 }
f656d135
PH
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 {
f656d135
PH
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 }
7c5214ec 2848 if (check_mail_address(filter,&from)!=1)
f656d135 2849 return -1;
f656d135 2850 }
059ec3d9
PH
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 }
f656d135
PH
2873 else if (parse_identifier(filter,CUS ":mime")==1)
2874 reason_is_mime=1;
2875 else if (parse_identifier(filter,CUS ":handle")==1)
059ec3d9
PH
2876 {
2877 if (parse_white(filter)==-1) return -1;
f656d135 2878 if ((m=parse_string(filter,&from))!=1)
059ec3d9 2879 {
f656d135 2880 if (m==0) filter->errmsg=CUS "handle string expected";
059ec3d9
PH
2881 return -1;
2882 }
2883 }
059ec3d9
PH
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 }
5ea81592
PH
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 }
059ec3d9
PH
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 {
4929acf0
PH
2920 if (filter_test == FTEST_NONE)
2921 {
2922 /* ensure oncelog directory exists; failure will be detected later */
059ec3d9 2923
4929acf0
PH
2924 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
2925 }
059ec3d9
PH
2926 /* build oncelog filename */
2927
2928 key.character=(uschar*)0;
2929 key.length=0;
2930 capacity=0;
f656d135 2931 if (handle.length==-1)
8e669ac1 2932 {
f656d135
PH
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);
8e669ac1 2937 }
f656d135
PH
2938 else
2939 key=handle;
059ec3d9
PH
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]);
50c99ba6 2943 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9 2944 {
4929acf0 2945 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
059ec3d9 2946 }
50c99ba6 2947 if (filter_test == FTEST_NONE)
059ec3d9 2948 {
4929acf0
PH
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 {
50c99ba6
PH
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 }
4929acf0
PH
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);
f656d135
PH
2987 if (from.length==-1)
2988 addr->reply->from = expand_string(US"$local_part@$domain");
2989 else
2990 addr->reply->from = from.character;
4929acf0 2991 /* Allocation is larger than neccessary, but enough even for split MIME words */
50c99ba6 2992 buffer_capacity=32+4*subject.length;
4929acf0 2993 buffer=store_get(buffer_capacity);
46218253 2994 addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
4929acf0
PH
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;
4929acf0 3003 static const uschar nlnl[]="\r\n\r\n";
059ec3d9 3004
4929acf0
PH
3005 for
3006 (
3007 mime_body=reason.character,reason_end=reason.character+reason.length;
50c99ba6 3008 mime_body<(reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body,nlnl,(sizeof(nlnl)-1));
4929acf0
PH
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;
50c99ba6 3017 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
4929acf0
PH
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 {
91ecef39 3024 struct String qp = { NULL, 0 }; /* Keep compiler happy (PH) */
4929acf0
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 }
059ec3d9 3034 }
4929acf0 3035 }
50c99ba6 3036 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9 3037 {
4929acf0 3038 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
059ec3d9 3039 }
059ec3d9
PH
3040 }
3041 }
4929acf0 3042 else break;
059ec3d9 3043#endif
059ec3d9
PH
3044 }
3045return 1;
3046}
3047
3048
3049/*************************************************
3050* Parse and interpret a sieve filter *
3051*************************************************/
3052
3053/*
3054Arguments:
3055 filter points to the Sieve filter including its state
3056 exec Execute parsed statements
3057 generated where to hang newly-generated addresses
3058
3059Returns: 1 success
3060 -1 syntax or execution error
3061*/
3062
3063static int parse_start(struct Sieve *filter, int exec,
3064 address_item **generated)
3065{
3066filter->pc=filter->filter;
3067filter->line=1;
3068filter->keep=1;
3069filter->require_envelope=0;
3070filter->require_fileinto=0;
84024b72
PH
3071#ifdef ENCODED_CHARACTER
3072filter->require_encoded_character=0;
3073#endif
024bd3c2
PH
3074#ifdef ENVELOPE_AUTH
3075filter->require_envelope_auth=0;
3076#endif
7c5214ec
PH
3077#ifdef ENOTIFY
3078filter->require_enotify=0;
50c99ba6 3079filter->notified=(struct Notification*)0;
87fcc8b9 3080#endif
059ec3d9
PH
3081#ifdef SUBADDRESS
3082filter->require_subaddress=0;
3083#endif
3084#ifdef VACATION
3085filter->require_vacation=0;
3086filter->vacation_ran=0;
3087#endif
3088filter->require_copy=0;
3089filter->require_iascii_numeric=0;
3090
3091if (parse_white(filter)==-1) return -1;
3092
4929acf0 3093if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
059ec3d9
PH
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
3127while (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 {
5ea81592
PH
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;
84024b72
PH
3146#ifdef ENCODED_CHARACTER
3147 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3148#endif
024bd3c2
PH
3149#ifdef ENVELOPE_AUTH
3150 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3151#endif
7c5214ec
PH
3152#ifdef ENOTIFY
3153 else if (eq_octet(check,&str_enotify,0)) filter->require_enotify=1;
87fcc8b9 3154#endif
059ec3d9 3155#ifdef SUBADDRESS
5ea81592 3156 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
059ec3d9
PH
3157#endif
3158#ifdef VACATION
5ea81592 3159 else if (eq_octet(check,&str_vacation,0))
059ec3d9 3160 {
4929acf0 3161 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
059ec3d9
PH
3162 {
3163 filter->errmsg=CUS "vacation disabled";
3164 return -1;
3165 }
3166 filter->require_vacation=1;
3167 }
3168#endif
5ea81592
PH
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;
059ec3d9
PH
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/*
3197Arguments:
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
e4a89c47
PH
3202 useraddress string expression for :user part of address
3203 subaddress string expression for :subaddress part of address
059ec3d9
PH
3204 generated where to hang newly-generated addresses
3205 error where to pass back an error text
3206
3207Returns: 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
3215int
3216sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
e4a89c47 3217 uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
059ec3d9
PH
3218{
3219struct Sieve sieve;
3220int r;
3221uschar *msg;
3222
3223options = options; /* Keep picky compilers happy */
3224error = error;
3225
3226DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3227sieve.filter=filter;
3228
3229if (vacation_directory == NULL)
3230 sieve.vacation_directory = NULL;
3231else
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
e4a89c47
PH
3243sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3244sieve.subaddress = subaddress;
3245
059ec3d9
PH
3246#ifdef COMPILE_SYNTAX_CHECKER
3247if (parse_start(&sieve,0,generated)==1)
3248#else
3249if (parse_start(&sieve,1,generated)==1)
3250#endif
3251 {
3252 if (sieve.keep)
3253 {
3254 add_addr(generated,US"inbox",1,0,0,0);
1c59d63b 3255 msg = string_sprintf("Implicit keep");
059ec3d9
PH
3256 r = FF_DELIVERED;
3257 }
1c59d63b 3258 else
059ec3d9 3259 {
1c59d63b 3260 msg = string_sprintf("No implicit keep");
059ec3d9
PH
3261 r = FF_DELIVERED;
3262 }
3263 }
3264else
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
f05da2e8 3277if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
059ec3d9
PH
3278 else debug_printf("%s\n", msg);
3279#endif
3280
3281DEBUG(D_route) debug_printf("Sieve: end of processing\n");
3282return r;
3283}