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