diff --git a/Makefile.am b/Makefile.am index 7e5a978c0a3d7786a52d2e29b1b5992246029d54..b46d3673e3ba8a8828be60b01cff011ed03594ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,7 @@ test_cases = \ tests/extensions/variables/errors.svtest \ tests/extensions/include/variables.svtest \ tests/extensions/imapflags/basic.svtest \ - tests/extensions/imapflags/rfc.svtest \ + tests/extensions/imapflags/hasflag.svtest \ tests/compile/compile.svtest \ tests/compile/compile-examples.svtest \ tests/compile/errors.svtest diff --git a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c index c8bc024f6fb9c9cb7b058ecde964ee50094f80fe..708ceedbf4325cf7b9ace6ac806df66f9b590702 100644 --- a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c +++ b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c @@ -58,12 +58,22 @@ bool ext_imapflags_command_validate if ( sieve_ast_argument_type(arg) != SAAT_STRING ) { - sieve_command_validate_error(validator, cmd, - "if a second argument is specified for the %s %s, the first " - "must be a string (variable name), but %s was found", - cmd->command->identifier, sieve_command_type_name(cmd->command), - sieve_ast_argument_name(arg)); - return FALSE; + if ( cmd->command == &tst_hasflag ) { + if ( sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) { + sieve_command_validate_error(validator, cmd, + "if a second argument is specified for the hasflag, the first " + "must be a string-list (variable-list), but %s was found", + sieve_ast_argument_name(arg)); + return FALSE; + } + } else { + sieve_command_validate_error(validator, cmd, + "if a second argument is specified for the %s %s, the first " + "must be a string (variable name), but %s was found", + cmd->command->identifier, sieve_command_type_name(cmd->command), + sieve_ast_argument_name(arg)); + return FALSE; + } } /* Then, check whether the second argument is permitted */ @@ -76,7 +86,8 @@ bool ext_imapflags_command_validate return FALSE; } - if ( !sieve_variable_argument_activate(validator, cmd, arg, TRUE) ) + if ( !sieve_variable_argument_activate(validator, cmd, arg, + cmd->command != &tst_hasflag ) ) return FALSE; if ( sieve_ast_argument_type(arg2) != SAAT_STRING && @@ -464,16 +475,14 @@ const char *ext_imapflags_get_flags_string void ext_imapflags_get_flags_init (struct ext_imapflags_iter *iter, const struct sieve_runtime_env *renv, - struct sieve_variable_storage *storage, unsigned int var_index) + string_t *flags_list) { string_t *cur_flags; - if ( storage != NULL ) { - string_t *raw_flags; + if ( flags_list != NULL ) { cur_flags = t_str_new(256); - sieve_variable_get_modifiable(storage, var_index, &raw_flags); - flags_list_set_flags(cur_flags, raw_flags); + flags_list_set_flags(cur_flags, flags_list); } else cur_flags = _get_flags_string(renv->result); diff --git a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.h b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.h index c2f6ff6109a84879245b189d11bc9c0cc83264f5..b50d7fb82669526fd0fbaeae40a9b47f9187d59d 100644 --- a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.h +++ b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.h @@ -79,7 +79,7 @@ void ext_imapflags_remove_flags void ext_imapflags_get_flags_init (struct ext_imapflags_iter *iter, const struct sieve_runtime_env *renv, - struct sieve_variable_storage *storage, unsigned int var_index); + string_t *flags_list); void ext_imapflags_get_implicit_flags_init (struct ext_imapflags_iter *iter, struct sieve_result *result); diff --git a/src/lib-sieve/plugins/imapflags/tst-hasflag.c b/src/lib-sieve/plugins/imapflags/tst-hasflag.c index 66ebe188ea3c68c777dc338aa8a07769bda16ba3..cf1115149d54e108898908ef52bd6c6e531c874d 100644 --- a/src/lib-sieve/plugins/imapflags/tst-hasflag.c +++ b/src/lib-sieve/plugins/imapflags/tst-hasflag.c @@ -64,9 +64,7 @@ const struct sieve_operation hasflag_operation = { */ enum tst_hasflag_optional { - OPT_END, - OPT_COMPARATOR, - OPT_MATCH_TYPE + OPT_VARIABLES = SIEVE_MATCH_OPT_LAST, }; /* @@ -78,8 +76,8 @@ static bool tst_hasflag_registered struct sieve_command_registration *cmd_reg) { /* The order of these is not significant */ - sieve_comparators_link_tag(validator, cmd_reg, OPT_COMPARATOR); - sieve_match_types_link_tags(validator, cmd_reg, OPT_MATCH_TYPE); + sieve_comparators_link_tag(validator, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(validator, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); return TRUE; } @@ -91,13 +89,21 @@ static bool tst_hasflag_registered static bool tst_hasflag_validate (struct sieve_validator *validator, struct sieve_command_context *tst) { + struct sieve_ast_argument *vars = tst->first_positional; + struct sieve_ast_argument *keys = sieve_ast_argument_next(vars); + if ( !ext_imapflags_command_validate(validator, tst) ) return FALSE; - - /* Validate the key argument to a specified match type */ - return sieve_match_type_validate(validator, tst, - sieve_ast_argument_next(tst->first_positional)); + if ( keys == NULL ) { + keys = vars; + vars = NULL; + } else { + vars->arg_id_code = OPT_VARIABLES; + } + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate(validator, tst, keys); } /* @@ -130,13 +136,23 @@ static bool tst_hasflag_operation_dump sieve_code_descend(denv); /* Handle any optional arguments */ - if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) ) - return FALSE; + do { + if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) ) + return FALSE; - if ( opt_code != SIEVE_MATCH_OPT_END ) - return FALSE; - - return ext_imapflags_command_operands_dump(denv, address); + switch ( opt_code ) { + case SIEVE_MATCH_OPT_END: + break; + case OPT_VARIABLES: + sieve_opr_stringlist_dump(denv, address); + break; + default: + return FALSE; + } + } while ( opt_code != SIEVE_MATCH_OPT_END ); + + return + sieve_opr_stringlist_dump(denv, address); } /* @@ -177,15 +193,13 @@ static int tst_hasflag_operation_execute (const struct sieve_operation *op ATTR_UNUSED, const struct sieve_runtime_env *renv, sieve_size_t *address) { - int ret = SIEVE_EXEC_OK; - int mret; + int ret, mret; + bool result = TRUE; int opt_code = 0; const struct sieve_comparator *cmp = &i_ascii_casemap_comparator; const struct sieve_match_type *mtch = &is_match_type; struct sieve_match_context *mctx; - struct sieve_coded_stringlist *flag_list; - struct sieve_variable_storage *storage; - unsigned int var_index; + struct sieve_coded_stringlist *flag_list, *variables_list = NULL; struct ext_imapflags_iter iter; const char *flag; bool matched; @@ -195,21 +209,33 @@ static int tst_hasflag_operation_execute */ /* Handle match-type and comparator operands */ - if ( (ret=sieve_match_read_optional_operands - (renv, address, &opt_code, &cmp, &mtch)) <= 0 ) - return ret; + do { + if ( (ret=sieve_match_read_optional_operands + (renv, address, &opt_code, &cmp, &mtch)) <= 0 ) + return ret; - /* Check whether we neatly finished the list of optional operands*/ - if ( opt_code != SIEVE_MATCH_OPT_END) { - sieve_runtime_trace_error(renv, "invalid optional operand"); + /* Check whether we neatly finished the list of optional operands*/ + switch ( opt_code ) { + case SIEVE_MATCH_OPT_END: + break; + case OPT_VARIABLES: + if ( (variables_list=sieve_opr_stringlist_read(renv, address)) == NULL ) { + sieve_runtime_trace_error(renv, "invalid variables-list operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + break; + default: + sieve_runtime_trace_error(renv, "invalid optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } while ( opt_code != SIEVE_MATCH_OPT_END ); + + /* Read flag list */ + if ( (flag_list=sieve_opr_stringlist_read(renv, address)) == NULL ) { + sieve_runtime_trace_error(renv, "invalid flag-list operand"); return SIEVE_EXEC_BIN_CORRUPT; } - /* Read the common imap4flags command operands [variable] <flag-list> */ - if ( (ret=ext_imapflags_command_operands_read - (renv, address, &flag_list, &storage, &var_index)) <= 0 ) - return ret; - /* * Perform operation */ @@ -220,29 +246,51 @@ static int tst_hasflag_operation_execute mctx = sieve_match_begin (renv->interp, mtch, cmp, &_flag_extractor, flag_list); - ext_imapflags_get_flags_init(&iter, renv, storage, var_index); - - while ( !matched && (flag=ext_imapflags_iter_get_flag(&iter)) != NULL ) { - if ( (mret=sieve_match_value(mctx, flag, strlen(flag))) < 0 ) { - sieve_runtime_trace_error(renv, "invalid string list item"); - ret = SIEVE_EXEC_BIN_CORRUPT; - break; - } + matched = FALSE; - matched = ( mret > 0 ); + if ( variables_list != NULL ) { + string_t *var_item = NULL; + + /* Iterate through all requested variables to match */ + while ( result && !matched && + (result=sieve_coded_stringlist_next_item(variables_list, &var_item)) + && var_item != NULL ) { + + ext_imapflags_get_flags_init(&iter, renv, var_item); + while ( !matched && (flag=ext_imapflags_iter_get_flag(&iter)) != NULL ) { + if ( (mret=sieve_match_value(mctx, flag, strlen(flag))) < 0 ) { + result = FALSE; + break; + } + + matched = ( mret > 0 ); + } + } + } else { + ext_imapflags_get_flags_init(&iter, renv, NULL); + while ( !matched && (flag=ext_imapflags_iter_get_flag(&iter)) != NULL ) { + if ( (mret=sieve_match_value(mctx, flag, strlen(flag))) < 0 ) { + result = FALSE; + break; + } + + matched = ( mret > 0 ); + } } if ( (mret=sieve_match_end(mctx)) < 0 ) { - sieve_runtime_trace_error(renv, "invalid string list item"); - ret = SIEVE_EXEC_BIN_CORRUPT; + result = FALSE; } else matched = ( mret > 0 || matched ); /* Assign test result */ - if ( ret == SIEVE_EXEC_OK ) + if ( result ) { sieve_interpreter_set_test_result(renv->interp, matched); + return SIEVE_EXEC_OK; + } - return ret; + sieve_runtime_trace_error(renv, "invalid string list item"); + return SIEVE_EXEC_BIN_CORRUPT; } diff --git a/src/lib-sieve/plugins/variables/ext-variables-arguments.c b/src/lib-sieve/plugins/variables/ext-variables-arguments.c index 3f8382650c00b470b06d214e2565de992710e12e..afc80054de3df48ebcdd6239fc46f3112f7b428a 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-arguments.c +++ b/src/lib-sieve/plugins/variables/ext-variables-arguments.c @@ -49,7 +49,7 @@ static struct sieve_ast_argument *ext_variables_variable_argument_create return arg; } -bool sieve_variable_argument_activate +static bool _sieve_variable_argument_activate (struct sieve_validator *validator, struct sieve_command_context *cmd, struct sieve_ast_argument *arg, bool assignment) { @@ -113,6 +113,37 @@ bool sieve_variable_argument_activate return result; } +bool sieve_variable_argument_activate +(struct sieve_validator *validator, struct sieve_command_context *cmd, + struct sieve_ast_argument *arg, bool assignment) +{ + if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { + /* Single string */ + return _sieve_variable_argument_activate(validator, cmd, arg, assignment); + + } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { + /* String list */ + struct sieve_ast_argument *stritem; + + i_assert ( !assignment ); + + stritem = sieve_ast_strlist_first(arg); + while ( stritem != NULL ) { + if ( !_sieve_variable_argument_activate + (validator, cmd, stritem, assignment) ) + return FALSE; + + stritem = sieve_ast_strlist_next(stritem); + } + + arg->argument = &string_list_argument; + + return TRUE; + } + + return FALSE; +} + static bool arg_variable_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command_context *context ATTR_UNUSED) diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c index 12b0d84dc6bf24f58e4af2aa41a1113be16a531f..994110b1feb55796889eef1ba06be80f39fcff55 100644 --- a/src/lib-sieve/sieve-generator.c +++ b/src/lib-sieve/sieve-generator.c @@ -225,7 +225,7 @@ bool sieve_generate_arguments(const struct sieve_codegen_env *cgenv, else { /* Mark start of optional operands with 0 operand identifier */ sieve_binary_emit_byte(cgenv->sbin, SIEVE_OPERAND_OPTIONAL); - + /* Emit argument id for optional operand */ sieve_binary_emit_byte(cgenv->sbin, (unsigned char) arg->arg_id_code); diff --git a/tests/extensions/imapflags/rfc.svtest b/tests/extensions/imapflags/hasflag.svtest similarity index 80% rename from tests/extensions/imapflags/rfc.svtest rename to tests/extensions/imapflags/hasflag.svtest index 059d3c9d069c7e78ee1815231806d4aca276d80e..07596496df9f0ca45a23c21951bb6d5276656a65 100644 --- a/tests/extensions/imapflags/rfc.svtest +++ b/tests/extensions/imapflags/hasflag.svtest @@ -5,11 +5,36 @@ require "relational"; require "variables"; require "comparator-i;ascii-numeric"; +/* + * Generic tests + */ + +test "Ignoring \"\"" { + setflag ""; + + if hasflag "" { + test_fail "hasflag fails to ignore empty string"; + } +} + +/* + * Variables + */ + +test "Multiple variables" { + setflag "A" "Aflag"; + setflag "B" "Bflag"; + setflag "C" "Cflag"; + + if not hasflag ["a", "b", "c"] ["Bflag"] { + test_fail "hasflag failed to match multiple flags variables"; + } +} + /* * RFC examples */ - test "RFC hasflag example - :is" { setflag "A B";