diff --git a/src/lib-sieve/plugins/relational/ext-relational.c b/src/lib-sieve/plugins/relational/ext-relational.c index 5b46af083f8b2a1c552e8ddcae5f4412f9524d91..333320d06aab9065ee59cfd03253952a8eed7526 100644 --- a/src/lib-sieve/plugins/relational/ext-relational.c +++ b/src/lib-sieve/plugins/relational/ext-relational.c @@ -36,6 +36,11 @@ static bool ext_relational_interpreter_load /* Types */ +enum ext_relational_match_type { + RELATIONAL_VALUE, + RELATIONAL_COUNT +}; + enum relational_match { REL_MATCH_GREATER, REL_MATCH_GREATER_EQUAL, @@ -46,6 +51,9 @@ enum relational_match { REL_MATCH_INVALID }; +#define REL_MATCH_INDEX(type, match) \ + (type * REL_MATCH_INVALID + match) + /* Extension definitions */ static int ext_my_id; @@ -69,6 +77,8 @@ static bool ext_relational_load(int ext_id) /* Validation */ +static const struct sieve_match_type rel_match_types[]; + static bool ext_relational_parameter_validate (struct sieve_validator *validator, struct sieve_ast_argument **arg, struct sieve_match_type_context *ctx) @@ -86,7 +96,7 @@ static bool ext_relational_parameter_validate /* Did we get a string in the first place ? */ if ( (*arg)->type != SAAT_STRING ) { sieve_command_validate_error(validator, ctx->command_ctx, - "the :%s match-type requires a constant string argument containing " + "the :%s match-type requires a constant string argument being " "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", " "but %s was found", ctx->match_type->identifier, sieve_ast_argument_name(*arg)); @@ -145,7 +155,7 @@ static bool ext_relational_parameter_validate if ( rel_match >= REL_MATCH_INVALID ) { sieve_command_validate_error(validator, ctx->command_ctx, - "the :%s match-type requires a constant string argument containing " + "the :%s match-type requires a constant string argument being " "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", " "but \"%s\" was found", ctx->match_type->identifier, rel_match_id); @@ -155,6 +165,13 @@ static bool ext_relational_parameter_validate /* Delete argument */ *arg = sieve_ast_arguments_delete(*arg, 1); + /* Not used just yet */ + ctx->ctx_data = (void *) rel_match; + + /* Override the actual match type with a parameter-specific one */ + ctx->match_type = &rel_match_types + [REL_MATCH_INDEX(ctx->match_type->ext_code, rel_match)]; + return TRUE; } @@ -163,13 +180,10 @@ static bool ext_relational_parameter_validate /* Extension access structures */ -enum ext_relational_match_type { - RELATIONAL_VALUE, - RELATIONAL_COUNT -}; - extern const struct sieve_match_type_extension relational_match_extension; +/* Parameter-independent match type objects, only used during validation */ + const struct sieve_match_type value_match_type = { "value", SIEVE_MATCH_TYPE_CUSTOM, @@ -186,18 +200,50 @@ const struct sieve_match_type count_match_type = { ext_relational_parameter_validate }; +/* Per-parameter match type objects, used for generation/interpretation + * FIXME: This is fast, but kinda hideous.. however, otherwise context data + * would have to be passed along with the match type objects everywhere.. also + * not such a great idea. This needs more thought + */ + +#define VALUE_MATCH_TYPE(name, rel_match, func) { \ + "value-" name, \ + SIEVE_MATCH_TYPE_CUSTOM, \ + &relational_match_extension, \ + REL_MATCH_INDEX(RELATIONAL_VALUE, rel_match), \ + NULL, \ + } + +#define COUNT_MATCH_TYPE(name, rel_match, func) { \ + "count-" name, \ + SIEVE_MATCH_TYPE_CUSTOM, \ + &relational_match_extension, \ + REL_MATCH_INDEX(RELATIONAL_COUNT, rel_match), \ + NULL, \ + } + +static const struct sieve_match_type rel_match_types[] = { + VALUE_MATCH_TYPE("gt", REL_MATCH_GREATER, NULL), + VALUE_MATCH_TYPE("ge", REL_MATCH_GREATER_EQUAL, NULL), + VALUE_MATCH_TYPE("lt", REL_MATCH_LESS, NULL), + VALUE_MATCH_TYPE("le", REL_MATCH_LESS_EQUAL, NULL), + VALUE_MATCH_TYPE("eq", REL_MATCH_EQUAL, NULL), + VALUE_MATCH_TYPE("ne", REL_MATCH_NOT_EQUAL, NULL), + + COUNT_MATCH_TYPE("gt", REL_MATCH_GREATER, NULL), + COUNT_MATCH_TYPE("ge", REL_MATCH_GREATER_EQUAL, NULL), + COUNT_MATCH_TYPE("lt", REL_MATCH_LESS, NULL), + COUNT_MATCH_TYPE("le", REL_MATCH_LESS_EQUAL, NULL), + COUNT_MATCH_TYPE("eq", REL_MATCH_EQUAL, NULL), + COUNT_MATCH_TYPE("ne", REL_MATCH_NOT_EQUAL, NULL) +}; + static const struct sieve_match_type *ext_relational_get_match (unsigned int code) { - switch ( code ) { - case RELATIONAL_VALUE: - return &value_match_type; - case RELATIONAL_COUNT: - return &count_match_type; - default: - break; - } - + if ( code < N_ELEMENTS(rel_match_types) ) + return &rel_match_types[code]; + return NULL; } diff --git a/src/lib-sieve/plugins/relational/relational.sieve b/src/lib-sieve/plugins/relational/relational.sieve index 51179a38c90201608385d76949a264478d6f299c..1acc32f8f70123ef1ba066cc2e2b6f2c4b9fbbf2 100644 --- a/src/lib-sieve/plugins/relational/relational.sieve +++ b/src/lib-sieve/plugins/relational/relational.sieve @@ -5,4 +5,19 @@ if header :value "gt" :comparator "i;ascii-numeric" "x-spam-score" "2" { stop; } +if header :value "ne" :comparator "i;ascii-numeric" "x-spam-score" "2" { + discard; + stop; +} + +if header :count "lt" :comparator "i;ascii-numeric" "x-spam-score" "2" { + discard; + stop; +} + +if header :count "eq" :comparator "i;ascii-numeric" "x-spam-score" "2" { + discard; + stop; +} + keep; diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c index 2ef62a893b78c3e0caab1b564f29ecbf91b2a681..f4f2ce3f8b650657c11f15ee89de947ee9f6a0af 100644 --- a/src/lib-sieve/sieve-match-types.c +++ b/src/lib-sieve/sieve-match-types.c @@ -265,6 +265,10 @@ static bool tag_match_type_validate /* Skip tag */ *arg = sieve_ast_argument_next(*arg); + /* Check whether this match type requires additional validation. + * Additional validation can override the match type recorded in the context + * for later code generation. + */ if ( mtch->validate != NULL ) { return mtch->validate(validator, arg, mtctx); } @@ -311,22 +315,22 @@ const struct sieve_match_type *sieve_opr_match_type_read return NULL; } else { int ext_id = -1; - const struct sieve_match_type_extension *ap_ext; + const struct sieve_match_type_extension *mtch_ext; if ( sieve_binary_extension_get_by_index(sbin, mtch_code - SIEVE_MATCH_TYPE_CUSTOM, &ext_id) == NULL ) return NULL; - ap_ext = sieve_match_type_extension_get(interpreter, ext_id); + mtch_ext = sieve_match_type_extension_get(interpreter, ext_id); - if ( ap_ext != NULL ) { + if ( mtch_ext != NULL ) { unsigned int code; - if ( ap_ext->match_type != NULL ) - return ap_ext->match_type; + if ( mtch_ext->match_type != NULL ) + return mtch_ext->match_type; if ( sieve_binary_read_byte(sbin, address, &code) && - ap_ext->get_part != NULL ) - return ap_ext->get_part(code); + mtch_ext->get_match != NULL ) + return mtch_ext->get_match(code); } else { i_info("Unknown match-type modifier %d.", mtch_code); } diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h index 08814f1bd7028f5da1f230898060bec75b4716c4..9e8e6e112c2e92cef7548fdbd2659584871506ae 100644 --- a/src/lib-sieve/sieve-match-types.h +++ b/src/lib-sieve/sieve-match-types.h @@ -33,13 +33,18 @@ struct sieve_match_type_extension { const struct sieve_match_type *match_type; /* ... or multiple: then the extension must handle emit/read */ - const struct sieve_match_type *(*get_part) + const struct sieve_match_type *(*get_match) (unsigned int code); }; struct sieve_match_type_context { struct sieve_command_context *command_ctx; const struct sieve_match_type *match_type; + + /* 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. + */ void *ctx_data; };