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