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