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