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