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