Use C99 initialisations for iterators
[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
d7978c0f
JH
231static struct String *
232quoted_printable_encode(const struct String *src, struct String *dst)
059ec3d9 233{
059ec3d9
PH
234uschar *new = NULL;
235uschar ch;
236size_t line;
237
8d468c4c
JH
238/* Two passes: one to count output allocation size, second
239to do the encoding */
240
d7978c0f 241for (int pass = 0; pass <= 1; pass++)
059ec3d9
PH
242 {
243 line=0;
244 if (pass==0)
245 dst->length=0;
246 else
247 {
248 dst->character=store_get(dst->length+1); /* plus one for \0 */
249 new=dst->character;
250 }
d7978c0f
JH
251 for (const uschar * start = src->character, * end = start + src->length;
252 start < end; ++start)
059ec3d9
PH
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
d7978c0f
JH
362static int
363uri_decode(struct String *str)
50c99ba6
PH
364{
365uschar *s,*t,*e;
366
367if (str->length==0) return 0;
368for (s=str->character,t=s,e=s+str->length; s<e; )
50c99ba6
PH
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++;
d7978c0f 381
50c99ba6
PH
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:
059ec3d9
PH
857 switch (co)
858 {
859 case COMP_OCTET:
059ec3d9
PH
860 if (eq_octet(needle,haystack,0)) r=1;
861 break;
5ea81592 862 case COMP_EN_ASCII_CASEMAP:
059ec3d9
PH
863 if (eq_asciicase(needle,haystack,0)) r=1;
864 break;
059ec3d9 865 case COMP_ASCII_NUMERIC:
059ec3d9
PH
866 if (!filter->require_iascii_numeric)
867 {
868 filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";";
869 return -1;
870 }
871 if (eq_asciinumeric(needle,haystack,EQ)) r=1;
872 break;
059ec3d9
PH
873 }
874 break;
d7978c0f 875
059ec3d9
PH
876 case MATCH_CONTAINS:
877 {
878 struct String h;
879
880 switch (co)
881 {
882 case COMP_OCTET:
d7978c0f
JH
883 for (h = *haystack; h.length; ++h.character,--h.length)
884 if (eq_octet(needle,&h,1)) { r=1; break; }
059ec3d9 885 break;
5ea81592 886 case COMP_EN_ASCII_CASEMAP:
d7978c0f
JH
887 for (h = *haystack; h.length; ++h.character, --h.length)
888 if (eq_asciicase(needle,&h,1)) { r=1; break; }
059ec3d9 889 break;
059ec3d9 890 default:
059ec3d9
PH
891 filter->errmsg=CUS "comparator does not offer specified matchtype";
892 return -1;
059ec3d9
PH
893 }
894 break;
895 }
d7978c0f 896
059ec3d9 897 case MATCH_MATCHES:
059ec3d9
PH
898 switch (co)
899 {
900 case COMP_OCTET:
50c99ba6 901 if ((r=eq_glob(needle,haystack,0,1))==-1)
1c59d63b
PH
902 {
903 filter->errmsg=CUS "syntactically invalid pattern";
904 return -1;
905 }
059ec3d9 906 break;
5ea81592 907 case COMP_EN_ASCII_CASEMAP:
50c99ba6 908 if ((r=eq_glob(needle,haystack,1,1))==-1)
1c59d63b
PH
909 {
910 filter->errmsg=CUS "syntactically invalid pattern";
911 return -1;
912 }
059ec3d9 913 break;
059ec3d9 914 default:
059ec3d9
PH
915 filter->errmsg=CUS "comparator does not offer specified matchtype";
916 return -1;
059ec3d9
PH
917 }
918 break;
059ec3d9 919 }
f05da2e8 920if ((filter_test != FTEST_NONE && debug_selector != 0) ||
059ec3d9
PH
921 (debug_selector & D_filter) != 0)
922 debug_printf(" Result %s\n",r?"true":"false");
923return r;
924}
925
926
927/*************************************************
928* Check header field syntax *
929*************************************************/
930
931/*
932RFC 2822, section 3.6.8 says:
933
934 field-name = 1*ftext
935
936 ftext = %d33-57 / ; Any character except
937 %d59-126 ; controls, SP, and
938 ; ":".
939
940That forbids 8-bit header fields. This implementation accepts them, since
941all of Exim is 8-bit clean, so it adds %d128-%d255.
942
943Arguments:
944 header header field to quote for suitable use in Exim expansions
945
946Returns: 0 string is not a valid header field
947 1 string is a value header field
948*/
949
950static int is_header(const struct String *header)
951{
952size_t l;
953const uschar *h;
954
955l=header->length;
956h=header->character;
957if (l==0) return 0;
958while (l)
959 {
960 if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0;
961 else
962 {
963 ++h;
964 --l;
965 }
966 }
967return 1;
968}
969
970
971/*************************************************
972* Quote special characters string *
973*************************************************/
974
975/*
976Arguments:
977 header header field to quote for suitable use in Exim expansions
978 or as debug output
979
980Returns: quoted string
981*/
982
94e1f16d
JH
983static const uschar *
984quote(const struct String *header)
059ec3d9 985{
94e1f16d 986gstring * quoted = NULL;
059ec3d9
PH
987size_t l;
988const uschar *h;
989
990l=header->length;
991h=header->character;
992while (l)
993 {
994 switch (*h)
995 {
996 case '\0':
94e1f16d 997 quoted = string_catn(quoted, CUS "\\0", 2);
059ec3d9 998 break;
059ec3d9
PH
999 case '$':
1000 case '{':
1001 case '}':
94e1f16d 1002 quoted = string_catn(quoted, CUS "\\", 1);
059ec3d9 1003 default:
94e1f16d 1004 quoted = string_catn(quoted, h, 1);
059ec3d9
PH
1005 }
1006 ++h;
1007 --l;
1008 }
94e1f16d
JH
1009quoted = string_catn(quoted, CUS "", 1);
1010return string_from_gstring(quoted);
059ec3d9
PH
1011}
1012
1013
1014/*************************************************
1015* Add address to list of generated addresses *
1016*************************************************/
1017
1018/*
deaf311d 1019According to RFC 5228, duplicate delivery to the same address must
059ec3d9
PH
1020not happen, so the list is first searched for the address.
1021
1022Arguments:
1023 generated list of generated addresses
1024 addr new address to add
1025 file address denotes a file
1026
1027Returns: nothing
1028*/
1029
7eb0e5d2
JH
1030static void
1031add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxmessages, int maxstorage)
059ec3d9
PH
1032{
1033address_item *new_addr;
1034
d7978c0f 1035for (new_addr = *generated; new_addr; new_addr = new_addr->next)
7eb0e5d2
JH
1036 if ( Ustrcmp(new_addr->address,addr) == 0
1037 && ( !file
1038 || testflag(new_addr, af_pfr)
1039 || testflag(new_addr, af_file)
1040 )
1041 )
059ec3d9 1042 {
f05da2e8 1043 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9 1044 debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
7eb0e5d2 1045
059ec3d9
PH
1046 return;
1047 }
059ec3d9 1048
f05da2e8 1049if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
059ec3d9 1050 debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
7eb0e5d2
JH
1051
1052new_addr = deliver_make_addr(addr,TRUE);
059ec3d9
PH
1053if (file)
1054 {
7eb0e5d2
JH
1055 setflag(new_addr, af_pfr);
1056 setflag(new_addr, af_file);
059ec3d9
PH
1057 new_addr->mode = 0;
1058 }
d43cbe25 1059new_addr->prop.errors_address = NULL;
059ec3d9
PH
1060new_addr->next = *generated;
1061*generated = new_addr;
1062}
1063
1064
1065/*************************************************
1066* Return decoded header field *
1067*************************************************/
1068
1069/*
50c99ba6
PH
1070Unfold the header field as described in RFC 2822 and remove all
1071leading and trailing white space, then perform MIME decoding and
1072translate the header field to UTF-8.
1073
059ec3d9
PH
1074Arguments:
1075 value returned value of the field
1076 header name of the header field
1077
1078Returns: nothing The expanded string is empty
1079 in case there is no such header
1080*/
1081
1082static void expand_header(struct String *value, const struct String *header)
1083{
1084uschar *s,*r,*t;
1085uschar *errmsg;
1086
1087value->length=0;
1088value->character=(uschar*)0;
1089
1090t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
50c99ba6 1091while (*r==' ' || *r=='\t') ++r;
059ec3d9
PH
1092while (*r)
1093 {
1094 if (*r=='\n')
059ec3d9 1095 ++r;
059ec3d9
PH
1096 else
1097 *t++=*r++;
1098 }
50c99ba6
PH
1099while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t;
1100*t='\0';
a0d6ba8a 1101value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg);
059ec3d9
PH
1102}
1103
1104
1105/*************************************************
1106* Parse remaining hash comment *
1107*************************************************/
1108
1109/*
1110Token definition:
1111 Comment up to terminating CRLF
1112
1113Arguments:
1114 filter points to the Sieve filter including its state
1115
1116Returns: 1 success
1117 -1 syntax error
1118*/
1119
1120static int parse_hashcomment(struct Sieve *filter)
1121{
1122++filter->pc;
1123while (*filter->pc)
1124 {
1125#ifdef RFC_EOL
1126 if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1127#else
1128 if (*filter->pc=='\n')
1129#endif
1130 {
1131#ifdef RFC_EOL
1132 filter->pc+=2;
1133#else
1134 ++filter->pc;
1135#endif
1136 ++filter->line;
1137 return 1;
1138 }
1139 else ++filter->pc;
1140 }
1141filter->errmsg=CUS "missing end of comment";
1142return -1;
1143}
1144
1145
1146/*************************************************
1147* Parse remaining C-style comment *
1148*************************************************/
1149
1150/*
1151Token definition:
1152 Everything up to star slash
1153
1154Arguments:
1155 filter points to the Sieve filter including its state
1156
1157Returns: 1 success
1158 -1 syntax error
1159*/
1160
1161static int parse_comment(struct Sieve *filter)
1162{
1163 filter->pc+=2;
1164 while (*filter->pc)
1165 {
1166 if (*filter->pc=='*' && *(filter->pc+1)=='/')
1167 {
1168 filter->pc+=2;
1169 return 1;
1170 }
1171 else ++filter->pc;
1172 }
1173 filter->errmsg=CUS "missing end of comment";
1174 return -1;
1175}
1176
1177
1178/*************************************************
1179* Parse optional white space *
1180*************************************************/
1181
1182/*
1183Token definition:
1184 Spaces, tabs, CRLFs, hash comments or C-style comments
1185
1186Arguments:
1187 filter points to the Sieve filter including its state
1188
1189Returns: 1 success
1190 -1 syntax error
1191*/
1192
1193static int parse_white(struct Sieve *filter)
1194{
1195while (*filter->pc)
1196 {
1197 if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1198#ifdef RFC_EOL
1199 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1200#else
1201 else if (*filter->pc=='\n')
1202#endif
1203 {
1204#ifdef RFC_EOL
1205 filter->pc+=2;
1206#else
1207 ++filter->pc;
1208#endif
1209 ++filter->line;
1210 }
1211 else if (*filter->pc=='#')
1212 {
1213 if (parse_hashcomment(filter)==-1) return -1;
1214 }
1215 else if (*filter->pc=='/' && *(filter->pc+1)=='*')
1216 {
1217 if (parse_comment(filter)==-1) return -1;
1218 }
1219 else break;
1220 }
1221return 1;
1222}
1223
1224
a43a27c5
PH
1225#ifdef ENCODED_CHARACTER
1226/*************************************************
eb4c0de6 1227* Decode hex-encoded-character string *
a43a27c5
PH
1228*************************************************/
1229
1230/*
1231Encoding definition:
eb4c0de6
PH
1232 blank = SP / TAB / CRLF
1233 hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
a43a27c5
PH
1234 hex-pair = 1*2HEXDIG
1235
1236Arguments:
1237 src points to a hex-pair-seq
1238 end points to its end
1239 dst points to the destination of the decoded octets,
1240 optionally to (uschar*)0 for checking only
1241
1242Returns: >=0 number of decoded octets
1243 -1 syntax error
1244*/
1245
1246static int hex_decode(uschar *src, uschar *end, uschar *dst)
1247{
1248int decoded=0;
1249
eb4c0de6
PH
1250while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1251do
a43a27c5 1252 {
eb4c0de6
PH
1253 int x,d,n;
1254
d7978c0f
JH
1255 for (x = 0, d = 0;
1256 d<2 && src<end && isxdigit(n=tolower(*src));
1257 x=(x<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ;
eb4c0de6
PH
1258 if (d==0) return -1;
1259 if (dst) *dst++=x;
1260 ++decoded;
1261 if (src==end) return decoded;
1262 if (*src==' ' || *src=='\t' || *src=='\n')
1263 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1264 else
1265 return -1;
a43a27c5 1266 }
eb4c0de6
PH
1267while (src<end);
1268return decoded;
a43a27c5
PH
1269}
1270
1271
1272/*************************************************
eb4c0de6 1273* Decode unicode-encoded-character string *
a43a27c5
PH
1274*************************************************/
1275
1276/*
1277Encoding definition:
eb4c0de6
PH
1278 blank = SP / TAB / CRLF
1279 unicode-hex-seq = *blank unicode-hex *(blank unicode-hex) *blank
1280 unicode-hex = 1*HEXDIG
a43a27c5
PH
1281
1282 It is an error for a script to use a hexadecimal value that isn't in
1283 either the range 0 to D7FF or the range E000 to 10FFFF.
1284
eb4c0de6
PH
1285 At this time, strings are already scanned, thus the CRLF is converted
1286 to the internally used \n (should RFC_EOL have been used).
1287
a43a27c5
PH
1288Arguments:
1289 src points to a unicode-hex-seq
1290 end points to its end
1291 dst points to the destination of the decoded octets,
1292 optionally to (uschar*)0 for checking only
1293
1294Returns: >=0 number of decoded octets
1295 -1 syntax error
1296 -2 semantic error (character range violation)
1297*/
1298
d7978c0f
JH
1299static int
1300unicode_decode(uschar *src, uschar *end, uschar *dst)
a43a27c5
PH
1301{
1302int decoded=0;
1303
eb4c0de6
PH
1304while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1305do
a43a27c5 1306 {
eb4c0de6
PH
1307 uschar *hex_seq;
1308 int c,d,n;
1309
1310 unicode_hex:
d7978c0f
JH
1311 for (hex_seq = src; src < end && *src=='0'; ) src++;
1312 for (c = 0, d = 0;
1313 d < 7 && src < end && isxdigit(n=tolower(*src));
1314 c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ;
1315 if (src == hex_seq) return -1;
eb4c0de6
PH
1316 if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2;
1317 if (c<128)
a43a27c5 1318 {
eb4c0de6
PH
1319 if (dst) *dst++=c;
1320 ++decoded;
1321 }
1322 else if (c>=0x80 && c<=0x7ff)
1323 {
1324 if (dst)
1325 {
1326 *dst++=192+(c>>6);
1327 *dst++=128+(c&0x3f);
1328 }
1329 decoded+=2;
1330 }
1331 else if (c>=0x800 && c<=0xffff)
1332 {
1333 if (dst)
1334 {
1335 *dst++=224+(c>>12);
1336 *dst++=128+((c>>6)&0x3f);
1337 *dst++=128+(c&0x3f);
1338 }
1339 decoded+=3;
1340 }
1341 else if (c>=0x10000 && c<=0x1fffff)
1342 {
1343 if (dst)
1344 {
1345 *dst++=240+(c>>18);
1346 *dst++=128+((c>>10)&0x3f);
1347 *dst++=128+((c>>6)&0x3f);
1348 *dst++=128+(c&0x3f);
1349 }
1350 decoded+=4;
1351 }
1352 if (*src==' ' || *src=='\t' || *src=='\n')
1353 {
1354 while (*src==' ' || *src=='\t' || *src=='\n') ++src;
1355 if (src==end) return decoded;
1356 goto unicode_hex;
a43a27c5 1357 }
a43a27c5 1358 }
eb4c0de6
PH
1359while (src<end);
1360return decoded;
a43a27c5
PH
1361}
1362
1363
1364/*************************************************
1365* Decode encoded-character string *
1366*************************************************/
1367
1368/*
1369Encoding definition:
1370 encoded-arb-octets = "${hex:" hex-pair-seq "}"
1371 encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
1372
1373Arguments:
1374 encoded points to an encoded string, returns decoded string
1375 filter points to the Sieve filter including its state
1376
1377Returns: 1 success
1378 -1 syntax error
1379*/
1380
1381static int string_decode(struct Sieve *filter, struct String *data)
1382{
1383uschar *src,*dst,*end;
1384
1385src=data->character;
1386dst=src;
1387end=data->character+data->length;
1388while (src<end)
1389 {
1390 uschar *brace;
1391
1392 if (
eb4c0de6 1393 strncmpic(src,US "${hex:",6)==0
a43a27c5
PH
1394 && (brace=Ustrchr(src+6,'}'))!=(uschar*)0
1395 && (hex_decode(src+6,brace,(uschar*)0))>=0
1396 )
1397 {
1398 dst+=hex_decode(src+6,brace,dst);
1399 src=brace+1;
1400 }
1401 else if (
eb4c0de6 1402 strncmpic(src,US "${unicode:",10)==0
a43a27c5
PH
1403 && (brace=Ustrchr(src+10,'}'))!=(uschar*)0
1404 )
1405 {
1406 switch (unicode_decode(src+10,brace,(uschar*)0))
1407 {
1408 case -2:
1409 {
1410 filter->errmsg=CUS "unicode character out of range";
1411 return -1;
1412 }
1413 case -1:
1414 {
1415 *dst++=*src++;
1416 break;
1417 }
1418 default:
1419 {
1420 dst+=unicode_decode(src+10,brace,dst);
1421 src=brace+1;
1422 }
1423 }
1424 }
1425 else *dst++=*src++;
1426 }
1427 data->length=dst-data->character;
1428 *dst='\0';
1429return 1;
1430}
1431#endif
1432
1433
059ec3d9 1434/*************************************************
a43a27c5 1435* Parse an optional string *
059ec3d9
PH
1436*************************************************/
1437
1438/*
1439Token definition:
1440 quoted-string = DQUOTE *CHAR DQUOTE
1441 ;; in general, \ CHAR inside a string maps to CHAR
1442 ;; so \" maps to " and \\ maps to \
1443 ;; note that newlines and other characters are all allowed
1444 ;; in strings
1445
1446 multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
1447 *(multi-line-literal / multi-line-dotstuff)
1448 "." CRLF
1449 multi-line-literal = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
1450 multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
1451 ;; A line containing only "." ends the multi-line.
1452 ;; Remove a leading '.' if followed by another '.'.
1453 string = quoted-string / multi-line
1454
1455Arguments:
1456 filter points to the Sieve filter including its state
1457 id specifies identifier to match
1458
1459Returns: 1 success
1460 -1 syntax error
1461 0 identifier not matched
1462*/
1463
27fd1318
JH
1464static int
1465parse_string(struct Sieve *filter, struct String *data)
059ec3d9 1466{
94e1f16d 1467gstring * g = NULL;
059ec3d9 1468
94e1f16d
JH
1469data->length = 0;
1470data->character = NULL;
1471
059ec3d9
PH
1472if (*filter->pc=='"') /* quoted string */
1473 {
1474 ++filter->pc;
1475 while (*filter->pc)
1476 {
1477 if (*filter->pc=='"') /* end of string */
1478 {
059ec3d9 1479 ++filter->pc;
94e1f16d
JH
1480
1481 if (g)
1482 {
1483 data->character = string_from_gstring(g);
1484 data->length = g->ptr;
1485 }
1486 else
1487 data->character = US"\0";
50c99ba6 1488 /* that way, there will be at least one character allocated */
94e1f16d 1489
a43a27c5
PH
1490#ifdef ENCODED_CHARACTER
1491 if (filter->require_encoded_character
1492 && string_decode(filter,data)==-1)
1493 return -1;
1494#endif
059ec3d9
PH
1495 return 1;
1496 }
1497 else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */
1498 {
94e1f16d 1499 g = string_catn(g, filter->pc+1, 1);
059ec3d9
PH
1500 filter->pc+=2;
1501 }
1502 else /* regular character */
1503 {
50c99ba6
PH
1504#ifdef RFC_EOL
1505 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line;
1506#else
1507 if (*filter->pc=='\n')
1508 {
94e1f16d 1509 g = string_catn(g, US"\r", 1);
50c99ba6
PH
1510 ++filter->line;
1511 }
1512#endif
94e1f16d 1513 g = string_catn(g, filter->pc, 1);
059ec3d9
PH
1514 filter->pc++;
1515 }
1516 }
1517 filter->errmsg=CUS "missing end of string";
1518 return -1;
1519 }
1520else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */
1521 {
1522 filter->pc+=5;
1523 /* skip optional white space followed by hashed comment or CRLF */
1524 while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc;
1525 if (*filter->pc=='#')
1526 {
1527 if (parse_hashcomment(filter)==-1) return -1;
1528 }
1529#ifdef RFC_EOL
1530 else if (*filter->pc=='\r' && *(filter->pc+1)=='\n')
1531#else
1532 else if (*filter->pc=='\n')
1533#endif
1534 {
1535#ifdef RFC_EOL
1536 filter->pc+=2;
1537#else
1538 ++filter->pc;
1539#endif
1540 ++filter->line;
1541 }
1542 else
1543 {
1544 filter->errmsg=CUS "syntax error";
1545 return -1;
1546 }
1547 while (*filter->pc)
1548 {
1549#ifdef RFC_EOL
1550 if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */
1551#else
1552 if (*filter->pc=='\n') /* end of line */
1553#endif
1554 {
94e1f16d 1555 g = string_catn(g, CUS "\r\n", 2);
059ec3d9
PH
1556#ifdef RFC_EOL
1557 filter->pc+=2;
1558#else
1559 ++filter->pc;
1560#endif
1561 ++filter->line;
1562#ifdef RFC_EOL
1563 if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */
1564#else
1565 if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */
1566#endif
1567 {
94e1f16d
JH
1568 if (g)
1569 {
1570 data->character = string_from_gstring(g);
1571 data->length = g->ptr;
1572 }
1573 else
1574 data->character = US"\0";
1575 /* that way, there will be at least one character allocated */
50c99ba6 1576
059ec3d9
PH
1577#ifdef RFC_EOL
1578 filter->pc+=3;
1579#else
1580 filter->pc+=2;
1581#endif
1582 ++filter->line;
a43a27c5
PH
1583#ifdef ENCODED_CHARACTER
1584 if (filter->require_encoded_character
1585 && string_decode(filter,data)==-1)
1586 return -1;
1587#endif
059ec3d9
PH
1588 return 1;
1589 }
1590 else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */
1591 {
94e1f16d 1592 g = string_catn(g, CUS ".", 1);
059ec3d9
PH
1593 filter->pc+=2;
1594 }
1595 }
1596 else /* regular character */
1597 {
94e1f16d 1598 g = string_catn(g, filter->pc, 1);
059ec3d9
PH
1599 filter->pc++;
1600 }
1601 }
1602 filter->errmsg=CUS "missing end of multi line string";
1603 return -1;
1604 }
1605else return 0;
1606}
1607
1608
1609/*************************************************
1610* Parse a specific identifier *
1611*************************************************/
1612
1613/*
1614Token definition:
1615 identifier = (ALPHA / "_") *(ALPHA DIGIT "_")
1616
1617Arguments:
1618 filter points to the Sieve filter including its state
1619 id specifies identifier to match
1620
1621Returns: 1 success
1622 0 identifier not matched
1623*/
1624
1625static int parse_identifier(struct Sieve *filter, const uschar *id)
1626{
1627 size_t idlen=Ustrlen(id);
1628
eb4c0de6 1629 if (strncmpic(US filter->pc,US id,idlen)==0)
059ec3d9
PH
1630 {
1631 uschar next=filter->pc[idlen];
1632
1633 if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0;
1634 filter->pc+=idlen;
1635 return 1;
1636 }
1637 else return 0;
1638}
1639
1640
1641/*************************************************
1642* Parse a number *
1643*************************************************/
1644
1645/*
1646Token definition:
1647 number = 1*DIGIT [QUANTIFIER]
1648 QUANTIFIER = "K" / "M" / "G"
1649
1650Arguments:
1651 filter points to the Sieve filter including its state
1652 data returns value
1653
1654Returns: 1 success
1655 -1 no string list found
1656*/
1657
1658static int parse_number(struct Sieve *filter, unsigned long *data)
1659{
1660unsigned long d,u;
1661
1662if (*filter->pc>='0' && *filter->pc<='9')
1663 {
1664 uschar *e;
1665
1666 errno=0;
1667 d=Ustrtoul(filter->pc,&e,10);
1668 if (errno==ERANGE)
1669 {
1670 filter->errmsg=CUstrerror(ERANGE);
1671 return -1;
1672 }
1673 filter->pc=e;
1674 u=1;
1675 if (*filter->pc=='K') { u=1024; ++filter->pc; }
1676 else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; }
1677 else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; }
1678 if (d>(ULONG_MAX/u))
1679 {
1680 filter->errmsg=CUstrerror(ERANGE);
1681 return -1;
1682 }
1683 d*=u;
1684 *data=d;
1685 return 1;
1686 }
1687else
1688 {
1689 filter->errmsg=CUS "missing number";
1690 return -1;
1691 }
1692}
1693
1694
1695/*************************************************
1696* Parse a string list *
1697*************************************************/
1698
1699/*
1700Grammar:
1701 string-list = "[" string *("," string) "]" / string
1702
1703Arguments:
1704 filter points to the Sieve filter including its state
1705 data returns string list
1706
1707Returns: 1 success
1708 -1 no string list found
1709*/
1710
4dc2379a
JH
1711static int
1712parse_stringlist(struct Sieve *filter, struct String **data)
059ec3d9
PH
1713{
1714const uschar *orig=filter->pc;
4dc2379a
JH
1715int dataCapacity = 0;
1716int dataLength = 0;
1717struct String *d = NULL;
059ec3d9
PH
1718int m;
1719
1720if (*filter->pc=='[') /* string list */
1721 {
1722 ++filter->pc;
1723 for (;;)
1724 {
1725 if (parse_white(filter)==-1) goto error;
4dc2379a 1726 if (dataLength+1 >= dataCapacity) /* increase buffer */
059ec3d9
PH
1727 {
1728 struct String *new;
4dc2379a
JH
1729
1730 dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
1731 new = store_get(sizeof(struct String) * dataCapacity);
1732
059ec3d9 1733 if (d) memcpy(new,d,sizeof(struct String)*dataLength);
4dc2379a 1734 d = new;
059ec3d9 1735 }
4dc2379a 1736
059ec3d9
PH
1737 m=parse_string(filter,&d[dataLength]);
1738 if (m==0)
1739 {
1740 if (dataLength==0) break;
1741 else
1742 {
1743 filter->errmsg=CUS "missing string";
1744 goto error;
1745 }
1746 }
1747 else if (m==-1) goto error;
1748 else ++dataLength;
1749 if (parse_white(filter)==-1) goto error;
1750 if (*filter->pc==',') ++filter->pc;
1751 else break;
1752 }
1753 if (*filter->pc==']')
1754 {
1755 d[dataLength].character=(uschar*)0;
1756 d[dataLength].length=-1;
1757 ++filter->pc;
1758 *data=d;
1759 return 1;
1760 }
1761 else
1762 {
1763 filter->errmsg=CUS "missing closing bracket";
1764 goto error;
1765 }
1766 }
1767else /* single string */
1768 {
1769 if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
1770 {
1771 return -1;
1772 }
1773 m=parse_string(filter,&d[0]);
1774 if (m==-1)
1775 {
1776 return -1;
1777 }
1778 else if (m==0)
1779 {
1780 filter->pc=orig;
1781 return 0;
1782 }
1783 else
1784 {
1785 d[1].character=(uschar*)0;
1786 d[1].length=-1;
1787 *data=d;
1788 return 1;
1789 }
1790 }
1791error:
1792filter->errmsg=CUS "missing string list";
1793return -1;
1794}
1795
1796
1797/*************************************************
1798* Parse an optional address part specifier *
1799*************************************************/
1800
1801/*
1802Grammar:
1803 address-part = ":localpart" / ":domain" / ":all"
1804 address-part =/ ":user" / ":detail"
1805
1806Arguments:
1807 filter points to the Sieve filter including its state
1808 a returns address part specified
1809
1810Returns: 1 success
1811 0 no comparator found
1812 -1 syntax error
1813*/
1814
1815static int parse_addresspart(struct Sieve *filter, enum AddressPart *a)
1816{
1817#ifdef SUBADDRESS
1818if (parse_identifier(filter,CUS ":user")==1)
1819 {
1820 if (!filter->require_subaddress)
1821 {
1822 filter->errmsg=CUS "missing previous require \"subaddress\";";
1823 return -1;
1824 }
1825 *a=ADDRPART_USER;
1826 return 1;
1827 }
1828else if (parse_identifier(filter,CUS ":detail")==1)
1829 {
1830 if (!filter->require_subaddress)
1831 {
1832 filter->errmsg=CUS "missing previous require \"subaddress\";";
1833 return -1;
1834 }
1835 *a=ADDRPART_DETAIL;
1836 return 1;
1837 }
1838else
1839#endif
1840if (parse_identifier(filter,CUS ":localpart")==1)
1841 {
1842 *a=ADDRPART_LOCALPART;
1843 return 1;
1844 }
1845else if (parse_identifier(filter,CUS ":domain")==1)
1846 {
1847 *a=ADDRPART_DOMAIN;
1848 return 1;
1849 }
1850else if (parse_identifier(filter,CUS ":all")==1)
1851 {
1852 *a=ADDRPART_ALL;
1853 return 1;
1854 }
1855else return 0;
1856}
1857
1858
1859/*************************************************
1860* Parse an optional comparator *
1861*************************************************/
1862
1863/*
1864Grammar:
1865 comparator = ":comparator" <comparator-name: string>
1866
1867Arguments:
1868 filter points to the Sieve filter including its state
1869 c returns comparator
1870
1871Returns: 1 success
1872 0 no comparator found
1873 -1 incomplete comparator found
1874*/
1875
1876static int parse_comparator(struct Sieve *filter, enum Comparator *c)
1877{
1878struct String comparator_name;
1879
1880if (parse_identifier(filter,CUS ":comparator")==0) return 0;
1881if (parse_white(filter)==-1) return -1;
1882switch (parse_string(filter,&comparator_name))
1883 {
1884 case -1: return -1;
1885 case 0:
1886 {
1887 filter->errmsg=CUS "missing comparator";
1888 return -1;
1889 }
1890 default:
1891 {
1892 int match;
1893
1894 if (eq_asciicase(&comparator_name,&str_ioctet,0))
1895 {
1896 *c=COMP_OCTET;
1897 match=1;
1898 }
1899 else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0))
1900 {
5ea81592
PH
1901 *c=COMP_EN_ASCII_CASEMAP;
1902 match=1;
1903 }
1904 else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0))
1905 {
1906 *c=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
1907 match=1;
1908 }
1909 else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0))
1910 {
1911 *c=COMP_ASCII_NUMERIC;
1912 match=1;
1913 }
1914 else
1915 {
1916 filter->errmsg=CUS "invalid comparator";
1917 match=-1;
1918 }
1919 return match;
1920 }
1921 }
1922}
1923
1924
1925/*************************************************
1926* Parse an optional match type *
1927*************************************************/
1928
1929/*
1930Grammar:
1931 match-type = ":is" / ":contains" / ":matches"
1932
1933Arguments:
1934 filter points to the Sieve filter including its state
1935 m returns match type
1936
1937Returns: 1 success
1938 0 no match type found
1939*/
1940
1941static int parse_matchtype(struct Sieve *filter, enum MatchType *m)
1942{
1943 if (parse_identifier(filter,CUS ":is")==1)
1944 {
1945 *m=MATCH_IS;
1946 return 1;
1947 }
1948 else if (parse_identifier(filter,CUS ":contains")==1)
1949 {
1950 *m=MATCH_CONTAINS;
1951 return 1;
1952 }
1953 else if (parse_identifier(filter,CUS ":matches")==1)
1954 {
1955 *m=MATCH_MATCHES;
1956 return 1;
1957 }
1958 else return 0;
1959}
1960
1961
1962/*************************************************
1963* Parse and interpret an optional test list *
1964*************************************************/
1965
1966/*
1967Grammar:
1968 test-list = "(" test *("," test) ")"
1969
1970Arguments:
1971 filter points to the Sieve filter including its state
1972 n total number of tests
deaf311d 1973 num_true number of passed tests
059ec3d9
PH
1974 exec Execute parsed statements
1975
1976Returns: 1 success
1977 0 no test list found
1978 -1 syntax or execution error
1979*/
1980
f7572e5a 1981static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
059ec3d9
PH
1982{
1983if (parse_white(filter)==-1) return -1;
1984if (*filter->pc=='(')
1985 {
1986 ++filter->pc;
1987 *n=0;
f7572e5a 1988 *num_true=0;
059ec3d9
PH
1989 for (;;)
1990 {
1991 int cond;
1992
1993 switch (parse_test(filter,&cond,exec))
1994 {
1995 case -1: return -1;
1996 case 0: filter->errmsg=CUS "missing test"; return -1;
f7572e5a 1997 default: ++*n; if (cond) ++*num_true; break;
059ec3d9
PH
1998 }
1999 if (parse_white(filter)==-1) return -1;
2000 if (*filter->pc==',') ++filter->pc;
2001 else break;
2002 }
2003 if (*filter->pc==')')
2004 {
2005 ++filter->pc;
2006 return 1;
2007 }
2008 else
2009 {
2010 filter->errmsg=CUS "missing closing paren";
2011 return -1;
2012 }
2013 }
2014else return 0;
2015}
2016
2017
2018/*************************************************
2019* Parse and interpret an optional test *
2020*************************************************/
2021
2022/*
2023Arguments:
2024 filter points to the Sieve filter including its state
2025 cond returned condition status
2026 exec Execute parsed statements
2027
2028Returns: 1 success
2029 0 no test found
2030 -1 syntax or execution error
2031*/
2032
d7978c0f
JH
2033static int
2034parse_test(struct Sieve *filter, int *cond, int exec)
059ec3d9
PH
2035{
2036if (parse_white(filter)==-1) return -1;
2037if (parse_identifier(filter,CUS "address"))
2038 {
2039 /*
2040 address-test = "address" { [address-part] [comparator] [match-type] }
2041 <header-list: string-list> <key-list: string-list>
2042
2043 header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To
2044 */
2045
2046 enum AddressPart addressPart=ADDRPART_ALL;
5ea81592 2047 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9 2048 enum MatchType matchType=MATCH_IS;
d7978c0f 2049 struct String *hdr,*key;
059ec3d9
PH
2050 int m;
2051 int ap=0,co=0,mt=0;
2052
2053 for (;;)
2054 {
2055 if (parse_white(filter)==-1) return -1;
2056 if ((m=parse_addresspart(filter,&addressPart))!=0)
2057 {
2058 if (m==-1) return -1;
2059 if (ap)
2060 {
2061 filter->errmsg=CUS "address part already specified";
2062 return -1;
2063 }
2064 else ap=1;
2065 }
2066 else if ((m=parse_comparator(filter,&comparator))!=0)
2067 {
2068 if (m==-1) return -1;
2069 if (co)
2070 {
2071 filter->errmsg=CUS "comparator already specified";
2072 return -1;
2073 }
2074 else co=1;
2075 }
2076 else if ((m=parse_matchtype(filter,&matchType))!=0)
2077 {
2078 if (m==-1) return -1;
2079 if (mt)
2080 {
2081 filter->errmsg=CUS "match type already specified";
2082 return -1;
2083 }
2084 else mt=1;
2085 }
2086 else break;
2087 }
2088 if (parse_white(filter)==-1) return -1;
2089 if ((m=parse_stringlist(filter,&hdr))!=1)
2090 {
2091 if (m==0) filter->errmsg=CUS "header string list expected";
2092 return -1;
2093 }
2094 if (parse_white(filter)==-1) return -1;
2095 if ((m=parse_stringlist(filter,&key))!=1)
2096 {
2097 if (m==0) filter->errmsg=CUS "key string list expected";
2098 return -1;
2099 }
2100 *cond=0;
d7978c0f 2101 for (struct String * h = hdr; h->length!=-1 && !*cond; ++h)
059ec3d9
PH
2102 {
2103 uschar *header_value=(uschar*)0,*extracted_addr,*end_addr;
2104
2105 if
2106 (
2107 !eq_asciicase(h,&str_from,0)
2108 && !eq_asciicase(h,&str_to,0)
2109 && !eq_asciicase(h,&str_cc,0)
2110 && !eq_asciicase(h,&str_bcc,0)
2111 && !eq_asciicase(h,&str_sender,0)
2112 && !eq_asciicase(h,&str_resent_from,0)
2113 && !eq_asciicase(h,&str_resent_to,0)
2114 )
2115 {
2116 filter->errmsg=CUS "invalid header field";
2117 return -1;
2118 }
2119 if (exec)
2120 {
2121 /* We are only interested in addresses below, so no MIME decoding */
2122 header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
2123 if (header_value == NULL)
2124 {
2125 filter->errmsg=CUS "header string expansion failed";
2126 return -1;
2127 }
8768d548 2128 f.parse_allow_group = TRUE;
059ec3d9
PH
2129 while (*header_value && !*cond)
2130 {
2131 uschar *error;
2132 int start, end, domain;
2133 int saveend;
2134 uschar *part=NULL;
2135
2136 end_addr = parse_find_address_end(header_value, FALSE);
2137 saveend = *end_addr;
2138 *end_addr = 0;
2139 extracted_addr = parse_extract_address(header_value, &error, &start, &end, &domain, FALSE);
2140
2141 if (extracted_addr) switch (addressPart)
2142 {
2143 case ADDRPART_ALL: part=extracted_addr; break;
2144#ifdef SUBADDRESS
2145 case ADDRPART_USER:
2146#endif
2147 case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
2148 case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
2149#ifdef SUBADDRESS
e4a89c47 2150 case ADDRPART_DETAIL: part=NULL; break;
059ec3d9 2151#endif
059ec3d9
PH
2152 }
2153
2154 *end_addr = saveend;
2155 if (part)
2156 {
d7978c0f 2157 for (struct String * k = key; k->length !=- 1; ++k)
059ec3d9 2158 {
d7978c0f 2159 struct String partStr = {.character = part, .length = Ustrlen(part)};
059ec3d9 2160
059ec3d9
PH
2161 if (extracted_addr)
2162 {
2163 *cond=compare(filter,k,&partStr,comparator,matchType);
2164 if (*cond==-1) return -1;
2165 if (*cond) break;
2166 }
2167 }
2168 }
2169 if (saveend == 0) break;
2170 header_value = end_addr + 1;
2171 }
8768d548
JH
2172 f.parse_allow_group = FALSE;
2173 f.parse_found_group = FALSE;
059ec3d9
PH
2174 }
2175 }
2176 return 1;
2177 }
2178else if (parse_identifier(filter,CUS "allof"))
2179 {
2180 /*
2181 allof-test = "allof" <tests: test-list>
2182 */
2183
f7572e5a 2184 int n,num_true;
059ec3d9 2185
f7572e5a 2186 switch (parse_testlist(filter,&n,&num_true,exec))
059ec3d9
PH
2187 {
2188 case -1: return -1;
2189 case 0: filter->errmsg=CUS "missing test list"; return -1;
f7572e5a 2190 default: *cond=(n==num_true); return 1;
059ec3d9
PH
2191 }
2192 }
2193else if (parse_identifier(filter,CUS "anyof"))
2194 {
2195 /*
2196 anyof-test = "anyof" <tests: test-list>
2197 */
2198
f7572e5a 2199 int n,num_true;
059ec3d9 2200
f7572e5a 2201 switch (parse_testlist(filter,&n,&num_true,exec))
059ec3d9
PH
2202 {
2203 case -1: return -1;
2204 case 0: filter->errmsg=CUS "missing test list"; return -1;
f7572e5a 2205 default: *cond=(num_true>0); return 1;
059ec3d9
PH
2206 }
2207 }
2208else if (parse_identifier(filter,CUS "exists"))
2209 {
2210 /*
2211 exists-test = "exists" <header-names: string-list>
2212 */
2213
d7978c0f 2214 struct String *hdr;
059ec3d9
PH
2215 int m;
2216
2217 if (parse_white(filter)==-1) return -1;
2218 if ((m=parse_stringlist(filter,&hdr))!=1)
2219 {
2220 if (m==0) filter->errmsg=CUS "header string list expected";
2221 return -1;
2222 }
2223 if (exec)
2224 {
2225 *cond=1;
d7978c0f 2226 for (struct String * h = hdr; h->length != -1 && *cond; ++h)
059ec3d9
PH
2227 {
2228 uschar *header_def;
2229
2230 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2231 if (header_def == NULL)
2232 {
2233 filter->errmsg=CUS "header string expansion failed";
2234 return -1;
2235 }
2236 if (Ustrcmp(header_def,"false")==0) *cond=0;
2237 }
2238 }
2239 return 1;
2240 }
2241else if (parse_identifier(filter,CUS "false"))
2242 {
2243 /*
2244 false-test = "false"
2245 */
2246
2247 *cond=0;
2248 return 1;
2249 }
2250else if (parse_identifier(filter,CUS "header"))
2251 {
2252 /*
2253 header-test = "header" { [comparator] [match-type] }
2254 <header-names: string-list> <key-list: string-list>
2255 */
2256
5ea81592 2257 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9 2258 enum MatchType matchType=MATCH_IS;
d7978c0f 2259 struct String *hdr,*key;
059ec3d9
PH
2260 int m;
2261 int co=0,mt=0;
2262
2263 for (;;)
2264 {
2265 if (parse_white(filter)==-1) return -1;
2266 if ((m=parse_comparator(filter,&comparator))!=0)
2267 {
2268 if (m==-1) return -1;
2269 if (co)
2270 {
2271 filter->errmsg=CUS "comparator already specified";
2272 return -1;
2273 }
2274 else co=1;
2275 }
2276 else if ((m=parse_matchtype(filter,&matchType))!=0)
2277 {
2278 if (m==-1) return -1;
2279 if (mt)
2280 {
2281 filter->errmsg=CUS "match type already specified";
2282 return -1;
2283 }
2284 else mt=1;
2285 }
2286 else break;
2287 }
2288 if (parse_white(filter)==-1) return -1;
2289 if ((m=parse_stringlist(filter,&hdr))!=1)
2290 {
2291 if (m==0) filter->errmsg=CUS "header string list expected";
2292 return -1;
2293 }
2294 if (parse_white(filter)==-1) return -1;
2295 if ((m=parse_stringlist(filter,&key))!=1)
2296 {
2297 if (m==0) filter->errmsg=CUS "key string list expected";
2298 return -1;
2299 }
2300 *cond=0;
d7978c0f 2301 for (struct String * h = hdr; h->length != -1 && !*cond; ++h)
059ec3d9
PH
2302 {
2303 if (!is_header(h))
2304 {
2305 filter->errmsg=CUS "invalid header field";
2306 return -1;
2307 }
2308 if (exec)
2309 {
2310 struct String header_value;
2311 uschar *header_def;
2312
2313 expand_header(&header_value,h);
2314 header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
2315 if (header_value.character == NULL || header_def == NULL)
2316 {
2317 filter->errmsg=CUS "header string expansion failed";
2318 return -1;
2319 }
d7978c0f 2320 for (struct String * k = key; k->length != -1; ++k)
059ec3d9
PH
2321 if (Ustrcmp(header_def,"true")==0)
2322 {
2323 *cond=compare(filter,k,&header_value,comparator,matchType);
2324 if (*cond==-1) return -1;
2325 if (*cond) break;
2326 }
059ec3d9
PH
2327 }
2328 }
2329 return 1;
2330 }
2331else if (parse_identifier(filter,CUS "not"))
2332 {
2333 if (parse_white(filter)==-1) return -1;
2334 switch (parse_test(filter,cond,exec))
2335 {
2336 case -1: return -1;
2337 case 0: filter->errmsg=CUS "missing test"; return -1;
2338 default: *cond=!*cond; return 1;
2339 }
2340 }
2341else if (parse_identifier(filter,CUS "size"))
2342 {
2343 /*
2344 relop = ":over" / ":under"
2345 size-test = "size" relop <limit: number>
2346 */
2347
2348 unsigned long limit;
2349 int overNotUnder;
2350
2351 if (parse_white(filter)==-1) return -1;
2352 if (parse_identifier(filter,CUS ":over")) overNotUnder=1;
2353 else if (parse_identifier(filter,CUS ":under")) overNotUnder=0;
2354 else
2355 {
2356 filter->errmsg=CUS "missing :over or :under";
2357 return -1;
2358 }
2359 if (parse_white(filter)==-1) return -1;
2360 if (parse_number(filter,&limit)==-1) return -1;
2361 *cond=(overNotUnder ? (message_size>limit) : (message_size<limit));
2362 return 1;
2363 }
2364else if (parse_identifier(filter,CUS "true"))
2365 {
2366 *cond=1;
2367 return 1;
2368 }
2369else if (parse_identifier(filter,CUS "envelope"))
2370 {
2371 /*
2372 envelope-test = "envelope" { [comparator] [address-part] [match-type] }
2373 <envelope-part: string-list> <key-list: string-list>
2374
2375 envelope-part is case insensitive "from" or "to"
024bd3c2
PH
2376#ifdef ENVELOPE_AUTH
2377 envelope-part =/ "auth"
2378#endif
059ec3d9
PH
2379 */
2380
5ea81592 2381 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
059ec3d9
PH
2382 enum AddressPart addressPart=ADDRPART_ALL;
2383 enum MatchType matchType=MATCH_IS;
d7978c0f 2384 struct String *env,*key;
059ec3d9
PH
2385 int m;
2386 int co=0,ap=0,mt=0;
2387
2388 if (!filter->require_envelope)
2389 {
2390 filter->errmsg=CUS "missing previous require \"envelope\";";
2391 return -1;
2392 }
2393 for (;;)
2394 {
2395 if (parse_white(filter)==-1) return -1;
2396 if ((m=parse_comparator(filter,&comparator))!=0)
2397 {
2398 if (m==-1) return -1;
2399 if (co)
2400 {
2401 filter->errmsg=CUS "comparator already specified";
2402 return -1;
2403 }
2404 else co=1;
2405 }
2406 else if ((m=parse_addresspart(filter,&addressPart))!=0)
2407 {
2408 if (m==-1) return -1;
2409 if (ap)
2410 {
2411 filter->errmsg=CUS "address part already specified";
2412 return -1;
2413 }
2414 else ap=1;
2415 }
2416 else if ((m=parse_matchtype(filter,&matchType))!=0)
2417 {
2418 if (m==-1) return -1;
2419 if (mt)
2420 {
2421 filter->errmsg=CUS "match type already specified";
2422 return -1;
2423 }
2424 else mt=1;
2425 }
2426 else break;
2427 }
2428 if (parse_white(filter)==-1) return -1;
2429 if ((m=parse_stringlist(filter,&env))!=1)
2430 {
2431 if (m==0) filter->errmsg=CUS "envelope string list expected";
2432 return -1;
2433 }
2434 if (parse_white(filter)==-1) return -1;
2435 if ((m=parse_stringlist(filter,&key))!=1)
2436 {
2437 if (m==0) filter->errmsg=CUS "key string list expected";
2438 return -1;
2439 }
2440 *cond=0;
d7978c0f 2441 for (struct String * e = env; e->length != -1 && !*cond; ++e)
059ec3d9
PH
2442 {
2443 const uschar *envelopeExpr=CUS 0;
2444 uschar *envelope=US 0;
2445
2446 if (eq_asciicase(e,&str_from,0))
2447 {
2448 switch (addressPart)
2449 {
2450 case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break;
2451#ifdef SUBADDRESS
2452 case ADDRPART_USER:
2453#endif
2454 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
2455 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
2456#ifdef SUBADDRESS
e4a89c47 2457 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
059ec3d9
PH
2458#endif
2459 }
2460 }
2461 else if (eq_asciicase(e,&str_to,0))
2462 {
2463 switch (addressPart)
2464 {
2465 case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
2466#ifdef SUBADDRESS
e4a89c47
PH
2467 case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
2468 case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
059ec3d9
PH
2469#endif
2470 case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
2471 case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
2472 }
2473 }
024bd3c2
PH
2474#ifdef ENVELOPE_AUTH
2475 else if (eq_asciicase(e,&str_auth,0))
2476 {
2477 switch (addressPart)
2478 {
2479 case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break;
2480#ifdef SUBADDRESS
2481 case ADDRPART_USER:
2482#endif
2483 case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break;
2484 case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break;
2485#ifdef SUBADDRESS
2486 case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
2487#endif
2488 }
2489 }
2490#endif
059ec3d9
PH
2491 else
2492 {
2493 filter->errmsg=CUS "invalid envelope string";
2494 return -1;
2495 }
2496 if (exec && envelopeExpr)
2497 {
2498 if ((envelope=expand_string(US envelopeExpr)) == NULL)
2499 {
2500 filter->errmsg=CUS "header string expansion failed";
2501 return -1;
2502 }
d7978c0f 2503 for (struct String * k = key; k->length != -1; ++k)
059ec3d9 2504 {
d7978c0f 2505 struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)};
059ec3d9 2506
7fe1560f 2507 *cond=compare(filter,k,&envelopeStr,comparator,matchType);
059ec3d9
PH
2508 if (*cond==-1) return -1;
2509 if (*cond) break;
2510 }
2511 }
2512 }
2513 return 1;
2514 }
7c5214ec 2515#ifdef ENOTIFY
d6a60c0f 2516else if (parse_identifier(filter,CUS "valid_notify_method"))
7c5214ec
PH
2517 {
2518 /*
d6a60c0f
PH
2519 valid_notify_method = "valid_notify_method"
2520 <notification-uris: string-list>
7c5214ec
PH
2521 */
2522
d7978c0f 2523 struct String *uris;
7c5214ec
PH
2524 int m;
2525
2526 if (!filter->require_enotify)
2527 {
2528 filter->errmsg=CUS "missing previous require \"enotify\";";
2529 return -1;
2530 }
2531 if (parse_white(filter)==-1) return -1;
2532 if ((m=parse_stringlist(filter,&uris))!=1)
2533 {
2534 if (m==0) filter->errmsg=CUS "URI string list expected";
2535 return -1;
2536 }
2537 if (exec)
2538 {
2539 *cond=1;
d7978c0f 2540 for (struct String * u = uris; u->length != -1 && *cond; ++u)
7c5214ec
PH
2541 {
2542 string_item *recipient;
4be39b49 2543 struct String header,subject,body;
7c5214ec
PH
2544
2545 recipient=NULL;
2546 header.length=-1;
2547 header.character=(uschar*)0;
4be39b49
MH
2548 subject.length=-1;
2549 subject.character=(uschar*)0;
7c5214ec
PH
2550 body.length=-1;
2551 body.character=(uschar*)0;
4be39b49 2552 if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1)
7c5214ec
PH
2553 *cond=0;
2554 }
2555 }
2556 return 1;
2557 }
d6a60c0f
PH
2558else if (parse_identifier(filter,CUS "notify_method_capability"))
2559 {
2560 /*
2561 notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE]
2562 <notification-uri: string>
2563 <notification-capability: string>
2564 <key-list: string-list>
2565 */
2566
2567 int m;
2568 int co=0,mt=0;
2569
2570 enum Comparator comparator=COMP_EN_ASCII_CASEMAP;
2571 enum MatchType matchType=MATCH_IS;
d7978c0f 2572 struct String uri,capa,*keys;
d6a60c0f
PH
2573
2574 if (!filter->require_enotify)
2575 {
2576 filter->errmsg=CUS "missing previous require \"enotify\";";
2577 return -1;
2578 }
2579 for (;;)
2580 {
2581 if (parse_white(filter)==-1) return -1;
2582 if ((m=parse_comparator(filter,&comparator))!=0)
2583 {
2584 if (m==-1) return -1;
2585 if (co)
2586 {
2587 filter->errmsg=CUS "comparator already specified";
2588 return -1;
2589 }
2590 else co=1;
2591 }
2592 else if ((m=parse_matchtype(filter,&matchType))!=0)
2593 {
2594 if (m==-1) return -1;
2595 if (mt)
2596 {
2597 filter->errmsg=CUS "match type already specified";
2598 return -1;
2599 }
2600 else mt=1;
2601 }
2602 else break;
2603 }
2604 if ((m=parse_string(filter,&uri))!=1)
2605 {
2606 if (m==0) filter->errmsg=CUS "missing notification URI string";
2607 return -1;
2608 }
2609 if (parse_white(filter)==-1) return -1;
2610 if ((m=parse_string(filter,&capa))!=1)
2611 {
2612 if (m==0) filter->errmsg=CUS "missing notification capability string";
2613 return -1;
2614 }
2615 if (parse_white(filter)==-1) return -1;
2616 if ((m=parse_stringlist(filter,&keys))!=1)
2617 {
2618 if (m==0) filter->errmsg=CUS "missing key string list";
2619 return -1;
2620 }
2621 if (exec)
2622 {
2623 string_item *recipient;
4be39b49 2624 struct String header,subject,body;
d6a60c0f
PH
2625
2626 *cond=0;
2627 recipient=NULL;
2628 header.length=-1;
2629 header.character=(uschar*)0;
4be39b49
MH
2630 subject.length=-1;
2631 subject.character=(uschar*)0;
d6a60c0f
PH
2632 body.length=-1;
2633 body.character=(uschar*)0;
4be39b49 2634 if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1)
d6a60c0f 2635 if (eq_asciicase(&capa,&str_online,0)==1)
d7978c0f 2636 for (struct String * k = keys; k->length != -1; ++k)
d6a60c0f
PH
2637 {
2638 *cond=compare(filter,k,&str_maybe,comparator,matchType);
2639 if (*cond==-1) return -1;
2640 if (*cond) break;
2641 }
d6a60c0f
PH
2642 }
2643 return 1;
2644 }
7c5214ec 2645#endif
059ec3d9
PH
2646else return 0;
2647}
2648
2649
2650/*************************************************
2651* Parse and interpret an optional block *
2652*************************************************/
2653
2654/*
2655Arguments:
2656 filter points to the Sieve filter including its state
2657 exec Execute parsed statements
2658 generated where to hang newly-generated addresses
2659
2660Returns: 2 success by stop
2661 1 other success
2662 0 no block command found
2663 -1 syntax or execution error
2664*/
2665
2666static int parse_block(struct Sieve *filter, int exec,
2667 address_item **generated)
2668{
2669int r;
2670
2671if (parse_white(filter)==-1) return -1;
2672if (*filter->pc=='{')
2673 {
2674 ++filter->pc;
2675 if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r;
2676 if (*filter->pc=='}')
2677 {
2678 ++filter->pc;
2679 return 1;
2680 }
2681 else
2682 {
8e669ac1 2683 filter->errmsg=CUS "expecting command or closing brace";
059ec3d9
PH
2684 return -1;
2685 }
2686 }
2687else return 0;
2688}
2689
2690
2691/*************************************************
2692* Match a semicolon *
2693*************************************************/
2694
2695/*
2696Arguments:
2697 filter points to the Sieve filter including its state
2698
2699Returns: 1 success
2700 -1 syntax error
2701*/
2702
2703static int parse_semicolon(struct Sieve *filter)
2704{
2705 if (parse_white(filter)==-1) return -1;
2706 if (*filter->pc==';')
2707 {
2708 ++filter->pc;
2709 return 1;
2710 }
2711 else
2712 {
2713 filter->errmsg=CUS "missing semicolon";
2714 return -1;
2715 }
2716}
2717
2718
2719/*************************************************
2720* Parse and interpret a Sieve command *
2721*************************************************/
2722
2723/*
2724Arguments:
2725 filter points to the Sieve filter including its state
2726 exec Execute parsed statements
2727 generated where to hang newly-generated addresses
2728
2729Returns: 2 success by stop
2730 1 other success
2731 -1 syntax or execution error
2732*/
55414b25
JH
2733static int
2734parse_commands(struct Sieve *filter, int exec, address_item **generated)
059ec3d9
PH
2735{
2736while (*filter->pc)
2737 {
2738 if (parse_white(filter)==-1) return -1;
2739 if (parse_identifier(filter,CUS "if"))
2740 {
2741 /*
2742 if-command = "if" test block *( "elsif" test block ) [ else block ]
2743 */
2744
2745 int cond,m,unsuccessful;
2746
2747 /* test block */
2748 if (parse_white(filter)==-1) return -1;
2749 if ((m=parse_test(filter,&cond,exec))==-1) return -1;
2750 if (m==0)
2751 {
2752 filter->errmsg=CUS "missing test";
2753 return -1;
2754 }
87fcc8b9
PH
2755 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2756 (debug_selector & D_filter) != 0)
2757 {
2758 if (exec) debug_printf("if %s\n",cond?"true":"false");
2759 }
059ec3d9
PH
2760 m=parse_block(filter,exec ? cond : 0, generated);
2761 if (m==-1 || m==2) return m;
2762 if (m==0)
2763 {
2764 filter->errmsg=CUS "missing block";
2765 return -1;
2766 }
2767 unsuccessful = !cond;
2768 for (;;) /* elsif test block */
2769 {
2770 if (parse_white(filter)==-1) return -1;
2771 if (parse_identifier(filter,CUS "elsif"))
2772 {
2773 if (parse_white(filter)==-1) return -1;
2774 m=parse_test(filter,&cond,exec && unsuccessful);
2775 if (m==-1 || m==2) return m;
2776 if (m==0)
2777 {
2778 filter->errmsg=CUS "missing test";
2779 return -1;
2780 }
87fcc8b9
PH
2781 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
2782 (debug_selector & D_filter) != 0)
2783 {
2784 if (exec) debug_printf("elsif %s\n",cond?"true":"false");
2785 }
059ec3d9
PH
2786 m=parse_block(filter,exec && unsuccessful ? cond : 0, generated);
2787 if (m==-1 || m==2) return m;
2788 if (m==0)
2789 {
2790 filter->errmsg=CUS "missing block";
2791 return -1;
2792 }
2793 if (exec && unsuccessful && cond) unsuccessful = 0;
2794 }
2795 else break;
2796 }
2797 /* else block */
2798 if (parse_white(filter)==-1) return -1;
2799 if (parse_identifier(filter,CUS "else"))
2800 {
2801 m=parse_block(filter,exec && unsuccessful, generated);
2802 if (m==-1 || m==2) return m;
2803 if (m==0)
2804 {
2805 filter->errmsg=CUS "missing block";
2806 return -1;
2807 }
2808 }
2809 }
2810 else if (parse_identifier(filter,CUS "stop"))
2811 {
2812 /*
2813 stop-command = "stop" { stop-options } ";"
2814 stop-options =
2815 */
2816
2817 if (parse_semicolon(filter)==-1) return -1;
2818 if (exec)
2819 {
2820 filter->pc+=Ustrlen(filter->pc);
2821 return 2;
2822 }
2823 }
2824 else if (parse_identifier(filter,CUS "keep"))
2825 {
2826 /*
2827 keep-command = "keep" { keep-options } ";"
2828 keep-options =
2829 */
2830
2831 if (parse_semicolon(filter)==-1) return -1;
2832 if (exec)
2833 {
2834 add_addr(generated,US"inbox",1,0,0,0);
2835 filter->keep = 0;
2836 }
2837 }
2838 else if (parse_identifier(filter,CUS "discard"))
2839 {
2840 /*
2841 discard-command = "discard" { discard-options } ";"
2842 discard-options =
2843 */
2844
2845 if (parse_semicolon(filter)==-1) return -1;
2846 if (exec) filter->keep=0;
2847 }
2848 else if (parse_identifier(filter,CUS "redirect"))
2849 {
2850 /*
2851 redirect-command = "redirect" redirect-options "string" ";"
2852 redirect-options =
2853 redirect-options =) ":copy"
2854 */
2855
2856 struct String recipient;
2857 int m;
2858 int copy=0;
2859
2860 for (;;)
2861 {
2862 if (parse_white(filter)==-1) return -1;
2863 if (parse_identifier(filter,CUS ":copy")==1)
2864 {
2865 if (!filter->require_copy)
2866 {
2867 filter->errmsg=CUS "missing previous require \"copy\";";
2868 return -1;
2869 }
2870 copy=1;
2871 }
2872 else break;
2873 }
2874 if (parse_white(filter)==-1) return -1;
2875 if ((m=parse_string(filter,&recipient))!=1)
2876 {
2877 if (m==0) filter->errmsg=CUS "missing redirect recipient string";
2878 return -1;
2879 }
2880 if (strchr(CCS recipient.character,'@')==(char*)0)
2881 {
2882 filter->errmsg=CUS "unqualified recipient address";
2883 return -1;
2884 }
2885 if (exec)
2886 {
2887 add_addr(generated,recipient.character,0,0,0,0);
2888 if (!copy) filter->keep = 0;
2889 }
2890 if (parse_semicolon(filter)==-1) return -1;
2891 }
2892 else if (parse_identifier(filter,CUS "fileinto"))
2893 {
2894 /*
2895 fileinto-command = "fileinto" { fileinto-options } string ";"
2896 fileinto-options =
2897 fileinto-options =) [ ":copy" ]
87fcc8b9 2898 */
059ec3d9
PH
2899
2900 struct String folder;
2901 uschar *s;
2902 int m;
2903 unsigned long maxage, maxmessages, maxstorage;
2904 int copy=0;
2905
2906 maxage = maxmessages = maxstorage = 0;
2907 if (!filter->require_fileinto)
2908 {
2909 filter->errmsg=CUS "missing previous require \"fileinto\";";
2910 return -1;
2911 }
2912 for (;;)
2913 {
2914 if (parse_white(filter)==-1) return -1;
2915 if (parse_identifier(filter,CUS ":copy")==1)
2916 {
2917 if (!filter->require_copy)
2918 {
2919 filter->errmsg=CUS "missing previous require \"copy\";";
2920 return -1;
2921 }
2922 copy=1;
2923 }
2924 else break;
2925 }
2926 if (parse_white(filter)==-1) return -1;
2927 if ((m=parse_string(filter,&folder))!=1)
2928 {
2929 if (m==0) filter->errmsg=CUS "missing fileinto folder string";
2930 return -1;
2931 }
2932 m=0; s=folder.character;
2933 if (folder.length==0) m=1;
2934 if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1;
2935 else while (*s)
2936 {
2937 if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; }
2938 ++s;
2939 }
2940 if (m)
2941 {
2942 filter->errmsg=CUS "invalid folder";
2943 return -1;
2944 }
2945 if (exec)
2946 {
2947 add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage);
2948 if (!copy) filter->keep = 0;
2949 }
2950 if (parse_semicolon(filter)==-1) return -1;
2951 }
7c5214ec 2952#ifdef ENOTIFY
87fcc8b9
PH
2953 else if (parse_identifier(filter,CUS "notify"))
2954 {
2955 /*
7c5214ec
PH
2956 notify-command = "notify" { notify-options } <method: string> ";"
2957 notify-options = [":from" string]
2958 [":importance" <"1" / "2" / "3">]
2959 [":options" 1*(string-list / number)]
87fcc8b9
PH
2960 [":message" string]
2961 */
2962
2963 int m;
7c5214ec
PH
2964 struct String from;
2965 struct String importance;
87fcc8b9 2966 struct String message;
7c5214ec 2967 struct String method;
50c99ba6
PH
2968 struct Notification *already;
2969 string_item *recipient;
7c5214ec 2970 struct String header;
4be39b49 2971 struct String subject;
50c99ba6 2972 struct String body;
c61f2b4f 2973 uschar *envelope_from;
c66441d7
MH
2974 struct String auto_submitted_value;
2975 uschar *auto_submitted_def;
87fcc8b9 2976
7c5214ec 2977 if (!filter->require_enotify)
87fcc8b9 2978 {
7c5214ec 2979 filter->errmsg=CUS "missing previous require \"enotify\";";
87fcc8b9
PH
2980 return -1;
2981 }
7c5214ec
PH
2982 from.character=(uschar*)0;
2983 from.length=-1;
2984 importance.character=(uschar*)0;
2985 importance.length=-1;
87fcc8b9
PH
2986 message.character=(uschar*)0;
2987 message.length=-1;
50c99ba6 2988 recipient=NULL;
7c5214ec
PH
2989 header.length=-1;
2990 header.character=(uschar*)0;
4be39b49
MH
2991 subject.length=-1;
2992 subject.character=(uschar*)0;
50c99ba6
PH
2993 body.length=-1;
2994 body.character=(uschar*)0;
5ca6d115 2995 envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
87fcc8b9
PH
2996 for (;;)
2997 {
2998 if (parse_white(filter)==-1) return -1;
7c5214ec 2999 if (parse_identifier(filter,CUS ":from")==1)
87fcc8b9
PH
3000 {
3001 if (parse_white(filter)==-1) return -1;
7c5214ec 3002 if ((m=parse_string(filter,&from))!=1)
87fcc8b9 3003 {
7c5214ec 3004 if (m==0) filter->errmsg=CUS "from string expected";
87fcc8b9
PH
3005 return -1;
3006 }
50c99ba6 3007 }
7c5214ec 3008 else if (parse_identifier(filter,CUS ":importance")==1)
50c99ba6
PH
3009 {
3010 if (parse_white(filter)==-1) return -1;
7c5214ec 3011 if ((m=parse_string(filter,&importance))!=1)
50c99ba6 3012 {
7c5214ec
PH
3013 if (m==0) filter->errmsg=CUS "importance string expected";
3014 return -1;
3015 }
3016 if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
3017 {
3018 filter->errmsg=CUS "invalid importance";
50c99ba6
PH
3019 return -1;
3020 }
87fcc8b9 3021 }
7c5214ec
PH
3022 else if (parse_identifier(filter,CUS ":options")==1)
3023 {
3024 if (parse_white(filter)==-1) return -1;
3025 }
87fcc8b9
PH
3026 else if (parse_identifier(filter,CUS ":message")==1)
3027 {
3028 if (parse_white(filter)==-1) return -1;
3029 if ((m=parse_string(filter,&message))!=1)
3030 {
3031 if (m==0) filter->errmsg=CUS "message string expected";
3032 return -1;
3033 }
3034 }
3035 else break;
3036 }
7c5214ec
PH
3037 if (parse_white(filter)==-1) return -1;
3038 if ((m=parse_string(filter,&method))!=1)
3039 {
3040 if (m==0) filter->errmsg=CUS "missing method string";
3041 return -1;
3042 }
87fcc8b9 3043 if (parse_semicolon(filter)==-1) return -1;
4be39b49
MH
3044 if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
3045 return -1;
c61f2b4f 3046 if (exec)
50c99ba6 3047 {
c61f2b4f
MH
3048 if (message.length==-1) message=subject;
3049 if (message.length==-1) expand_header(&message,&str_subject);
3050 expand_header(&auto_submitted_value,&str_auto_submitted);
3051 auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
3052 if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
50c99ba6 3053 {
c61f2b4f
MH
3054 filter->errmsg=CUS "header string expansion failed";
3055 return -1;
50c99ba6 3056 }
c61f2b4f 3057 if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
7c5214ec 3058 {
c61f2b4f 3059 for (already=filter->notified; already; already=already->next)
7c5214ec 3060 {
c61f2b4f 3061 if (already->method.length==method.length
5ca6d115 3062 && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
c61f2b4f 3063 && already->importance.length==importance.length
5ca6d115 3064 && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
c61f2b4f 3065 && already->message.length==message.length
5ca6d115 3066 && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
c61f2b4f 3067 break;
c66441d7 3068 }
d7978c0f 3069 if (!already)
c61f2b4f 3070 /* New notification, process it */
c66441d7 3071 {
c61f2b4f
MH
3072 struct Notification *sent;
3073 sent=store_get(sizeof(struct Notification));
3074 sent->method=method;
3075 sent->importance=importance;
3076 sent->message=message;
3077 sent->next=filter->notified;
3078 filter->notified=sent;
3079 #ifndef COMPILE_SYNTAX_CHECKER
3080 if (filter_test == FTEST_NONE)
d2ee6114 3081 {
d7978c0f 3082 int pid, fd;
c61f2b4f
MH
3083
3084 if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
c66441d7 3085 {
c61f2b4f
MH
3086 FILE *f;
3087 uschar *buffer;
3088 int buffer_capacity;
3089
3090 f = fdopen(fd, "wb");
5ca6d115 3091 fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
d7978c0f
JH
3092 for (string_item * p = recipient; p; p=p->next)
3093 fprintf(f,"To: %s\n",p->text);
5f71124d 3094 fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
c61f2b4f
MH
3095 if (header.length>0) fprintf(f,"%s",header.character);
3096 if (message.length==-1)
3097 {
3098 message.character=US"Notification";
3099 message.length=Ustrlen(message.character);
3100 }
4c04137d 3101 /* Allocation is larger than necessary, but enough even for split MIME words */
c61f2b4f
MH
3102 buffer_capacity=32+4*message.length;
3103 buffer=store_get(buffer_capacity);
3104 if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
3105 fprintf(f,"\n");
3106 if (body.length>0) fprintf(f,"%s\n",body.character);
3107 fflush(f);
3108 (void)fclose(f);
3109 (void)child_close(pid, 0);
c66441d7 3110 }
d2ee6114 3111 }
c61f2b4f
MH
3112 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3113 {
3114 debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
3115 }
7c5214ec 3116#endif
c61f2b4f
MH
3117 }
3118 else
3119 {
3120 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3121 {
3122 debug_printf("Repeated notification to `%s' ignored.\n",method.character);
3123 }
3124 }
c66441d7
MH
3125 }
3126 else
3127 {
3128 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
3129 {
c61f2b4f 3130 debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
c66441d7
MH
3131 }
3132 }
7c5214ec 3133 }
87fcc8b9
PH
3134 }
3135#endif
059ec3d9
PH
3136#ifdef VACATION
3137 else if (parse_identifier(filter,CUS "vacation"))
3138 {
3139 /*
3140 vacation-command = "vacation" { vacation-options } <reason: string> ";"
3141 vacation-options = [":days" number]
059ec3d9 3142 [":subject" string]
f656d135
PH
3143 [":from" string]
3144 [":addresses" string-list]
059ec3d9 3145 [":mime"]
f656d135 3146 [":handle" string]
059ec3d9
PH
3147 */
3148
3149 int m;
3150 unsigned long days;
059ec3d9 3151 struct String subject;
f656d135
PH
3152 struct String from;
3153 struct String *addresses;
059ec3d9
PH
3154 int reason_is_mime;
3155 string_item *aliases;
f656d135 3156 struct String handle;
059ec3d9
PH
3157 struct String reason;
3158
3159 if (!filter->require_vacation)
3160 {
3161 filter->errmsg=CUS "missing previous require \"vacation\";";
3162 return -1;
3163 }
3164 if (exec)
3165 {
3166 if (filter->vacation_ran)
3167 {
3168 filter->errmsg=CUS "trying to execute vacation more than once";
3169 return -1;
3170 }
3171 filter->vacation_ran=1;
3172 }
3173 days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7;
3174 subject.character=(uschar*)0;
3175 subject.length=-1;
f656d135
PH
3176 from.character=(uschar*)0;
3177 from.length=-1;
3178 addresses=(struct String*)0;
059ec3d9
PH
3179 aliases=NULL;
3180 reason_is_mime=0;
f656d135
PH
3181 handle.character=(uschar*)0;
3182 handle.length=-1;
059ec3d9
PH
3183 for (;;)
3184 {
3185 if (parse_white(filter)==-1) return -1;
3186 if (parse_identifier(filter,CUS ":days")==1)
3187 {
3188 if (parse_white(filter)==-1) return -1;
3189 if (parse_number(filter,&days)==-1) return -1;
3190 if (days<VACATION_MIN_DAYS) days=VACATION_MIN_DAYS;
3191 else if (days>VACATION_MAX_DAYS) days=VACATION_MAX_DAYS;
3192 }
f656d135
PH
3193 else if (parse_identifier(filter,CUS ":subject")==1)
3194 {
3195 if (parse_white(filter)==-1) return -1;
3196 if ((m=parse_string(filter,&subject))!=1)
3197 {
3198 if (m==0) filter->errmsg=CUS "subject string expected";
3199 return -1;
3200 }
3201 }
3202 else if (parse_identifier(filter,CUS ":from")==1)
3203 {
f656d135
PH
3204 if (parse_white(filter)==-1) return -1;
3205 if ((m=parse_string(filter,&from))!=1)
3206 {
3207 if (m==0) filter->errmsg=CUS "from string expected";
3208 return -1;
3209 }
7c5214ec 3210 if (check_mail_address(filter,&from)!=1)
f656d135 3211 return -1;
f656d135 3212 }
059ec3d9
PH
3213 else if (parse_identifier(filter,CUS ":addresses")==1)
3214 {
059ec3d9
PH
3215 if (parse_white(filter)==-1) return -1;
3216 if ((m=parse_stringlist(filter,&addresses))!=1)
3217 {
3218 if (m==0) filter->errmsg=CUS "addresses string list expected";
3219 return -1;
3220 }
d7978c0f 3221 for (struct String * a = addresses; a->length != -1; ++a)
059ec3d9 3222 {
d7978c0f 3223 string_item * new = store_get(sizeof(string_item));
059ec3d9 3224
059ec3d9
PH
3225 new->text=store_get(a->length+1);
3226 if (a->length) memcpy(new->text,a->character,a->length);
3227 new->text[a->length]='\0';
3228 new->next=aliases;
3229 aliases=new;
3230 }
3231 }
f656d135
PH
3232 else if (parse_identifier(filter,CUS ":mime")==1)
3233 reason_is_mime=1;
3234 else if (parse_identifier(filter,CUS ":handle")==1)
059ec3d9
PH
3235 {
3236 if (parse_white(filter)==-1) return -1;
f656d135 3237 if ((m=parse_string(filter,&from))!=1)
059ec3d9 3238 {
f656d135 3239 if (m==0) filter->errmsg=CUS "handle string expected";
059ec3d9
PH
3240 return -1;
3241 }
3242 }
059ec3d9
PH
3243 else break;
3244 }
3245 if (parse_white(filter)==-1) return -1;
3246 if ((m=parse_string(filter,&reason))!=1)
3247 {
3248 if (m==0) filter->errmsg=CUS "missing reason string";
3249 return -1;
3250 }
5ea81592
PH
3251 if (reason_is_mime)
3252 {
3253 uschar *s,*end;
3254
d7978c0f
JH
3255 for (s = reason.character, end = reason.character + reason.length;
3256 s<end && (*s&0x80)==0; ) s++;
5ea81592
PH
3257 if (s<end)
3258 {
3259 filter->errmsg=CUS "MIME reason string contains 8bit text";
3260 return -1;
3261 }
3262 }
059ec3d9
PH
3263 if (parse_semicolon(filter)==-1) return -1;
3264
3265 if (exec)
3266 {
3267 address_item *addr;
059ec3d9
PH
3268 uschar *buffer;
3269 int buffer_capacity;
059ec3d9
PH
3270 md5 base;
3271 uschar digest[16];
3272 uschar hexdigest[33];
94e1f16d 3273 gstring * once;
059ec3d9
PH
3274
3275 if (filter_personal(aliases,TRUE))
3276 {
4929acf0
PH
3277 if (filter_test == FTEST_NONE)
3278 {
3279 /* ensure oncelog directory exists; failure will be detected later */
059ec3d9 3280
4929acf0
PH
3281 (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
3282 }
059ec3d9
PH
3283 /* build oncelog filename */
3284
94e1f16d
JH
3285 md5_start(&base);
3286
f656d135 3287 if (handle.length==-1)
8e669ac1 3288 {
94e1f16d
JH
3289 gstring * key = NULL;
3290 if (subject.length!=-1) key =string_catn(key, subject.character, subject.length);
3291 if (from.length!=-1) key = string_catn(key, from.character, from.length);
3292 key = string_catn(key, reason_is_mime?US"1":US"0", 1);
3293 key = string_catn(key, reason.character, reason.length);
3294 md5_end(&base, key->s, key->ptr, digest);
8e669ac1 3295 }
f656d135 3296 else
94e1f16d
JH
3297 md5_end(&base, handle.character, handle.length, digest);
3298
d7978c0f 3299 for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
94e1f16d 3300
50c99ba6 3301 if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
4929acf0 3302 debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
94e1f16d 3303
50c99ba6 3304 if (filter_test == FTEST_NONE)
059ec3d9 3305 {
94e1f16d
JH
3306 once = string_cat (NULL, filter->vacation_directory);
3307 once = string_catn(once, US"/", 1);
3308 once = string_catn(once, hexdigest, 33);
4929acf0
PH
3309
3310 /* process subject */
3311
3312 if (subject.length==-1)
3313 {
50c99ba6
PH
3314 uschar *subject_def;
3315
3316 subject_def=expand_string(US"${if def:header_subject {true}{false}}");
3317 if (Ustrcmp(subject_def,"true")==0)
3318 {
94e1f16d
JH
3319 gstring * g = string_catn(NULL, US"Auto: ", 6);
3320
50c99ba6 3321 expand_header(&subject,&str_subject);
94e1f16d
JH
3322 g = string_catn(g, subject.character, subject.length);
3323 subject.character = string_from_gstring(g);
3324 subject.length = g->ptr;
50c99ba6
PH
3325 }
3326 else
3327 {
3328 subject.character=US"Automated reply";
3329 subject.length=Ustrlen(subject.character);
3330 }
4929acf0
PH
3331 }
3332
3333 /* add address to list of generated addresses */
3334
3335 addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
3336 setflag(addr, af_pfr);
7eb0e5d2 3337 addr->prop.ignore_error = TRUE;
4929acf0
PH
3338 addr->next = *generated;
3339 *generated = addr;
3340 addr->reply = store_get(sizeof(reply_item));
3341 memset(addr->reply,0,sizeof(reply_item)); /* XXX */
3342 addr->reply->to = string_copy(sender_address);
f656d135
PH
3343 if (from.length==-1)
3344 addr->reply->from = expand_string(US"$local_part@$domain");
3345 else
3346 addr->reply->from = from.character;
4c04137d 3347 /* Allocation is larger than necessary, but enough even for split MIME words */
50c99ba6 3348 buffer_capacity=32+4*subject.length;
4929acf0 3349 buffer=store_get(buffer_capacity);
55414b25
JH
3350 /* deconst cast safe as we pass in a non-const item */
3351 addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
94e1f16d 3352 addr->reply->oncelog = string_from_gstring(once);
4929acf0
PH
3353 addr->reply->once_repeat=days*86400;
3354
3355 /* build body and MIME headers */
3356
3357 if (reason_is_mime)
3358 {
3359 uschar *mime_body,*reason_end;
4929acf0 3360 static const uschar nlnl[]="\r\n\r\n";
059ec3d9 3361
4929acf0
PH
3362 for
3363 (
27fd1318
JH
3364 mime_body = reason.character, reason_end = reason.character + reason.length;
3365 mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1));
d7978c0f 3366 ) mime_body++;
94e1f16d
JH
3367
3368 addr->reply->headers = string_copyn(reason.character, mime_body-reason.character);
3369
50c99ba6 3370 if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=(sizeof(nlnl)-1);
4929acf0 3371 else mime_body=reason_end-1;
94e1f16d 3372 addr->reply->text = string_copyn(mime_body, reason_end-mime_body);
4929acf0
PH
3373 }
3374 else
3375 {
f2ed27cf 3376 struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */
4929acf0 3377
4929acf0
PH
3378 addr->reply->headers = US"MIME-Version: 1.0\n"
3379 "Content-Type: text/plain;\n"
3380 "\tcharset=\"utf-8\"\n"
3381 "Content-Transfer-Encoding: quoted-printable";
3382 addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
3383 }
059ec3d9 3384 }
4929acf0 3385 }
50c99ba6 3386 else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
4929acf0 3387 debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
059ec3d9
PH
3388 }
3389 }
4929acf0 3390 else break;
059ec3d9 3391#endif
059ec3d9
PH
3392 }
3393return 1;
3394}
3395
3396
3397/*************************************************
3398* Parse and interpret a sieve filter *
3399*************************************************/
3400
3401/*
3402Arguments:
3403 filter points to the Sieve filter including its state
3404 exec Execute parsed statements
3405 generated where to hang newly-generated addresses
3406
3407Returns: 1 success
3408 -1 syntax or execution error
3409*/
3410
c007c974
JH
3411static int
3412parse_start(struct Sieve *filter, int exec, address_item **generated)
059ec3d9
PH
3413{
3414filter->pc=filter->filter;
3415filter->line=1;
3416filter->keep=1;
3417filter->require_envelope=0;
3418filter->require_fileinto=0;
84024b72
PH
3419#ifdef ENCODED_CHARACTER
3420filter->require_encoded_character=0;
3421#endif
024bd3c2
PH
3422#ifdef ENVELOPE_AUTH
3423filter->require_envelope_auth=0;
3424#endif
7c5214ec
PH
3425#ifdef ENOTIFY
3426filter->require_enotify=0;
50c99ba6 3427filter->notified=(struct Notification*)0;
87fcc8b9 3428#endif
059ec3d9
PH
3429#ifdef SUBADDRESS
3430filter->require_subaddress=0;
3431#endif
3432#ifdef VACATION
3433filter->require_vacation=0;
3434filter->vacation_ran=0;
3435#endif
3436filter->require_copy=0;
3437filter->require_iascii_numeric=0;
3438
3439if (parse_white(filter)==-1) return -1;
3440
4929acf0 3441if (exec && filter->vacation_directory != NULL && filter_test == FTEST_NONE)
059ec3d9
PH
3442 {
3443 DIR *oncelogdir;
3444 struct dirent *oncelog;
3445 struct stat properties;
3446 time_t now;
3447
3448 /* clean up old vacation log databases */
3449
3450 oncelogdir=opendir(CS filter->vacation_directory);
3451
3452 if (oncelogdir ==(DIR*)0 && errno != ENOENT)
3453 {
3454 filter->errmsg=CUS "unable to open vacation directory";
3455 return -1;
3456 }
3457
3458 if (oncelogdir != NULL)
3459 {
3460 time(&now);
3461
3462 while ((oncelog=readdir(oncelogdir))!=(struct dirent*)0)
3463 {
3464 if (strlen(oncelog->d_name)==32)
3465 {
3466 uschar *s=string_sprintf("%s/%s",filter->vacation_directory,oncelog->d_name);
3467 if (Ustat(s,&properties)==0 && (properties.st_mtime+VACATION_MAX_DAYS*86400)<now)
3468 Uunlink(s);
3469 }
3470 }
3471 closedir(oncelogdir);
3472 }
3473 }
3474
3475while (parse_identifier(filter,CUS "require"))
3476 {
3477 /*
3478 require-command = "require" <capabilities: string-list>
3479 */
3480
d7978c0f 3481 struct String *cap;
059ec3d9
PH
3482 int m;
3483
3484 if (parse_white(filter)==-1) return -1;
3485 if ((m=parse_stringlist(filter,&cap))!=1)
3486 {
3487 if (m==0) filter->errmsg=CUS "capability string list expected";
3488 return -1;
3489 }
d7978c0f 3490 for (struct String * check = cap; check->character; ++check)
059ec3d9 3491 {
5ea81592
PH
3492 if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1;
3493 else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1;
84024b72
PH
3494#ifdef ENCODED_CHARACTER
3495 else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1;
3496#endif
024bd3c2
PH
3497#ifdef ENVELOPE_AUTH
3498 else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1;
3499#endif
7c5214ec 3500#ifdef ENOTIFY
efd9a422
MH
3501 else if (eq_octet(check,&str_enotify,0))
3502 {
3503 if (filter->enotify_mailto_owner == NULL)
3504 {
3505 filter->errmsg=CUS "enotify disabled";
3506 return -1;
3507 }
3508 filter->require_enotify=1;
3509 }
87fcc8b9 3510#endif
059ec3d9 3511#ifdef SUBADDRESS
5ea81592 3512 else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1;
059ec3d9
PH
3513#endif
3514#ifdef VACATION
5ea81592 3515 else if (eq_octet(check,&str_vacation,0))
059ec3d9 3516 {
4929acf0 3517 if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
059ec3d9
PH
3518 {
3519 filter->errmsg=CUS "vacation disabled";
3520 return -1;
3521 }
3522 filter->require_vacation=1;
3523 }
3524#endif
5ea81592
PH
3525 else if (eq_octet(check,&str_copy,0)) filter->require_copy=1;
3526 else if (eq_octet(check,&str_comparator_ioctet,0)) ;
3527 else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ;
3528 else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ;
3529 else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1;
059ec3d9
PH
3530 else
3531 {
3532 filter->errmsg=CUS "unknown capability";
3533 return -1;
3534 }
3535 }
3536 if (parse_semicolon(filter)==-1) return -1;
3537 }
3538 if (parse_commands(filter,exec,generated)==-1) return -1;
3539 if (*filter->pc)
3540 {
3541 filter->errmsg=CUS "syntax error";
3542 return -1;
3543 }
3544 return 1;
3545}
3546
3547
3548/*************************************************
3549* Interpret a sieve filter file *
3550*************************************************/
3551
3552/*
3553Arguments:
3554 filter points to the entire file, read into store as a single string
3555 options controls whether various special things are allowed, and requests
3556 special actions (not currently used)
efd9a422
MH
3557 vacation_directory where to store vacation "once" files
3558 enotify_mailto_owner owner of mailto notifications
e4a89c47
PH
3559 useraddress string expression for :user part of address
3560 subaddress string expression for :subaddress part of address
059ec3d9
PH
3561 generated where to hang newly-generated addresses
3562 error where to pass back an error text
3563
3564Returns: FF_DELIVERED success, a significant action was taken
3565 FF_NOTDELIVERED success, no significant action
3566 FF_DEFER defer requested
3567 FF_FAIL fail requested
3568 FF_FREEZE freeze requested
3569 FF_ERROR there was a problem
3570*/
3571
3572int
3573sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
efd9a422
MH
3574 uschar *enotify_mailto_owner, uschar *useraddress, uschar *subaddress,
3575 address_item **generated, uschar **error)
059ec3d9
PH
3576{
3577struct Sieve sieve;
3578int r;
3579uschar *msg;
3580
3581options = options; /* Keep picky compilers happy */
3582error = error;
3583
3584DEBUG(D_route) debug_printf("Sieve: start of processing\n");
3585sieve.filter=filter;
3586
3587if (vacation_directory == NULL)
3588 sieve.vacation_directory = NULL;
3589else
3590 {
3591 sieve.vacation_directory=expand_string(vacation_directory);
3592 if (sieve.vacation_directory == NULL)
3593 {
3594 *error = string_sprintf("failed to expand \"%s\" "
3595 "(sieve_vacation_directory): %s", vacation_directory,
3596 expand_string_message);
3597 return FF_ERROR;
3598 }
3599 }
3600
efd9a422
MH
3601if (enotify_mailto_owner == NULL)
3602 sieve.enotify_mailto_owner = NULL;
3603else
3604 {
3605 sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
3606 if (sieve.enotify_mailto_owner == NULL)
3607 {
3608 *error = string_sprintf("failed to expand \"%s\" "
3609 "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
3610 expand_string_message);
3611 return FF_ERROR;
3612 }
3613 }
3614
e4a89c47
PH
3615sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
3616sieve.subaddress = subaddress;
3617
059ec3d9
PH
3618#ifdef COMPILE_SYNTAX_CHECKER
3619if (parse_start(&sieve,0,generated)==1)
3620#else
3621if (parse_start(&sieve,1,generated)==1)
3622#endif
3623 {
3624 if (sieve.keep)
3625 {
3626 add_addr(generated,US"inbox",1,0,0,0);
1c59d63b 3627 msg = string_sprintf("Implicit keep");
059ec3d9
PH
3628 r = FF_DELIVERED;
3629 }
1c59d63b 3630 else
059ec3d9 3631 {
1c59d63b 3632 msg = string_sprintf("No implicit keep");
059ec3d9
PH
3633 r = FF_DELIVERED;
3634 }
3635 }
3636else
3637 {
3638 msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line);
3639#ifdef COMPILE_SYNTAX_CHECKER
3640 r = FF_ERROR;
3641 *error = msg;
3642#else
3643 add_addr(generated,US"inbox",1,0,0,0);
3644 r = FF_DELIVERED;
3645#endif
3646 }
3647
3648#ifndef COMPILE_SYNTAX_CHECKER
f05da2e8 3649if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
059ec3d9
PH
3650 else debug_printf("%s\n", msg);
3651#endif
3652
3653DEBUG(D_route) debug_printf("Sieve: end of processing\n");
3654return r;
3655}