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