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