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