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