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