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