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