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