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