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