diff --git a/TODO b/TODO index 2e60742f86e64cd8a2771ea91e7ed9eb308aa227..84fb67717de831b669176ae4d040dfe00d5af777 100644 --- a/TODO +++ b/TODO @@ -3,10 +3,10 @@ Next (in order of descending priority/precedence): * Full standards compliance review for the engine and all fully implemented sieve extensions. Issues discovered so far: - - Implementations SHOULD consider unknown envelope parts an error. - The null reverse-path is matched against as the empty string, regardless of the ADDRESS-PART argument specified. - Header test does not strip trailing whitespace + - Fix/Report issues listed in 'doc/RFC Controversy.txt' * Code cleanup * Full security review. Enforce limits on number of created objects, script size, execution time, etc... diff --git a/doc/rfc/RFC Controversy.txt b/doc/rfc/RFC Controversy.txt index cbd91f723b34cdf61ed0cd7dcb3503e106c23b24..3ad9d520dd721b89d501443c58749305471869a4 100644 --- a/doc/rfc/RFC Controversy.txt +++ b/doc/rfc/RFC Controversy.txt @@ -16,7 +16,7 @@ these issues by my implementation is displayed inside [ ... ]. headers simply ignored? [fatal validation error] - Given the variables extension, sometimes the header names aren't known until runtime. If previous answer was to cause a fatal error, will this abort the - script? [not checked (FIXME); will be: ignore header] + script? [not checked (FIXME); will first be: ignore header] * RFC 5228 (Sieve) : 5.4. Test envelope "The "envelope" test is true if the specified part of the [SMTP] (or @@ -27,5 +27,5 @@ these issues by my implementation is displayed inside [ ... ]. error." - Are envelope parts required to be addresses? And if not, what becomes the - meaning of the ADDRESS-PART modifiers? [not handled (FIXME); will be: if - none specified full string match and test validation error otherwise] + meaning of the ADDRESS-PART modifiers? [not handled (FIXME); will first be: + if none specified full string match and trigger validation error otherwise] diff --git a/sieve/errors/envelope-errors.sieve b/sieve/errors/envelope-errors.sieve new file mode 100644 index 0000000000000000000000000000000000000000..e5b7b1020657981bd25da9f3479befe39a715b3b --- /dev/null +++ b/sieve/errors/envelope-errors.sieve @@ -0,0 +1,13 @@ +require "envelope"; + +if envelope :is "to" "frop@rename-it.nl" { +} + +if envelope :is "frop" "frop@rename-it.nl" { +} + +if envelope :is ["to","from"] "frop@rename-it.nl" { +} + +if envelope :is ["to","frop"] "frop@rename-it.nl" { +} diff --git a/sieve/examples/elvey.sieve b/sieve/examples/elvey.sieve index 92c30f4f6b501122216eab2b6b80a4d62e5d724e..869f1c90521f3b5bb83e8ae06503b0b7952f1029 100644 --- a/sieve/examples/elvey.sieve +++ b/sieve/examples/elvey.sieve @@ -1,5 +1,5 @@ # Example Sieve Script -# Author: Matthew Elvey +# Author: Matthew Elvey (Slightly modified to remove syntax and context errors) # URL: http://www.elvey.com/it/sieve/SieveScript.txt # Initial version completed and put in place 4/1/02 by Matthew Elvey (firstname@lastname.com ; I've checked and it's not a valid address.); Copyright (C).and.current as of 5/19/2002 @@ -87,7 +87,7 @@ keep; #this one is important - don't want to miss any bounce messages! # LINE 106. -} elsif anyof (envelope :all :is ["To", "CC", "BCC"] "Firstname.lastname@fastmail.fm", #a couple people send to this, but I have have all their addrs in whitelist so OK. +} elsif anyof (address :all :is ["To", "CC", "BCC"] "Firstname.lastname@fastmail.fm", #a couple people send to this, but I have have all their addrs in whitelist so OK. header :matches "X-Spam-score" ["9.?" , "10.?", "9", "10", "11.?", "12.?" ,"13.?", "14.?", "11", "12","13", "14", "15.?", "16.?", "17.?" ,"18.?", "19.?", "15", "16", "17" ,"18", "19", "2?.?", "2?", "3?.?" , "3?", "40"]) { #"5.?", "6.?", "5", "6" "7.?" , "8.?" , "7", "8" reject text: Hello. The server content filter/spam detector I use has bounced your message. It appears to be spam. diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c index fd1fd45b8c9f8754362eb62948d34dc8b078b08e..efaafeb11b63f3fa8bd9c5f8362823a23095d52a 100644 --- a/src/lib-sieve/ext-envelope.c +++ b/src/lib-sieve/ext-envelope.c @@ -132,10 +132,41 @@ static bool tst_envelope_registered * Validation */ +static const char * const _supported_envelope_parts[] = { + /* Required */ + "from", "to", + + /* Non-standard */ + "auth", + + NULL +}; + +static int _envelope_part_is_supported +(void *context ATTR_UNUSED, struct sieve_ast_argument *arg) +{ + if ( sieve_argument_is_string_literal(arg) ) { + const char *epart = sieve_ast_strlist_strc(arg); + + const char * const *epsp = _supported_envelope_parts; + while ( *epsp != NULL ) { + if ( strcasecmp( *epsp, epart ) == 0 ) + return TRUE; + + epsp++; + } + + return FALSE; + } + + return TRUE; +} + static bool tst_envelope_validate (struct sieve_validator *validator, struct sieve_command_context *tst) { struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *epart; if ( !sieve_validate_positional_argument (validator, tst, arg, "envelope part", 1, SAAT_STRING_LIST) ) { @@ -144,6 +175,14 @@ static bool tst_envelope_validate if ( !sieve_validator_argument_activate(validator, tst, arg, FALSE) ) return FALSE; + + epart = arg; + if ( !sieve_ast_stringlist_map(&epart, NULL, _envelope_part_is_supported) ) { + sieve_command_validate_error(validator, tst, + "specified envelope part '%s' is not supported by the envelope test", + sieve_ast_strlist_strc(epart)); + return FALSE; + } arg = sieve_ast_argument_next(arg); diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index cdb86f82feafd41135114e4704f1ccb5050e64d4..e1fb2850be8cda9e4148be5ca10b9d40f2c1aded 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -564,6 +564,39 @@ struct sieve_ast_node *sieve_ast_command_create return command; } +/* + * Utility + */ + +int sieve_ast_stringlist_map +(struct sieve_ast_argument **listitem, void *context, + int (*map_function)(void *context, struct sieve_ast_argument *arg)) +{ + if ( sieve_ast_argument_type(*listitem) == SAAT_STRING ) { + /* Single string */ + return map_function(context, *listitem); + } else if ( sieve_ast_argument_type(*listitem) == SAAT_STRING_LIST ) { + int ret = 0; + + /* String list */ + *listitem = sieve_ast_strlist_first(*listitem); + + while ( *listitem != NULL ) { + + if ( (ret=map_function(context, *listitem)) <= 0 ) + return ret; + + *listitem = sieve_ast_strlist_next(*listitem); + } + + return ret; + } + + i_unreached(); + return -1; +} + + /* Debug */ diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h index 67eeb579cc4209d662ba0a8e6749f7bc419d8047..55e1b1112dc6c93f27421961932b70fdf21f7dc9 100644 --- a/src/lib-sieve/sieve-ast.h +++ b/src/lib-sieve/sieve-ast.h @@ -237,11 +237,24 @@ struct sieve_ast_node *sieve_ast_test_create struct sieve_ast_node *sieve_ast_command_create (struct sieve_ast_node *parent, const char *identifier, unsigned int source_line); + +/* + * Utility + */ + +int sieve_ast_stringlist_map + (struct sieve_ast_argument **listitem, void *context, + int (*map_function)(void *context, struct sieve_ast_argument *arg)); -/* Debug */ +/* + * Debug + */ + void sieve_ast_unparse(struct sieve_ast *ast); -/* AST access macros */ +/* + * AST access macros + */ /* Generic list access macros */ #define __AST_LIST_FIRST(list) \ diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c index 55a01cb9c12d139e85c565dc24624045f52599aa..7c693b0926e0d3c6ece86fba76225251172d542f 100644 --- a/src/lib-sieve/tst-address.c +++ b/src/lib-sieve/tst-address.c @@ -107,24 +107,31 @@ static const char * const _allowed_headers[] = { NULL }; -static bool _header_is_allowed(const char *header) +static int _header_is_allowed +(void *context ATTR_UNUSED, struct sieve_ast_argument *arg) { - const char * const *hdsp = _allowed_headers; - while ( *hdsp != NULL ) { - if ( strcasecmp( *hdsp, header ) == 0 ) - return TRUE; + if ( sieve_argument_is_string_literal(arg) ) { + const char *header = sieve_ast_strlist_strc(arg); + + const char * const *hdsp = _allowed_headers; + while ( *hdsp != NULL ) { + if ( strcasecmp( *hdsp, header ) == 0 ) + return TRUE; - hdsp++; + hdsp++; + } + + return FALSE; } - return FALSE; + return TRUE; } static bool tst_address_validate (struct sieve_validator *validator, struct sieve_command_context *tst) { struct sieve_ast_argument *arg = tst->first_positional; - const char *not_allowed = NULL; + struct sieve_ast_argument *header; if ( !sieve_validate_positional_argument (validator, tst, arg, "header list", 1, SAAT_STRING_LIST) ) { @@ -138,33 +145,11 @@ static bool tst_address_validate /* Check if supplied header names are allowed * FIXME: verify dynamic header names at runtime */ - - if ( sieve_argument_is_string_literal(arg) ) { - const char *header = sieve_ast_argument_strc(arg); - - /* Single string */ - if ( !_header_is_allowed(header) ) - not_allowed = header; - - } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { - /* String list */ - struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg); - - while ( not_allowed == NULL && stritem != NULL ) { - if ( sieve_argument_is_string_literal(stritem) ) { - const char *header = sieve_ast_strlist_strc(stritem); - - if ( !_header_is_allowed(header) ) - not_allowed = header; - - stritem = sieve_ast_strlist_next(stritem); - } - } - } - - if ( not_allowed != NULL ) { + header = arg; + if ( !sieve_ast_stringlist_map(&header, NULL, _header_is_allowed) ) { sieve_command_validate_error(validator, tst, - "specified header '%s' is not allowed for the address test", not_allowed); + "specified header '%s' is not allowed for the address test", + sieve_ast_strlist_strc(header)); return FALSE; }