diff --git a/Makefile.am b/Makefile.am index 128ea65443dc2af3667a07d5b0bfaa874a285ac3..62ea4386efe88ecc866ca420a998d82c09489801 100644 --- a/Makefile.am +++ b/Makefile.am @@ -162,6 +162,8 @@ test_cases = \ tests/extensions/metadata/execute.svtest \ tests/extensions/metadata/errors.svtest \ tests/extensions/vnd.dovecot/debug/execute.svtest \ + tests/extensions/vnd.dovecot/environment/basic.svtest \ + tests/extensions/vnd.dovecot/environment/variables.svtest \ tests/deprecated/notify/basic.svtest \ tests/deprecated/notify/mailto.svtest \ tests/deprecated/notify/errors.svtest \ diff --git a/configure.ac b/configure.ac index 0d478385679573d56bb429e2a4911b88df4b675d..97a585c596afd71fe213aff57c545362f13870ad 100644 --- a/configure.ac +++ b/configure.ac @@ -211,6 +211,7 @@ src/lib-sieve/plugins/duplicate/Makefile src/lib-sieve/plugins/index/Makefile src/lib-sieve/plugins/vnd.dovecot/Makefile src/lib-sieve/plugins/vnd.dovecot/debug/Makefile +src/lib-sieve/plugins/vnd.dovecot/environment/Makefile src/lib-sieve-tool/Makefile src/lib-managesieve/Makefile src/plugins/Makefile diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index bdc35c80c4978c5a2d275aaf8ae076c07efa68d2..98ae11f178897b31dc4a646659179a3ae7796564 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -78,6 +78,7 @@ plugins = \ $(extdir)/index/libsieve_ext_index.la \ $(extdir)/metadata/libsieve_ext_metadata.la \ $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ + $(extdir)/vnd.dovecot/environment/libsieve_ext_vnd_environment.la \ $(unfinished_plugins) libdovecot_sieve_la_DEPENDENCIES = \ diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c index b8680c1f000bc91ddd7128b9eb5ac68ea3225ef4..9fd49ba6011b069013ff50e701c70b7707bd0e88 100644 --- a/src/lib-sieve/plugins/environment/ext-environment-common.c +++ b/src/lib-sieve/plugins/environment/ext-environment-common.c @@ -6,14 +6,10 @@ #include "sieve-common.h" #include "sieve-extensions.h" +#include "sieve-interpreter.h" #include "ext-environment-common.h" -struct ext_environment_context { - HASH_TABLE(const char *, - const struct sieve_environment_item *) environment_items; -}; - /* * Core environment items */ @@ -30,71 +26,121 @@ static const struct sieve_environment_item *core_env_items[] = { static unsigned int core_env_items_count = N_ELEMENTS(core_env_items); /* - * Registration + * Validator context */ -static void ext_environment_item_register -(struct ext_environment_context *ectx, - const struct sieve_environment_item *item) +struct ext_environment_interpreter_context { + HASH_TABLE(const char *, + const struct sieve_environment_item *) environment_items; + + unsigned int active:1; +}; + +static void ext_environment_interpreter_extension_free + (const struct sieve_extension *ext, struct sieve_interpreter *interp, + void *context); + +struct sieve_interpreter_extension environment_interpreter_extension = { + .ext_def = &environment_extension, + .free = ext_environment_interpreter_extension_free, +}; + +static struct ext_environment_interpreter_context * +ext_environment_interpreter_context_create +(const struct sieve_extension *this_ext, struct sieve_interpreter *interp) { - hash_table_insert(ectx->environment_items, item->name, item); + pool_t pool = sieve_interpreter_pool(interp); + struct ext_environment_interpreter_context *ctx; + + ctx = p_new(pool, struct ext_environment_interpreter_context, 1); + + hash_table_create + (&ctx->environment_items, default_pool, 0, str_hash, strcmp); + + sieve_interpreter_extension_register + (interp, this_ext, &environment_interpreter_extension, (void *)ctx); + return ctx; } -void sieve_ext_environment_item_register -(const struct sieve_extension *ext, const struct sieve_environment_item *item) +static void ext_environment_interpreter_extension_free +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_interpreter *interp ATTR_UNUSED, void *context) { - struct ext_environment_context *ectx = - (struct ext_environment_context *) ext->context; + struct ext_environment_interpreter_context *ctx = + (struct ext_environment_interpreter_context *)context; - ext_environment_item_register(ectx, item); + hash_table_destroy(&ctx->environment_items); } -/* - * Initialization - */ - -bool ext_environment_init -(const struct sieve_extension *ext ATTR_UNUSED, void **context) +static struct ext_environment_interpreter_context * +ext_environment_interpreter_context_get +(const struct sieve_extension *this_ext, struct sieve_interpreter *interp) { - struct ext_environment_context *ectx = - i_new(struct ext_environment_context, 1); + struct ext_environment_interpreter_context *ctx = + (struct ext_environment_interpreter_context *) + sieve_interpreter_extension_get_context(interp, this_ext); + if ( ctx == NULL ) + ctx = ext_environment_interpreter_context_create(this_ext, interp); + + return ctx; +} + +void ext_environment_interpreter_init +(const struct sieve_extension *this_ext, struct sieve_interpreter *interp) +{ + struct ext_environment_interpreter_context *ctx; unsigned int i; - hash_table_create - (&ectx->environment_items, default_pool, 0, str_hash, strcmp); + /* Create our context */ + ctx = ext_environment_interpreter_context_get(this_ext, interp); for ( i = 0; i < core_env_items_count; i++ ) { - ext_environment_item_register(ectx, core_env_items[i]); + const struct sieve_environment_item *item = core_env_items[i]; + hash_table_insert(ctx->environment_items, item->name, item); } - *context = (void *) ectx; - - return TRUE; + ctx->active = TRUE; } -void ext_environment_deinit(const struct sieve_extension *ext) +bool sieve_ext_environment_is_active +(const struct sieve_extension *env_ext, struct sieve_interpreter *interp) { - struct ext_environment_context *ectx = - (struct ext_environment_context *) ext->context; + struct ext_environment_interpreter_context *ctx = + ext_environment_interpreter_context_get(env_ext, interp); - hash_table_destroy(&ectx->environment_items); - i_free(ectx); + return ( ctx != NULL && ctx->active ); } +/* + * Registration + */ + +void sieve_environment_item_register +(const struct sieve_extension *env_ext, struct sieve_interpreter *interp, + const struct sieve_environment_item *item) +{ + struct ext_environment_interpreter_context *ctx; + + i_assert( sieve_extension_is(env_ext, environment_extension) ); + ctx = ext_environment_interpreter_context_get(env_ext, interp); + hash_table_insert(ctx->environment_items, item->name, item); +} /* * Retrieval */ const char *ext_environment_item_get_value -(const struct sieve_extension *ext, const char *name, - const struct sieve_script_env *senv) +(const struct sieve_extension *env_ext, + const struct sieve_runtime_env *renv, const char *name) { - struct ext_environment_context *ectx = - (struct ext_environment_context *) ext->context; + struct ext_environment_interpreter_context *ctx = + ext_environment_interpreter_context_get(env_ext, renv->interp); const struct sieve_environment_item *item = - hash_table_lookup(ectx->environment_items, name); + hash_table_lookup(ctx->environment_items, name); + + i_assert( sieve_extension_is(env_ext, environment_extension) ); if ( item == NULL ) return NULL; @@ -103,7 +149,7 @@ const char *ext_environment_item_get_value return item->value; if ( item->get_value != NULL ) - return item->get_value(ext->svinst, senv); + return item->get_value(renv); return NULL; } @@ -119,10 +165,9 @@ const char *ext_environment_item_get_value */ static const char *envit_domain_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) +(const struct sieve_runtime_env *renv) { - return svinst->domainname; + return renv->svinst->domainname; } const struct sieve_environment_item domain_env_item = { @@ -137,10 +182,9 @@ const struct sieve_environment_item domain_env_item = { */ static const char *envit_host_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) +(const struct sieve_runtime_env *renv) { - return svinst->hostname; + return renv->svinst->hostname; } const struct sieve_environment_item host_env_item = { @@ -160,10 +204,9 @@ const struct sieve_environment_item host_env_item = { */ static const char *envit_location_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) +(const struct sieve_runtime_env *renv) { - switch ( svinst->env_location ) { + switch ( renv->svinst->env_location ) { case SIEVE_ENV_LOCATION_MDA: return "MDA"; case SIEVE_ENV_LOCATION_MTA: @@ -190,10 +233,9 @@ const struct sieve_environment_item location_env_item = { */ static const char *envit_phase_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) +(const struct sieve_runtime_env *renv) { - switch ( svinst->delivery_phase ) { + switch ( renv->svinst->delivery_phase ) { case SIEVE_DELIVERY_PHASE_PRE: return "pre"; case SIEVE_DELIVERY_PHASE_DURING: diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.h b/src/lib-sieve/plugins/environment/ext-environment-common.h index eedb6ee7a0ea9dad479bfa2ada027ac90f10e1d6..9c8fec73a6494d6e4a43f24cccc1baf7074f0f2f 100644 --- a/src/lib-sieve/plugins/environment/ext-environment-common.h +++ b/src/lib-sieve/plugins/environment/ext-environment-common.h @@ -47,11 +47,10 @@ bool ext_environment_init(const struct sieve_extension *ext, void **context); void ext_environment_deinit(const struct sieve_extension *ext); /* - * Environment item retrieval + * Validator context */ -const char *ext_environment_item_get_value - (const struct sieve_extension *ext, const char *name, - const struct sieve_script_env *senv); +void ext_environment_interpreter_init +(const struct sieve_extension *this_ext, struct sieve_interpreter *interp); #endif /* __EXT_VARIABLES_COMMON_H */ diff --git a/src/lib-sieve/plugins/environment/ext-environment.c b/src/lib-sieve/plugins/environment/ext-environment.c index 8abc659032565417cedb29fb5edb68036f6cfd2c..e536864c43dcc212c990f039d97f417c74406fef 100644 --- a/src/lib-sieve/plugins/environment/ext-environment.c +++ b/src/lib-sieve/plugins/environment/ext-environment.c @@ -30,12 +30,14 @@ static bool ext_environment_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_environment_interpreter_load +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_extension_def environment_extension = { .name = "environment", - .load = ext_environment_init, - .unload = ext_environment_deinit, .validator_load = ext_environment_validator_load, + .interpreter_load = ext_environment_interpreter_load, SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation) }; @@ -43,7 +45,15 @@ static bool ext_environment_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr) { sieve_validator_register_command(valdtr, ext, &tst_environment); + return TRUE; +} +static bool ext_environment_interpreter_load +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + ext_environment_interpreter_init(ext, renv->interp); return TRUE; } diff --git a/src/lib-sieve/plugins/environment/sieve-ext-environment.h b/src/lib-sieve/plugins/environment/sieve-ext-environment.h index a0c3259fe5340d97fb09ffd2ab6811cbd30c00fb..f5a52e2460bd211ed6cb75d365c75da2f741efd6 100644 --- a/src/lib-sieve/plugins/environment/sieve-ext-environment.h +++ b/src/lib-sieve/plugins/environment/sieve-ext-environment.h @@ -6,16 +6,43 @@ #include "sieve-common.h" +/* + * Environment extension + */ + +/* FIXME: this is not suitable for future plugin support */ + +extern const struct sieve_extension_def environment_extension; + +static inline const struct sieve_extension * +sieve_ext_environment_get_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_register + (svinst, &environment_extension, FALSE); +} + +bool sieve_ext_environment_is_active + (const struct sieve_extension *env_ext, + struct sieve_interpreter *interp); + +/* + * Environment item + */ + struct sieve_environment_item { const char *name; const char *value; const char *(*get_value) - (struct sieve_instance *svinst, const struct sieve_script_env *senv); + (const struct sieve_runtime_env *renv); }; -void sieve_ext_environment_item_register - (const struct sieve_extension *ext, +void sieve_environment_item_register + (const struct sieve_extension *env_ext, struct sieve_interpreter *interp, const struct sieve_environment_item *item); +const char *ext_environment_item_get_value + (const struct sieve_extension *env_ext, + const struct sieve_runtime_env *renv, const char *name); #endif diff --git a/src/lib-sieve/plugins/environment/tst-environment.c b/src/lib-sieve/plugins/environment/tst-environment.c index bb3fef34f42e7846c0e7e1bf0d78e78fe70e3450..68040291bcc5883301044f6c532c18369810a771 100644 --- a/src/lib-sieve/plugins/environment/tst-environment.c +++ b/src/lib-sieve/plugins/environment/tst-environment.c @@ -188,7 +188,7 @@ static int tst_environment_operation_execute sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "environment test"); env_item = ext_environment_item_get_value - (this_ext, str_c(name), renv->scriptenv); + (this_ext, renv, str_c(name)); if ( env_item != NULL ) { /* Construct value list */ diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c index 2564efefffa136b2d023baad9419c40b195e5221..2309cd90c864da4cd448a63502637eed4e741689 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-common.c +++ b/src/lib-sieve/plugins/variables/ext-variables-common.c @@ -608,8 +608,10 @@ ext_variables_validator_context_create struct ext_variables_validator_context *ext_variables_validator_context_get (const struct sieve_extension *this_ext, struct sieve_validator *valdtr) { - struct ext_variables_validator_context *ctx = - (struct ext_variables_validator_context *) + struct ext_variables_validator_context *ctx; + + i_assert( sieve_extension_is(this_ext, variables_extension) ); + ctx = (struct ext_variables_validator_context *) sieve_validator_extension_get_context(valdtr, this_ext); if ( ctx == NULL ) { @@ -768,8 +770,13 @@ static inline struct ext_variables_interpreter_context * ext_variables_interpreter_context_get (const struct sieve_extension *this_ext, struct sieve_interpreter *interp) { - return (struct ext_variables_interpreter_context *) + struct ext_variables_interpreter_context *ctx; + + i_assert( sieve_extension_is(this_ext, variables_extension) ); + ctx = (struct ext_variables_interpreter_context *) sieve_interpreter_extension_get_context(interp, this_ext); + + return ctx; } struct sieve_variable_storage *sieve_ext_variables_runtime_get_storage diff --git a/src/lib-sieve/plugins/variables/ext-variables-dump.c b/src/lib-sieve/plugins/variables/ext-variables-dump.c index db4aa886adc6a496c856e07e2cf0abec48760bac..b574ea38bc084d92577164b38276f12418cb6487 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-dump.c +++ b/src/lib-sieve/plugins/variables/ext-variables-dump.c @@ -49,10 +49,12 @@ static struct ext_variables_dump_context *ext_variables_dump_get_context (const struct sieve_extension *this_ext, const struct sieve_dumptime_env *denv) { struct sieve_code_dumper *dumper = denv->cdumper; - struct ext_variables_dump_context *dctx = sieve_dump_extension_get_context - (dumper, this_ext); + struct ext_variables_dump_context *dctx; pool_t pool; + i_assert( sieve_extension_is(this_ext, variables_extension) ); + dctx = sieve_dump_extension_get_context(dumper, this_ext); + if ( dctx == NULL ) { /* Create dumper context */ pool = sieve_code_dumper_pool(dumper); diff --git a/src/lib-sieve/plugins/variables/ext-variables-namespaces.c b/src/lib-sieve/plugins/variables/ext-variables-namespaces.c index 8f62a78d99951159336f78e554c919d82a552889..5bd9e9c4b8d1fa4d74341b71951b5c80a633b4a2 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-namespaces.c +++ b/src/lib-sieve/plugins/variables/ext-variables-namespaces.c @@ -182,6 +182,7 @@ void sieve_variables_opr_namespace_variable_emit const struct sieve_extension *ext, const struct sieve_variables_namespace_def *nspc_def) { + i_assert( sieve_extension_is(var_ext, variables_extension) ); sieve_operand_emit(sblock, var_ext, &namespace_variable_operand); sieve_opr_object_emit(sblock, ext, &nspc_def->obj_def); } diff --git a/src/lib-sieve/plugins/variables/ext-variables-operands.c b/src/lib-sieve/plugins/variables/ext-variables-operands.c index 476e7279c8123cf00ae528727d3cdcaee4d1e8c7..a4930fcd0bdc5af7ecab1379afcd902fadaf5bb1 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-operands.c +++ b/src/lib-sieve/plugins/variables/ext-variables-operands.c @@ -52,6 +52,8 @@ void sieve_variables_opr_variable_emit (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, struct sieve_variable *var) { + i_assert( sieve_extension_is(var_ext, variables_extension) ); + if ( var->ext == NULL ) { /* Default variable storage */ (void) sieve_operand_emit(sblock, var_ext, &variable_operand); @@ -222,6 +224,7 @@ void sieve_variables_opr_match_value_emit (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, unsigned int index) { + i_assert( sieve_extension_is(var_ext, variables_extension) ); (void) sieve_operand_emit(sblock, var_ext, &match_value_operand); (void) sieve_binary_emit_unsigned(sblock, index); } diff --git a/src/lib-sieve/plugins/vnd.dovecot/Makefile.am b/src/lib-sieve/plugins/vnd.dovecot/Makefile.am index 3c46867ff1f7b519e0efb9b24c05204041061084..65d394e41964b56fc01ac882b6c1d5fc8f01c4a8 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/Makefile.am +++ b/src/lib-sieve/plugins/vnd.dovecot/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS = debug +SUBDIRS = debug environment diff --git a/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am b/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..5d4e438835c9d834538cd3913ee99c33960a45de --- /dev/null +++ b/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libsieve_ext_vnd_environment.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../environment \ + -I$(srcdir)/../../variables \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_vnd_environment_la_SOURCES = \ + ext-vnd-environment.c \ + ext-vnd-environment-items.c \ + ext-vnd-environment-variables.c + +noinst_HEADERS = + ext-vnd-environment-common.h + diff --git a/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h new file mode 100644 index 0000000000000000000000000000000000000000..97b795e85ebfcb8ea941eeca4284f28e54dee8cb --- /dev/null +++ b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __EXT_VND_ENVIRONMENT_COMMON_H +#define __EXT_VND_ENVIRONMENT_COMMON_H + +#include "sieve-ext-environment.h" + +/* + * Extension + */ + +struct ext_vnd_environment_context { + const struct sieve_extension *env_ext; + const struct sieve_extension *var_ext; +}; + +const struct sieve_extension_def vnd_environment_extension; + +/* + * Operands + */ + +extern const struct sieve_operand_def environment_namespace_operand; + +/* + * Environment items + */ + +void ext_vnd_environment_items_register +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv); + +/* + * Variables + */ + +void ext_environment_variables_init +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr); + +#endif diff --git a/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c new file mode 100644 index 0000000000000000000000000000000000000000..eab13c3ebb691e01126899abfd238d9d3eb8c137 --- /dev/null +++ b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-vnd-environment-common.h" + +/* + * Environment items + */ + +/* default_mailbox */ + +static const char *envit_default_mailbox_get_value +(const struct sieve_runtime_env *renv) +{ + i_assert(renv->scriptenv->default_mailbox != NULL); + return renv->scriptenv->default_mailbox; +} + +const struct sieve_environment_item default_mailbox_env_item = { + .name = "vnd.dovecot.default-mailbox", + .get_value = envit_default_mailbox_get_value +}; + +/* username */ + +static const char *envit_username_get_value +(const struct sieve_runtime_env *renv) +{ + return renv->svinst->username; +} + +const struct sieve_environment_item username_env_item = { + .name = "vnd.dovecot.username", + .get_value = envit_username_get_value +}; + +/* + * Register + */ + +void ext_vnd_environment_items_register +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv) +{ + struct ext_vnd_environment_context *ectx = + (struct ext_vnd_environment_context *) ext->context; + + sieve_environment_item_register + (ectx->env_ext, renv->interp, &default_mailbox_env_item); + sieve_environment_item_register + (ectx->env_ext, renv->interp, &username_env_item); +} diff --git a/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c new file mode 100644 index 0000000000000000000000000000000000000000..4f7082676404a1e817a7eb0bba1d247d5e57600a --- /dev/null +++ b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c @@ -0,0 +1,207 @@ +/* Copyright (c) 2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-vnd-environment-common.h" + +static bool vnspc_vnd_environment_validate + (struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment); +static bool vnspc_vnd_environment_generate + (const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, + struct sieve_command *cmd, void *var_data); +static bool vnspc_vnd_environment_dump_variable + (const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address); +static int vnspc_vnd_environment_read_variable + (const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +static const struct sieve_variables_namespace_def +environment_namespace = { + SIEVE_OBJECT("env", &environment_namespace_operand, 0), + vnspc_vnd_environment_validate, + vnspc_vnd_environment_generate, + vnspc_vnd_environment_dump_variable, + vnspc_vnd_environment_read_variable +}; + +static bool vnspc_vnd_environment_validate +(struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment) +{ + struct sieve_ast *ast = arg->ast; + const struct sieve_variable_name *name_elements; + unsigned int i, count; + const char *variable; + string_t *name; + + /* Compose environment name from parsed variable name */ + name = t_str_new(64); + name_elements = array_get(var_name, &count); + i_assert(count > 1); + for (i = 1; i < count; i++) { + if ( name_elements[i].num_variable >= 0 ) { + sieve_argument_validate_error(valdtr, arg, + "vnd.dovecot.environment: invalid variable name within " + "env namespace `env.%d': " + "encountered numeric variable name", + name_elements[i].num_variable); + return FALSE; + } + if (str_len(name) > 0) + str_append_c(name, '.'); + str_append_str(name, name_elements[i].identifier); + } + + variable = str_c(name); + + if ( assignment ) { + sieve_argument_validate_error(valdtr, arg, + "vnd.dovecot.environment: cannot assign to environment " + "variable `env.%s'", variable); + return FALSE; + } + + *var_data = (void *) p_strdup(sieve_ast_pool(ast), variable); + return TRUE; +} + +static bool vnspc_vnd_environment_generate +(const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg ATTR_UNUSED, + struct sieve_command *cmd ATTR_UNUSED, void *var_data) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + const char *variable = (const char *) var_data; + struct ext_vnd_environment_context *ext_data; + + if ( this_ext == NULL ) + return FALSE; + + ext_data = (struct ext_vnd_environment_context *) this_ext->context; + + sieve_variables_opr_namespace_variable_emit + (cgenv->sblock, ext_data->var_ext, this_ext, &environment_namespace); + sieve_binary_emit_cstring(cgenv->sblock, variable); + + return TRUE; +} + +static bool vnspc_vnd_environment_dump_variable +(const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + const struct sieve_operand *oprnd, sieve_size_t *address) +{ + string_t *var_name; + + if ( !sieve_binary_read_string(denv->sblock, address, &var_name) ) + return FALSE; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: VAR ${env.%s}", + oprnd->field_name, str_c(var_name)); + else + sieve_code_dumpf(denv, "VAR ${env.%s}", + str_c(var_name)); + + return TRUE; +} + +static int vnspc_vnd_environment_read_variable +(const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address, + string_t **str_r) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + struct ext_vnd_environment_context *ectx = + (struct ext_vnd_environment_context *) this_ext->context; + string_t *var_name; + const char *ext_value; + + if ( !sieve_binary_read_string(renv->sblock, address, &var_name) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "environment variable operand corrupt: invalid name"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( str_r != NULL ) { + const char *vname = str_c(var_name); + + ext_value = ext_environment_item_get_value + (ectx->env_ext, renv, vname); + + if ( ext_value == NULL && strchr(vname, '_') != NULL) { + char *p, *aname; + + /* Try again with '_' replaced with '-' */ + aname = t_strdup_noconst(vname); + for (p = aname; *p != '\0'; p++) { + if (*p == '_') + *p = '-'; + } + ext_value = ext_environment_item_get_value + (ectx->env_ext, renv, aname); + } + + if ( ext_value == NULL ) { + *str_r = t_str_new_const("", 0); + return SIEVE_EXEC_OK; + } + + *str_r = t_str_new_const(ext_value, strlen(ext_value)); + } + return SIEVE_EXEC_OK; +} + +/* + * Namespace registration + */ + +static const struct sieve_extension_objects environment_namespaces = + SIEVE_VARIABLES_DEFINE_NAMESPACE(environment_namespace); + +const struct sieve_operand_def environment_namespace_operand = { + "env-namespace", + &vnd_environment_extension, + 0, + &sieve_variables_namespace_operand_class, + &environment_namespaces +}; + +void ext_environment_variables_init +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr) +{ + struct ext_vnd_environment_context *ext_data = + (struct ext_vnd_environment_context *) this_ext->context; + + sieve_variables_namespace_register + (ext_data->var_ext, valdtr, this_ext, &environment_namespace); +} diff --git a/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c new file mode 100644 index 0000000000000000000000000000000000000000..849d7d7b6e8353c006d373fc6b27ec70ec49d3a7 --- /dev/null +++ b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.environment + * --------------------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined + * Implementation: preliminary + * Status: experimental + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-vnd-environment-common.h" + +/* + * Extension + */ + +static bool ext_vnd_environment_load + (const struct sieve_extension *ext, void **context); +static void ext_vnd_environment_unload + (const struct sieve_extension *ext); +static bool ext_vnd_environment_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_vnd_environment_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_extension_def vnd_environment_extension = { + .name = "vnd.dovecot.environment", + .load = ext_vnd_environment_load, + .unload = ext_vnd_environment_unload, + .validator_load = ext_vnd_environment_validator_load, + .interpreter_load = ext_vnd_environment_interpreter_load, + SIEVE_EXT_DEFINE_OPERAND(environment_namespace_operand) +}; + +static bool ext_vnd_environment_load +(const struct sieve_extension *ext, void **context) +{ + struct ext_vnd_environment_context *ectx; + + if ( *context != NULL ) + ext_vnd_environment_unload(ext); + + ectx = i_new(struct ext_vnd_environment_context, 1); + ectx->env_ext = sieve_ext_environment_get_extension(ext->svinst); + ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst); + *context = (void *) ectx; + + return TRUE; +} + +static void ext_vnd_environment_unload +(const struct sieve_extension *ext) +{ + struct ext_vnd_environment_context *ectx = + (struct ext_vnd_environment_context *) ext->context; + + i_free(ectx); +} + +/* + * Validator + */ + +static bool ext_vnd_environment_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + ext_environment_variables_init(ext, valdtr); + return TRUE; +} + +/* + * Interpreter + */ + +static bool ext_vnd_environment_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + ext_vnd_environment_items_register(ext, renv); + return TRUE; +} diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c index 9af09c0c5fdf381fc45e8b452ac1733c0825bf5d..8c8303ab15aab41b09967e4e782b0199d53076c5 100644 --- a/src/lib-sieve/sieve-code.c +++ b/src/lib-sieve/sieve-code.c @@ -800,12 +800,17 @@ int sieve_opr_stringlist_read_data return SIEVE_EXEC_FAILURE; } - if ( (ret=intf->read(renv, oprnd, address, NULL)) <= 0 ) - return ret; + if ( strlist_r == NULL ) { + if ( (ret=intf->read(renv, oprnd, address, NULL)) <= 0 ) + return ret; + } else { + string_t *stritem; + if ( (ret=intf->read(renv, oprnd, address, &stritem)) <= 0 ) + return ret; - if ( strlist_r != NULL ) - *strlist_r = sieve_code_stringlist_create - (renv, oprnd->address, 1, *address); + *strlist_r = sieve_single_stringlist_create + (renv, stritem, FALSE); + } return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index 2f3cc8df9906954ff18c097b747ccf4ef91506c2..c910a4db940a51deaf80023fcb5583da16b7511d 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -139,6 +139,7 @@ extern const struct sieve_extension_def virustest_extension; extern const struct sieve_extension_def editheader_extension; extern const struct sieve_extension_def vnd_debug_extension; +extern const struct sieve_extension_def vnd_environment_extension; const struct sieve_extension_def *sieve_extra_extensions[] = { &vacation_seconds_extension, &spamtest_extension, &spamtestplus_extension, @@ -146,7 +147,7 @@ const struct sieve_extension_def *sieve_extra_extensions[] = { &mboxmetadata_extension, &servermetadata_extension, /* vnd.dovecot. */ - &vnd_debug_extension + &vnd_debug_extension, &vnd_environment_extension }; const unsigned int sieve_extra_extensions_count = diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c index 8e0f4e063af990d42c2f3bd8597b41319defffab..9790966820a1f590d1c977cceaa771b0a4e517d1 100644 --- a/src/sieve-tools/sieve-filter.c +++ b/src/sieve-tools/sieve-filter.c @@ -58,6 +58,7 @@ struct sieve_filter_data { unsigned int execute:1; unsigned int source_write:1; + unsigned int default_move:1; }; struct sieve_filter_context { @@ -227,6 +228,17 @@ static int filter_message return -1; case SIEVE_EXEC_FAILURE: case SIEVE_EXEC_TEMP_FAILURE: + if ( source_write && execute && sfctx->data->default_move && + !estatus.keep_original && estatus.message_saved ) { + /* The implicit keep action moved message to default mailbox, so + the source message still needs to be expunged */ + sieve_error(ehandler, NULL, + "sieve script execution failed for this message; " + "message moved to default mailbox"); + mail_expunge(mail); + return 0; + } + /* Fall through */ case SIEVE_EXEC_KEEP_FAILED: sieve_error(ehandler, NULL, "sieve script execution failed for this message; " @@ -360,7 +372,7 @@ int main(int argc, char **argv) struct sieve_binary *main_sbin; struct sieve_script_env scriptenv; struct sieve_error_handler *ehandler; - bool force_compile, execute, source_write, verbose; + bool force_compile, execute, source_write, verbose, default_move; struct mail_namespace *ns; struct mailbox *src_box = NULL, *move_box = NULL; enum mailbox_flags open_flags = MAILBOX_FLAG_IGNORE_ACLS; @@ -374,7 +386,8 @@ int main(int argc, char **argv) /* Parse arguments */ dst_mailbox = move_mailbox = NULL; - force_compile = execute = source_write = verbose = FALSE; + force_compile = execute = source_write = default_move = FALSE; + verbose = FALSE; while ((c = sieve_tool_getopt(sieve_tool)) > 0) { switch (c) { case 'm': @@ -475,6 +488,9 @@ int main(int argc, char **argv) if ( dst_mailbox == NULL ) { dst_mailbox = src_mailbox; + } else { + /* The (implicit) keep action will move the message */ + default_move = TRUE; } /* Finish tool initialization */ @@ -552,6 +568,7 @@ int main(int argc, char **argv) sfdata.ehandler = ehandler; sfdata.execute = execute; sfdata.source_write = source_write; + sfdata.default_move = default_move; /* Apply Sieve filter to all messages found */ (void) filter_mailbox(&sfdata, src_box); diff --git a/tests/extensions/vnd.dovecot/environment/basic.svtest b/tests/extensions/vnd.dovecot/environment/basic.svtest new file mode 100644 index 0000000000000000000000000000000000000000..a966d8954134ded4ee5f8204256b30f9d46b18e3 --- /dev/null +++ b/tests/extensions/vnd.dovecot/environment/basic.svtest @@ -0,0 +1,19 @@ +require "vnd.dovecot.testsuite"; +require "environment"; +require "vnd.dovecot.environment"; +require "variables"; + +test "default-mailbox" { + if not environment :is "vnd.dovecot.default-mailbox" "INBOX" { + if environment :matches "vnd.dovecot.default-mailbox" "*" { set "env" "${1}"; } + + test_fail "vnd.dovecot.default-mailbox environment returned invalid value(1): `${env}'"; + } +} + +test "username" { + if not environment :contains "vnd.dovecot.username" "" { + test_fail "vnd.dovecot.username environment does not exist"; + } +} + diff --git a/tests/extensions/vnd.dovecot/environment/variables.svtest b/tests/extensions/vnd.dovecot/environment/variables.svtest new file mode 100644 index 0000000000000000000000000000000000000000..7339bba576ba999b617d8c17e4837cdf69067512 --- /dev/null +++ b/tests/extensions/vnd.dovecot/environment/variables.svtest @@ -0,0 +1,19 @@ +require "vnd.dovecot.testsuite"; +require "environment"; +require "vnd.dovecot.environment"; +require "variables"; +require "relational"; + +test "default_mailbox" { + if not string "${env.vnd.dovecot.default_mailbox}" "INBOX" { + test_fail "The env.vnd.dovecot.default_mailbox variable returned invalid value: `${env.vnd.dovecot.default_mailbox}'"; + } +} + +test "username" { + set :length "userlen" "${env.vnd.dovecot.username}"; + if not string :value "ge" "${userlen}" "1" { + test_fail "The env.vnd.dovecot.username variable is empty or does not exist"; + } +} +