diff --git a/Makefile.am b/Makefile.am index e34e744240c3386579cb568d48ea8d63f2915e53..b9d38a6a2171590f0942e4dbcfb928762984fd44 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,7 @@ test_cases = \ tests/extensions/include/variables.svtest \ tests/extensions/imap4flags/basic.svtest \ tests/extensions/imap4flags/hasflag.svtest \ + tests/extensions/imap4flags/errors.svtest \ tests/extensions/imap4flags/execute.svtest \ tests/extensions/body/basic.svtest \ tests/extensions/body/match-values.svtest \ diff --git a/src/lib-sieve/plugins/imap4flags/Makefile.am b/src/lib-sieve/plugins/imap4flags/Makefile.am index e3af73faf7ec05c4e35af576dc602dc4fcbbea4e..1f8bbad6c65e0e145cbb7980aee4e4262bac3693 100644 --- a/src/lib-sieve/plugins/imap4flags/Makefile.am +++ b/src/lib-sieve/plugins/imap4flags/Makefile.am @@ -22,7 +22,9 @@ libsieve_ext_imap4flags_la_SOURCES = \ $(commands) \ $(tests) \ $(tags) \ - ext-imap4flags.c + ext-imap4flags.c \ + ext-imapflags.c + noinst_HEADERS = \ ext-imap4flags-common.h diff --git a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c new file mode 100644 index 0000000000000000000000000000000000000000..5023aef4f2aa996a815ef63d7cf06e7526c001f8 --- /dev/null +++ b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +/* Extension imapflags + * -------------------- + * + * Authors: Stephan Bosch + * Specification: draft-melnikov-sieve-imapflags-03.txt + * Implementation: depricated; provided for backwards compatibility + * Status: depricated + * + */ + +#include "lib.h" +#include "mempool.h" +#include "str.h" + +#include "sieve-common.h" + +#include "sieve-ast.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-imap4flags-common.h" + +/* + * Commands + */ + +static bool cmd_mark_validate + (struct sieve_validator *valdtr, struct sieve_command_context *cmd); + +/* Mark command + * + * Syntax: + * mark + */ + +static const struct sieve_command cmd_mark = { + "mark", + SCT_COMMAND, + 0, 0, FALSE, FALSE, + NULL, NULL, + cmd_mark_validate, + NULL, NULL, +}; + +/* Unmark command + * + * Syntax: + * unmark + */ +static const struct sieve_command cmd_unmark = { + "unmark", + SCT_COMMAND, + 0, 0, FALSE, FALSE, + NULL, NULL, + cmd_mark_validate, + NULL, NULL, +}; + +/* + * Extension + */ + +static bool ext_imapflags_load(void); +static bool ext_imapflags_validator_load(struct sieve_validator *valdtr); +static bool ext_imapflags_interpreter_load + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +int ext_imapflags_my_id = -1; + +const struct sieve_extension imapflags_extension = { + "imapflags", + &ext_imapflags_my_id, + ext_imapflags_load, + NULL, + ext_imapflags_validator_load, + NULL, + ext_imapflags_interpreter_load, + NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS +}; + +static bool ext_imapflags_load(void) +{ + /* Make sure real extension is registered, it is needed by the binary */ + (void)sieve_extension_require(&imap4flags_extension); + + return TRUE; +} + +/* + * Validator + */ + +static bool ext_imapflags_validator_extension_validate + (struct sieve_validator *valdtr, void *context, struct sieve_ast_argument *require_arg); + +const struct sieve_validator_extension imapflags_validator_extension = { + &imapflags_extension, + ext_imapflags_validator_extension_validate, + NULL +}; + +static bool ext_imapflags_validator_load +(struct sieve_validator *valdtr) +{ + sieve_validator_extension_register + (valdtr, &imapflags_validator_extension, NULL); + + /* Register commands */ + sieve_validator_register_command(valdtr, &cmd_setflag); + sieve_validator_register_command(valdtr, &cmd_addflag); + sieve_validator_register_command(valdtr, &cmd_removeflag); + + sieve_validator_register_command(valdtr, &cmd_mark); + sieve_validator_register_command(valdtr, &cmd_unmark); + + return TRUE; +} + +static bool ext_imapflags_validator_extension_validate +(struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg) +{ + if ( sieve_validator_extension_loaded(valdtr, &imap4flags_extension) ) { + sieve_argument_validate_error(valdtr, require_arg, + "the (depricated) imapflags extension cannot be used " + "together with the imap4flags extension"); + return FALSE; + } + + return TRUE; +} + +/* + * Interpreter + */ + +static bool ext_imapflags_interpreter_load +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register + (renv->interp, &imap4flags_interpreter_extension, NULL); + + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_mark_validate +(struct sieve_validator *valdtr, struct sieve_command_context *cmd) +{ + if ( cmd->command == &cmd_mark ) + cmd->command = &cmd_addflag; + else + cmd->command = &cmd_removeflag; + + cmd->first_positional = sieve_ast_argument_cstring_create + (cmd->ast_node, "\\flagged", cmd->ast_node->source_line); + + if ( !sieve_validator_argument_activate(valdtr, cmd, cmd->first_positional, FALSE) ) + return FALSE; + + return TRUE; +} diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index 53b5ad9858966c5613b591600d3c61ff09ccdc2a..2a7cb929b68b7a0c524792391e7916f1e7eb7b41 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -493,6 +493,28 @@ struct sieve_ast_argument *sieve_ast_argument_string_create return argument; } +struct sieve_ast_argument *sieve_ast_argument_cstring_create +(struct sieve_ast_node *node, const char *str, unsigned int source_line) +{ + struct sieve_ast_argument *argument; + string_t *newstr; + + /* Allocate new internal string buffer */ + newstr = str_new(node->ast->pool, strlen(str)); + + /* Clone string */ + str_append(newstr, str); + + /* Create string argument */ + argument = sieve_ast_argument_string_create_raw + (node->ast, newstr, source_line); + + /* Add argument to command/test node */ + sieve_ast_node_add_argument(node, argument); + + return argument; +} + void sieve_ast_argument_string_set (struct sieve_ast_argument *argument, string_t *newstr) { diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h index 3109fab6d69c177a54ff4a63e21667ace4150fb4..77567a2410206dbed1dd2634c29b03e18a12113c 100644 --- a/src/lib-sieve/sieve-ast.h +++ b/src/lib-sieve/sieve-ast.h @@ -237,6 +237,8 @@ struct sieve_ast_argument *sieve_ast_argument_string_create_raw (struct sieve_ast *ast, string_t *str, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_string_create (struct sieve_ast_node *node, const string_t *str, unsigned int source_line); +struct sieve_ast_argument *sieve_ast_argument_cstring_create + (struct sieve_ast_node *node, const char *str, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_tag_create (struct sieve_ast_node *node, const char *tag, unsigned int source_line); diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index d5441ea3ce4b9be412e62eaa55dee00bcf893ce6..e26f4013dbe6efb3cf57b0ad6666042446f9c5b4 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -114,6 +114,20 @@ const struct sieve_extension *sieve_core_extensions[] = { const unsigned int sieve_core_extensions_count = N_ELEMENTS(sieve_core_extensions); +/* + * Depricated extensions + */ + +extern const struct sieve_extension imapflags_extension; + +const struct sieve_extension *sieve_depricated_extensions[] = { + /* Depricated extensions */ + &imapflags_extension +}; + +const unsigned int sieve_depricated_extensions_count = + N_ELEMENTS(sieve_depricated_extensions); + /* * Extensions init/deinit */ @@ -127,7 +141,12 @@ bool sieve_extensions_init(void) /* Pre-load core extensions */ for ( i = 0; i < sieve_core_extensions_count; i++ ) { - (void) sieve_extension_register(sieve_core_extensions[i]); + (void)sieve_extension_register(sieve_core_extensions[i], TRUE); + } + + /* Pre-load core extensions */ + for ( i = 0; i < sieve_depricated_extensions_count; i++ ) { + (void)sieve_extension_register(sieve_depricated_extensions[i], TRUE); } /* More extensions can be added through plugins */ @@ -149,6 +168,7 @@ struct sieve_extension_registration { const struct sieve_extension *extension; int id; bool required; + bool loaded; }; static ARRAY_DEFINE(extensions, struct sieve_extension_registration); @@ -161,8 +181,21 @@ static void sieve_extensions_init_registry(void) (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); } -static struct sieve_extension_registration *_sieve_extension_register +static bool _sieve_extension_load (const struct sieve_extension *extension) +{ + /* Call load handler */ + if ( extension->load != NULL && !extension->load() ) { + sieve_sys_error("failed to load '%s' extension support.", + extension->name); + return FALSE; + } + + return TRUE; +} + +static struct sieve_extension_registration *_sieve_extension_register +(const struct sieve_extension *extension, bool load) { struct sieve_extension_registration *ereg = (struct sieve_extension_registration *) hash_table_lookup(extension_index, extension->name); @@ -181,18 +214,17 @@ static struct sieve_extension_registration *_sieve_extension_register /* Enable extension */ if ( ereg->extension == NULL && extension != NULL ) { - if ( extension->_id != NULL ) { - int ext_id = *(extension->_id); - + if ( extension->_id != NULL && load ) { /* Make sure extension is enabled */ *(extension->_id) = ereg->id; - /* Call load handler if extension was not enabled */ - if ( ext_id == -1 && extension->load != NULL && !extension->load() ) { - sieve_sys_error("failed to load '%s' extension support.", - extension->name); - return NULL; + /* Call load handler if extension was not loaded already */ + if ( !ereg->loaded ) { + if ( !_sieve_extension_load(extension) ) + return NULL; } + + ereg->loaded = TRUE; } ereg->extension = extension; @@ -201,12 +233,13 @@ static struct sieve_extension_registration *_sieve_extension_register return ereg; } -int sieve_extension_register(const struct sieve_extension *extension) +int sieve_extension_register +(const struct sieve_extension *extension, bool load) { struct sieve_extension_registration *ereg; /* Register the extension */ - if ( (ereg=_sieve_extension_register(extension)) == NULL ) { + if ( (ereg=_sieve_extension_register(extension, load)) == NULL ) { return -1; } @@ -218,7 +251,7 @@ int sieve_extension_require(const struct sieve_extension *extension) struct sieve_extension_registration *ereg; /* Register (possibly unknown) extension */ - if ( (ereg=_sieve_extension_register(extension)) == NULL ) { + if ( (ereg=_sieve_extension_register(extension, TRUE)) == NULL ) { return -1; } @@ -302,6 +335,25 @@ const char *sieve_extensions_get_string(void) return str_c(extstr); } +static void sieve_extension_enable(struct sieve_extension_registration *ereg) +{ + if ( ereg->extension->_id != NULL ) { + *(ereg->extension->_id) = ereg->id; + + if ( !ereg->loaded ) { + (void)_sieve_extension_load(ereg->extension); + } + } + + ereg->loaded = TRUE; +} + +static void sieve_extension_disable(struct sieve_extension_registration *ereg) +{ + if ( ereg->extension->_id != NULL ) + *(ereg->extension->_id) = -1; +} + void sieve_extensions_set_string(const char *ext_string) { ARRAY_DEFINE(enabled_extensions, const struct sieve_extension_registration *); @@ -315,10 +367,8 @@ void sieve_extensions_set_string(const char *ext_string) /* Enable all */ eregs = array_get_modifiable(&extensions, &ext_count); - for ( i = 0; i < ext_count; i++ ) { - if ( eregs[i].extension->_id != NULL ) - *(eregs[i].extension->_id) = eregs[i].id; - } + for ( i = 0; i < ext_count; i++ ) + sieve_extension_enable(&eregs[i]); return; } @@ -373,11 +423,10 @@ void sieve_extensions_set_string(const char *ext_string) if ( eregs[i].extension->_id != NULL && *(eregs[i].extension->name) != '@' ) { - if ( disabled && !eregs[i].required ) { - *(eregs[i].extension->_id) = -1; - } else { - *(eregs[i].extension->_id) = eregs[i].id; - } + if ( disabled && !eregs[i].required ) + sieve_extension_disable(&eregs[i]); + else + sieve_extension_enable(&eregs[i]); } } } diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h index 7affa072e247d631667527ea1feae64750f469aa..717dfc145f374efe4887c4d0c61d0cb99150c1dc 100644 --- a/src/lib-sieve/sieve-extensions.h +++ b/src/lib-sieve/sieve-extensions.h @@ -89,7 +89,7 @@ void sieve_extensions_deinit(void); * Extension registry */ -int sieve_extension_register(const struct sieve_extension *extension); +int sieve_extension_register(const struct sieve_extension *extension, bool load); int sieve_extension_require(const struct sieve_extension *extension); int sieve_extensions_get_count(void); const struct sieve_extension *sieve_extension_get_by_id(unsigned int ext_id); diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index d3fd9cdc38999d0b4d5e92cb1bf7cc68b53bae52..2534a8a2e0d0fff3ef8d87536653b82242eeefc4 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -80,6 +80,8 @@ struct sieve_validator { struct sieve_script *script; struct sieve_error_handler *ehandler; + + bool finished_require; /* Registries */ @@ -1108,12 +1110,32 @@ static bool sieve_validate_command result = FALSE; } - - /* - * Descend further into the AST - */ - + if ( command != NULL ) { + /* Check if this is the first non-require command */ + + if ( !valdtr->finished_require && command != &cmd_require ) { + const struct sieve_validator_extension_reg *extrs; + unsigned int ext_count, i; + + /* Validate all 'require'd extensions */ + + extrs = array_get(&valdtr->extensions, &ext_count); + for ( i = 0; i < ext_count; i++ ) { + if ( extrs[i].val_ext != NULL && extrs[i].val_ext->validate != NULL ) { + if ( !extrs[i].val_ext->validate + (valdtr, extrs[i].context, extrs[i].arg) ) + return FALSE; + } + } + + valdtr->finished_require = TRUE; + } + + /* + * Descend further into the AST + */ + /* Tests */ if ( command->subtests > 0 && (result || sieve_errors_more_allowed(valdtr->ehandler)) ) @@ -1164,19 +1186,8 @@ static bool sieve_validate_block return result; } -bool sieve_validator_run(struct sieve_validator *validator) { - const struct sieve_validator_extension_reg *extrs; - unsigned int ext_count, i; - - /* Validate registered extensions */ - extrs = array_get(&validator->extensions, &ext_count); - for ( i = 0; i < ext_count; i++ ) { - if ( extrs[i].val_ext != NULL && extrs[i].val_ext->validate != NULL ) { - if ( !extrs[i].val_ext->validate(validator, extrs[i].context, extrs[i].arg) ) - return FALSE; - } - } - +bool sieve_validator_run(struct sieve_validator *validator) +{ return sieve_validate_block(validator, sieve_ast_root(validator->ast)); } diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c index 38dde8ab13420c592060143a20fe5baf1fdb4c03..17b8a04a0e0c4d7be015f2fdf28010d867546e97 100644 --- a/src/testsuite/testsuite.c +++ b/src/testsuite/testsuite.c @@ -46,7 +46,9 @@ static void testsuite_tool_init(void) { sieve_tool_init(); - (void) sieve_extension_register(&testsuite_extension); + sieve_extensions_set_string(NULL); + + (void) sieve_extension_register(&testsuite_extension, TRUE); testsuite_init(); } diff --git a/tests/extensions/imap4flags/errors.svtest b/tests/extensions/imap4flags/errors.svtest new file mode 100644 index 0000000000000000000000000000000000000000..02c6c153278b75a27c577f089aa18f3bda03c534 --- /dev/null +++ b/tests/extensions/imap4flags/errors.svtest @@ -0,0 +1,14 @@ +require "vnd.dovecot.testsuite"; + +require "comparator-i;ascii-numeric"; +require "relational"; + +test "Depricated imapflags extension used with imap4flags" { + if test_script_compile "errors/imapflags.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"; + } +} diff --git a/tests/extensions/imap4flags/errors/imapflags.sieve b/tests/extensions/imap4flags/errors/imapflags.sieve new file mode 100644 index 0000000000000000000000000000000000000000..1b18a4267921f00e9cb0b691fda2b24c8b86a09c --- /dev/null +++ b/tests/extensions/imap4flags/errors/imapflags.sieve @@ -0,0 +1,4 @@ +require "imapflags"; +require "imap4flags"; + +addflag "\\flagged"; diff --git a/tests/extensions/imap4flags/execute/imapflags.sieve b/tests/extensions/imap4flags/execute/imapflags.sieve new file mode 100644 index 0000000000000000000000000000000000000000..c071d339b625b083f14c9c995139192fc944fa56 --- /dev/null +++ b/tests/extensions/imap4flags/execute/imapflags.sieve @@ -0,0 +1,6 @@ +require "imapflags"; + +mark; +unmark; +mark; +