diff --git a/sieve/errors/match-type-errors.sieve b/sieve/errors/match-type-errors.sieve deleted file mode 100644 index f2b08be9f81f13c9c538e007f7e23aed7690d7c6..0000000000000000000000000000000000000000 --- a/sieve/errors/match-type-errors.sieve +++ /dev/null @@ -1,16 +0,0 @@ -require "comparator-i;ascii-numeric"; - -if address :contains :is :comparator "i;ascii-casemap" :localpart "from" "STEPHAN" { - discard; - - if address :contains :domain :comparator "i;octet" :matches "from" "drunksnipers.com" { - keep; - } - stop; -} - -if header :contains :comparator "i;ascii-numeric" "from" "drunksnipers.com" { - keep; -} - -keep; diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c index f8c8deaa3ad2bc463de633e1a1fe6a500c49f495..5dba8dd295cd4fa18580ce0d2b1fdbd4d2a1b3e6 100644 --- a/src/lib-sieve/ext-envelope.c +++ b/src/lib-sieve/ext-envelope.c @@ -280,7 +280,8 @@ static bool tst_envelope_validate return FALSE; /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, arg); + return sieve_match_type_validate + (validator, tst, arg, &is_match_type, &i_ascii_casemap_comparator); } /* diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c index 1d58e30f1de0fa0313bab4c0be0c1322f2d01c82..5ea705450f64e317cb23b4ed214bca91eebe0972 100644 --- a/src/lib-sieve/plugins/body/tst-body.c +++ b/src/lib-sieve/plugins/body/tst-body.c @@ -207,7 +207,8 @@ static bool tst_body_validate return FALSE; /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, arg); + return sieve_match_type_validate + (validator, tst, arg, &is_match_type, &i_ascii_casemap_comparator); } /* diff --git a/src/lib-sieve/plugins/imapflags/tst-hasflag.c b/src/lib-sieve/plugins/imapflags/tst-hasflag.c index cf1115149d54e108898908ef52bd6c6e531c874d..54afc918470a44307b07185808998e1f5d10dc6e 100644 --- a/src/lib-sieve/plugins/imapflags/tst-hasflag.c +++ b/src/lib-sieve/plugins/imapflags/tst-hasflag.c @@ -103,7 +103,8 @@ static bool tst_hasflag_validate } /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, keys); + return sieve_match_type_validate + (validator, tst, keys, &is_match_type, &i_ascii_casemap_comparator); } /* diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c index d38ba333815ee5c9bd3c5cb42ee745e648543d74..a7935c2f1750df58c4d943c58eea880abde91ed3 100644 --- a/src/lib-sieve/plugins/regex/mcht-regex.c +++ b/src/lib-sieve/plugins/regex/mcht-regex.c @@ -127,18 +127,15 @@ bool mcht_regex_validate_context (struct sieve_validator *validator, struct sieve_ast_argument *arg ATTR_UNUSED, struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg) { + const struct sieve_comparator *cmp = ctx->comparator; int cflags = REG_EXTENDED | REG_NOSUB; struct _regex_key_context keyctx; - struct sieve_ast_argument *cmp_arg; struct sieve_ast_argument *kitem; - cmp_arg = sieve_command_find_argument(ctx->command_ctx, &comparator_tag); - if ( cmp_arg != NULL ) { - /* FIXME: new commands might use incompatible default comparator */ - - if ( sieve_comparator_tag_is(cmp_arg, &i_ascii_casemap_comparator) ) + if ( cmp != NULL ) { + if ( cmp == &i_ascii_casemap_comparator ) cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; - else if ( sieve_comparator_tag_is(cmp_arg, &i_octet_comparator) ) + else if ( cmp == &i_octet_comparator ) cflags = REG_EXTENDED | REG_NOSUB; else { sieve_command_validate_error(validator, ctx->command_ctx, @@ -150,14 +147,14 @@ bool mcht_regex_validate_context /* Validate regular expression keys */ - keyctx.valdtr = validator; + keyctx.valdtr = validator; keyctx.mctx = ctx; keyctx.cflags = cflags; kitem = key_arg; - if ( !sieve_ast_stringlist_map(&kitem, (void *) &keyctx, + if ( !sieve_ast_stringlist_map(&kitem, (void *) &keyctx, mcht_regex_validate_key_argument) ) - return FALSE; + return FALSE; return TRUE; } diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c index a80eabd3c7d3f070cc9096ff8ea8cceb9bb39b0e..04c05b73fd9e73c5e1c62932471e82a81c9d9608 100644 --- a/src/lib-sieve/plugins/variables/tst-string.c +++ b/src/lib-sieve/plugins/variables/tst-string.c @@ -110,7 +110,8 @@ static bool tst_string_validate return FALSE; /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, arg); + return sieve_match_type_validate + (validator, tst, arg, &is_match_type, &i_octet_comparator); } /* diff --git a/src/lib-sieve/sieve-comparators.h b/src/lib-sieve/sieve-comparators.h index 76fa69beb81cb2d6826156feeba57bea32577e3d..c3adb7199c97509a094fd55f0ee78ac0eb559f6b 100644 --- a/src/lib-sieve/sieve-comparators.h +++ b/src/lib-sieve/sieve-comparators.h @@ -5,8 +5,9 @@ #define __SIEVE_COMPARATORS_H #include "sieve-common.h" -#include "sieve-objects.h" #include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-objects.h" #include "sieve-code.h" /* @@ -63,6 +64,12 @@ struct sieve_comparator { extern const struct sieve_argument comparator_tag; +static inline bool sieve_argument_is_comparator + (struct sieve_ast_argument *arg) +{ + return arg->argument == &comparator_tag; +} + void sieve_comparators_link_tag (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, int id_code); diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c index 1153a4901ab256d4dedbe27a94137f65042f8a23..be1af4c64efe6d3b71534eb86bc457744e95699c 100644 --- a/src/lib-sieve/sieve-match-types.c +++ b/src/lib-sieve/sieve-match-types.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ + #include <stdio.h> #include "lib.h" @@ -362,6 +365,7 @@ static bool tag_match_type_is_instance_of mtctx = p_new(sieve_command_pool(cmd), struct sieve_match_type_context, 1); mtctx->match_type = mtch; mtctx->command_ctx = cmd; + mtctx->comparator = NULL; /* Can be filled in later */ arg->context = (void *) mtctx; @@ -406,52 +410,69 @@ static bool tag_match_type_generate return TRUE; } -bool sieve_match_type_validate_argument -(struct sieve_validator *validator, struct sieve_ast_argument *arg, - struct sieve_ast_argument *key_arg ) -{ - struct sieve_match_type_context *mtctx = - (struct sieve_match_type_context *) arg->context; - - i_assert(arg->argument == &match_type_tag); - - /* Check whether this match type requires additional validation. - * Additional validation can override the match type recorded in the context - * for later code generation. - */ - if ( mtctx != NULL && mtctx->match_type != NULL && - mtctx->match_type->validate_context != NULL ) { - return mtctx->match_type->validate_context(validator, arg, mtctx, key_arg); - } - - return TRUE; +void sieve_match_types_link_tags + (struct sieve_validator *validator, + struct sieve_command_registration *cmd_reg, int id_code) +{ + sieve_validator_register_tag + (validator, cmd_reg, &match_type_tag, id_code); } +/* + * Validation + */ + bool sieve_match_type_validate (struct sieve_validator *validator, struct sieve_command_context *cmd, - struct sieve_ast_argument *key_arg ) + struct sieve_ast_argument *key_arg, + const struct sieve_match_type *mcht_default, + const struct sieve_comparator *cmp_default) { struct sieve_ast_argument *arg = sieve_command_first_argument(cmd); + struct sieve_ast_argument *mt_arg = NULL; + struct sieve_match_type_context *mtctx; + const struct sieve_match_type *mcht = NULL; + const struct sieve_comparator *cmp = NULL; + /* Find match type and comparator among the arguments */ while ( arg != NULL && arg != cmd->first_positional ) { - if ( arg->argument == &match_type_tag ) { - if ( !sieve_match_type_validate_argument(validator, arg, key_arg) ) - return FALSE; + if ( sieve_argument_is_comparator(arg) ) { + cmp = sieve_comparator_tag_get(arg); + if ( mt_arg != NULL ) break; + } + + if ( sieve_argument_is_match_type(arg) ) { + mt_arg = arg; + if ( cmp != NULL ) break; } arg = sieve_ast_argument_next(arg); } + + /* Verify using the default comparator if none is specified explicitly */ + if ( cmp == NULL ) + cmp = cmp_default; + + /* Verify the default match type if none is specified explicitly */ + if ( mt_arg == NULL || mt_arg->context == NULL ) { + mtctx = NULL; + mcht = mcht_default; + } else { + mtctx = (struct sieve_match_type_context *) mt_arg->context; + mcht = mtctx->match_type; + mtctx->comparator = cmp; + } + /* Check whether this match type requires additional validation. + * Additional validation can override the match type recorded in the context + * for later code generation. + */ + if ( mcht != NULL && mcht->validate_context != NULL ) { + return mcht->validate_context(validator, mt_arg, mtctx, key_arg); + } + return TRUE; } -void sieve_match_types_link_tags - (struct sieve_validator *validator, - struct sieve_command_registration *cmd_reg, int id_code) -{ - sieve_validator_register_tag - (validator, cmd_reg, &match_type_tag, id_code); -} - /* * Match-type operand */ @@ -471,35 +492,27 @@ const struct sieve_operand match_type_operand = { }; /* - * Matching + * Common validation implementation */ bool sieve_match_substring_validate_context -(struct sieve_validator *validator, struct sieve_ast_argument *arg, - struct sieve_match_type_context *ctx, +(struct sieve_validator *validator, struct sieve_ast_argument *arg ATTR_UNUSED, + struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg ATTR_UNUSED) { - struct sieve_ast_argument *carg = - sieve_command_first_argument(ctx->command_ctx); - - while ( carg != NULL && carg != ctx->command_ctx->first_positional ) { - if ( carg != arg && carg->argument == &comparator_tag ) { - const struct sieve_comparator *cmp = - sieve_comparator_tag_get(carg); + const struct sieve_comparator *cmp = ctx->comparator; + + if ( cmp == NULL ) + return TRUE; - if ( (cmp->flags & SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH) == 0 ) { - sieve_command_validate_error(validator, ctx->command_ctx, - "the specified %s comparator does not support " - "sub-string matching as required by the :%s match type", - cmp->object.identifier, ctx->match_type->object.identifier ); - - return FALSE; - } - return TRUE; - } + if ( (cmp->flags & SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH) == 0 ) { + sieve_command_validate_error(validator, ctx->command_ctx, + "the specified %s comparator does not support " + "sub-string matching as required by the :%s match type", + cmp->object.identifier, ctx->match_type->object.identifier ); - carg = sieve_ast_argument_next(carg); + return FALSE; } - + return TRUE; } diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h index 87da2a3edccd0232bae6b9e3ce1479e0a0b8b5b1..775d67a6bce48de43a3e5602a26b4888342f42e5 100644 --- a/src/lib-sieve/sieve-match-types.h +++ b/src/lib-sieve/sieve-match-types.h @@ -1,11 +1,21 @@ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ + #ifndef __SIEVE_MATCH_TYPES_H #define __SIEVE_MATCH_TYPES_H #include "sieve-common.h" #include "sieve-extensions.h" +#include "sieve-commands.h" #include "sieve-code.h" #include "sieve-objects.h" +/* + * Types + */ + +struct sieve_match_type_context; + /* * Core match types */ @@ -21,9 +31,10 @@ extern const struct sieve_match_type is_match_type; extern const struct sieve_match_type contains_match_type; extern const struct sieve_match_type matches_match_type; -struct sieve_match_type; -struct sieve_match_type_context; - +/* + * Match type object + */ + struct sieve_match_type { struct sieve_object object; @@ -50,11 +61,11 @@ struct sieve_match_type { void (*match_init)(struct sieve_match_context *mctx); - /* WARNING: some tests may pass a val == NULL parameter indicating that the passed - * value has no significance. For string-type matches this should map to the empty - * string "", but for match types that consider the passed values as objects rather - * than strings (e.g. :count) this means that the passed value should be skipped. - * This is currently only used by string test of the variables extension. + /* WARNING: some tests may pass a val == NULL parameter indicating that the + * passed value has no significance. For string-type matches this should map + * to the empty string "", but for match types that consider the passed values + * as objects rather than strings (e.g. :count) this means that the passed + * value should be skipped. */ int (*match) (struct sieve_match_context *mctx, const char *val, size_t val_size, @@ -66,6 +77,9 @@ struct sieve_match_type_context { struct sieve_command_context *command_ctx; const struct sieve_match_type *match_type; + /* Only filled in when match_type->validate_context() is called */ + const struct sieve_comparator *comparator; + /* Context data could be used in the future to pass data between validator and * generator in match types that use extra parameters. Currently not * necessary, not even for the relational extension. @@ -73,7 +87,18 @@ struct sieve_match_type_context { void *ctx_data; }; -/* Match values */ +/* + * Match type registration + */ + +void sieve_match_type_register + (struct sieve_validator *validator, const struct sieve_match_type *mcht); +const struct sieve_match_type *sieve_match_type_find + (struct sieve_validator *validator, const char *identifier); + +/* + * Match values + */ struct sieve_match_values; @@ -98,29 +123,34 @@ void sieve_match_values_commit void sieve_match_values_abort (struct sieve_match_values **mvalues); - void sieve_match_values_get (struct sieve_interpreter *interp, unsigned int index, string_t **value_r); -/* ... */ +/* + * Match type tagged argument + */ + +extern const struct sieve_argument match_type_tag; + +static inline bool sieve_argument_is_match_type + (struct sieve_ast_argument *arg) +{ + return ( arg->argument == &match_type_tag ); +} void sieve_match_types_link_tags (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, int id_code); -bool sieve_match_type_validate_argument -(struct sieve_validator *validator, struct sieve_ast_argument *arg, - struct sieve_ast_argument *key_arg); -bool sieve_match_type_validate -(struct sieve_validator *validator, struct sieve_command_context *cmd, - struct sieve_ast_argument *key_arg); - -void sieve_match_type_register - (struct sieve_validator *validator, - const struct sieve_match_type *addrp); -const struct sieve_match_type *sieve_match_type_find - (struct sieve_validator *validator, const char *identifier); -extern const struct sieve_argument match_type_tag; +/* + * Validation + */ + +bool sieve_match_type_validate + (struct sieve_validator *validator, struct sieve_command_context *cmd, + struct sieve_ast_argument *key_arg, + const struct sieve_match_type *mcht_default, + const struct sieve_comparator *cmp_default); /* * Match type operand @@ -159,11 +189,11 @@ static inline bool sieve_opr_match_type_dump (denv, &sieve_match_type_operand_class, address, NULL); } -/* Match Utility */ +/* Common validation implementation */ bool sieve_match_substring_validate_context -(struct sieve_validator *validator, struct sieve_ast_argument *arg, - struct sieve_match_type_context *ctx, - struct sieve_ast_argument *key_arg); + (struct sieve_validator *validator, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, + struct sieve_ast_argument *key_arg); #endif /* __SIEVE_MATCH_TYPES_H */ diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c index 2cf3685ef8e91eac46275ba3e548492f459b4f09..afbe9ec75dd50a1187072794826e5f513fb7cdc3 100644 --- a/src/lib-sieve/tst-address.c +++ b/src/lib-sieve/tst-address.c @@ -168,7 +168,8 @@ static bool tst_address_validate return FALSE; /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, arg); + return sieve_match_type_validate + (validator, tst, arg, &is_match_type, &i_ascii_casemap_comparator); } /* diff --git a/src/lib-sieve/tst-header.c b/src/lib-sieve/tst-header.c index 4335aab70cc6d14449889ba81b5a25dcd2eb5648..934d5996403410561b1f0d1671290d9501d5c0bb 100644 --- a/src/lib-sieve/tst-header.c +++ b/src/lib-sieve/tst-header.c @@ -99,7 +99,8 @@ static bool tst_header_validate return FALSE; /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, arg); + return sieve_match_type_validate + (validator, tst, arg, &is_match_type, &i_ascii_casemap_comparator); } /* diff --git a/src/testsuite/tst-test-error.c b/src/testsuite/tst-test-error.c index 07a8811808c7dceddb48ed53a12fffcdbe9b45d1..772787f4b199628fb4c0da950b931d473e9fe10c 100644 --- a/src/testsuite/tst-test-error.c +++ b/src/testsuite/tst-test-error.c @@ -143,7 +143,8 @@ static bool tst_test_error_validate return FALSE; /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(valdtr, tst, arg); + return sieve_match_type_validate + (valdtr, tst, arg, &is_match_type, &i_octet_comparator); } /* diff --git a/tests/compile/errors.svtest b/tests/compile/errors.svtest index bcc5a44bf25edc28402e4948e8b7342213abd7aa..af68e17e808cd82f494a0d345720c0a5ce2ead29 100644 --- a/tests/compile/errors.svtest +++ b/tests/compile/errors.svtest @@ -256,6 +256,20 @@ test "ADDRESS-PART errors (FIXME: count only)" { } } +/* + * MATCH-TYPE errors + */ + +test "MATCH-TYPE errors (FIXME: count only)" { + if test_compile "errors/match-type.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + /* * Encoded-character errors */ diff --git a/tests/compile/errors/match-type.sieve b/tests/compile/errors/match-type.sieve new file mode 100644 index 0000000000000000000000000000000000000000..4df4564054b9101262551a0078141ad61a33bd29 --- /dev/null +++ b/tests/compile/errors/match-type.sieve @@ -0,0 +1,7 @@ +require "comparator-i;ascii-numeric"; + +if header :contains :comparator "i;ascii-numeric" "from" "drunksnipers.com" { + keep; +} + +keep;