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