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