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