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