diff --git a/Makefile.am b/Makefile.am index 385857e09c775e49bfa7f4e2471a3f94543a9ca5..ff26045e92b760ca2985865198c22299957f87fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -123,6 +123,7 @@ test_cases = \ tests/extensions/spamvirustest/virustest.svtest \ tests/extensions/spamvirustest/spamtestplus.svtest \ tests/extensions/spamvirustest/errors.svtest \ + tests/extensions/ihave/execute.svtest \ tests/extensions/vnd.dovecot/debug/execute.svtest \ tests/deprecated/notify/basic.svtest \ tests/deprecated/notify/mailto.svtest \ diff --git a/configure.in b/configure.in index f47b46deeaf4223314a3fa76a4ad511aef40e972..54d1b0d7110e7ab466b11bd2b9a3fc2ef31924e5 100644 --- a/configure.in +++ b/configure.in @@ -117,6 +117,7 @@ src/lib-sieve/plugins/environment/Makefile src/lib-sieve/plugins/mailbox/Makefile src/lib-sieve/plugins/date/Makefile src/lib-sieve/plugins/spamvirustest/Makefile +src/lib-sieve/plugins/ihave/Makefile src/lib-sieve/plugins/vnd.dovecot/Makefile src/lib-sieve/plugins/vnd.dovecot/debug/Makefile src/lib-sieve-tool/Makefile diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 17312919dc875d243882888c529969b61908b228..30aab213deb800ba41906a43bd02bf944d72831a 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -63,6 +63,7 @@ plugins = \ $(extdir)/mailbox/libsieve_ext_mailbox.la \ $(extdir)/date/libsieve_ext_date.la \ $(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \ + $(extdir)/ihave/libsieve_ext_ihave.la \ $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ $(unfinished_plugins) diff --git a/src/lib-sieve/cmd-require.c b/src/lib-sieve/cmd-require.c index 66506731822206be133adf05661d06eb2df961fc..62ecce05ebc0e42693850af2a76a7752684dcac4 100644 --- a/src/lib-sieve/cmd-require.c +++ b/src/lib-sieve/cmd-require.c @@ -55,8 +55,8 @@ static bool cmd_require_validate arg = cmd->first_positional; if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { /* Single string */ - const struct sieve_extension *ext = sieve_validator_extension_load - (valdtr, cmd, arg, sieve_ast_argument_str(arg)); + const struct sieve_extension *ext = sieve_validator_extension_load_by_name + (valdtr, cmd, arg, sieve_ast_argument_strc(arg)); if ( ext == NULL ) result = FALSE; @@ -65,8 +65,8 @@ static bool cmd_require_validate struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg); while ( stritem != NULL ) { - const struct sieve_extension *ext = sieve_validator_extension_load - (valdtr, cmd, stritem, sieve_ast_strlist_str(stritem)); + const struct sieve_extension *ext = sieve_validator_extension_load_by_name + (valdtr, cmd, stritem, sieve_ast_strlist_strc(stritem)); if ( ext == NULL ) result = FALSE; diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am index 25db0dfa1efa1cf192e5e4614d8655deb13df722..8a660edbbf7e525302d348502a19a5b92546a7b9 100644 --- a/src/lib-sieve/plugins/Makefile.am +++ b/src/lib-sieve/plugins/Makefile.am @@ -19,6 +19,7 @@ SUBDIRS = \ mailbox \ date \ spamvirustest \ + ihave \ vnd.dovecot \ $(UNFINISHED) diff --git a/src/lib-sieve/plugins/ihave/Makefile.am b/src/lib-sieve/plugins/ihave/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..ba12d807646fcef181285b39236ede4679cff3fc --- /dev/null +++ b/src/lib-sieve/plugins/ihave/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libsieve_ext_ihave.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-ihave.c + +libsieve_ext_ihave_la_SOURCES = \ + $(tests) \ + ext-ihave-binary.c \ + ext-ihave-common.c \ + ext-ihave.c + +noinst_HEADERS = \ + ext-ihave-binary.h \ + ext-ihave-common.h diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-binary.c b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c new file mode 100644 index 0000000000000000000000000000000000000000..b416c4656286041148842b66022266a9b58cf2b4 --- /dev/null +++ b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c @@ -0,0 +1,242 @@ +/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-ihave-common.h" +#include "ext-ihave-binary.h" + +/* + * Forward declarations + */ + +static bool ext_ihave_binary_save + (const struct sieve_extension *ext, struct sieve_binary *sbin, void *context); +static bool ext_ihave_binary_open + (const struct sieve_extension *ext, struct sieve_binary *sbin, void *context); +static bool ext_ihave_binary_up_to_date + (const struct sieve_extension *ext, struct sieve_binary *sbin, void *context); + +/* + * Binary include extension + */ + +const struct sieve_binary_extension ihave_binary_ext = { + &ihave_extension, + ext_ihave_binary_save, + ext_ihave_binary_open, + NULL, + ext_ihave_binary_up_to_date +}; + +/* + * Binary context management + */ + +struct ext_ihave_binary_context { + struct sieve_binary *binary; + struct sieve_binary_block *block; + + ARRAY_DEFINE(missing_extensions, const char *); +}; + +static struct ext_ihave_binary_context *ext_ihave_binary_create_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + pool_t pool = sieve_binary_pool(sbin); + + struct ext_ihave_binary_context *ctx = + p_new(pool, struct ext_ihave_binary_context, 1); + + ctx->binary = sbin; + p_array_init(&ctx->missing_extensions, pool, 64); + + sieve_binary_extension_set(sbin, this_ext, &ihave_binary_ext, ctx); + return ctx; +} + +struct ext_ihave_binary_context *ext_ihave_binary_get_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + struct ext_ihave_binary_context *ctx = (struct ext_ihave_binary_context *) + sieve_binary_extension_get_context(sbin, this_ext); + + if ( ctx == NULL ) + ctx = ext_ihave_binary_create_context(this_ext, sbin); + + return ctx; +} + +struct ext_ihave_binary_context *ext_ihave_binary_init +(const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast) +{ + struct ext_ihave_ast_context *ast_ctx = + ext_ihave_get_ast_context(this_ext, ast); + struct ext_ihave_binary_context *binctx; + const char *const *exts; + unsigned int i, count; + + binctx = ext_ihave_binary_get_context(this_ext, sbin); + + exts = array_get(&ast_ctx->missing_extensions, &count); + + if ( count > 0 ) { + pool_t pool = sieve_binary_pool(sbin); + + if ( binctx->block == NULL ) + binctx->block = sieve_binary_extension_create_block(sbin, this_ext); + + for ( i = 0; i < count; i++ ) { + const char *ext_name = p_strdup(pool, exts[i]); + + array_append(&binctx->missing_extensions, &ext_name, 1); + } + } + + return binctx; +} + +/* + * Binary extension + */ + +static bool ext_ihave_binary_save +(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context) +{ + struct ext_ihave_binary_context *binctx = + (struct ext_ihave_binary_context *) context; + const char *const *exts; + unsigned int count, i; + + exts = array_get(&binctx->missing_extensions, &count); + + if ( binctx->block != NULL ) + sieve_binary_block_clear(binctx->block); + + if ( count > 0 ) { + if ( binctx->block == NULL ) + binctx->block = sieve_binary_extension_create_block(sbin, ext); + + sieve_binary_emit_unsigned(binctx->block, count); + + for ( i = 0; i < count; i++ ) { + sieve_binary_emit_cstring(binctx->block, exts[i]); + } + } + + return TRUE; +} + +static bool ext_ihave_binary_open +(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_ihave_binary_context *binctx = + (struct ext_ihave_binary_context *) context; + struct sieve_binary_block *sblock; + unsigned int i, count, block_id; + sieve_size_t offset; + + sblock = sieve_binary_extension_get_block(sbin, ext); + + if ( sblock != NULL ) { + binctx->block = sblock; + block_id = sieve_binary_block_get_id(sblock); + + offset = 0; + + /* Read number of missing extensions to read subsequently */ + if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) { + sieve_sys_error(svinst, + "ihave: failed to read missing extension count " + "from block %d of binary %s", block_id, sieve_binary_path(sbin)); + return FALSE; + } + + /* Read dependencies */ + for ( i = 0; i < count; i++ ) { + string_t *ext_name; + const char *name; + + if ( !sieve_binary_read_string(sblock, &offset, &ext_name) ) { + /* Binary is corrupt, recompile */ + sieve_sys_error(svinst, + "ihave: failed to read missing extension name " + "from block %d of binary %s", block_id, sieve_binary_path(sbin)); + return FALSE; + } + + name = str_c(ext_name); + array_append(&binctx->missing_extensions, &name, 1); + } + } + + return TRUE; +} + +static bool ext_ihave_binary_up_to_date +(const struct sieve_extension *ext, struct sieve_binary *sbin ATTR_UNUSED, + void *context) +{ + struct ext_ihave_binary_context *binctx = + (struct ext_ihave_binary_context *) context; + const char *const *exts; + unsigned int count, i; + + exts = array_get(&binctx->missing_extensions, &count); + for ( i = 0; i < count; i++ ) { + if ( sieve_extension_get_by_name(ext->svinst, exts[i]) != NULL ) + return FALSE; + } + + return TRUE; +} + +/* + * Main extension interface + */ + +bool ext_ihave_binary_load +(const struct sieve_extension *ext, struct sieve_binary *sbin) +{ + (void)ext_ihave_binary_get_context(ext, sbin); + + return TRUE; +} + +bool ext_ihave_binary_dump +(const struct sieve_extension *ext, struct sieve_dumptime_env *denv) +{ + struct sieve_binary *sbin = denv->sbin; + struct ext_ihave_binary_context *binctx = + ext_ihave_binary_get_context(ext, sbin); + const char *const *exts; + unsigned int count, i; + + exts = array_get(&binctx->missing_extensions, &count); + + if ( count > 0 ) { + sieve_binary_dump_sectionf(denv, + "Extensions missing at compile (block: %d)", + sieve_binary_block_get_id(binctx->block)); + + for ( i = 0; i < count; i++ ) { + sieve_binary_dumpf(denv, " - %s\n", exts[i]); + } + } + + return TRUE; +} + + diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-binary.h b/src/lib-sieve/plugins/ihave/ext-ihave-binary.h new file mode 100644 index 0000000000000000000000000000000000000000..770b625d001d122341c8ac00ef619a390759e82a --- /dev/null +++ b/src/lib-sieve/plugins/ihave/ext-ihave-binary.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __EXT_IHAVE_BINARY_H +#define __EXT_IHAVE_BINARY_H + +/* + * Binary context management + */ + +struct ext_ihave_binary_context; + +struct ext_ihave_binary_context *ext_ihave_binary_get_context + (const struct sieve_extension *this_ext, struct sieve_binary *sbin); +struct ext_ihave_binary_context *ext_ihave_binary_init + (const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast); + +/* + * Registering missing extension + */ + +void ext_ihave_binary_add_missing_extension + (struct ext_ihave_binary_context *binctx, const char *ext_name); + +/* + * Main extension interface + */ + +bool ext_ihave_binary_load + (const struct sieve_extension *ext, struct sieve_binary *sbin); +bool ext_ihave_binary_dump + (const struct sieve_extension *ext, struct sieve_dumptime_env *denv); + +#endif /* __EXT_IHAVE_BINARY_H */ + diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-common.c b/src/lib-sieve/plugins/ihave/ext-ihave-common.c new file mode 100644 index 0000000000000000000000000000000000000000..5f56ae8d47b920f27d83acafc158a4be33331b14 --- /dev/null +++ b/src/lib-sieve/plugins/ihave/ext-ihave-common.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-ast.h" + +#include "ext-ihave-common.h" + +/* + * AST context management + */ + +struct ext_ihave_ast_context *ext_ihave_get_ast_context +(const struct sieve_extension *this_ext, struct sieve_ast *ast) +{ + struct ext_ihave_ast_context *actx = (struct ext_ihave_ast_context *) + sieve_ast_extension_get_context(ast, this_ext); + pool_t pool; + + if ( actx != NULL ) + return actx; + + pool = sieve_ast_pool(ast); + actx = p_new(pool, struct ext_ihave_ast_context, 1); + p_array_init(&actx->missing_extensions, pool, 64); + + sieve_ast_extension_set_context(ast, this_ext, (void *) actx); + + return actx; +} + +void ext_ihave_ast_add_missing_extension +(const struct sieve_extension *this_ext, struct sieve_ast *ast, + const char *ext_name) +{ + struct ext_ihave_ast_context *actx = + ext_ihave_get_ast_context(this_ext, ast); + const char *const *exts; + unsigned int i, count; + + exts = array_get(&actx->missing_extensions, &count); + for ( i = 0; i < count; i++ ) { + if ( strcmp(exts[i], ext_name) == 0 ) + return; + } + + array_append(&actx->missing_extensions, &ext_name, 1); +} + diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-common.h b/src/lib-sieve/plugins/ihave/ext-ihave-common.h new file mode 100644 index 0000000000000000000000000000000000000000..ad0ac476181dffcfc79a96c13893ae5a40ebbcf6 --- /dev/null +++ b/src/lib-sieve/plugins/ihave/ext-ihave-common.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __EXT_IHAVE_COMMON_H +#define __EXT_IHAVE_COMMON_H + +#include "sieve-common.h" + +/* + * Extensions + */ + +extern const struct sieve_extension_def ihave_extension; + +/* + * Tests + */ + +extern const struct sieve_command_def ihave_test; + +/* + * Commands + */ + +//extern const struct sieve_command_def error_command; + +/* + * Operations + */ + +//extern const struct sieve_operation_def error_operation; + +/* + * AST context + */ + +struct ext_ihave_ast_context { + ARRAY_DEFINE(missing_extensions, const char *); +}; + +struct ext_ihave_ast_context *ext_ihave_get_ast_context + (const struct sieve_extension *this_ext, struct sieve_ast *ast); + +void ext_ihave_ast_add_missing_extension + (const struct sieve_extension *this_ext, struct sieve_ast *ast, + const char *ext_name); + + +#endif /* __EXT_IHAVE_COMMON_H */ diff --git a/src/lib-sieve/plugins/ihave/ext-ihave.c b/src/lib-sieve/plugins/ihave/ext-ihave.c new file mode 100644 index 0000000000000000000000000000000000000000..1ce4b45dc4c1bf3eb2a8d8e9c9439bc9db2c0ae0 --- /dev/null +++ b/src/lib-sieve/plugins/ihave/ext-ihave.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file + */ + +/* Extension ihave + * --------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5463 + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +#include "ext-ihave-common.h" +#include "ext-ihave-binary.h" + +/* + * Extension + */ + +static bool ext_ihave_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); +static bool ext_ihave_generator_load + (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv); + +const struct sieve_extension_def ihave_extension = { + "ihave", + NULL, NULL, + ext_ihave_validator_load, + ext_ihave_generator_load, + NULL, + ext_ihave_binary_load, + ext_ihave_binary_dump, + NULL, +// SIEVE_EXT_DEFINE_OPERATION(error_operation), + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS +}; + +static bool ext_ihave_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + /* Register new test */ + sieve_validator_register_command(validator, ext, &ihave_test); + + return TRUE; +} + +static bool ext_ihave_generator_load +(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv) +{ + (void)ext_ihave_binary_init(ext, cgenv->sbin, cgenv->ast); + + return TRUE; +} + diff --git a/src/lib-sieve/plugins/ihave/tst-ihave.c b/src/lib-sieve/plugins/ihave/tst-ihave.c new file mode 100644 index 0000000000000000000000000000000000000000..6be5e4c48047e973a8bc492aedf5e8b8be48c6a6 --- /dev/null +++ b/src/lib-sieve/plugins/ihave/tst-ihave.c @@ -0,0 +1,154 @@ +/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +#include "ext-ihave-common.h" + +/* + * Ihave test + * + * Syntax: + * ihave <capabilities: string-list> + */ + +static bool tst_ihave_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_ihave_validate_const + (struct sieve_validator *valdtr, struct sieve_command *tst, + int *const_current, int const_next); + +const struct sieve_command_def ihave_test = { + "ihave", + SCT_TEST, + 1, 0, FALSE, FALSE, + NULL, NULL, + tst_ihave_validate, + tst_ihave_validate_const, + NULL, NULL +}; + +/* + * Code validation + */ + +static bool tst_ihave_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct _capability { + const struct sieve_extension *ext; + struct sieve_ast_argument *arg; + }; + + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *stritem; + ARRAY_DEFINE(capabilities, struct _capability); + struct _capability capability; + const struct _capability *caps; + unsigned int i, count; + bool all_known = TRUE; + + t_array_init(&capabilities, 64); + + tst->data = (void *) FALSE; + + /* Check stringlist argument */ + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "capabilities", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + switch ( sieve_ast_argument_type(arg) ) { + case SAAT_STRING: + /* Single string */ + capability.arg = arg; + capability.ext = sieve_extension_get_by_name + (tst->ext->svinst, sieve_ast_argument_strc(arg)); + array_append(&capabilities, &capability, 1); + + if ( capability.ext == NULL ) { + all_known = FALSE; + + ext_ihave_ast_add_missing_extension + (tst->ext, tst->ast_node->ast, sieve_ast_argument_strc(arg)); + } + + break; + + case SAAT_STRING_LIST: + /* String list */ + stritem = sieve_ast_strlist_first(arg); + + while ( stritem != NULL ) { + capability.arg = stritem; + capability.ext = sieve_extension_get_by_name + (tst->ext->svinst, sieve_ast_argument_strc(stritem)); + array_append(&capabilities, &capability, 1); + + if ( capability.ext == NULL ) { + all_known = FALSE; + + ext_ihave_ast_add_missing_extension + (tst->ext, tst->ast_node->ast, sieve_ast_argument_strc(stritem)); + } + + stritem = sieve_ast_strlist_next(stritem); + } + + break; + default: + i_unreached(); + } + + if ( !all_known ) + return TRUE; + + /* RFC 5463, Section 4, page 4: + * + * The "ihave" extension is designed to be used with other extensions + * that add tests, actions, comparators, or arguments. Implementations + * MUST NOT allow it to be used with extensions that change the + * underlying Sieve grammar, or extensions like encoded-character + * [RFC5228], or variables [RFC5229] that change how the content of + * Sieve scripts are interpreted. The test MUST fail and the extension + * MUST NOT be enabled if such usage is attempted. + * + * FIXME: current implementation of this restriction is hardcoded and + * therefore highly inflexible + */ + caps = array_get(&capabilities, &count); + for ( i = 0; i < count; i++ ) { + if ( sieve_extension_name_is(caps[i].ext, "variables") || + sieve_extension_name_is(caps[i].ext, "encoded-character") ) + return TRUE; + } + + /* Load all extensions */ + caps = array_get(&capabilities, &count); + for ( i = 0; i < count; i++ ) { + if ( !sieve_validator_extension_load + (valdtr, tst, caps[i].arg, caps[i].ext) ) + return FALSE; + } + + tst->data = (void *) TRUE; + return TRUE; +} + +static bool tst_ihave_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *tst, + int *const_current, int const_next ATTR_UNUSED) +{ + if ( (bool)tst->data == TRUE ) + *const_current = 1; + else + *const_current = 0; + return TRUE; +} diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index ecd27029ee164450fb5628a7f0226fb9a9b6f459..1e385ef4d4e2d0c3eff1acf96c025aa8eb009d16 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -129,23 +129,24 @@ struct sieve_script *sieve_ast_script(struct sieve_ast *ast) * Extension support */ -void sieve_ast_extension_link +bool sieve_ast_extension_link (struct sieve_ast *ast, const struct sieve_extension *ext) { unsigned int i, ext_count; const struct sieve_extension *const *extensions; - if ( ext->id < 0 ) return; + if ( ext->id < 0 ) return TRUE; /* Prevent duplicates */ extensions = array_get(&ast->linked_extensions, &ext_count); for ( i = 0; i < ext_count; i++ ) { if ( extensions[i] == ext ) - return; + return FALSE; } /* Add extension */ array_append(&ast->linked_extensions, &ext, 1); + return TRUE; } const struct sieve_extension * const *sieve_ast_extensions_get @@ -169,6 +170,17 @@ void sieve_ast_extension_register reg->context = context; } +void sieve_ast_extension_set_context +(struct sieve_ast *ast, const struct sieve_extension *ext, void *context) +{ + struct sieve_ast_extension_reg *reg; + + if ( ext->id < 0 ) return; + + reg = array_idx_modifiable(&ast->extensions, (unsigned int) ext->id); + reg->context = context; +} + void *sieve_ast_extension_get_context (struct sieve_ast *ast, const struct sieve_extension *ext) { diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h index c6a40ffc54cda3cd0aadcf35aad6f170f96e7f44..007b4a730d15e8a281efc26fc260da31e292dcd0 100644 --- a/src/lib-sieve/sieve-ast.h +++ b/src/lib-sieve/sieve-ast.h @@ -186,7 +186,7 @@ struct sieve_ast_extension { void *context); }; -void sieve_ast_extension_link +bool sieve_ast_extension_link (struct sieve_ast *ast, const struct sieve_extension *ext); const struct sieve_extension * const *sieve_ast_extensions_get (struct sieve_ast *ast, unsigned int *count_r); @@ -194,6 +194,8 @@ const struct sieve_extension * const *sieve_ast_extensions_get void sieve_ast_extension_register (struct sieve_ast *ast, const struct sieve_extension *ext, const struct sieve_ast_extension *ast_ext, void *context); +void sieve_ast_extension_set_context + (struct sieve_ast *ast, const struct sieve_extension *ext, void *context); void *sieve_ast_extension_get_context (struct sieve_ast *ast, const struct sieve_extension *ext); diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index c8b78f9c0e1414bc35d966803f3971dceba0067c..5294b29dd81a139ce7a33e1de5f11b3ca4d200fb 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -103,6 +103,7 @@ extern const struct sieve_extension_def date_extension; extern const struct sieve_extension_def spamtest_extension; extern const struct sieve_extension_def spamtestplus_extension; extern const struct sieve_extension_def virustest_extension; +extern const struct sieve_extension_def ihave_extension; /* vnd.dovecot. */ extern const struct sieve_extension_def debug_extension; @@ -132,7 +133,7 @@ const struct sieve_extension_def *sieve_core_extensions[] = { &relational_extension, ®ex_extension, &imap4flags_extension, ©_extension, &include_extension, &body_extension, &variables_extension, &enotify_extension, &environment_extension, - &mailbox_extension, &date_extension + &mailbox_extension, &date_extension, &ihave_extension }; const unsigned int sieve_core_extensions_count = @@ -463,6 +464,9 @@ const struct sieve_extension *sieve_extension_get_by_name if ( *name == '@' ) return NULL; + if ( strlen(name) > 128 ) + return NULL; + ext = (const struct sieve_extension *) hash_table_lookup(ext_reg->extension_index, name); diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h index c5f62d83e9b117007120cae956edc578418cbd30..cff814e1ad2d12bbdc7a8c440cd311cede1db3f6 100644 --- a/src/lib-sieve/sieve-extensions.h +++ b/src/lib-sieve/sieve-extensions.h @@ -77,10 +77,12 @@ struct sieve_extension { unsigned int dummy:1; }; -#define sieve_extension_name(ext) \ - (ext)->def->name #define sieve_extension_is(ext, definition) \ ( (ext)->def == &(definition) ) +#define sieve_extension_name(ext) \ + (ext)->def->name +#define sieve_extension_name_is(ext, _name) \ + ( strcmp((ext)->def->name, (_name)) == 0 ) /* * Defining opcodes and operands diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index a443b74d97c9142c4a674a0d4c85a1bd36ffdccf..03c31887571992a1c3c998ef7eb7d59f9444a485 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -528,24 +528,52 @@ static struct sieve_tag_registration *sieve_validator_command_tag_get * Extension support */ -const struct sieve_extension *sieve_validator_extension_load +bool sieve_validator_extension_load (struct sieve_validator *valdtr, struct sieve_command *cmd, - struct sieve_ast_argument *ext_arg, string_t *ext_name) + struct sieve_ast_argument *ext_arg, const struct sieve_extension *ext) { + const struct sieve_extension_def *extdef = ext->def; struct sieve_validator_extension_reg *reg; - const struct sieve_extension *ext; - const struct sieve_extension_def *extdef; - const char *name = str_c(ext_name); - if ( str_len(ext_name) > 128 ) { - sieve_argument_validate_error(valdtr, ext_arg, - "%s %s: unknown Sieve capability '%s' (name is impossibly long)", - sieve_command_identifier(cmd), sieve_command_type_name(cmd), - str_sanitize(name, 128)); - return NULL; + if ( !sieve_ast_extension_link(valdtr->ast, ext) ) { + /*if ( cmd != NULL && ext_arg != NULL ) { + sieve_argument_validate_warning(valdtr, ext_arg, + "%s %s: sieve capability `%s' already loaded", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + sieve_extension_name(ext)); + }*/ + } else { + if ( extdef->validator_load != NULL && + !extdef->validator_load(ext, valdtr) ) { + if ( cmd != NULL && ext_arg != NULL ) { + sieve_argument_validate_error(valdtr, ext_arg, + "%s %s: failed to load Sieve capability '%s'", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + sieve_extension_name(ext)); + } + return FALSE; + } + } + + /* Register extension no matter what and store the AST argument registering it + */ + if ( ext->id >= 0 ) { + reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext->id); + if ( reg->arg == NULL ) + reg->arg = ext_arg; + reg->loaded = TRUE; } - ext = sieve_extension_get_by_name(valdtr->svinst, name); + return TRUE; +} + +const struct sieve_extension *sieve_validator_extension_load_by_name +(struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, const char *ext_name) +{ + const struct sieve_extension *ext; + + ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); if ( ext == NULL || ext->def == NULL || !ext->enabled ) { unsigned int i; @@ -553,12 +581,12 @@ const struct sieve_extension *sieve_validator_extension_load bool core_command = FALSE; for ( i = 0; !core_command && i < sieve_core_commands_count; i++ ) { - if ( strcasecmp(sieve_core_commands[i]->identifier, name) == 0 ) + if ( strcasecmp(sieve_core_commands[i]->identifier, ext_name) == 0 ) core_command = TRUE; } for ( i = 0; !core_test && i < sieve_core_tests_count; i++ ) { - if ( strcasecmp(sieve_core_tests[i]->identifier, name) == 0 ) + if ( strcasecmp(sieve_core_tests[i]->identifier, ext_name) == 0 ) core_test = TRUE; } @@ -567,36 +595,18 @@ const struct sieve_extension *sieve_validator_extension_load "%s %s: '%s' is not known as a Sieve capability, " "but it is known as a Sieve %s that is always available", sieve_command_identifier(cmd), sieve_command_type_name(cmd), - name, ( core_test ? "test" : "command" )); + str_sanitize(ext_name, 128), ( core_test ? "test" : "command" )); } else { sieve_argument_validate_error(valdtr, ext_arg, "%s %s: unknown Sieve capability '%s'", sieve_command_identifier(cmd), sieve_command_type_name(cmd), - name); + str_sanitize(ext_name, 128)); } return NULL; } - sieve_ast_extension_link(valdtr->ast, ext); - - extdef = ext->def; - - if ( extdef->validator_load != NULL && - !extdef->validator_load(ext, valdtr) ) { - sieve_argument_validate_error(valdtr, ext_arg, - "%s %s: failed to load Sieve capability '%s'", - sieve_command_identifier(cmd), sieve_command_type_name(cmd), - sieve_extension_name(ext)); + if ( !sieve_validator_extension_load(valdtr, cmd, ext_arg, ext) ) return NULL; - } - - /* Register extension no matter what and store the AST argument registering it - */ - if ( ext->id >= 0 ) { - reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext->id); - reg->arg = ext_arg; - reg->loaded = TRUE; - } return ext; } @@ -604,29 +614,16 @@ const struct sieve_extension *sieve_validator_extension_load const struct sieve_extension *sieve_validator_extension_load_implicit (struct sieve_validator *valdtr, const char *ext_name) { - struct sieve_validator_extension_reg *reg; const struct sieve_extension *ext; - const struct sieve_extension_def *extdef; ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); if ( ext == NULL || ext->def == NULL ) return NULL; - sieve_ast_extension_link(valdtr->ast, ext); - - extdef = ext->def; - - if ( extdef->validator_load != NULL && !extdef->validator_load(ext, valdtr) ) + if ( !sieve_validator_extension_load(valdtr, NULL, NULL, ext) ) return NULL; - /* Register extension no matter what and store the AST argument registering it - */ - if ( ext->id >= 0 ) { - reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext->id); - reg->loaded = TRUE; - } - return ext; } @@ -1261,7 +1258,9 @@ static bool sieve_validate_command } } - /* Skip block if result of test is const FALSE */ + /* Skip block if result of test is const FALSEconst struct sieve_extension *sieve_validator_extension_load +(struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, string_t *ext_name) */ if ( result && *const_r == 0 ) return TRUE; diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h index 90b0308b6a1aa1022ac1534f27408b9489efcd68..e9d6eefdacb6e8533cdbc31a49110c6d6006a7d5 100644 --- a/src/lib-sieve/sieve-validator.h +++ b/src/lib-sieve/sieve-validator.h @@ -133,9 +133,12 @@ struct sieve_validator_extension { void *context); }; -const struct sieve_extension *sieve_validator_extension_load +bool sieve_validator_extension_load (struct sieve_validator *valdtr, struct sieve_command *cmd, - struct sieve_ast_argument *ext_arg, string_t *ext_name); + struct sieve_ast_argument *ext_arg, const struct sieve_extension *ext); +const struct sieve_extension *sieve_validator_extension_load_by_name + (struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, const char *ext_name); const struct sieve_extension *sieve_validator_extension_load_implicit (struct sieve_validator *valdtr, const char *ext_name); diff --git a/tests/extensions/ihave/execute.svtest b/tests/extensions/ihave/execute.svtest new file mode 100644 index 0000000000000000000000000000000000000000..701d817a4ee45dfc281635e5ebef00392436f983 --- /dev/null +++ b/tests/extensions/ihave/execute.svtest @@ -0,0 +1,23 @@ +require "vnd.dovecot.testsuite"; + +/* + * Execution testing (currently just meant to trigger any segfaults) + */ + +test "Basic" { + if not test_script_compile "execute/ihave.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } + + test_binary_save "ihave-basic"; + test_binary_load "ihave-basic"; +} + diff --git a/tests/extensions/ihave/execute/ihave.sieve b/tests/extensions/ihave/execute/ihave.sieve new file mode 100644 index 0000000000000000000000000000000000000000..0fe84c8278b9cb8d997b6f9ac8db9139f88fc754 --- /dev/null +++ b/tests/extensions/ihave/execute/ihave.sieve @@ -0,0 +1,7 @@ +require "ihave"; + +if ihave "nonsense-extension" { + nonsense_command "Frop!"; +} + +redirect "frop@example.com";