From 475bf84eace6b4337238fc12bf2eb615f75bfd19 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan.bosch@open-xchange.com> Date: Wed, 6 Mar 2024 22:21:21 +0100 Subject: [PATCH] lib-sieve: sieve-storage - Migrate settings to new config structure Since the script storage code is widely engrained in the Pigeonhole sources, this is a very large commit that cannot be reduced further into smaller preparatory bits. --- src/lib-sieve-tool/sieve-tool.c | 87 +- src/lib-sieve/Makefile.am | 2 + src/lib-sieve/plugins/include/cmd-include.c | 1 + .../plugins/include/ext-include-binary.c | 7 +- .../plugins/include/ext-include-common.c | 58 +- .../plugins/include/ext-include-common.h | 6 +- src/lib-sieve/sieve-binary.h | 2 +- src/lib-sieve/sieve-script-private.h | 3 +- src/lib-sieve/sieve-script.c | 167 +++- src/lib-sieve/sieve-script.h | 33 +- src/lib-sieve/sieve-storage-private.h | 40 +- src/lib-sieve/sieve-storage-settings.c | 148 ++++ src/lib-sieve/sieve-storage-settings.h | 33 + src/lib-sieve/sieve-storage.c | 821 ++++++++++-------- src/lib-sieve/sieve-storage.h | 38 +- src/lib-sieve/sieve-types.h | 9 + src/lib-sieve/sieve-validator.c | 5 + src/lib-sieve/sieve-validator.h | 1 + src/lib-sieve/sieve.c | 20 +- src/lib-sieve/sieve.h | 10 +- .../storage/data/sieve-data-script.c | 11 +- .../storage/data/sieve-data-storage.c | 3 +- .../storage/data/sieve-data-storage.h | 3 +- .../storage/dict/sieve-dict-script.c | 10 +- .../storage/dict/sieve-dict-storage.c | 51 +- src/lib-sieve/storage/file/Makefile.am | 1 + .../storage/file/sieve-file-script.c | 86 +- .../storage/file/sieve-file-storage-active.c | 5 +- .../file/sieve-file-storage-settings.c | 40 + .../file/sieve-file-storage-settings.h | 9 + .../storage/file/sieve-file-storage.c | 108 ++- .../storage/file/sieve-file-storage.h | 3 + src/lib-sieve/storage/ldap/sieve-ldap-db.c | 57 +- .../storage/ldap/sieve-ldap-script.c | 28 +- .../ldap/sieve-ldap-storage-settings.c | 169 ++-- .../ldap/sieve-ldap-storage-settings.h | 19 +- .../storage/ldap/sieve-ldap-storage.c | 61 +- .../storage/ldap/sieve-ldap-storage.h | 12 +- src/managesieve/managesieve-client.c | 1 + src/plugins/doveadm-sieve/doveadm-sieve-cmd.c | 1 + .../doveadm-sieve/doveadm-sieve-sync.c | 3 +- .../imap-filter-sieve/imap-filter-sieve.c | 22 +- src/plugins/imapsieve/imap-sieve-settings.c | 8 + src/plugins/imapsieve/imap-sieve-settings.h | 3 + src/plugins/imapsieve/imap-sieve-storage.c | 359 +------- src/plugins/imapsieve/imap-sieve.c | 135 ++- src/plugins/imapsieve/imap-sieve.h | 16 +- src/plugins/lda-sieve/lda-sieve-plugin.c | 112 +-- src/plugins/settings/Makefile.am | 10 +- src/plugins/settings/settings-get.pl | 4 + src/testsuite/testsuite-script.c | 34 +- src/testsuite/testsuite.c | 75 +- tests/extensions/include/execute.svtest | 25 +- 53 files changed, 1669 insertions(+), 1306 deletions(-) create mode 100644 src/lib-sieve/sieve-storage-settings.c create mode 100644 src/lib-sieve/sieve-storage-settings.h create mode 100644 src/lib-sieve/storage/file/sieve-file-storage-settings.c diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c index c21fd8eef..6937c9f3f 100644 --- a/src/lib-sieve-tool/sieve-tool.c +++ b/src/lib-sieve-tool/sieve-tool.c @@ -7,6 +7,7 @@ #include "ioloop.h" #include "ostream.h" #include "hostpid.h" +#include "settings.h" #include "dict.h" #include "mail-namespace.h" #include "mail-storage.h" @@ -20,6 +21,7 @@ #include "sieve.h" #include "sieve-plugins.h" #include "sieve-extensions.h" +#include "sieve-storage.h" #include "mail-raw.h" @@ -285,6 +287,8 @@ sieve_tool_init_finish(struct sieve_tool *tool, bool init_mailstore, svenv.hostname = my_hostdomain(); svenv.base_dir = tool->mail_user_dovecot->set->base_dir; svenv.temp_dir = tool->mail_user_dovecot->set->mail_temp_dir; + svenv.event_parent = tool->mail_user_dovecot->event; + svenv.flags = SIEVE_FLAG_COMMAND_LINE; svenv.location = SIEVE_ENV_LOCATION_MS; svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; @@ -578,19 +582,77 @@ struct ostream *sieve_tool_open_output_stream(const char *filename) * Sieve script handling */ +static void +sieve_tool_script_parse_location(struct sieve_tool *tool, const char *location, + const char **storage_name_r) +{ + struct sieve_instance *svinst = tool->svinst; + const char *data = strchr(location, ':'); + const char *script_driver = "file"; + const char *script_path = NULL; + const char *storage_name = "_file"; + + if (data != NULL) { + script_driver = t_strdup_until(location, data++); + if (strcmp(script_driver, "file") == 0) + script_path = data; + else + storage_name = data; + } else { + script_path = location; + } + + struct settings_instance *set_instance = + settings_instance_find(svinst->event); + const char *prefix = t_strdup_printf("sieve_script/%s", storage_name); + + settings_override(set_instance, "sieve_script", storage_name, + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("%s/sieve_script_storage", prefix), + storage_name, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("%s/sieve_script_type", prefix), + "command-line", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("%s/sieve_script_driver", prefix), + script_driver, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + if (script_path != NULL) { + settings_override( + set_instance, t_strdup_printf("%s/sieve_script_path", + prefix), + script_path, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + } + + *storage_name_r = storage_name; +} + struct sieve_binary * sieve_tool_script_compile(struct sieve_tool *tool, const char *location) { struct sieve_instance *svinst = tool->svinst; struct sieve_error_handler *ehandler; - struct sieve_binary *sbin; + enum sieve_error error_code; + struct sieve_binary *sbin = NULL; ehandler = sieve_stderr_ehandler_create(svinst, 0); sieve_error_handler_accept_infolog(ehandler, TRUE); sieve_error_handler_accept_debuglog(ehandler, svinst->debug); - if (sieve_compile(svinst, location, NULL, ehandler, 0, &sbin, NULL) < 0) - i_fatal("failed to compile sieve script '%s'", location); + if (sieve_storage_name_is_valid(location) && + sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, location, NULL, + ehandler, 0, &sbin, &error_code) < 0 && + error_code != SIEVE_ERROR_NOT_FOUND) + i_fatal("failed to compile sieve script storage"); + + if (sbin == NULL) { + const char *storage_name; + + sieve_tool_script_parse_location(tool, location, &storage_name); + if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, storage_name, + NULL, ehandler, 0, &sbin, NULL) < 0) + i_fatal("failed to compile sieve script"); + } i_assert(sbin != NULL); sieve_error_handler_unref(&ehandler); @@ -602,14 +664,27 @@ sieve_tool_script_open(struct sieve_tool *tool, const char *location) { struct sieve_instance *svinst = tool->svinst; struct sieve_error_handler *ehandler; - struct sieve_binary *sbin; + enum sieve_error error_code; + struct sieve_binary *sbin = NULL; ehandler = sieve_stderr_ehandler_create(svinst, 0); sieve_error_handler_accept_infolog(ehandler, TRUE); sieve_error_handler_accept_debuglog(ehandler, svinst->debug); - if (sieve_open(svinst, location, NULL, ehandler, 0, &sbin, NULL) < 0) - i_fatal("failed to compile sieve script"); + if (sieve_storage_name_is_valid(location) && + sieve_open(svinst, SIEVE_SCRIPT_CAUSE_ANY, location, NULL, + ehandler, 0, &sbin, &error_code) < 0 && + error_code != SIEVE_ERROR_NOT_FOUND) + i_fatal("failed to open sieve script storage"); + + if (sbin == NULL) { + const char *storage_name; + + sieve_tool_script_parse_location(tool, location, &storage_name); + if (sieve_open(svinst, SIEVE_SCRIPT_CAUSE_ANY, storage_name, + NULL, ehandler, 0, &sbin, NULL) < 0) + i_fatal("failed to open sieve script"); + } i_assert(sbin != NULL); sieve_error_handler_unref(&ehandler); diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 22420fae9..d697ee9d6 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -104,6 +104,7 @@ libdovecot_sieve_la_SOURCES = \ sieve-lexer.c \ sieve-script.c \ sieve-storage.c \ + sieve-storage-settings.c \ sieve-storage-sync.c \ sieve-ast.c \ sieve-binary.c \ @@ -154,6 +155,7 @@ headers = \ sieve-script-private.h \ sieve-storage.h \ sieve-storage-private.h \ + sieve-storage-settings.h \ sieve-ast.h \ sieve-binary.h \ sieve-binary-private.h \ diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c index f83e4d925..8c2d1e70d 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -258,6 +258,7 @@ cmd_include_validate(struct sieve_validator *valdtr, /* Open script */ if (ext_include_open_script(this_ext, ctx_data->location, + sieve_validator_script_cause(valdtr), script_name, &script, &error_code) < 0) { if (error_code != SIEVE_ERROR_NOT_FOUND) { sieve_argument_validate_error( diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index 0988424f2..57017bf50 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -290,6 +290,10 @@ ext_include_binary_open(const struct sieve_extension *ext, struct ext_include_context *extctx = ext->context; struct ext_include_binary_context *binctx = (struct ext_include_binary_context *)context; + struct sieve_script *bin_script = sieve_binary_script(sbin); + const char *cause = (bin_script == NULL ? + SIEVE_SCRIPT_CAUSE_ANY : + sieve_script_cause(bin_script)); struct sieve_binary_block *sblock; unsigned int depcount, i, block_id; sieve_size_t offset; @@ -367,7 +371,8 @@ ext_include_binary_open(const struct sieve_extension *ext, } /* Can we open the script dependency ? */ - if (ext_include_open_script(ext, location, str_c(script_name), + if (ext_include_open_script(ext, location, cause, + str_c(script_name), &script, &error_code) < 0) { if (error_code != SIEVE_ERROR_NOT_FOUND) { /* No, recompile */ diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index 6f9c2fdde..b2aee1de8 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -78,7 +78,6 @@ int ext_include_load(const struct sieve_extension *ext, void **context_r) struct sieve_instance *svinst = ext->svinst; const struct sieve_extension *var_ext; struct ext_include_context *extctx; - const char *location; unsigned long long int uint_setting; /* Extension dependencies */ @@ -88,17 +87,6 @@ int ext_include_load(const struct sieve_extension *ext, void **context_r) extctx = i_new(struct ext_include_context, 1); extctx->var_ext = var_ext; - /* Get location for :global scripts */ - location = sieve_setting_get(svinst, "sieve_global"); - - if (location == NULL) { - e_debug(svinst->event, "include: " - "sieve_global is not set; " - "it is currently not possible to include ':global' scripts."); - } - - extctx->global_location = i_strdup(location); - /* Get limits */ extctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH; extctx->max_includes = EXT_INCLUDE_DEFAULT_MAX_INCLUDES; @@ -119,8 +107,6 @@ void ext_include_unload(const struct sieve_extension *ext) struct ext_include_context *extctx = ext->context; sieve_storage_unref(&extctx->personal_storage); - - i_free(extctx->global_location); i_free(extctx); } @@ -131,12 +117,12 @@ void ext_include_unload(const struct sieve_extension *ext) static int ext_include_open_script_personal(struct sieve_instance *svinst, struct ext_include_context *extctx, - const char *script_name, + const char *cause, const char *script_name, struct sieve_script **script_r, enum sieve_error *error_code_r) { if (extctx->personal_storage == NULL && - sieve_storage_create_personal(svinst, NULL, 0, + sieve_storage_create_personal(svinst, NULL, cause, 0, &extctx->personal_storage, error_code_r) < 0) return -1; @@ -147,29 +133,18 @@ ext_include_open_script_personal(struct sieve_instance *svinst, static int ext_include_open_script_global(struct sieve_instance *svinst, - struct ext_include_context *extctx, - const char *script_name, + const char *cause, const char *script_name, struct sieve_script **script_r, enum sieve_error *error_code_r) { - if (extctx->global_location == NULL) { - e_info(svinst->event, "include: " - "sieve_global is unconfigured; " - "include of ':global' script '%s' is therefore not possible", - str_sanitize(script_name, 80)); - if (error_code_r != NULL) - *error_code_r = SIEVE_ERROR_NOT_FOUND; - return -1; - } - - return sieve_script_create_open(svinst, extctx->global_location, - script_name, + return sieve_script_create_open(svinst, cause, + SIEVE_STORAGE_TYPE_GLOBAL, script_name, script_r, error_code_r, NULL); } int ext_include_open_script(const struct sieve_extension *ext, enum ext_include_script_location location, - const char *script_name, + const char *cause, const char *script_name, struct sieve_script **script_r, enum sieve_error *error_code_r) { @@ -180,13 +155,12 @@ int ext_include_open_script(const struct sieve_extension *ext, *script_r = NULL; switch (location) { case EXT_INCLUDE_LOCATION_PERSONAL: - ret = ext_include_open_script_personal(svinst, extctx, + ret = ext_include_open_script_personal(svinst, extctx, cause, script_name, script_r, error_code_r); break; case EXT_INCLUDE_LOCATION_GLOBAL: - ret = ext_include_open_script_global(svinst, extctx, - script_name, + ret = ext_include_open_script_global(svinst, cause, script_name, script_r, error_code_r); break; default: @@ -346,8 +320,22 @@ void ext_include_register_generator_context( /* Initialize generator context if necessary */ if (ctx == NULL) { i_assert(cgenv->script != NULL); + + enum ext_include_script_location location; + const char *storage_type = + sieve_script_storage_type(cgenv->script); + + if (strcasecmp(storage_type, + SIEVE_STORAGE_TYPE_PERSONAL) == 0) + location = EXT_INCLUDE_LOCATION_PERSONAL; + else if (strcasecmp(storage_type, + SIEVE_STORAGE_TYPE_GLOBAL) == 0) + location = EXT_INCLUDE_LOCATION_GLOBAL; + else + location = EXT_INCLUDE_LOCATION_INVALID; + ctx = ext_include_create_generator_context( - cgenv->gentr, NULL, EXT_INCLUDE_LOCATION_PERSONAL, + cgenv->gentr, NULL, location, sieve_script_name(cgenv->script), cgenv->script); sieve_generator_extension_set_context( diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index aacd1e097..6f82a4f1a 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -6,6 +6,7 @@ #include "sieve-common.h" #include "sieve-extensions.h" +#include "sieve-storage.h" /* * Forward declarations @@ -84,7 +85,7 @@ extern const struct sieve_operation_def global_operation; int ext_include_open_script(const struct sieve_extension *ext, enum ext_include_script_location location, - const char *script_name, + const char *cause, const char *script_name, struct sieve_script **script_r, enum sieve_error *error_code_r); @@ -98,9 +99,6 @@ struct ext_include_context { /* Extension dependencies */ const struct sieve_extension *var_ext; - /* Configuration */ - char *global_location; - struct sieve_storage *personal_storage; unsigned int max_nesting_depth; diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h index e9397b58e..227a1c464 100644 --- a/src/lib-sieve/sieve-binary.h +++ b/src/lib-sieve/sieve-binary.h @@ -9,7 +9,7 @@ * Config */ -#define SIEVE_BINARY_VERSION_MAJOR 2 +#define SIEVE_BINARY_VERSION_MAJOR 3 #define SIEVE_BINARY_VERSION_MINOR 0 #define SIEVE_BINARY_BASE_HEADER_SIZE 20 diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h index 1e8455cc8..6a0c7bdb3 100644 --- a/src/lib-sieve/sieve-script-private.h +++ b/src/lib-sieve/sieve-script-private.h @@ -56,7 +56,6 @@ struct sieve_script { struct sieve_script_vfuncs v; const char *name; - const char *location; /* Stream */ struct istream *stream; @@ -67,7 +66,7 @@ struct sieve_script { void sieve_script_init(struct sieve_script *script, struct sieve_storage *storage, const struct sieve_script *script_class, - const char *location, const char *name); + const char *name); /* * Binary diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index be225b736..5caeb9153 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -102,18 +102,16 @@ static void sieve_script_update_event(struct sieve_script *script) void sieve_script_init(struct sieve_script *script, struct sieve_storage *storage, const struct sieve_script *script_class, - const char *location, const char *name) + const char *name) { i_assert(storage != NULL); script->script_class = script_class; script->refcount = 1; script->storage = storage; - script->location = p_strdup_empty(script->pool, location); script->name = p_strdup_empty(script->pool, name); script->event = event_create(storage->event); - event_add_str(script->event, "script_location", location); sieve_script_update_event(script); sieve_storage_ref(storage); @@ -121,7 +119,8 @@ void sieve_script_init(struct sieve_script *script, static int sieve_script_create_common(struct sieve_instance *svinst, - const char *location, const char *name, bool open, + const char *cause, const char *type, + const char *name, bool open, struct sieve_script **script_r, enum sieve_error *error_code_r, const char **error_r) @@ -131,7 +130,7 @@ sieve_script_create_common(struct sieve_instance *svinst, *script_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - if (sieve_storage_sequence_create(svinst, svinst->event, location, + if (sieve_storage_sequence_create(svinst, svinst->event, cause, type, &sseq, error_code_r, error_r) < 0) return -1; @@ -193,14 +192,35 @@ sieve_script_create_common(struct sieve_instance *svinst, } int sieve_script_create(struct sieve_instance *svinst, - const char *location, const char *name, + const char *cause, const char *type, const char *name, struct sieve_script **script_r, enum sieve_error *error_code_r, const char **error_r) { - return sieve_script_create_common(svinst, location, name, FALSE, + return sieve_script_create_common(svinst, cause, type, name, FALSE, script_r, error_code_r, error_r); } +int sieve_script_create_in(struct sieve_instance *svinst, const char *cause, + const char *storage_name, const char *name, + struct sieve_script **script_r, + enum sieve_error *error_code_r, + const char **error_r) +{ + struct sieve_storage *storage; + int ret; + + *script_r = NULL; + + if (sieve_storage_create(svinst, svinst->event, cause, storage_name, 0, + &storage, error_code_r, error_r) < 0) + return -1; + ret = sieve_storage_get_script_direct(storage, name, script_r, NULL); + if (ret < 0) + *error_r = sieve_storage_get_last_error(storage, error_code_r); + sieve_storage_unref(&storage); + return ret; +} + void sieve_script_ref(struct sieve_script *script) { script->refcount++; @@ -255,12 +275,11 @@ int sieve_script_open(struct sieve_script *script, return -1; } - i_assert(script->location != NULL); i_assert(script->name != NULL); script->open = TRUE; sieve_script_update_event(script); - e_debug(script->event, "Opened from '%s'", script->location); + e_debug(script->event, "Opened from '%s'", storage->name); return 0; } @@ -278,17 +297,43 @@ int sieve_script_open_as(struct sieve_script *script, const char *name, } int sieve_script_create_open(struct sieve_instance *svinst, - const char *location, const char *name, - struct sieve_script **script_r, + const char *cause, const char *type, + const char *name, struct sieve_script **script_r, enum sieve_error *error_code_r, const char **error_r) { - return sieve_script_create_common(svinst, location, name, TRUE, + return sieve_script_create_common(svinst, cause, type, name, TRUE, script_r, error_code_r, error_r); } +int sieve_script_create_open_in(struct sieve_instance *svinst, + const char *cause, + const char *storage_name, const char *name, + struct sieve_script **script_r, + enum sieve_error *error_code_r, + const char **error_r) +{ + struct sieve_script *script; + + *script_r = NULL; + sieve_error_args_init(&error_code_r, &error_r); + + if (sieve_script_create_in(svinst, cause, storage_name, name, + &script, error_code_r, error_r) < 0) + return -1; + + if (sieve_script_open(script, NULL) < 0) { + *error_r = sieve_script_get_last_error(script, error_code_r); + sieve_script_unref(&script); + return -1; + } + + *script_r = script; + return 0; +} + int sieve_script_check(struct sieve_instance *svinst, - const char *location, const char *name, + const char *cause, const char *type, const char *name, enum sieve_error *error_code_r, const char **error_r) { struct sieve_script *script; @@ -296,7 +341,7 @@ int sieve_script_check(struct sieve_instance *svinst, sieve_error_args_init(&error_code_r, &error_r); - if (sieve_script_create_open(svinst, location, name, + if (sieve_script_create_open(svinst, cause, type, name, &script, &error_code, error_r) < 0) return (*error_code_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); @@ -313,14 +358,21 @@ const char *sieve_script_name(const struct sieve_script *script) return script->name; } -const char *sieve_script_location(const struct sieve_script *script) +const char *sieve_script_label(const struct sieve_script *script) { - return script->location; + if (*script->name == '\0') + return script->storage->name; + return t_strconcat(script->storage->name, "/", script->name, NULL); } -const char *sieve_script_label(const struct sieve_script *script) +const char *sieve_script_storage_type(const struct sieve_script *script) { - return script->location; + return script->storage->type; +} + +const char *sieve_script_cause(const struct sieve_script *script) +{ + return script->storage->cause; } struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) @@ -462,7 +514,7 @@ int sieve_script_binary_read_metadata(struct sieve_script *script, sieve_size_t *offset) { struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); - string_t *storage_class, *location; + string_t *storage_class, *storage_name, *name; unsigned int version; if ((sieve_binary_block_get_size(sblock) - *offset) == 0) @@ -504,21 +556,38 @@ int sieve_script_binary_read_metadata(struct sieve_script *script, return 0; } - /* location */ - if (!sieve_binary_read_string(sblock, offset, &location)) { + /* storage */ + if (!sieve_binary_read_string(sblock, offset, &storage_name)) { e_error(script->event, "Binary '%s' has invalid metadata for script '%s': " - "Invalid location", + "Invalid storage name", sieve_binary_path(sbin), sieve_script_label(script)); return -1; } - i_assert(script->location != NULL); - if (strcmp(str_c(location), script->location) != 0) { + if (str_len(storage_name) > 0 && + strcmp(str_c(storage_name), script->storage->name) != 0) { e_debug(script->event, - "Binary '%s' reports different location " + "Binary '%s' reports different storage " "for script '%s' (binary points to '%s')", - sieve_binary_path(sbin), script->location, - str_c(location)); + sieve_binary_path(sbin), sieve_script_label(script), + str_c(storage_name)); + return 0; + } + + /* name */ + if (!sieve_binary_read_string(sblock, offset, &name)) { + e_error(script->event, + "Binary '%s' has invalid metadata for script '%s': " + "Invalid script name", + sieve_binary_path(sbin), sieve_script_label(script)); + return -1; + } + if (str_len(name) > 0 && strcmp(str_c(name), script->name) != 0) { + e_debug(script->event, + "Binary '%s' reports different script name " + "for script '%s' (binary points to '%s/%s')", + sieve_binary_path(sbin), sieve_script_label(script), + str_c(storage_name), str_c(name)); return 0; } @@ -531,10 +600,19 @@ int sieve_script_binary_read_metadata(struct sieve_script *script, void sieve_script_binary_write_metadata(struct sieve_script *script, struct sieve_binary_block *sblock) { + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + struct sieve_instance *svinst = sieve_binary_svinst(sbin); + sieve_binary_emit_cstring(sblock, script->driver_name); sieve_binary_emit_unsigned(sblock, script->storage->version); - sieve_binary_emit_cstring(sblock, (script->location == NULL ? - "" : script->location)); + + if (HAS_ALL_BITS(svinst->flags, SIEVE_FLAG_COMMAND_LINE)) { + sieve_binary_emit_cstring(sblock, ""); + sieve_binary_emit_cstring(sblock, ""); + } else { + sieve_binary_emit_cstring(sblock, script->storage->name); + sieve_binary_emit_cstring(sblock, script->name); + } if (script->v.binary_write_metadata == NULL) return; @@ -549,7 +627,7 @@ bool sieve_script_binary_dump_metadata(struct sieve_script *script, { struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); struct sieve_instance *svinst = sieve_binary_svinst(sbin); - string_t *storage_class, *location; + string_t *storage_class, *storage_name, *name; struct sieve_script *adhoc_script = NULL; unsigned int version; bool result = TRUE; @@ -564,15 +642,27 @@ bool sieve_script_binary_dump_metadata(struct sieve_script *script, return FALSE; sieve_binary_dumpf(denv, "class.version = %d\n", version); - /* location */ - if (!sieve_binary_read_string(sblock, offset, &location)) + /* storage */ + if (!sieve_binary_read_string(sblock, offset, &storage_name)) + return FALSE; + if (str_len(storage_name) == 0) + sieve_binary_dumpf(denv, "storage = (unavailable)\n"); + else + sieve_binary_dumpf(denv, "storage = %s\n", str_c(storage_name)); + + /* name */ + if (!sieve_binary_read_string(sblock, offset, &name)) return FALSE; - sieve_binary_dumpf(denv, "location = %s\n", str_c(location)); + if (str_len(name) == 0) + sieve_binary_dumpf(denv, "name = (unavailable)\n"); + else + sieve_binary_dumpf(denv, "name = %s\n", str_c(name)); if (script == NULL) { adhoc_script = NULL; - if (sieve_script_create(svinst, str_c(location), - NULL, &script, NULL, NULL) == 0) + if (sieve_script_create_in(svinst, SIEVE_SCRIPT_CAUSE_ANY, + str_c(storage_name), str_c(name), + &script, NULL, NULL) == 0) adhoc_script = script; } @@ -1042,7 +1132,8 @@ const char *sieve_script_get_last_error_lcase(struct sieve_script *script) */ int sieve_script_sequence_create(struct sieve_instance *svinst, - const char *location, + struct event *event_parent, + const char *cause, const char *type, struct sieve_script_sequence **sseq_r, enum sieve_error *error_code_r, const char **error_r) @@ -1053,8 +1144,8 @@ int sieve_script_sequence_create(struct sieve_instance *svinst, *sseq_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - if (sieve_storage_sequence_create(svinst, svinst->event, location, - &storage_seq, + if (sieve_storage_sequence_create(svinst, event_parent, + cause, type, &storage_seq, error_code_r, error_r) < 0) return -1; diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h index 798ebafef..f8918aa4b 100644 --- a/src/lib-sieve/sieve-script.h +++ b/src/lib-sieve/sieve-script.h @@ -17,6 +17,8 @@ bool sieve_script_name_is_valid(const char *scriptname); */ bool sieve_script_file_has_extension(const char *filename); +const char *sieve_script_file_get_scriptname(const char *filename); +const char *sieve_script_file_from_name(const char *name); /* * Sieve script class @@ -36,10 +38,14 @@ struct sieve_script; ARRAY_DEFINE_TYPE(sieve_script, struct sieve_script *); int sieve_script_create(struct sieve_instance *svinst, - const char *location, const char *name, + const char *cause, const char *type, const char *name, struct sieve_script **script_r, enum sieve_error *error_code_r, const char **error_r); - +int sieve_script_create_in(struct sieve_instance *svinst, const char *cause, + const char *storage_name, const char *name, + struct sieve_script **script_r, + enum sieve_error *error_code_r, + const char **error_r); void sieve_script_ref(struct sieve_script *script); void sieve_script_unref(struct sieve_script **script); @@ -49,12 +55,19 @@ int sieve_script_open_as(struct sieve_script *script, const char *name, enum sieve_error *error_code_r); int sieve_script_create_open(struct sieve_instance *svinst, - const char *location, const char *name, - struct sieve_script **script_r, + const char *cause, const char *type, + const char *name, struct sieve_script **script_r, enum sieve_error *error_code_r, const char **error_r); +int sieve_script_create_open_in(struct sieve_instance *svinst, + const char *cause, + const char *storage_name, const char *name, + struct sieve_script **script_r, + enum sieve_error *error_code_r, + const char **error_r); + int sieve_script_check(struct sieve_instance *svinst, - const char *location, const char *name, + const char *cause, const char *type, const char *name, enum sieve_error *error_code_r, const char **error_r); /* @@ -63,7 +76,8 @@ int sieve_script_check(struct sieve_instance *svinst, struct sieve_script * sieve_data_script_create_from_input(struct sieve_instance *svinst, - const char *name, struct istream *input); + const char *cause, const char *name, + struct istream *input); /* * Binary @@ -113,7 +127,9 @@ int sieve_script_delete(struct sieve_script *script, bool ignore_active); const char *sieve_script_name(const struct sieve_script *script) ATTR_PURE; const char *sieve_script_label(const struct sieve_script *script) ATTR_PURE; -const char *sieve_script_location(const struct sieve_script *script) ATTR_PURE; +const char * +sieve_script_storage_type(const struct sieve_script *script) ATTR_PURE; +const char *sieve_script_cause(const struct sieve_script *script) ATTR_PURE; struct sieve_instance * sieve_script_svinst(const struct sieve_script *script) ATTR_PURE; @@ -156,7 +172,8 @@ const char *sieve_script_get_last_error_lcase(struct sieve_script *script); struct sieve_script_sequence; int sieve_script_sequence_create(struct sieve_instance *svinst, - const char *location, + struct event *event_parent, + const char *cause, const char *type, struct sieve_script_sequence **sseq_r, enum sieve_error *error_code_r, const char **error_r); diff --git a/src/lib-sieve/sieve-storage-private.h b/src/lib-sieve/sieve-storage-private.h index a73a9b109..46820227a 100644 --- a/src/lib-sieve/sieve-storage-private.h +++ b/src/lib-sieve/sieve-storage-private.h @@ -5,6 +5,7 @@ #include "sieve-error-private.h" #include "sieve-storage.h" +#include "sieve-storage-settings.h" #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" @@ -23,10 +24,11 @@ ARRAY_DEFINE_TYPE(sieve_storage_class, const struct sieve_storage *); struct sieve_storage_vfuncs { struct sieve_storage *(*alloc)(void); void (*destroy)(struct sieve_storage *storage); - int (*init)(struct sieve_storage *storage, const char *const *options); + int (*init)(struct sieve_storage *storage); int (*autodetect)(struct sieve_instance *svinst, - const char *active_path, + struct event *event, const char *cause, + const struct sieve_storage_settings *storage_set, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, @@ -100,8 +102,9 @@ struct sieve_storage { const struct sieve_storage *storage_class; struct sieve_storage_vfuncs v; - const char *data; - const char *location; + const char *name; + const char *cause; + const char *type; const char *script_name; const char *bin_path; @@ -111,25 +114,33 @@ struct sieve_storage { char *error; enum sieve_error error_code; - const char *default_name; - const char *default_location; struct sieve_storage *default_storage, *default_storage_for; struct mail_namespace *sync_inbox_ns; enum sieve_storage_flags flags; - /* this is the main personal storage */ - bool main_storage:1; bool allows_synchronization:1; bool is_default:1; }; -int sieve_storage_alloc(struct sieve_instance *svinst, struct event *event, +int sieve_storage_alloc(struct sieve_instance *svinst, + struct event *event_parent, const struct sieve_storage *storage_class, - const char *data, enum sieve_storage_flags flags, - bool main, struct sieve_storage **storage_r, + const char *cause, const char *script_type, + const char *storage_name, const char *script_name, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r); +int sieve_storage_alloc_with_settings(struct sieve_instance *svinst, + struct event *event_parent, + const struct sieve_storage *storage_class, + const char *cause, + const struct sieve_storage_settings *set, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, + const char **error_r); int sieve_storage_setup_bin_path(struct sieve_storage *storage, mode_t mode); @@ -186,9 +197,12 @@ struct sieve_storage_save_context { struct sieve_storage_sequence { struct sieve_instance *svinst; struct event *event_parent; - char *location; + char *cause; + char *type; - bool done:1; + const struct sieve_storage_settings *storage_set; + const char **storage_names; + unsigned int storage_count, storage_index; }; /* diff --git a/src/lib-sieve/sieve-storage-settings.c b/src/lib-sieve/sieve-storage-settings.c new file mode 100644 index 000000000..1e08b2340 --- /dev/null +++ b/src/lib-sieve/sieve-storage-settings.c @@ -0,0 +1,148 @@ +/* Copyright (c) 2024 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "array.h" +#include "sort.h" +#include "settings.h" +#include "settings-parser.h" + +#include "sieve-script.h" +#include "sieve-storage.h" +#include "sieve-storage-settings.h" + +static bool +sieve_storage_settings_check(void *_set, pool_t pool, const char **error_r); + +#undef DEF +#define DEF(type, name) SETTING_DEFINE_STRUCT_##type( \ + "sieve_"#name, name, \ + struct sieve_storage_settings) + +static const struct setting_filter_array_order sieve_storage_order_precedence = { + .info = &sieve_storage_setting_parser_info, + .field_name = "sieve_script_precedence", +}; + +static const struct setting_define sieve_storage_setting_defines[] = { + DEF(STR, script_storage), + DEF(UINT, script_precedence), + + DEF(STR, script_type), + DEF(BOOLLIST, script_cause), + DEF(STR, script_driver), + DEF(STR, script_name), + DEF(STR, script_bin_path), + + DEF(SIZE, quota_max_storage), + DEF(UINT, quota_max_scripts), + + { .type = SET_FILTER_ARRAY, .key = "sieve_script", + .offset = offsetof(struct sieve_storage_settings, storages), + .filter_array_field_name = "sieve_script_storage", + .filter_array_order = &sieve_storage_order_precedence }, + + SETTING_DEFINE_LIST_END, +}; + +static const struct sieve_storage_settings sieve_storage_default_settings = { + .script_storage = "", + .script_precedence = UINT_MAX, + + .script_type = SIEVE_STORAGE_TYPE_PERSONAL, + .script_cause = ARRAY_INIT, + + .script_driver = "", + .script_name = "", + .script_bin_path = "", + + .quota_max_storage = 0, + .quota_max_scripts = 0, + + .storages = ARRAY_INIT, +}; + +const struct setting_parser_info sieve_storage_setting_parser_info = { + .name = "sieve_storage", + + .defines = sieve_storage_setting_defines, + .defaults = &sieve_storage_default_settings, + + .struct_size = sizeof(struct sieve_storage_settings), + + .pool_offset1 = 1 + offsetof(struct sieve_storage_settings, pool), + + .check_func = sieve_storage_settings_check, +}; + +/* <settings checks> */ +static bool +sieve_storage_settings_check(void *_set, pool_t pool ATTR_UNUSED, + const char **error_r) +{ + struct sieve_storage_settings *set = _set; + + if (*set->script_storage != '\0' && + !sieve_storage_name_is_valid(set->script_storage)) { + *error_r = t_strdup_printf( + "Invalid script storage name '%s'", + str_sanitize(set->script_storage, 128)); + return FALSE; + } + if (*set->script_name != '\0' && + !sieve_script_name_is_valid(set->script_name)) { + *error_r = t_strdup_printf( + "Invalid script name '%s'", + str_sanitize(set->script_name, 128)); + return FALSE; + } + + if (array_is_created(&set->script_cause)) + array_sort(&set->script_cause, i_strcmp_p); + + return TRUE; +} +/* </settings checks> */ + +bool sieve_storage_settings_match_script_type( + const struct sieve_storage_settings *set, const char *type) +{ + if (strcasecmp(type, SIEVE_STORAGE_TYPE_ANY) == 0) + return TRUE; + if (strcasecmp(type, set->script_type) == 0) + return TRUE; + return FALSE; +} + +bool sieve_storage_settings_match_script_cause( + const struct sieve_storage_settings *set, const char *cause) +{ + if (strcasecmp(cause, SIEVE_SCRIPT_CAUSE_ANY) == 0) { + /* Any cause will match */ + return TRUE; + } + if (!array_is_created(&set->script_cause)) { + /* Causes are not configured for this storage */ + if (strcasecmp(set->script_type, + SIEVE_STORAGE_TYPE_PERSONAL) == 0) { + /* For personal storages the default is to match any + cause. */ + return TRUE; + } + if (strcasecmp(cause, SIEVE_SCRIPT_CAUSE_DELIVERY) == 0) { + /* The default cause is delivery */ + return TRUE; + } + return FALSE; + } + + /* Causes are configured for this storage: perform lookup */ + + unsigned int set_cause_count; + const char *const *set_cause; + + set_cause = array_get(&set->script_cause, &set_cause_count); + return (i_bsearch(cause, set_cause, set_cause_count, + sizeof(const char *), search_strcasecmp) != NULL); +} diff --git a/src/lib-sieve/sieve-storage-settings.h b/src/lib-sieve/sieve-storage-settings.h new file mode 100644 index 000000000..4d7cc2158 --- /dev/null +++ b/src/lib-sieve/sieve-storage-settings.h @@ -0,0 +1,33 @@ +#ifndef SIEVE_STORAGE_SETTINGS_H +#define SIEVE_STORAGE_SETTINGS_H + +#define SIEVE_STORAGE_SETTINGS_FILTER "sieve_script" + +struct sieve_storage_settings { + pool_t pool; + + const char *script_storage; + unsigned int script_precedence; + + const char *script_type; + ARRAY_TYPE(const_string) script_cause; + + const char *script_driver; + const char *script_name; + const char *script_bin_path; + + uoff_t quota_max_storage; + unsigned int quota_max_scripts; + + ARRAY_TYPE(const_string) storages; +}; + +extern const struct setting_parser_info sieve_storage_setting_parser_info; + +bool sieve_storage_settings_match_script_type( + const struct sieve_storage_settings *set, const char *type); +bool sieve_storage_settings_match_script_cause( + const struct sieve_storage_settings *set, const char *cause); + + +#endif diff --git a/src/lib-sieve/sieve-storage.c b/src/lib-sieve/sieve-storage.c index 9cd8aaabb..139a36be2 100644 --- a/src/lib-sieve/sieve-storage.c +++ b/src/lib-sieve/sieve-storage.c @@ -3,12 +3,14 @@ #include "lib.h" #include "array.h" +#include "str.h" #include "str-sanitize.h" #include "hash.h" #include "home-expand.h" #include "eacces-error.h" #include "mkdir-parents.h" #include "ioloop.h" +#include "settings.h" #include "sieve-common.h" #include "sieve-settings.old.h" @@ -28,6 +30,15 @@ struct event_category event_category_sieve_storage = { .name = "sieve-storage", }; +/* + * Storage name + */ + +bool sieve_storage_name_is_valid(const char *name) +{ + return sieve_script_name_is_valid(name); +} + /* * Storage class */ @@ -118,17 +129,37 @@ bool sieve_storage_class_exists(struct sieve_instance *svinst, * Storage event */ +static void +sieve_storage_update_event_prefix(struct event *event, const char *storage_name, + bool is_default) +{ + string_t *prefix = t_str_new(128); + + str_append(prefix, "storage"); + if (storage_name != NULL && *storage_name != '\0') { + str_append_c(prefix, ' '); + str_append(prefix, storage_name); + } + if (is_default) + str_append(prefix, " (default)"); + str_append(prefix, ": "); + event_set_append_log_prefix(event, str_c(prefix)); +} + static struct event * sieve_storage_create_event(struct sieve_instance *svinst, - struct event *event_parent) + struct event *event_parent, + const char *storage_name) { struct event *event; event = event_create(event_parent == NULL ? svinst->event : event_parent); + if (event_parent != svinst->event) + event_add_category(event, &event_category_sieve); event_add_category(event, &event_category_sieve_storage); - event_set_append_log_prefix(event, "storage: "); + sieve_storage_update_event_prefix(event, storage_name, FALSE); return event; } @@ -146,146 +177,6 @@ sieve_storage_create_driver_event(struct event *event_parent, return event; } -/* - * Storage options - */ - -static const char *split_next_arg(const char *const **_args) -{ - const char *const *args = *_args; - const char *str = args[0]; - - /* join arguments for escaped ";" separator */ - - args++; - while (*args != NULL && **args == '\0') { - args++; - if (*args == NULL) { - /* string ends with ";", just ignore it. */ - break; - } - str = t_strconcat(str, ";", *args, NULL); - args++; - } - *_args = args; - return str; -} - -static int -sieve_storage_driver_parse(struct sieve_instance *svinst, const char **data, - const struct sieve_storage **driver_r) -{ - const struct sieve_storage *storage_class = NULL; - const char *p; - - p = strchr(*data, ':'); - if (p == NULL) - return 0; - - /* Lookup storage driver */ - T_BEGIN { - const char *driver; - - driver = t_strdup_until(*data, p); - *data = p+1; - - storage_class = sieve_storage_class_find(svinst, driver); - if (storage_class == NULL) { - e_error(svinst->event, - "Unknown storage driver module '%s'", - driver); - } else if (storage_class->v.alloc == NULL) { - e_error(svinst->event, - "Support not compiled in for storage driver '%s'", - driver); - storage_class = NULL; - } - } T_END; - - *driver_r = storage_class; - return (storage_class == NULL ? -1 : 1); -} - -static int -sieve_storage_data_parse(struct sieve_storage *storage, const char *data, - const char **location_r, const char *const **options_r) -{ - ARRAY_TYPE(const_string) options; - const char *const *args; - const char *value; - - if (*data == '\0') { - *options_r = NULL; - *location_r = data; - return 0; - } - - /* <location> */ - args = t_strsplit(data, ";"); - *location_r = split_next_arg(&args); - - if (options_r != NULL) { - t_array_init(&options, 8); - - /* [<option> *(';' <option>)] */ - while (*args != NULL) { - const char *option = split_next_arg(&args); - - if (str_begins_icase(option, "name=", &value)) { - if (*value == '\0') { - e_error(storage->event, - "Failed to parse storage location: " - "Empty name not allowed"); - return -1; - } - - if (storage->script_name == NULL) { - if (!sieve_script_name_is_valid(value)) { - e_error(storage->event, - "Failed to parse storage location: " - "Invalid script name '%s'.", - str_sanitize(value, 80)); - return -1; - } - storage->script_name = p_strdup(storage->pool, value); - } - - } else if (str_begins_icase(option, "bindir=", &value)) { - if (value[0] == '\0') { - e_error(storage->event, - "Failed to parse storage location: " - "Empty bindir not allowed"); - return -1; - } - - if (value[0] == '~') { - /* home-relative path. change to absolute. */ - const char *home = sieve_environment_get_homedir(storage->svinst); - - if (home != NULL) { - value = home_expand_tilde(value, home); - } else if (value[1] == '/' || value[1] == '\0') { - e_error(storage->event, - "Failed to parse storage location: " - "bindir is relative to home directory (~/), " - "but home directory cannot be determined"); - return -1; - } - } - - storage->bin_path = p_strdup(storage->pool, value); - } else { - array_append(&options, &option, 1); - } - } - - (void)array_append_space(&options); - *options_r = array_idx(&options, 0); - } - - return 0; -} - /* * Storage instance */ @@ -294,7 +185,9 @@ static int sieve_storage_alloc_from_class(struct sieve_instance *svinst, struct event *event, const struct sieve_storage *storage_class, - const char *data, + const char *cause, const char *script_type, + const char *storage_name, + const char *script_name, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, @@ -307,7 +200,7 @@ sieve_storage_alloc_from_class(struct sieve_instance *svinst, if (storage_class->v.alloc == NULL) { e_error(event, "Support not compiled in for this driver"); sieve_error_create_script_not_found( - NULL, error_code_r, error_r); + script_name, error_code_r, error_r); return -1; } @@ -328,9 +221,18 @@ sieve_storage_alloc_from_class(struct sieve_instance *svinst, storage->storage_class = storage_class; storage->refcount = 1; storage->svinst = svinst; - storage->data = p_strdup_empty(storage->pool, data); + storage->cause = p_strdup(storage->pool, cause); + storage->type = p_strdup(storage->pool, script_type); + storage->script_name = p_strdup(storage->pool, script_name); storage->flags = flags; + if (storage_name != NULL && *storage_name != '\0') + storage->name = p_strdup(storage->pool, storage_name); + else { + storage->name = p_strconcat( + storage->pool, "auto:", storage->type, NULL); + } + storage->event = event; event_ref(event); @@ -341,53 +243,62 @@ sieve_storage_alloc_from_class(struct sieve_instance *svinst, int sieve_storage_alloc(struct sieve_instance *svinst, struct event *event_parent, const struct sieve_storage *storage_class, - const char *data, enum sieve_storage_flags flags, - bool main, struct sieve_storage **storage_r, + const char *cause, const char *script_type, + const char *storage_name, const char *script_name, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r) { struct event *storage_event, *event; - struct sieve_storage *storage; int ret; *storage_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - storage_event = sieve_storage_create_event(svinst, event_parent); + storage_event = sieve_storage_create_event(svinst, event_parent, + storage_name); event = sieve_storage_create_driver_event(storage_event, storage_class->driver_name); event_unref(&storage_event); ret = sieve_storage_alloc_from_class(svinst, event, storage_class, - data, flags, - &storage, error_code_r, error_r); - if (ret == 0) - storage->main_storage = main; + cause, script_type, + storage_name, script_name, flags, + storage_r, error_code_r, error_r); event_unref(&event); - *storage_r = storage; return ret; } -static void sieve_storage_get_quota_settings(struct sieve_storage *storage) +int sieve_storage_alloc_with_settings(struct sieve_instance *svinst, + struct event *event_parent, + const struct sieve_storage *storage_class, + const char *cause, + const struct sieve_storage_settings *set, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, + const char **error_r) { - struct sieve_instance *svinst = storage->svinst; - unsigned long long int uint_setting; - size_t size_setting; + struct sieve_storage *storage; + int ret; - /* Get quota settings if storage driver provides none */ + *storage_r = NULL; + sieve_error_args_init(&error_code_r, &error_r); - if (storage->max_storage == 0 && - sieve_setting_get_size_value(svinst, "sieve_quota_max_storage", - &size_setting)) { - storage->max_storage = size_setting; - } + ret = sieve_storage_alloc_from_class(svinst, event_parent, + storage_class, + cause, set->script_type, + set->script_storage, + set->script_name, flags, + &storage, error_code_r, error_r); + if (ret < 0) + return -1; - if (storage->max_scripts == 0 && - sieve_setting_get_uint_value(svinst, "sieve_quota_max_scripts", - &uint_setting)) { - storage->max_scripts = uint_setting; - } + storage->bin_path = p_strdup_empty(storage->pool, set->script_bin_path); + storage->max_storage = set->quota_max_storage; + storage->max_scripts = set->quota_max_scripts; if (storage->max_storage > 0) { e_debug(storage->event, "quota: " @@ -399,47 +310,205 @@ static void sieve_storage_get_quota_settings(struct sieve_storage *storage) "Script count limit: %u scripts", storage->max_scripts); } + + *storage_r = storage; + return 0; +} + +static int +sieve_storage_alloc_from_settings(struct sieve_instance *svinst, + struct event *event_parent, const char *cause, + const struct sieve_storage_settings *set, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, + const char **error_r) +{ + const struct sieve_storage *storage_class; + struct event *event = event_parent; + int ret; + + *storage_r = NULL; + + if (!sieve_storage_settings_match_script_cause(set, cause)) + return 0; + + storage_class = sieve_storage_class_find(svinst, set->script_driver); + // FIXME: add support for automatic module loading (no such modules yet) + if (storage_class == NULL) { + e_error(event, "Unknown storage driver: %s", + set->script_driver); + sieve_error_create_script_not_found(set->script_name, + error_code_r, error_r); + event_unref(&event); + return -1; + } + + event = sieve_storage_create_driver_event(event_parent, + storage_class->driver_name); + + ret = sieve_storage_alloc_with_settings(svinst, event, storage_class, + cause, set, flags, + storage_r, error_code_r, + error_r); + + event_unref(&event); + + if (ret < 0) + return -1; + return 1; +} + +static int +sieve_storage_autodetect(struct sieve_instance *svinst, struct event *event, + const char *cause, const char *type, + const struct sieve_storage_settings *set, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, const char **error_r) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + int ret; + + *storage_r = NULL; + sieve_error_args_init(&error_code_r, &error_r); + + if (!sieve_storage_settings_match_script_cause(set, cause)) + return 0; + if (!sieve_storage_settings_match_script_type(set, type)) + return 0; + + const struct sieve_storage *const *classes; + unsigned int i, count; + + classes = array_get(®->storage_classes, &count); + ret = 0; + for (i = 0; i < count; i++) { + if (classes[i]->v.autodetect == NULL) + continue; + if (set->script_driver[0] != '\0' && + strcasecmp(set->script_driver, + classes[i]->driver_name) != 0) + continue; + + struct event *driver_event = + sieve_storage_create_driver_event( + event, classes[i]->driver_name); + + *storage_r = NULL; + ret = classes[i]->v.autodetect(svinst, driver_event, + cause, set, flags, + storage_r, error_code_r, + error_r); + + event_unref(&driver_event); + + if (ret < 0) { + i_assert(*error_code_r != SIEVE_ERROR_NONE); + i_assert(*error_r != NULL); + if (*error_code_r == SIEVE_ERROR_NOT_FOUND) { + *error_code_r = SIEVE_ERROR_NONE; + *error_r = NULL; + ret = 0; + } + } + i_assert(ret <= 0 || *storage_r != NULL); + if (ret != 0) + break; + } + + if (ret == 0) + e_debug(event, "Autodetection failed"); + return ret; +} + +static int +sieve_storage_autodetect_any(struct sieve_instance *svinst, + struct event *event_parent, + const char *cause, const char *type, + const struct sieve_storage_settings *set, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, + const char **error_r) +{ + struct event *event; + int ret; + + event = sieve_storage_create_event(svinst, event_parent, NULL); + + ret = sieve_storage_autodetect(svinst, event, cause, type, set, + flags, storage_r, error_code_r, error_r); + + event_unref(&event); + return ret; } static int sieve_storage_init_real(struct sieve_instance *svinst, struct event *event, - const struct sieve_storage *storage_class, - const char *data, - enum sieve_storage_flags flags, bool main, + const char *cause, const char *type, + const char *storage_name, bool try, + enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r) { + const struct sieve_storage_settings *set; struct sieve_storage *storage; - struct event *driver_event; - const char *const *options; - const char *location; + const char *error; int ret; - driver_event = sieve_storage_create_driver_event( - event, storage_class->driver_name); - ret = sieve_storage_alloc(svinst, driver_event, storage_class, - data, flags, main, &storage, - error_code_r, error_r); - event_unref(&driver_event); - if (ret < 0) + if (try) { + ret = settings_try_get_filter( + event, "sieve_script", storage_name, + &sieve_storage_setting_parser_info, 0, + &set, &error); + if (ret == 0) + return 0; + } else { + ret = settings_get_filter( + event, "sieve_script", storage_name, + &sieve_storage_setting_parser_info, 0, + &set, &error); + } + if (ret < 0) { + e_error(event, "%s", error); + sieve_error_create_internal(error_code_r, error_r); + return -1; + } + + if (!sieve_storage_settings_match_script_type(set, type)) { + settings_free(set); + return 0; + } + + event_add_str(event, "sieve_script", storage_name); + if (set->script_driver[0] == '\0') { + ret = sieve_storage_autodetect(svinst, event, cause, type, + set, flags, storage_r, + error_code_r, error_r); + if (ret != 0) { + settings_free(set); + return ret; + } + e_error(event, "sieve_script_driver is empty"); + sieve_error_create_script_not_found(set->script_name, + error_code_r, error_r); + settings_free(set); return -1; + } + + ret = sieve_storage_alloc_from_settings(svinst, event, cause, + set, flags, &storage, + error_code_r, error_r); + settings_free(set); + if (ret <= 0) + return ret; i_assert(storage != NULL); i_assert(storage->v.init != NULL); T_BEGIN { - if (sieve_storage_data_parse(storage, data, - &location, &options) < 0) { - sieve_error_create_internal(error_code_r, error_r); - ret = -1; - } else { - storage->location = p_strdup(storage->pool, location); - - event_add_str(event, "script_location", - storage->location); - - ret = storage_class->v.init(storage, options); - i_assert(ret <= 0); - } + ret = storage->v.init(storage); + i_assert(ret <= 0); } T_END; if (ret < 0) { i_assert(storage->error_code != SIEVE_ERROR_NONE); @@ -450,13 +519,14 @@ sieve_storage_init_real(struct sieve_instance *svinst, struct event *event, return -1; } *storage_r = storage; - return 0; + return 1; } static int sieve_storage_init(struct sieve_instance *svinst, struct event *event_parent, - const struct sieve_storage *storage_class, const char *data, - enum sieve_storage_flags flags, bool main, + const char *cause, const char *type, + const char *storage_name, bool try, + enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r) { @@ -465,10 +535,10 @@ sieve_storage_init(struct sieve_instance *svinst, struct event *event_parent, *storage_r = NULL; - event = sieve_storage_create_event(svinst, event_parent); + event = sieve_storage_create_event(svinst, event_parent, storage_name); - ret = sieve_storage_init_real(svinst, event, storage_class, - data, flags, main, + ret = sieve_storage_init_real(svinst, event, cause, type, + storage_name, try, flags, storage_r, error_code_r, error_r); event_unref(&event); @@ -476,40 +546,112 @@ sieve_storage_init(struct sieve_instance *svinst, struct event *event_parent, return ret; } -int sieve_storage_create(struct sieve_instance *svinst, struct event *event, - const char *location, +int sieve_storage_create(struct sieve_instance *svinst, + struct event *event, const char *cause, + const char *storage_name, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r) { - const struct sieve_storage *storage_class; - const char *data; + struct sieve_storage *storage; int ret; - /* Dont use this function for creating a synchronizing storage */ - i_assert((flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0); + *storage_r = NULL; + sieve_error_args_init(&error_code_r, &error_r); + + ret = sieve_storage_init(svinst, event, cause, SIEVE_STORAGE_TYPE_ANY, + storage_name, TRUE, flags, + &storage, error_code_r, error_r); + if (ret < 0) { + if (*error_code_r != SIEVE_ERROR_NOT_FOUND) + return -1; + ret = 0; + } + if (ret == 0) { + e_debug(event, "Sieve script storage '%s' not found (cause=%s)", + storage_name, cause); + sieve_error_create_script_not_found( + NULL, error_code_r, error_r); + return -1; + } + i_assert(storage != NULL); + *storage_r = storage; + return 0; +} + +int sieve_storage_create_auto(struct sieve_instance *svinst, + struct event *event, + const char *cause, const char *type, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, + const char **error_r) +{ + const struct sieve_storage_settings *storage_set; + const char *const *storage_names; + unsigned int i, count; + const char *error; *storage_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - data = location; - if ((ret = sieve_storage_driver_parse(svinst, &data, - &storage_class)) < 0) { + if (settings_get(event, &sieve_storage_setting_parser_info, + SETTINGS_GET_FLAG_SORT_FILTER_ARRAYS, + &storage_set, &error) < 0) { + e_error(event, "%s", error); sieve_error_create_internal(error_code_r, error_r); return -1; } + if (array_is_created(&storage_set->storages)) + storage_names = array_get(&storage_set->storages, &count); + else { + storage_names = NULL; + count = 0; + } - if (ret == 0) - storage_class = &sieve_file_storage; + struct sieve_storage *storage = NULL; + int ret = 0; - return sieve_storage_init(svinst, event, storage_class, - data, flags, FALSE, - storage_r, error_code_r, error_r); + for (i = 0; i < count; i++) { + ret = sieve_storage_init(svinst, event, cause, type, + storage_names[i], FALSE, flags, + &storage, error_code_r, error_r); + if (ret < 0 && *error_code_r != SIEVE_ERROR_NOT_FOUND) { + settings_free(storage_set); + return -1; + } + if (ret > 0) { + i_assert(storage != NULL); + break; + } + } + if (ret <= 0) { + ret = sieve_storage_autodetect_any(svinst, event, cause, type, + storage_set, flags, &storage, + error_code_r, error_r); + if (ret < 0) { + settings_free(storage_set); + return -1; + } + } + settings_free(storage_set); + if (ret <= 0) { + e_debug(event, + "storage: No matching Sieve storage configured " + "(type=%s and cause=%s)", + type, cause); + sieve_error_create_script_not_found( + NULL, error_code_r, error_r); + return -1; + } + i_assert(storage != NULL); + *storage_r = storage; + return 0; } static int sieve_storage_create_default(struct sieve_instance *svinst, - struct event *event, const char *location, + struct event *event, const char *cause, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, @@ -522,33 +664,27 @@ sieve_storage_create_default(struct sieve_instance *svinst, *storage_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - if (location == NULL) { - sieve_error_create_script_not_found( - NULL, error_code_r, error_r); - return -1; - } - - ret = sieve_storage_create(svinst, event, location, flags, - &storage, &error_code, error_r); + ret = sieve_storage_create_auto(svinst, event, cause, + SIEVE_STORAGE_TYPE_DEFAULT, flags, + &storage, &error_code, error_r); if (ret >= 0) { storage->is_default = TRUE; + sieve_storage_update_event_prefix( + event_get_parent(storage->event), storage->name, TRUE); } else { switch (error_code) { case SIEVE_ERROR_NOT_FOUND: e_debug(event, "storage: " - "Default script location '%s' not found", - location); + "Default script not found"); break; case SIEVE_ERROR_TEMP_FAILURE: e_error(event, "storage: " - "Failed to access default script location '%s' " - "(temporary failure)", - location); + "Failed to access default script " + "(temporary failure)"); break; default: e_error(event, "storage: " - "Failed to access default script location '%s'", - location); + "Failed to access default script"); break; } *error_code_r = error_code; @@ -573,20 +709,13 @@ sieve_storage_create_default_for(struct sieve_storage *storage, return 0; } - if (storage->default_name == NULL) { - sieve_storage_set_not_found_error(storage, NULL); - *error_code_r = storage->error_code; - *error_r = storage->error; - return -1; - } - struct sieve_instance *svinst = storage->svinst; enum sieve_error error_code; const char *error; i_assert(storage->default_storage_for == NULL); if (sieve_storage_create_default(svinst, svinst->event, - storage->default_location, 0, + storage->cause, 0, &storage->default_storage, &error_code, &error) < 0) { sieve_storage_set_error(storage, error_code, "%s", error); @@ -602,77 +731,13 @@ sieve_storage_create_default_for(struct sieve_storage *storage, return 0; } -static int -sieve_storage_do_create_personal(struct sieve_instance *svinst, - enum sieve_storage_flags flags, - struct sieve_storage **storage_r, - enum sieve_error *error_code_r) -{ - struct sieve_storage *storage = NULL; - const struct sieve_storage *sieve_class = NULL; - const char *set_sieve, *data; - int ret; - - /* Sieve storage location */ - - set_sieve = sieve_setting_get(svinst, "sieve"); - - if (set_sieve != NULL) { - if (*set_sieve == '\0') { - /* disabled */ - e_debug(svinst->event, "storage: " - "Personal storage is disabled (sieve=\"\")"); - *error_code_r = SIEVE_ERROR_NOT_FOUND; - return -1; - } - - data = set_sieve; - if ((ret = sieve_storage_driver_parse(svinst, &data, - &sieve_class)) < 0) { - sieve_error_create_internal(error_code_r, NULL); - return -1; - } - - if (ret > 0) { - /* The normal case: explicit driver name */ - if (sieve_storage_init(svinst, svinst->event, - sieve_class, data, flags, TRUE, - &storage, error_code_r, - NULL) < 0) - return -1; - } - - /* No driver name */ - } - - if (storage == NULL) { - const char *error; - - i_assert(sieve_file_storage.v.autodetect != NULL); - if (sieve_file_storage.v.autodetect(svinst, set_sieve, flags, - &storage, error_code_r, - &error) < 0) - return -1; - i_assert(storage != NULL); - } - - if (storage == NULL) - return -1; - - sieve_storage_get_quota_settings(storage); - - *storage_r = storage; - return 0; -} - int sieve_storage_create_personal(struct sieve_instance *svinst, - struct mail_user *user, + struct mail_user *user, const char *cause, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r) { - struct sieve_storage *storage = NULL; - const char *set_default, *set_default_name; + struct sieve_storage *storage; int ret; *storage_r = NULL; @@ -686,54 +751,20 @@ int sieve_storage_create_personal(struct sieve_instance *svinst, return -1; } - /* Determine location for default script */ - set_default = sieve_setting_get(svinst, "sieve_default"); - /* Attempt to locate user's main storage */ - ret = sieve_storage_do_create_personal(svinst, flags, - &storage, error_code_r); + ret = sieve_storage_create_auto(svinst, svinst->event, cause, + SIEVE_STORAGE_TYPE_PERSONAL, flags, + &storage, error_code_r, NULL); if (ret == 0) { (void)sieve_storage_sync_init(storage, user); - - /* Success; record default script location for later use */ - storage->default_location = - p_strdup_empty(storage->pool, set_default); - - set_default_name = - sieve_setting_get(svinst, "sieve_default_name"); - if (set_default_name != NULL && *set_default_name != '\0' && - !sieve_script_name_is_valid(set_default_name)) { - e_error(storage->event, - "Invalid script name '%s' for 'sieve_default_name' setting.", - str_sanitize(set_default_name, 80)); - set_default_name = NULL; - } - storage->default_name = - p_strdup_empty(storage->pool, set_default_name); - - if (storage->default_location != NULL && - storage->default_name != NULL) { - e_debug(storage->event, - "Default script at '%s' is visible by name '%s'", - storage->default_location, storage->default_name); - } } else if (*error_code_r != SIEVE_ERROR_TEMP_FAILURE && (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && (flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) { /* Failed; try using default script location (not for temporary failures, read/write access, or dsync) */ - if (set_default == NULL) { - e_debug(svinst->event, "storage: " - "No default script location configured"); - } else { - e_debug(svinst->event, "storage: " - "Trying default script location '%s'", - set_default); - } - ret = sieve_storage_create_default(svinst, svinst->event, - set_default, 0, &storage, + cause, flags, &storage, error_code_r, NULL); } *storage_r = storage; @@ -864,6 +895,8 @@ void sieve_storage_set_modified(struct sieve_storage *storage, time_t mtime) int sieve_storage_cmp(const struct sieve_storage *storage1, const struct sieve_storage *storage2) { + int ret; + if (storage1 == storage2) return 0; if (storage1 == NULL || storage2 == NULL) @@ -872,7 +905,10 @@ int sieve_storage_cmp(const struct sieve_storage *storage1, return (storage1->storage_class > storage2->storage_class ? 1 : -1); } - return null_strcmp(storage1->location, storage2->location); + ret = null_strcmp(storage1->type, storage2->type); + if (ret != 0) + return (ret > 0 ? 1 : -1); + return null_strcmp(storage1->name, storage2->name); } unsigned int sieve_storage_hash(const struct sieve_storage *storage) @@ -880,7 +916,8 @@ unsigned int sieve_storage_hash(const struct sieve_storage *storage) unsigned int hash = 0; hash ^= POINTER_CAST_TO(storage->storage_class, unsigned int); - hash ^= str_hash(storage->location); + hash ^= str_hash(storage->type); + hash ^= str_hash(storage->name); return hash; } @@ -905,6 +942,8 @@ int sieve_storage_get_script_direct(struct sieve_storage *storage, *error_code_r = storage->error_code; return -1; } + if (name == NULL) + name = storage->script_name; i_assert(storage->v.get_script != NULL); ret = storage->v.get_script(storage, name, script_r); @@ -927,7 +966,8 @@ sieve_storage_get_default_script(struct sieve_storage *storage, int ret; if (*error_code_r != SIEVE_ERROR_NOT_FOUND || - (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0) + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 || + !sieve_storage_is_personal(storage)) return -1; /* Not found; if this name maps to the default script, @@ -941,7 +981,7 @@ sieve_storage_get_default_script(struct sieve_storage *storage, if (ret < 0) return -1; - if (strcmp(storage->default_name, name) != 0) { + if (strcmp(def_storage->script_name, name) != 0) { sieve_storage_set_error(storage, SIEVE_ERROR_NOT_FOUND, "Default script '%s' not found", @@ -1089,10 +1129,10 @@ sieve_storage_active_script_do_get_name(struct sieve_storage *storage, if (ret < 0) return -1; - *name_r = storage->default_name; + *name_r = def_storage->script_name; ret = sieve_storage_check_script(def_storage, - storage->default_name, NULL); + def_storage->script_name, NULL); if (ret < 0) { sieve_storage_copy_error(storage, def_storage); i_assert(storage->error_code != SIEVE_ERROR_NONE); @@ -1267,13 +1307,13 @@ sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r) struct sieve_storage *def_storage = lctx->def_storage; bool have_default = (def_storage != NULL && - storage->default_name != NULL); + def_storage->script_name != NULL); if (scriptname != NULL) { /* Remember when we see that the storage has its own script for default */ if (have_default && - strcmp(scriptname, storage->default_name) == 0) + strcmp(scriptname, def_storage->script_name) == 0) lctx->seen_default = TRUE; } else if (have_default && !lctx->seen_default && @@ -1282,7 +1322,7 @@ sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r) /* Return default script at the end if it was not listed thus far (storage backend has no script under default name) */ - scriptname = storage->default_name; + scriptname = def_storage->script_name; lctx->seen_default = TRUE; /* Mark default as active if no normal script is active */ @@ -1519,8 +1559,6 @@ sieve_storage_save_is_activating_default( { struct sieve_storage *storage = sctx->storage; - if (storage->default_name == NULL || storage->default_location == NULL) - return 0; if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0) return 0; if (!sieve_storage_save_will_activate(sctx)) @@ -1537,9 +1575,9 @@ sieve_storage_save_is_activating_default( return -1; } - if (strcmp(sctx->scriptname, storage->default_name) == 0) { + if (strcmp(sctx->scriptname, def_storage->script_name) == 0) { ret = sieve_storage_check_script_direct( - storage, storage->default_name, &error_code); + storage, def_storage->script_name, &error_code); if (ret == 0 || (ret < 0 && error_code == SIEVE_ERROR_NOT_FOUND)) ret = 1; @@ -1793,9 +1831,9 @@ int sieve_storage_quota_havespace(struct sieve_storage *storage, * Properties */ -const char *sieve_storage_location(const struct sieve_storage *storage) +const char *sieve_storage_name(const struct sieve_storage *storage) { - return storage->location; + return storage->name; } bool sieve_storage_is_default(const struct sieve_storage *storage) @@ -1803,6 +1841,11 @@ bool sieve_storage_is_default(const struct sieve_storage *storage) return storage->is_default; } +bool sieve_storage_is_personal(struct sieve_storage *storage) +{ + return (strcasecmp(storage->type, SIEVE_STORAGE_TYPE_PERSONAL) == 0); +} + /* * Error handling */ @@ -1910,19 +1953,43 @@ sieve_storage_get_last_error(struct sieve_storage *storage, int sieve_storage_sequence_create(struct sieve_instance *svinst, struct event *event_parent, - const char *location, + const char *cause, const char *type, struct sieve_storage_sequence **sseq_r, enum sieve_error *error_code_r, const char **error_r) { + const struct sieve_storage_settings *storage_set; + const char *const *storage_names; + unsigned int storage_count; + const char *error; + *sseq_r = NULL; sieve_error_args_init(&error_code_r, &error_r); + if (settings_get(event_parent, &sieve_storage_setting_parser_info, + SETTINGS_GET_FLAG_SORT_FILTER_ARRAYS, + &storage_set, &error) < 0) { + e_error(event_parent, "%s", error); + sieve_error_create_internal(error_code_r, error_r); + return -1; + } + if (array_is_created(&storage_set->storages)) + storage_names = array_get(&storage_set->storages, + &storage_count); + else { + storage_names = empty_str_array; + storage_count = 0; + } + struct sieve_storage_sequence *sseq; sseq = i_new(struct sieve_storage_sequence, 1); sseq->svinst = svinst; - sseq->location = i_strdup(location); + sseq->cause = i_strdup(cause); + sseq->type = i_strdup(type); + sseq->storage_set = storage_set; + sseq->storage_names = p_strarray_dup(default_pool, storage_names); + sseq->storage_count = storage_count; sseq->event_parent = event_parent; event_ref(event_parent); @@ -1937,18 +2004,33 @@ int sieve_storage_sequence_next(struct sieve_storage_sequence *sseq, const char **error_r) { struct sieve_instance *svinst = sseq->svinst; + int ret; *storage_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - if (sseq->done) - return 0; - sseq->done = TRUE; + while (sseq->storage_index < sseq->storage_count) { + unsigned int index = sseq->storage_index++; - if (sieve_storage_create(svinst, sseq->event_parent, sseq->location, 0, - storage_r, error_code_r, error_r) < 0) - return -1; - return 1; + ret = sieve_storage_init(svinst, sseq->event_parent, + sseq->cause, sseq->type, + sseq->storage_names[index], TRUE, 0, + storage_r, error_code_r, error_r); + if (ret < 0) { + if (*error_code_r == SIEVE_ERROR_NOT_FOUND) { + *error_code_r = SIEVE_ERROR_NONE; + *error_r = NULL; + continue; + } + return -1; + } + if (ret > 0) { + i_assert(*storage_r != NULL); + return 1; + } + } + + return 0; } void sieve_storage_sequence_free(struct sieve_storage_sequence **_sseq) @@ -1960,6 +2042,9 @@ void sieve_storage_sequence_free(struct sieve_storage_sequence **_sseq) *_sseq = NULL; event_unref(&sseq->event_parent); - i_free(sseq->location); + i_free(sseq->cause); + i_free(sseq->type); + settings_free(sseq->storage_set); + i_free(sseq->storage_names); i_free(sseq); } diff --git a/src/lib-sieve/sieve-storage.h b/src/lib-sieve/sieve-storage.h index 58ec5979e..868d8fea1 100644 --- a/src/lib-sieve/sieve-storage.h +++ b/src/lib-sieve/sieve-storage.h @@ -1,6 +1,8 @@ #ifndef SIEVE_STORAGE_H #define SIEVE_STORAGE_H +#include "sieve-common.h" + #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ @@ -11,6 +13,25 @@ #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' +/* + * Storage name + */ + +bool sieve_storage_name_is_valid(const char *name); + +/* + * Storage type + */ + +/* Common storage types; others may be defined in specific contexts. */ +#define SIEVE_STORAGE_TYPE_ANY "any" +#define SIEVE_STORAGE_TYPE_PERSONAL "personal" +#define SIEVE_STORAGE_TYPE_DEFAULT "default" +#define SIEVE_STORAGE_TYPE_GLOBAL "global" +#define SIEVE_STORAGE_TYPE_BEFORE "before" +#define SIEVE_STORAGE_TYPE_AFTER "after" +#define SIEVE_STORAGE_TYPE_DISCARD "discard" + /* * Storage object */ @@ -29,13 +50,21 @@ struct sieve_storage; bool sieve_storage_class_exists(struct sieve_instance *svinst, const char *name); -int sieve_storage_create(struct sieve_instance *svinst, struct event *event, - const char *location, +int sieve_storage_create(struct sieve_instance *svinst, + struct event *event, const char *cause, + const char *storage_name, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r); +int sieve_storage_create_auto(struct sieve_instance *svinst, + struct event *event, + const char *cause, const char *type, + enum sieve_storage_flags flags, + struct sieve_storage **storage_r, + enum sieve_error *error_code_r, + const char **error_r); int sieve_storage_create_personal(struct sieve_instance *svinst, - struct mail_user *user, + struct mail_user *user, const char *cause, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r); @@ -160,6 +189,7 @@ const char *sieve_storage_name(const struct sieve_storage *storage) ATTR_PURE; const char *sieve_storage_location(const struct sieve_storage *storage) ATTR_PURE; bool sieve_storage_is_default(const struct sieve_storage *storage) ATTR_PURE; +bool sieve_storage_is_personal(struct sieve_storage *storage) ATTR_PURE; int sieve_storage_is_singular(struct sieve_storage *storage); @@ -195,7 +225,7 @@ struct sieve_storage_sequence; int sieve_storage_sequence_create(struct sieve_instance *svinst, struct event *event_parent, - const char *location, + const char *cause, const char *type, struct sieve_storage_sequence **sseq_r, enum sieve_error *error_code_r, const char **error_r); diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h index bb1cb8571..fbdf53397 100644 --- a/src/lib-sieve/sieve-types.h +++ b/src/lib-sieve/sieve-types.h @@ -31,6 +31,8 @@ struct sieve_trace_log; enum sieve_flag { /* Relative paths are resolved to HOME */ SIEVE_FLAG_HOME_RELATIVE = (1 << 0), + /* Sieve is running in command line tool */ + SIEVE_FLAG_COMMAND_LINE = (1 << 1), }; /* Sieve evaluation can be performed at various different points as messages @@ -204,6 +206,13 @@ enum sieve_duplicate_check_result { SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE = -2, }; +/* + * Script invocation cause + */ + +#define SIEVE_SCRIPT_CAUSE_ANY "any" +#define SIEVE_SCRIPT_CAUSE_DELIVERY "delivery" + /* * Script environment * diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index 63d5103ba..4914ab97c 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -218,6 +218,11 @@ struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr) return valdtr->script; } +const char *sieve_validator_script_cause(struct sieve_validator *valdtr) +{ + return sieve_script_cause(valdtr->script); +} + struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr) { return valdtr->svinst; diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h index 21b41ccf1..a1af14b45 100644 --- a/src/lib-sieve/sieve-validator.h +++ b/src/lib-sieve/sieve-validator.h @@ -43,6 +43,7 @@ struct sieve_error_handler * sieve_validator_error_handler(struct sieve_validator *valdtr); struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr); struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr); +const char *sieve_validator_script_cause(struct sieve_validator *valdtr); struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr); enum sieve_compile_flags sieve_validator_compile_flags(struct sieve_validator *valdtr); diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index 70cd01c85..409d65f21 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -325,8 +325,9 @@ int sieve_compile_script(struct sieve_script *script, return 0; } -int sieve_compile(struct sieve_instance *svinst, const char *script_location, - const char *script_name, struct sieve_error_handler *ehandler, +int sieve_compile(struct sieve_instance *svinst, const char *script_cause, + const char *storage_name, const char *script_name, + struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, struct sieve_binary **sbin_r, enum sieve_error *error_code_r) { @@ -336,8 +337,9 @@ int sieve_compile(struct sieve_instance *svinst, const char *script_location, *sbin_r = NULL; sieve_error_args_init(&error_code_r, NULL); - if (sieve_script_create_open(svinst, script_location, script_name, - &script, error_code_r, NULL) < 0) { + if (sieve_script_create_open_in(svinst, script_cause, + storage_name, script_name, + &script, error_code_r, NULL) < 0) { switch (*error_code_r) { case SIEVE_ERROR_NOT_FOUND: if (no_error_result) { @@ -499,8 +501,9 @@ int sieve_open_script(struct sieve_script *script, return ret; } -int sieve_open(struct sieve_instance *svinst, const char *script_location, - const char *script_name, struct sieve_error_handler *ehandler, +int sieve_open(struct sieve_instance *svinst, const char *script_cause, + const char *storage_name, const char *script_name, + struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, struct sieve_binary **sbin_r, enum sieve_error *error_code_r) { @@ -512,8 +515,9 @@ int sieve_open(struct sieve_instance *svinst, const char *script_location, sieve_error_args_init(&error_code_r, NULL); /* First open the scriptfile itself */ - if (sieve_script_create_open(svinst, script_location, script_name, - &script, error_code_r, NULL) < 0) { + if (sieve_script_create_open_in(svinst, script_cause, + storage_name, script_name, + &script, error_code_r, NULL) < 0) { /* Failed */ switch (*error_code_r) { case SIEVE_ERROR_NOT_FOUND: diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index 29bd320a2..8fcf93968 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -50,8 +50,9 @@ int sieve_compile_script(struct sieve_script *script, /* Compile a Sieve script from a Sieve script location string. Returns Sieve binary upon success and NULL upon failure. The provided script_name is used for the internally created Sieve script object. */ -int sieve_compile(struct sieve_instance *svinst, const char *script_location, - const char *script_name, struct sieve_error_handler *ehandler, +int sieve_compile(struct sieve_instance *svinst, const char *script_cause, + const char *storage_name, const char *script_name, + struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, struct sieve_binary **sbin_r, enum sieve_error *error_code_r); @@ -75,8 +76,9 @@ int sieve_open_script(struct sieve_script *script, not exist or if it contains errors, the script is (re-)compiled. Note that errors in the bytecode are caught only at runtime. */ -int sieve_open(struct sieve_instance *svinst, const char *script_location, - const char *script_name, struct sieve_error_handler *ehandler, +int sieve_open(struct sieve_instance *svinst, const char *script_cause, + const char *script_storage, const char *script_name, + struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, struct sieve_binary **sbin_r, enum sieve_error *error_code_r); diff --git a/src/lib-sieve/storage/data/sieve-data-script.c b/src/lib-sieve/storage/data/sieve-data-script.c index 6f50b29c1..f84933a0b 100644 --- a/src/lib-sieve/storage/data/sieve-data-script.c +++ b/src/lib-sieve/storage/data/sieve-data-script.c @@ -32,19 +32,20 @@ static struct sieve_data_script *sieve_data_script_alloc(void) struct sieve_script * sieve_data_script_create_from_input(struct sieve_instance *svinst, - const char *name, struct istream *input) + const char *cause, const char *name, + struct istream *input) { struct sieve_storage *storage; struct sieve_data_script *dscript = NULL; int ret; - ret = sieve_storage_alloc(svinst, NULL, &sieve_data_storage, - "", 0, FALSE, &storage, NULL, NULL); + ret = sieve_storage_alloc(svinst, svinst->event, &sieve_data_storage, + cause, "data", "data", "data", 0, &storage, + NULL, NULL); i_assert(ret >= 0); dscript = sieve_data_script_alloc(); - sieve_script_init(&dscript->script, storage, &sieve_data_script, - "data:", name); + sieve_script_init(&dscript->script, storage, &sieve_data_script, name); dscript->data = input; i_stream_ref(dscript->data); diff --git a/src/lib-sieve/storage/data/sieve-data-storage.c b/src/lib-sieve/storage/data/sieve-data-storage.c index 4f645532f..1e62fa8e0 100644 --- a/src/lib-sieve/storage/data/sieve-data-storage.c +++ b/src/lib-sieve/storage/data/sieve-data-storage.c @@ -26,8 +26,7 @@ static struct sieve_storage *sieve_data_storage_alloc(void) } static int -sieve_data_storage_init(struct sieve_storage *storage ATTR_UNUSED, - const char *const *options ATTR_UNUSED) +sieve_data_storage_init(struct sieve_storage *storage ATTR_UNUSED) { return 0; } diff --git a/src/lib-sieve/storage/data/sieve-data-storage.h b/src/lib-sieve/storage/data/sieve-data-storage.h index 79132105c..3bf31811b 100644 --- a/src/lib-sieve/storage/data/sieve-data-storage.h +++ b/src/lib-sieve/storage/data/sieve-data-storage.h @@ -25,6 +25,7 @@ struct sieve_data_script { struct sieve_script * sieve_data_script_create_from_input(struct sieve_instance *svinst, - const char *name, struct istream *input); + const char *cause, const char *name, + struct istream *input); #endif diff --git a/src/lib-sieve/storage/dict/sieve-dict-script.c b/src/lib-sieve/storage/dict/sieve-dict-script.c index 8e8b83653..25de7d9d0 100644 --- a/src/lib-sieve/storage/dict/sieve-dict-script.c +++ b/src/lib-sieve/storage/dict/sieve-dict-script.c @@ -36,18 +36,12 @@ sieve_dict_script_init(struct sieve_dict_storage *dstorage, const char *name) { struct sieve_storage *storage = &dstorage->storage; struct sieve_dict_script *dscript = NULL; - const char *location; - if (name == NULL) { + if (name == NULL || *name == '\0') name = SIEVE_DICT_SCRIPT_DEFAULT; - location = storage->location; - } else { - location = t_strconcat(storage->location, ";name=", name, NULL); - } dscript = sieve_dict_script_alloc(); - sieve_script_init(&dscript->script, storage, &sieve_dict_script, - location, name); + sieve_script_init(&dscript->script, storage, &sieve_dict_script, name); return dscript; } diff --git a/src/lib-sieve/storage/dict/sieve-dict-storage.c b/src/lib-sieve/storage/dict/sieve-dict-storage.c index 894d67530..4bfba324e 100644 --- a/src/lib-sieve/storage/dict/sieve-dict-storage.c +++ b/src/lib-sieve/storage/dict/sieve-dict-storage.c @@ -2,6 +2,7 @@ */ #include "lib.h" +#include "settings.h" #include "dict.h" #include "sieve-common.h" @@ -27,55 +28,21 @@ static struct sieve_storage *sieve_dict_storage_alloc(void) } static int -sieve_dict_storage_init(struct sieve_storage *storage, - const char *const *options) +sieve_dict_storage_init(struct sieve_storage *storage) { struct sieve_dict_storage *dstorage = container_of(storage, struct sieve_dict_storage, storage); - struct sieve_instance *svinst = storage->svinst; - const char *value, *uri = storage->location; - - if (options != NULL) { - while (*options != NULL) { - const char *option = *options; - - if (str_begins_icase(option, "user=", &value) && - *value != '\0') { - /* Ignore */ - } else { - sieve_storage_set_critical( - storage, "Invalid option '%s'", option); - return -1; - } - - options++; - } - } - - if (svinst->base_dir == NULL) { - sieve_storage_set_critical( - storage, - "BUG: Sieve interpreter is initialized without a base_dir"); - return -1; - } - - e_debug(storage->event, "user=%s, uri=%s", svinst->username, uri); - - storage->location = p_strconcat( - storage->pool, SIEVE_DICT_STORAGE_DRIVER_NAME, ":", - storage->location, ";user=", svinst->username, NULL); - - struct dict_legacy_settings dict_set; const char *error; int ret; - i_zero(&dict_set); - dict_set.base_dir = svinst->base_dir; - ret = dict_init_legacy(uri, &dict_set, &dstorage->dict, &error); - if (ret < 0) { + struct event *event = event_create(storage->event); + event_set_ptr(event, SETTINGS_EVENT_FILTER_NAME, "sieve_script_dict"); + ret = dict_init_auto(event, &dstorage->dict, &error); + event_unref(&event); + if (ret <= 0) { sieve_storage_set_critical(storage, - "Failed to initialize dict with data '%s' for user '%s': %s", - uri, svinst->username, error); + "Failed to initialize sieve_script %s dict: %s", + storage->name, error); return -1; } diff --git a/src/lib-sieve/storage/file/Makefile.am b/src/lib-sieve/storage/file/Makefile.am index 86798121a..104470d93 100644 --- a/src/lib-sieve/storage/file/Makefile.am +++ b/src/lib-sieve/storage/file/Makefile.am @@ -9,6 +9,7 @@ AM_CPPFLAGS = \ libsieve_storage_file_la_SOURCES = \ sieve-file-script.c \ sieve-file-script-sequence.c \ + sieve-file-storage-settings.c \ sieve-file-storage-active.c \ sieve-file-storage-save.c \ sieve-file-storage-list.c \ diff --git a/src/lib-sieve/storage/file/sieve-file-script.c b/src/lib-sieve/storage/file/sieve-file-script.c index 6f7f7bd73..6cc1dc938 100644 --- a/src/lib-sieve/storage/file/sieve-file-script.c +++ b/src/lib-sieve/storage/file/sieve-file-script.c @@ -3,11 +3,13 @@ #include "lib.h" #include "mempool.h" +#include "str.h" #include "path-util.h" #include "istream.h" #include "time-util.h" #include "eacces-error.h" +#include "sieve-dump.h" #include "sieve-binary.h" #include "sieve-script-private.h" @@ -125,10 +127,12 @@ int sieve_file_script_init_from_filename(struct sieve_file_storage *fstorage, fscript = sieve_file_script_alloc(); sieve_script_init(&fscript->script, storage, &sieve_file_script, - sieve_file_storage_path_extend(fstorage, filename), scriptname); fscript->filename = p_strdup(fscript->script.pool, filename); + event_add_str(fscript->script.event, "sieve_script_file_path", + sieve_file_storage_path_extend(fstorage, filename)); + *fscript_r = fscript; return 0; } @@ -173,8 +177,11 @@ int sieve_file_script_init_from_name(struct sieve_file_storage *fstorage, } fscript = sieve_file_script_alloc(); - sieve_script_init(&fscript->script, storage, &sieve_file_script, - fstorage->active_path, name); + sieve_script_init(&fscript->script, storage, &sieve_file_script, name); + + event_add_str(fscript->script.event, "sieve_script_file_path", + fstorage->active_path); + *fscript_r = fscript; return 0; } @@ -215,7 +222,9 @@ int sieve_file_script_init_from_path(struct sieve_file_storage *fstorage, *fscript_r = NULL; - if (sieve_file_storage_init_from_path(svinst, path, 0, &fsubstorage, + if (sieve_file_storage_init_from_path(svinst, storage->cause, + storage->type, storage->name, + path, 0, &fsubstorage, &error_code, &error) < 0) { sieve_storage_set_error(storage, error_code, "%s", error); return -1; @@ -224,9 +233,12 @@ int sieve_file_script_init_from_path(struct sieve_file_storage *fstorage, fscript = sieve_file_script_alloc(); sieve_script_init(&fscript->script, substorage, &sieve_file_script, - path, scriptname); + scriptname); sieve_storage_unref(&substorage); + event_add_str(fscript->script.event, "sieve_script_file_path", + fstorage->active_path); + *fscript_r = fscript; return 0; } @@ -309,7 +321,8 @@ static int sieve_file_script_open(struct sieve_script *script) st = fstorage->st; lnk_st = fstorage->lnk_st; - if (name == NULL) + if (name == NULL && storage->script_name != NULL && + *storage->script_name != '\0') name = storage->script_name; T_BEGIN { @@ -411,10 +424,11 @@ static int sieve_file_script_open(struct sieve_script *script) fscript->bin_path = p_strdup(pool, bin_path); fscript->bin_prefix = p_strdup(pool, bin_prefix); - fscript->script.location = fscript->path; - if (fscript->script.name == NULL) fscript->script.name = p_strdup(pool, basename); + + event_add_str(script->event, "sieve_script_file_path", + fscript->path); } } T_END; @@ -477,12 +491,36 @@ sieve_file_script_get_stream(struct sieve_script *script, static int sieve_file_script_binary_read_metadata(struct sieve_script *script, struct sieve_binary_block *sblock, - sieve_size_t *offset ATTR_UNUSED) + sieve_size_t *offset) { + struct sieve_instance *svinst = script->storage->svinst; struct sieve_file_script *fscript = container_of(script, struct sieve_file_script, script); - struct sieve_instance *svinst = script->storage->svinst; struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + string_t *path; + + /* Open if not open already */ + if (sieve_script_open(script, NULL) < 0) + return 0; + + /* Metadata: path */ + if (!sieve_binary_read_string(sblock, offset, &path)) { + e_error(script->event, + "Binary '%s' has invalid metadata for script '%s': " + "Invalid file path", + sieve_binary_path(sbin), sieve_script_label(script)); + return -1; + } + i_assert(fscript->path != NULL); + if (strcmp(str_c(path), fscript->path) != 0) { + e_debug(script->event, + "Binary '%s' reports different file path for script '%s' " + "('%s' rather than '%s')", + sieve_binary_path(sbin), sieve_script_label(script), + str_c(path), fscript->path); + return 0; + } + const struct stat *sstat, *bstat; bstat = sieve_binary_stat(sbin); @@ -516,6 +554,31 @@ sieve_file_script_binary_read_metadata(struct sieve_script *script, return 1; } +static void +sieve_file_script_binary_write_metadata(struct sieve_script *script, + struct sieve_binary_block *sblock) +{ + struct sieve_file_script *fscript = + container_of(script, struct sieve_file_script, script); + + sieve_binary_emit_cstring(sblock, fscript->path); +} + +static bool +sieve_file_script_binary_dump_metadata(struct sieve_script *script ATTR_UNUSED, + struct sieve_dumptime_env *denv, + struct sieve_binary_block *sblock, + sieve_size_t *offset) +{ + string_t *path; + + if (!sieve_binary_read_string(sblock, offset, &path)) + return FALSE; + sieve_binary_dumpf(denv, "file.path = %s\n", str_c(path)); + + return TRUE; +} + static int sieve_file_script_binary_load(struct sieve_script *script, struct sieve_binary **sbin_r) @@ -851,6 +914,9 @@ const struct sieve_script sieve_file_script = { .get_stream = sieve_file_script_get_stream, .binary_read_metadata = sieve_file_script_binary_read_metadata, + .binary_write_metadata = + sieve_file_script_binary_write_metadata, + .binary_dump_metadata = sieve_file_script_binary_dump_metadata, .binary_load = sieve_file_script_binary_load, .binary_save = sieve_file_script_binary_save, .binary_get_prefix = sieve_file_script_binary_get_prefix, diff --git a/src/lib-sieve/storage/file/sieve-file-storage-active.c b/src/lib-sieve/storage/file/sieve-file-storage-active.c index 491d97981..8415d2c3b 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage-active.c +++ b/src/lib-sieve/storage/file/sieve-file-storage-active.c @@ -113,8 +113,9 @@ sieve_file_storage_active_parse_link(struct sieve_file_storage *fstorage, if (strcmp(scriptpath, fstorage->path) != 0) { e_warning(storage->event, "Active sieve script symlink %s is broken: " - "Invalid/unknown path to storage (points to %s).", - fstorage->active_path, scriptpath); + "Invalid/unknown path to storage " + "(points to %s, expected %s)", + fstorage->active_path, scriptpath, fstorage->path); return NULL; } diff --git a/src/lib-sieve/storage/file/sieve-file-storage-settings.c b/src/lib-sieve/storage/file/sieve-file-storage-settings.c new file mode 100644 index 000000000..255bc6d1b --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-storage-settings.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2024 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "array.h" +#include "settings.h" +#include "settings-parser.h" + +#include "sieve-script.h" + +#include "sieve-file-storage-settings.h" + +#undef DEF +#define DEF(type, name) SETTING_DEFINE_STRUCT_##type( \ + "sieve_"#name, name, \ + struct sieve_file_storage_settings) + +static const struct setting_define sieve_file_storage_setting_defines[] = { + DEF(STR, script_path), + DEF(STR, script_active_path), + + SETTING_DEFINE_LIST_END, +}; + +static const struct sieve_file_storage_settings sieve_file_storage_default_settings = { + .script_path = "", + .script_active_path = "", +}; + +const struct setting_parser_info sieve_file_storage_setting_parser_info = { + .name = "sieve_file_storage", + + .defines = sieve_file_storage_setting_defines, + .defaults = &sieve_file_storage_default_settings, + + .struct_size = sizeof(struct sieve_file_storage_settings), + + .pool_offset1 = 1 + offsetof(struct sieve_file_storage_settings, pool), +}; diff --git a/src/lib-sieve/storage/file/sieve-file-storage-settings.h b/src/lib-sieve/storage/file/sieve-file-storage-settings.h index c8a9f8501..35e97d7d9 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage-settings.h +++ b/src/lib-sieve/storage/file/sieve-file-storage-settings.h @@ -3,4 +3,13 @@ #define SIEVE_FILE_DEFAULT_ACTIVE_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT +struct sieve_file_storage_settings { + pool_t pool; + + const char *script_path; + const char *script_active_path; +}; + +extern const struct setting_parser_info sieve_file_storage_setting_parser_info; + #endif diff --git a/src/lib-sieve/storage/file/sieve-file-storage.c b/src/lib-sieve/storage/file/sieve-file-storage.c index 90fc18e48..829ee2e13 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage.c +++ b/src/lib-sieve/storage/file/sieve-file-storage.c @@ -8,6 +8,7 @@ #include "mkdir-parents.h" #include "eacces-error.h" #include "unlink-old-files.h" +#include "settings.h" #include "mail-storage-private.h" #include "sieve.h" @@ -473,38 +474,19 @@ sieve_file_storage_init_common(struct sieve_file_storage *fstorage, } fstorage->path = p_strdup(storage->pool, storage_path); - storage->location = fstorage->path; - return 0; } static int sieve_file_storage_init_from_settings( struct sieve_file_storage *fstorage, - const char *const *options) + const struct sieve_file_storage_settings *set) { struct sieve_storage *storage = &fstorage->storage; - const char *storage_path = storage->location; - const char *value, *active_path = ""; + const char *storage_path = set->script_path; + const char *active_path = set->script_active_path; bool exists = FALSE; - if (options != NULL) { - while (*options != NULL) { - const char *option = *options; - - if (str_begins_icase(option, "active=", &value) && - *value != '\0') { - active_path = value; - } else { - sieve_storage_set_critical( - storage, "Invalid option '%s'", option); - return -1; - } - - options++; - } - } - /* Get full storage path */ if (sieve_file_storage_get_full_path(fstorage, &storage_path) < 0) @@ -512,9 +494,12 @@ sieve_file_storage_init_from_settings( /* Stat storage directory */ + bool is_personal = sieve_storage_is_personal(storage); + if (storage_path != NULL && *storage_path != '\0') { if (sieve_file_storage_stat(fstorage, storage_path) < 0) { - if (storage->error_code != SIEVE_ERROR_NOT_FOUND) + if (!is_personal || + storage->error_code != SIEVE_ERROR_NOT_FOUND) return -1; if ((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) { /* For backwards compatibility, recognize when @@ -561,7 +546,7 @@ sieve_file_storage_init_from_settings( } if ((active_path == NULL || *active_path == '\0') && - (storage->main_storage || + (is_personal || (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0)) { e_debug(storage->event, "Active script path is unconfigured; " @@ -574,15 +559,23 @@ sieve_file_storage_init_from_settings( storage_path, exists); } -static int -sieve_file_storage_init(struct sieve_storage *storage, - const char *const *options) +static int sieve_file_storage_init(struct sieve_storage *storage) { struct sieve_file_storage *fstorage = container_of(storage, struct sieve_file_storage, storage); + const struct sieve_file_storage_settings *fstorage_set; + const char *error; int ret; - ret = sieve_file_storage_init_from_settings(fstorage, options); + if (settings_get(storage->event, + &sieve_file_storage_setting_parser_info, 0, + &fstorage_set, &error) < 0) { + sieve_storage_set_critical(storage, "%s",error); + return -1; + } + + ret = sieve_file_storage_init_from_settings(fstorage, fstorage_set); + settings_free(fstorage_set); if (ret < 0) return -1; @@ -591,15 +584,16 @@ sieve_file_storage_init(struct sieve_storage *storage, static int sieve_file_storage_do_autodetect( - struct sieve_instance *svinst, struct event *event, - const char *active_path, + struct sieve_instance *svinst, struct event *event, const char *cause, + const struct sieve_storage_settings *storage_set, + const struct sieve_file_storage_settings *fstorage_set, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r) { const char *home = sieve_environment_get_homedir(svinst); int mode = ((flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ? R_OK|W_OK|X_OK : R_OK|X_OK); - const char *storage_path = NULL; + const char *storage_path = fstorage_set->script_path; if (storage_path == NULL || *storage_path == '\0') { /* We'll need to figure out the storage location ourself. @@ -631,12 +625,14 @@ sieve_file_storage_do_autodetect( struct sieve_storage *storage; struct sieve_file_storage *fstorage; + const char *active_path = NULL; bool exists = FALSE; int ret; - ret = sieve_storage_alloc(svinst, event, &sieve_file_storage, - "", flags, TRUE, &storage, - error_code_r, error_r); + ret = sieve_storage_alloc_with_settings(svinst, event, + &sieve_file_storage, cause, + storage_set, flags, &storage, + error_code_r, error_r); if (ret < 0) return -1; @@ -647,7 +643,7 @@ sieve_file_storage_do_autodetect( bool tried_active = FALSE; while (!tried_active) { if (storage_path == NULL || *storage_path == '\0') { - storage_path = active_path; + storage_path = fstorage_set->script_active_path; if (storage_path == NULL || *storage_path == '\0') storage_path = SIEVE_FILE_DEFAULT_ACTIVE_PATH; tried_active = TRUE; @@ -701,6 +697,7 @@ sieve_file_storage_do_autodetect( /* Success */ exists = TRUE; + active_path = fstorage_set->script_active_path; } else if ((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) { exists = TRUE; active_path = storage_path; @@ -723,32 +720,48 @@ sieve_file_storage_do_autodetect( } *storage_r = storage; - return 0; + return 1; } static int sieve_file_storage_autodetect(struct sieve_instance *svinst, - const char *active_path, + struct event *event, const char *cause, + const struct sieve_storage_settings *storage_set, enum sieve_storage_flags flags, struct sieve_storage **storage_r, enum sieve_error *error_code_r, const char **error_r) { - struct event *event = svinst->event; + const struct sieve_file_storage_settings *fstorage_set; int ret; + if (!sieve_storage_settings_match_script_type( + storage_set, SIEVE_STORAGE_TYPE_PERSONAL)) + return 0; + e_debug(event, "Performing auto-detection"); - T_BEGIN { - ret = sieve_file_storage_do_autodetect( - svinst, event, active_path, flags, - storage_r, error_code_r, error_r); - } T_END_PASS_STR_IF(ret < 0, error_r); - + const char *error; + + if (settings_get(event, &sieve_file_storage_setting_parser_info, 0, + &fstorage_set, &error) < 0) { + e_error(event, "%s", error); + sieve_error_create_internal(error_code_r, error_r); + return -1; + } + + ret = sieve_file_storage_do_autodetect( + svinst, event, cause, storage_set, fstorage_set, flags, + storage_r, error_code_r, error_r); + + settings_free(fstorage_set); return ret; } int sieve_file_storage_init_from_path(struct sieve_instance *svinst, + const char *cause, + const char *script_type, + const char *storage_name, const char *path, enum sieve_storage_flags flags, struct sieve_file_storage **fstorage_r, @@ -764,9 +777,10 @@ int sieve_file_storage_init_from_path(struct sieve_instance *svinst, *fstorage_r = NULL; sieve_error_args_init(&error_code_r, &error_r); - ret = sieve_storage_alloc(svinst, NULL, &sieve_file_storage, - "", flags, FALSE, &storage, - error_code_r, error_r); + ret = sieve_storage_alloc(svinst, svinst->event, &sieve_file_storage, + cause, script_type, storage_name, + sieve_script_file_get_scriptname(path), + flags, &storage, error_code_r, error_r); if (ret < 0) return -1; fstorage = container_of(storage, struct sieve_file_storage, storage); diff --git a/src/lib-sieve/storage/file/sieve-file-storage.h b/src/lib-sieve/storage/file/sieve-file-storage.h index e07d8bff9..0187beb80 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage.h +++ b/src/lib-sieve/storage/file/sieve-file-storage.h @@ -49,6 +49,9 @@ sieve_file_storage_path_extend(struct sieve_file_storage *fstorage, const char *filename); int sieve_file_storage_init_from_path(struct sieve_instance *svinst, + const char *cause, + const char *script_type, + const char *storage_name, const char *path, enum sieve_storage_flags flags, struct sieve_file_storage **fstorage_r, diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-db.c b/src/lib-sieve/storage/ldap/sieve-ldap-db.c index b5accce7d..bdfb9eb29 100644 --- a/src/lib-sieve/storage/ldap/sieve-ldap-db.c +++ b/src/lib-sieve/storage/ldap/sieve-ldap-db.c @@ -256,19 +256,19 @@ void db_ldap_request(struct ldap_connection *conn, static int db_ldap_connect_finish(struct ldap_connection *conn, int ret) { struct sieve_storage *storage = &conn->lstorage->storage; - const struct sieve_ldap_storage_settings *set = conn->lstorage->set; + const struct sieve_ldap_settings *set = conn->lstorage->ldap_set; if (ret == LDAP_SERVER_DOWN) { e_error(storage->event, "db: " "Can't connect to server: %s", - set->uris != NULL ? + *set->uris != '\0' ? set->uris : set->hosts); return -1; } if (ret != LDAP_SUCCESS) { e_error(storage->event, "db: " "binding failed (dn %s): %s", - set->dn == NULL ? "(none)" : set->dn, + *set->dn == '\0' ? "(none)" : set->dn, ldap_get_error(conn)); return -1; } @@ -277,7 +277,7 @@ static int db_ldap_connect_finish(struct ldap_connection *conn, int ret) conn->conn_state = LDAP_CONN_STATE_BOUND; e_debug(storage->event, "db: " "Successfully bound (dn %s)", - set->dn == NULL ? "(none)" : set->dn); + *set->dn == '\0' ? "(none)" : set->dn); while (db_ldap_request_queue_next(conn)) ; return 0; @@ -575,7 +575,7 @@ static void ldap_connection_timeout(struct ldap_connection *conn) static int db_ldap_bind(struct ldap_connection *conn) { - const struct sieve_ldap_storage_settings *set = conn->lstorage->set; + const struct sieve_ldap_settings *set = conn->lstorage->ldap_set; int msgid; i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING); @@ -653,7 +653,7 @@ db_ldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value, static int db_ldap_set_tls_options(struct ldap_connection *conn) { - const struct sieve_ldap_storage_settings *set = conn->lstorage->set; + const struct sieve_ldap_settings *set = conn->lstorage->ldap_set; if (!set->tls) return 0; @@ -674,7 +674,7 @@ static int db_ldap_set_tls_options(struct ldap_connection *conn) if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_CIPHER_SUITE, set->tls_cipher_suite, "tls_cipher_suite") < 0) return -1; - if (set->tls_require_cert != NULL) { + if (*set->tls_require_cert != '\0') { if (db_ldap_set_opt(conn, LDAP_OPT_X_TLS_REQUIRE_CERT, &set->parsed.tls_require_cert, "tls_require_cert", @@ -682,11 +682,11 @@ static int db_ldap_set_tls_options(struct ldap_connection *conn) return -1; } #else - if (set->tls_ca_cert_file != NULL || - set->tls_ca_cert_dir != NULL || - set->tls_cert_file != NULL || - set->tls_key_file != NULL || - set->tls_cipher_suite != NULL) { + if (*set->tls_ca_cert_file != '\0' || + *set->tls_ca_cert_dir != '\0' || + *set->tls_cert_file != '\0' || + *set->tls_key_file != '\0' || + *set->tls_cipher_suite != '\0') { e_warning(&conn->lstorage->storage, "db: " "tls_* settings ignored, " "your LDAP library doesn't seem to support them"); @@ -697,7 +697,7 @@ static int db_ldap_set_tls_options(struct ldap_connection *conn) static int db_ldap_set_options(struct ldap_connection *conn) { - const struct sieve_ldap_storage_settings *set = conn->lstorage->set; + const struct sieve_ldap_settings *set = conn->lstorage->ldap_set; struct sieve_storage *storage = &conn->lstorage->storage; unsigned int ldap_version; int value; @@ -737,7 +737,7 @@ static int db_ldap_set_options(struct ldap_connection *conn) int sieve_ldap_db_connect(struct ldap_connection *conn) { - const struct sieve_ldap_storage_settings *set = conn->lstorage->set; + const struct sieve_ldap_settings *set = conn->lstorage->ldap_set; struct sieve_storage *storage = &conn->lstorage->storage; struct timeval start, end; int debug_level; @@ -757,7 +757,7 @@ int sieve_ldap_db_connect(struct ldap_connection *conn) i_gettimeofday(&start); i_assert(conn->pending_count == 0); if (conn->ld == NULL) { - if (set->uris != NULL) { + if (*set->uris != '\0') { #ifdef LDAP_HAVE_INITIALIZE if (ldap_initialize(&conn->ld, set->uris) != LDAP_SUCCESS) @@ -787,7 +787,7 @@ int sieve_ldap_db_connect(struct ldap_connection *conn) ret = ldap_start_tls_s(conn->ld, NULL, NULL); if (ret != LDAP_SUCCESS) { if (ret == LDAP_OPERATIONS_ERROR && - set->uris != NULL && + *set->uris != '\0' && str_begins_with(set->uris, "ldaps:")) { e_error(storage->event, "db: " "Don't use both tls=yes and ldaps URI"); @@ -1052,7 +1052,7 @@ sieve_ldap_db_get_script_modattr(struct ldap_connection *conn, attr = ldap_first_attribute(conn->ld, entry, &ber); while (attr != NULL) { - if (strcmp(attr, set->sieve_ldap_mod_attr) == 0) { + if (strcmp(attr, set->mod_attr) == 0) { vals = ldap_get_values(conn->ld, entry, attr); if (vals == NULL || vals[0] == NULL) return 0; @@ -1061,7 +1061,7 @@ sieve_ldap_db_get_script_modattr(struct ldap_connection *conn, e_warning(storage->event, "db: " "Search returned more than one Sieve modified attribute '%s'; " "using only the first one.", - set->sieve_ldap_mod_attr); + set->mod_attr); } *modattr_r = p_strdup(pool, vals[0]); @@ -1092,7 +1092,7 @@ sieve_ldap_db_get_script(struct ldap_connection *conn, LDAPMessage *entry, attr = ldap_first_attribute(conn->ld, entry, &ber); while (attr != NULL) { - if (strcmp(attr, set->sieve_ldap_script_attr) == 0) { + if (strcmp(attr, set->script_attr) == 0) { vals = ldap_get_values_len(conn->ld, entry, attr); if (vals == NULL || vals[0] == NULL) return 0; @@ -1101,7 +1101,7 @@ sieve_ldap_db_get_script(struct ldap_connection *conn, LDAPMessage *entry, e_warning(storage->event, "db: " "Search returned more than one Sieve script attribute '%s'; " "using only the first one.", - set->sieve_ldap_script_attr); + set->script_attr); } size = vals[0]->bv_len; @@ -1210,6 +1210,7 @@ int sieve_ldap_db_lookup_script(struct ldap_connection *conn, const char *name, { struct sieve_ldap_storage *lstorage = conn->lstorage; struct sieve_storage *storage = &lstorage->storage; + const struct sieve_ldap_settings *ldap_set = lstorage->ldap_set; const struct sieve_ldap_storage_settings *set = lstorage->set; struct sieve_ldap_script_lookup_request *request; char **attr_names; @@ -1226,31 +1227,31 @@ int sieve_ldap_db_lookup_script(struct ldap_connection *conn, const char *name, }; str = t_str_new(512); - if (var_expand(str, set->base, ¶ms, &error) < 0) { + if (var_expand(str, ldap_set->base, ¶ms, &error) < 0) { e_error(storage->event, "db: " "Failed to expand base=%s: %s", - set->base, error); + ldap_set->base, error); return -1; } request->request.base = p_strdup(pool, str_c(str)); attr_names = p_new(pool, char *, 3); - attr_names[0] = p_strdup(pool, set->sieve_ldap_mod_attr); + attr_names[0] = p_strdup(pool, set->mod_attr); str_truncate(str, 0); - if (var_expand(str, set->sieve_ldap_filter, ¶ms, &error) < 0) { + if (var_expand(str, set->filter, ¶ms, &error) < 0) { e_error(storage->event, "db: " "Failed to expand sieve_ldap_filter=%s: %s", - set->sieve_ldap_filter, error); + set->filter, error); return -1; } - request->request.scope = lstorage->set->parsed.scope; + request->request.scope = ldap_set->parsed.scope; request->request.filter = p_strdup(pool, str_c(str)); request->request.attributes = attr_names; e_debug(storage->event, "base=%s scope=%s filter=%s fields=%s", - request->request.base, lstorage->set->scope, + request->request.base, ldap_set->scope, request->request.filter, t_strarray_join((const char **)attr_names, ",")); @@ -1317,7 +1318,7 @@ int sieve_ldap_db_read_script(struct ldap_connection *conn, request->request.base = p_strdup(pool, dn); attr_names = p_new(pool, char *, 3); - attr_names[0] = p_strdup(pool, set->sieve_ldap_script_attr); + attr_names[0] = p_strdup(pool, set->script_attr); request->request.scope = LDAP_SCOPE_BASE; request->request.filter = "(objectClass=*)"; diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-script.c b/src/lib-sieve/storage/ldap/sieve-ldap-script.c index c372cf69a..e282ecbca 100644 --- a/src/lib-sieve/storage/ldap/sieve-ldap-script.c +++ b/src/lib-sieve/storage/ldap/sieve-ldap-script.c @@ -38,18 +38,12 @@ sieve_ldap_script_init(struct sieve_ldap_storage *lstorage, const char *name) { struct sieve_storage *storage = &lstorage->storage; struct sieve_ldap_script *lscript = NULL; - const char *location; - if (name == NULL) { + if (name == NULL || *name == '\0') name = SIEVE_LDAP_SCRIPT_DEFAULT; - location = storage->location; - } else { - location = t_strconcat(storage->location, ";name=", name, NULL); - } lscript = sieve_ldap_script_alloc(); - sieve_script_init(&lscript->script, storage, &sieve_ldap_script, - location, name); + sieve_script_init(&lscript->script, storage, &sieve_ldap_script, name); return lscript; } @@ -118,26 +112,12 @@ sieve_ldap_script_binary_read_metadata(struct sieve_script *script, struct sieve_ldap_script *lscript = container_of(script, struct sieve_ldap_script, script); struct sieve_storage *storage = script->storage; - struct sieve_instance *svinst = storage->svinst; struct sieve_ldap_storage *lstorage = container_of(storage, struct sieve_ldap_storage, storage); struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); - time_t bmtime = sieve_binary_mtime(sbin); string_t *dn, *modattr; - /* Config file changed? */ - if (bmtime <= lstorage->set_mtime) { - if (svinst->debug) { - e_debug(script->event, - "Sieve binary '%s' is not newer " - "than the LDAP configuration '%s' (%s <= %s)", - sieve_binary_path(sbin), lstorage->config_file, - t_strflocaltime("%Y-%m-%d %H:%M:%S", bmtime), - t_strflocaltime("%Y-%m-%d %H:%M:%S", - lstorage->set_mtime)); - } - return 0; - } + // FIXME: Maybe detect config changes somehow to trigger recompile /* Open script if not open already */ if (lscript->dn == NULL && sieve_script_open(script, NULL) < 0) @@ -149,7 +129,7 @@ sieve_ldap_script_binary_read_metadata(struct sieve_script *script, "LDAP entry for script '%s' " "has no modified attribute '%s'", sieve_script_label(script), - lstorage->set->sieve_ldap_mod_attr); + lstorage->set->mod_attr); return 0; } diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c index a61b8c03d..a385b6a6f 100644 --- a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c +++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c @@ -3,11 +3,12 @@ #include "lib.h" #include "env-util.h" -#include "settings-legacy.h" +#include "settings-parser.h" #include "sieve-common.h" #include "sieve-ldap-storage.h" +#include "sieve-ldap-storage-settings.h" #if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD) @@ -15,15 +16,15 @@ #include "sieve-ldap-db.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - #undef DEF #define DEF(type, name) \ - DEF_STRUCT_##type(name, sieve_ldap_storage_settings) + SETTING_DEFINE_STRUCT_##type("ldap_"#name, name, \ + struct sieve_ldap_settings) + +static bool +sieve_ldap_settings_check(void *_set, pool_t pool, const char **error_r); -static struct setting_def setting_defs[] = { +static const struct setting_define sieve_ldap_setting_defines[] = { DEF(STR, hosts), DEF(STR, uris), DEF(STR, dn), @@ -39,54 +40,80 @@ static struct setting_def setting_defs[] = { DEF(STR, tls_key_file), DEF(STR, tls_cipher_suite), DEF(STR, tls_require_cert), - DEF(STR, deref), - DEF(STR, scope), + DEF(ENUM, deref), + DEF(ENUM, scope), DEF(STR, base), - DEF(INT, ldap_version), + DEF(UINT, ldap_version), DEF(STR, debug_level), DEF(STR, ldaprc_path), - DEF(STR, sieve_ldap_script_attr), - DEF(STR, sieve_ldap_mod_attr), - DEF(STR, sieve_ldap_filter), - { 0, NULL, 0 } + SETTING_DEFINE_LIST_END }; -static struct sieve_ldap_storage_settings default_settings = { - .hosts = NULL, - .uris = NULL, - .dn = NULL, - .dnpass = NULL, +const struct sieve_ldap_settings sieve_ldap_default_settings = { + .hosts = "", + .uris = "", + .dn = "", + .dnpass = "", .tls = FALSE, .sasl_bind = FALSE, - .sasl_mech = NULL, - .sasl_realm = NULL, - .sasl_authz_id = NULL, - .tls_ca_cert_file = NULL, - .tls_ca_cert_dir = NULL, - .tls_cert_file = NULL, - .tls_key_file = NULL, - .tls_cipher_suite = NULL, - .tls_require_cert = NULL, - .deref = "never", - .scope = "subtree", - .base = NULL, + .sasl_mech = "", + .sasl_realm = "", + .sasl_authz_id = "", + .tls_ca_cert_file = "", + .tls_ca_cert_dir = "", + .tls_cert_file = "", + .tls_key_file = "", + .tls_cipher_suite = "", + .tls_require_cert = "", + .deref = "never:searching:finding:always", + .scope = "subtree:onelevel:base", + .base = "", .ldap_version = 3, .debug_level = "0", .ldaprc_path = "", - .sieve_ldap_script_attr = "mailSieveRuleSource", - .sieve_ldap_mod_attr = "modifyTimestamp", - .sieve_ldap_filter = "(&(objectClass=posixAccount)(uid=%u))", }; -static const char * -parse_setting(const char *key, const char *value, - struct sieve_ldap_storage *lstorage) -{ - return parse_setting_from_defs(lstorage->storage.pool, setting_defs, - lstorage->set, key, value); -} +const struct setting_parser_info sieve_ldap_setting_parser_info = { + .name = "sieve_ldap", + .defines = sieve_ldap_setting_defines, + .defaults = &sieve_ldap_default_settings, + + .pool_offset1 = 1 + offsetof(struct sieve_ldap_settings, pool), + .struct_size = sizeof(struct sieve_ldap_settings), + .check_func = sieve_ldap_settings_check, +}; + +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type("sieve_script_ldap_"#name, name, \ + struct sieve_ldap_storage_settings) + +static const struct setting_define sieve_ldap_storage_setting_defines[] = { + DEF(STR, script_attr), + DEF(STR, mod_attr), + DEF(STR, filter), + SETTING_DEFINE_LIST_END +}; + +static struct sieve_ldap_storage_settings sieve_ldap_storage_server_default_settings = { + .script_attr = "mailSieveRuleSource", + .mod_attr = "modifyTimestamp", + .filter = "(&(objectClass=posixAccount)(uid=%u))", +}; + +const struct setting_parser_info sieve_ldap_storage_setting_parser_info = { + .name = "sieve_ldap_storage", + + .defines = sieve_ldap_storage_setting_defines, + .defaults = &sieve_ldap_storage_server_default_settings, + + .pool_offset1 = 1 + offsetof(struct sieve_ldap_storage_settings, pool), + .struct_size = sizeof(struct sieve_ldap_storage_settings), +}; + +/* <settings checks> */ static int ldap_deref_from_str(const char *str, int *deref_r) { if (strcasecmp(str, "never") == 0) @@ -135,25 +162,22 @@ static int ldap_tls_require_cert_from_str(const char *str, int *opt_x_tls_r) #endif static bool -sieve_ldap_settings_check(struct sieve_ldap_storage_settings *set, +sieve_ldap_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { + struct sieve_ldap_settings *set = _set; const char *str; - if (set->base == NULL) { - *error_r = "No search base given"; - return FALSE; - } - - if (set->uris == NULL && set->hosts == NULL) { - *error_r = "No uris or hosts set"; + if (set->base[0] == '\0' && + settings_get_config_binary() == SETTINGS_BINARY_OTHER) { + *error_r = "ldap: No search base given"; return FALSE; } if (*set->ldaprc_path != '\0') { str = getenv("LDAPRC"); if (str != NULL && strcmp(str, set->ldaprc_path) != 0) { - *error_r = t_strdup_printf( + *error_r = t_strdup_printf("ldap: " "Multiple different ldaprc_path settings not allowed " "(%s and %s)", str, set->ldaprc_path); return FALSE; @@ -162,23 +186,23 @@ sieve_ldap_settings_check(struct sieve_ldap_storage_settings *set, } if (ldap_deref_from_str(set->deref, &set->parsed.deref) < 0) { - *error_r = t_strdup_printf( + *error_r = t_strdup_printf("ldap: " "Invalid deref option '%s'", set->deref); return FALSE; } if (ldap_scope_from_str(set->scope, &set->parsed.scope) < 0) { - *error_r = t_strdup_printf( + *error_r = t_strdup_printf("ldap: " "Invalid scope option '%s'", set->scope); return FALSE; } #ifdef OPENLDAP_TLS_OPTIONS - if (set->tls_require_cert != NULL && + if (*set->tls_require_cert != '\0' && ldap_tls_require_cert_from_str( set->tls_require_cert, &set->parsed.tls_require_cert) < 0) { - *error_r = t_strdup_printf( + *error_r = t_strdup_printf("ldap: " "Invalid tls_require_cert option '%s'", set->tls_require_cert); return FALSE; @@ -187,41 +211,6 @@ sieve_ldap_settings_check(struct sieve_ldap_storage_settings *set, return TRUE; } - -int sieve_ldap_storage_read_settings(struct sieve_ldap_storage *lstorage, - const char *config_path) -{ - struct sieve_storage *storage = &lstorage->storage; - const char *error; - struct stat st; - - if (stat(config_path, &st) < 0) { - sieve_storage_set_critical(storage, - "Failed to read LDAP storage config: " - "stat(%s) failed: %m", config_path); - return -1; - } - - lstorage->set = p_new(storage->pool, - struct sieve_ldap_storage_settings, 1); - *lstorage->set = default_settings; - lstorage->set_mtime = st.st_mtime; - - if (!settings_read_nosection(config_path, parse_setting, lstorage, - &error)) { - sieve_storage_set_critical(storage, - "Failed to read LDAP storage config '%s': %s", - config_path, error); - return -1; - } - - if (!sieve_ldap_settings_check(lstorage->set, &error)) { - sieve_storage_set_critical(storage, - "Invalid LDAP storage config '%s': %s", - config_path, error); - return -1; - } - return 0; -} +/* </settings checks> */ #endif diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h index 61587a696..22f90db74 100644 --- a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h +++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h @@ -1,7 +1,9 @@ #ifndef SIEVE_LDAP_STORAGE_SETTINGS_H #define SIEVE_LDAP_STORAGE_SETTINGS_H -struct sieve_ldap_storage_settings { +struct sieve_ldap_settings { + pool_t pool; + const char *hosts; const char *uris; const char *dn; @@ -28,14 +30,21 @@ struct sieve_ldap_storage_settings { const char *ldaprc_path; const char *debug_level; - const char *sieve_ldap_script_attr; - const char *sieve_ldap_mod_attr; - const char *sieve_ldap_filter; - /* ... */ struct { int deref, scope, tls_require_cert; } parsed; }; +struct sieve_ldap_storage_settings { + pool_t pool; + + const char *script_attr; + const char *mod_attr; + const char *filter; +}; + +extern const struct setting_parser_info sieve_ldap_setting_parser_info; +extern const struct setting_parser_info sieve_ldap_storage_setting_parser_info; + #endif diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage.c b/src/lib-sieve/storage/ldap/sieve-ldap-storage.c index 8a7e0980c..11ff505d4 100644 --- a/src/lib-sieve/storage/ldap/sieve-ldap-storage.c +++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage.c @@ -2,6 +2,7 @@ */ #include "lib.h" +#include "settings.h" //#include "ldap.h" #include "sieve-common.h" @@ -41,44 +42,44 @@ static struct sieve_storage *sieve_ldap_storage_alloc(void) } static int -sieve_ldap_storage_init(struct sieve_storage *storage, - const char *const *options) +sieve_ldap_storage_init(struct sieve_storage *storage) { struct sieve_ldap_storage *lstorage = container_of(storage, struct sieve_ldap_storage, storage); - struct sieve_instance *svinst = storage->svinst; - const char *value; - - if (options != NULL) { - while (*options != NULL) { - const char *option = *options; - - if (str_begins_icase(option, "user=", &value) && - *value != '\0') { - /* Ignore */ - } else { - sieve_storage_set_critical( - storage, "Invalid option '%s'", option); - return -1; - } - - options++; - } + const struct sieve_ldap_settings *ldap_set; + const struct sieve_ldap_storage_settings *set; + const char *error; + int ret; + + struct event *event = event_create(storage->event); + event_set_ptr(event, SETTINGS_EVENT_FILTER_NAME, "ldap"); + ret = settings_get(event, &sieve_ldap_setting_parser_info, 0, + &ldap_set, &error); + event_unref(&event); + if (ret < 0) { + sieve_storage_set_critical(storage, "%s", error); + return -1; + } + if (*ldap_set->uris == '\0' && *ldap_set->hosts == '\0') { + sieve_storage_set_critical(storage, + "sieve_script %s { ldap_uris / ldap_hosts } not set", + storage->name); + settings_free(ldap_set); + return -1; } - e_debug(storage->event, "user=%s, config=%s", - svinst->username, storage->location); - - if (sieve_ldap_storage_read_settings(lstorage, storage->location) < 0) + if (settings_get(storage->event, + &sieve_ldap_storage_setting_parser_info, 0, + &set, &error) < 0) { + sieve_storage_set_critical(storage, "%s", error); + settings_free(ldap_set); return -1; + } - lstorage->config_file = p_strdup(storage->pool, storage->location); + lstorage->ldap_set = ldap_set; + lstorage->set = set; lstorage->conn = sieve_ldap_db_init(lstorage); - storage->location = p_strconcat( - storage->pool, SIEVE_LDAP_STORAGE_DRIVER_NAME, ":", - storage->location, ";user=", svinst->username, NULL); - return 0; } @@ -88,6 +89,8 @@ static void sieve_ldap_storage_destroy(struct sieve_storage *storage) container_of(storage, struct sieve_ldap_storage, storage); sieve_ldap_db_unref(&lstorage->conn); + settings_free(lstorage->ldap_set); + settings_free(lstorage->set); } /* diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage.h b/src/lib-sieve/storage/ldap/sieve-ldap-storage.h index 703805628..394ddcc2c 100644 --- a/src/lib-sieve/storage/ldap/sieve-ldap-storage.h +++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage.h @@ -14,13 +14,6 @@ struct sieve_ldap_storage; -/* - * LDAP settings - */ - -int sieve_ldap_storage_read_settings(struct sieve_ldap_storage *lstorage, - const char *config_path); - /* * Storage class */ @@ -28,11 +21,10 @@ int sieve_ldap_storage_read_settings(struct sieve_ldap_storage *lstorage, struct sieve_ldap_storage { struct sieve_storage storage; - struct sieve_ldap_storage_settings *set; + const struct sieve_ldap_settings *ldap_set; + const struct sieve_ldap_storage_settings *set; time_t set_mtime; - const char *config_file; - struct ldap_connection *conn; }; diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c index 725946108..09e204edd 100644 --- a/src/managesieve/managesieve-client.c +++ b/src/managesieve/managesieve-client.c @@ -74,6 +74,7 @@ client_get_storage(struct sieve_instance *svinst, struct mail_user *user, /* Open personal script storage */ if (sieve_storage_create_personal(svinst, user, + SIEVE_SCRIPT_CAUSE_DELIVERY, SIEVE_STORAGE_FLAG_READWRITE, &storage, &error_code) < 0) { switch (error_code) { diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c index 53127b90b..be3915c7f 100644 --- a/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c @@ -118,6 +118,7 @@ doveadm_sieve_cmd_run(struct doveadm_mail_cmd_context *_ctx, return -1; if (sieve_storage_create_personal(ctx->svinst, user, + SIEVE_SCRIPT_CAUSE_ANY, SIEVE_STORAGE_FLAG_READWRITE, &ctx->storage, &error_code) < 0) { switch (error_code) { diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-sync.c b/src/plugins/doveadm-sieve/doveadm-sieve-sync.c index 2b1cae32f..baec83ebf 100644 --- a/src/plugins/doveadm-sieve/doveadm-sieve-sync.c +++ b/src/plugins/doveadm-sieve/doveadm-sieve-sync.c @@ -99,7 +99,8 @@ mail_sieve_user_init(struct mail_user *user, struct sieve_storage **svstorage_r) user->set->mail_debug, &suser->svinst) < 0) return -1; - if (sieve_storage_create_personal(suser->svinst, user, storage_flags, + if (sieve_storage_create_personal(suser->svinst, user, + SIEVE_SCRIPT_CAUSE_ANY, storage_flags, &suser->sieve_storage, &error_code) < 0) { switch (error_code) { diff --git a/src/plugins/imap-filter-sieve/imap-filter-sieve.c b/src/plugins/imap-filter-sieve/imap-filter-sieve.c index 1bc4f5295..0057dcd79 100644 --- a/src/plugins/imap-filter-sieve/imap-filter-sieve.c +++ b/src/plugins/imap-filter-sieve/imap-filter-sieve.c @@ -189,8 +189,9 @@ imap_filter_sieve_get_personal_storage(struct imap_filter_sieve_context *sctx, return -1; } - if (sieve_storage_create_personal(svinst, user, storage_flags, - &ifsuser->storage, + if (sieve_storage_create_personal(svinst, user, + SIEVE_SCRIPT_CAUSE_DELIVERY, + storage_flags, &ifsuser->storage, &error_code) == 0) { *storage_r = ifsuser->storage; return 0; @@ -437,7 +438,8 @@ void imap_filter_sieve_open_input(struct imap_filter_sieve_context *sctx, svinst = imap_filter_sieve_get_svinst(sctx); i_assert(svinst != NULL); - script = sieve_data_script_create_from_input(svinst, "script", input); + script = sieve_data_script_create_from_input( + svinst, SIEVE_SCRIPT_CAUSE_DELIVERY, "script", input); sctx->user_script = script; sctx->scripts = p_new(sctx->pool, struct imap_filter_sieve_script, 1); @@ -493,7 +495,6 @@ int imap_filter_sieve_open_global(struct imap_filter_sieve_context *sctx, const char **error_r) { struct sieve_instance *svinst; - const char *location; struct sieve_script *script; enum sieve_error error_code; @@ -504,17 +505,8 @@ int imap_filter_sieve_open_global(struct imap_filter_sieve_context *sctx, return -1; } - location = mail_user_plugin_getenv(sctx->user, "sieve_global"); - if (location == NULL) { - e_info(sieve_get_event(svinst), - "include: sieve_global is unconfigured; " - "include of ':global' script is therefore not possible"); - *error_code_r = MAIL_ERROR_NOTFOUND; - *error_r = "No global Sieve scripts available"; - return -1; - } - - if (sieve_script_create_open(svinst, location, name, + if (sieve_script_create_open(svinst, SIEVE_SCRIPT_CAUSE_DELIVERY, + SIEVE_STORAGE_TYPE_GLOBAL, name, &script, &error_code, error_r) < 0) { switch (error_code) { case SIEVE_ERROR_NOT_FOUND: diff --git a/src/plugins/imapsieve/imap-sieve-settings.c b/src/plugins/imapsieve/imap-sieve-settings.c index 2291cf277..a7afe0cf7 100644 --- a/src/plugins/imapsieve/imap-sieve-settings.c +++ b/src/plugins/imapsieve/imap-sieve-settings.c @@ -18,12 +18,20 @@ static const struct setting_define imap_sieve_setting_defines[] = { DEF(STR, url), DEF(BOOL, expunge_discarded), + { .type = SET_FILTER_ARRAY, .key = "imapsieve_from", + .offset = offsetof(struct imap_sieve_settings, from), + .filter_array_field_name = "imapsieve_from_name" }, + DEF(STR, from_name), + SETTING_DEFINE_LIST_END, }; static const struct imap_sieve_settings imap_sieve_default_settings = { .url = "", .expunge_discarded = FALSE, + + .from = ARRAY_INIT, + .from_name = "", }; static bool diff --git a/src/plugins/imapsieve/imap-sieve-settings.h b/src/plugins/imapsieve/imap-sieve-settings.h index 3b4eb52f5..f3ddaa35a 100644 --- a/src/plugins/imapsieve/imap-sieve-settings.h +++ b/src/plugins/imapsieve/imap-sieve-settings.h @@ -6,6 +6,9 @@ struct imap_sieve_settings { const char *url; bool expunge_discarded; + + ARRAY_TYPE(const_string) from; + const char *from_name; }; extern const struct setting_parser_info imap_sieve_setting_parser_info; diff --git a/src/plugins/imapsieve/imap-sieve-storage.c b/src/plugins/imapsieve/imap-sieve-storage.c index d9a849df1..3b2586749 100644 --- a/src/plugins/imapsieve/imap-sieve-storage.c +++ b/src/plugins/imapsieve/imap-sieve-storage.c @@ -33,7 +33,6 @@ #define IMAP_SIEVE_MAIL_CONTEXT(obj) \ MODULE_CONTEXT_REQUIRE(obj, imap_sieve_mail_module) -struct imap_sieve_mailbox_rule; struct imap_sieve_user; struct imap_sieve_mailbox_event; struct imap_sieve_mailbox_transaction; @@ -48,24 +47,9 @@ enum imap_sieve_command { IMAP_SIEVE_CMD_OTHER }; -ARRAY_DEFINE_TYPE(imap_sieve_mailbox_rule, - struct imap_sieve_mailbox_rule *); ARRAY_DEFINE_TYPE(imap_sieve_mailbox_event, struct imap_sieve_mailbox_event); -HASH_TABLE_DEFINE_TYPE(imap_sieve_mailbox_rule, - struct imap_sieve_mailbox_rule *, - struct imap_sieve_mailbox_rule *); - -struct imap_sieve_mailbox_rule { - unsigned int index; - const char *mailbox; - const char *from; - const char *const *causes; - const char *before, *after; - const char *copy_source_after; -}; - struct imap_sieve_user { union mail_user_module_context module_ctx; struct client *client; @@ -74,9 +58,6 @@ struct imap_sieve_user { enum imap_sieve_command cur_cmd; - HASH_TABLE_TYPE(imap_sieve_mailbox_rule) mbox_rules; - ARRAY_TYPE(imap_sieve_mailbox_rule) mbox_patterns; - bool sieve_active:1; bool user_script:1; bool expunge_discarded:1; @@ -120,12 +101,6 @@ static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_storage_module, static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_mail_module, &mail_module_register); -static void -imap_sieve_mailbox_rules_get(struct mail_user *user, - struct mailbox *dst_box, struct mailbox *src_box, - const char *cause, - ARRAY_TYPE(imap_sieve_mailbox_rule) *rules); - struct event_category event_category_imap_sieve = { .name = "imapsieve", }; @@ -592,51 +567,20 @@ imap_sieve_mailbox_transaction_run( /* Initialize execution */ T_BEGIN { - ARRAY_TYPE(imap_sieve_mailbox_rule) mbrules; - ARRAY_TYPE(const_string) scripts_before, scripts_after; - ARRAY_TYPE(const_string) scripts_copy_source; - struct imap_sieve_mailbox_rule *rule; - - /* Find matching rules */ - t_array_init(&mbrules, 16); - imap_sieve_mailbox_rules_get(user, dest_box, src_box, cause, - &mbrules); - - /* Apply all matched rules */ - t_array_init(&scripts_before, 8); - t_array_init(&scripts_after, 8); - t_array_init(&scripts_copy_source, 4); - array_foreach_elem(&mbrules, rule) { - if (rule->before != NULL) - array_append(&scripts_before, &rule->before, 1); - if (rule->after != NULL) - array_append(&scripts_after, &rule->after, 1); - if (rule->copy_source_after != NULL) { - array_append(&scripts_copy_source, - &rule->copy_source_after, 1); - } - } - (void)array_append_space(&scripts_before); - (void)array_append_space(&scripts_after); - /* Initialize */ - ret = imap_sieve_run_init(isuser->isieve, dest_box, src_box, - cause, script_name, - array_idx(&scripts_before, 0), - array_idx(&scripts_after, 0), &isrun); + ret = imap_sieve_run_init(isuser->isieve, dest_isbox->event, + dest_box, src_box, cause, script_name, + SIEVE_STORAGE_TYPE_BEFORE, + SIEVE_STORAGE_TYPE_AFTER, &isrun); /* Initialize source script execution */ isrun_src = NULL; if (ret > 0 && ismt->src_mail_trans != NULL && - isuser->cur_cmd == IMAP_SIEVE_CMD_COPY && - array_count(&scripts_copy_source) > 0) { - const char *no_scripts = NULL; - - (void)array_append_space(&scripts_copy_source); + isuser->cur_cmd == IMAP_SIEVE_CMD_COPY) { if (imap_sieve_run_init( - isuser->isieve, dest_box, src_box, cause, NULL, - &no_scripts, array_idx(&scripts_copy_source, 0), - &isrun_src) <= 0) + isuser->isieve, dest_isbox->event, + dest_box, src_box, cause, NULL, + NULL, "copy-source-after", &isrun_src) <= 0) isrun_src = NULL; } } T_END; @@ -808,289 +752,6 @@ static void imap_sieve_mailbox_allocated(struct mailbox *box) MODULE_CONTEXT_SET(box, imap_sieve_storage_module, isbox); } -/* - * Mailbox rules - */ - -static unsigned int -imap_sieve_mailbox_rule_hash(const struct imap_sieve_mailbox_rule *rule) -{ - unsigned int hash = str_hash(rule->mailbox); - - if (rule->from != NULL) - hash += str_hash(rule->from); - return hash; -} - -static int -imap_sieve_mailbox_rule_cmp(const struct imap_sieve_mailbox_rule *rule1, - const struct imap_sieve_mailbox_rule *rule2) -{ - int ret; - - ret = strcmp(rule1->mailbox, rule2->mailbox); - if (ret != 0) - return ret; - return null_strcmp(rule1->from, rule2->from); -} - -static bool rule_pattern_has_wildcards(const char *pattern) -{ - for (; *pattern != '\0'; pattern++) { - if (*pattern == '%' || *pattern == '*') - return TRUE; - } - return FALSE; -} - -static void imap_sieve_mailbox_rules_init(struct mail_user *user) -{ - struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); - string_t *identifier; - unsigned int i = 0; - size_t prefix_len; - - if (hash_table_is_created(isuser->mbox_rules)) - return; - - hash_table_create(&isuser->mbox_rules, default_pool, 0, - imap_sieve_mailbox_rule_hash, - imap_sieve_mailbox_rule_cmp); - i_array_init(&isuser->mbox_patterns, 8); - - identifier = t_str_new(256); - str_append(identifier, "imapsieve_mailbox"); - prefix_len = str_len(identifier); - - for (i = 1; ; i++) { - struct imap_sieve_mailbox_rule *mbrule; - const char *setval; - size_t id_len; - - str_truncate(identifier, prefix_len); - str_printfa(identifier, "%u", i); - id_len = str_len(identifier); - - str_append(identifier, "_name"); - setval = mail_user_plugin_getenv(user, str_c(identifier)); - if (setval == NULL || *setval == '\0') - break; - setval = t_str_trim(setval, "\t "); - if (strcasecmp(setval, "INBOX") == 0) - setval = t_str_ucase(setval); - - mbrule = p_new(user->pool, struct imap_sieve_mailbox_rule, 1); - mbrule->index = i; - mbrule->mailbox = p_strdup(user->pool, setval); - - str_truncate(identifier, id_len); - str_append(identifier, "_from"); - setval = mail_user_plugin_getenv(user, str_c(identifier)); - if (setval != NULL && *setval != '\0') { - setval = t_str_trim(setval, "\t "); - if (strcasecmp(setval, "INBOX") == 0) - setval = t_str_ucase(setval); - mbrule->from = p_strdup(user->pool, setval); - if (strcmp(mbrule->from, "*") == 0) - mbrule->from = NULL; - } - - if ((strcmp(mbrule->mailbox, "*") == 0 || - !rule_pattern_has_wildcards(mbrule->mailbox)) && - (mbrule->from == NULL || - !rule_pattern_has_wildcards(mbrule->from)) && - hash_table_lookup(isuser->mbox_rules, mbrule) != NULL) { - e_warning(isuser->event, - "Duplicate static mailbox rule [%u] for mailbox '%s' " - "(skipped)", i, mbrule->mailbox); - continue; - } - - str_truncate(identifier, id_len); - str_append(identifier, "_causes"); - setval = mail_user_plugin_getenv(user, str_c(identifier)); - if (setval != NULL && *setval != '\0') { - const char *const *cause; - - mbrule->causes = (const char *const *) - p_strsplit_spaces(user->pool, setval, " \t,"); - - for (cause = mbrule->causes; *cause != NULL; cause++) { - if (!imap_sieve_event_cause_valid(*cause)) - break; - } - if (*cause != NULL) { - e_warning(isuser->event, - "Static mailbox rule [%u] has invalid event cause '%s' " - "(skipped)", i, *cause); - continue; - } - } - - str_truncate(identifier, id_len); - str_append(identifier, "_before"); - setval = mail_user_plugin_getenv(user, str_c(identifier)); - mbrule->before = p_strdup_empty(user->pool, setval); - - str_truncate(identifier, id_len); - str_append(identifier, "_after"); - setval = mail_user_plugin_getenv(user, str_c(identifier)); - mbrule->after = p_strdup_empty(user->pool, setval); - - str_truncate(identifier, id_len); - str_append(identifier, "_copy_source_after"); - setval = mail_user_plugin_getenv(user, str_c(identifier)); - mbrule->copy_source_after = p_strdup_empty(user->pool, setval); - - e_debug(isuser->event, "Static mailbox rule [%u]: " - "mailbox='%s' from='%s' causes=(%s) => " - "before=%s after=%s%s", - mbrule->index, mbrule->mailbox, - (mbrule->from == NULL ? "*" : mbrule->from), - t_strarray_join(mbrule->causes, " "), - (mbrule->before == NULL ? "(none)" : - t_strconcat("'", mbrule->before, "'", NULL)), - (mbrule->after == NULL ? "(none)" : - t_strconcat("'", mbrule->after, "'", NULL)), - (mbrule->copy_source_after == NULL ? "": - t_strconcat(" copy_source_after='", - mbrule->copy_source_after, "'", NULL))); - - if ((strcmp(mbrule->mailbox, "*") == 0 || - !rule_pattern_has_wildcards(mbrule->mailbox)) && - (mbrule->from == NULL || - !rule_pattern_has_wildcards(mbrule->from))) { - hash_table_insert(isuser->mbox_rules, mbrule, mbrule); - } else { - array_append(&isuser->mbox_patterns, &mbrule, 1); - } - } - - if (i == 0) - e_debug(isuser->event, "No static mailbox rules"); -} - -static bool -imap_sieve_mailbox_rule_match_cause(struct imap_sieve_mailbox_rule *rule, - const char *cause) -{ - const char *const *cp; - - if (rule->causes == NULL || *rule->causes == NULL) - return TRUE; - - for (cp = rule->causes; *cp != NULL; cp++) { - if (strcasecmp(cause, *cp) == 0) - return TRUE; - } - return FALSE; -} - -static void -imap_sieve_mailbox_rules_match_patterns( - struct mail_user *user, struct mailbox *dst_box, - struct mailbox *src_box, const char *cause, - ARRAY_TYPE(imap_sieve_mailbox_rule) *rules) -{ - struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); - struct imap_sieve_mailbox_rule *rule; - struct mail_namespace *dst_ns, *src_ns; - - if (array_count(&isuser->mbox_patterns) == 0) - return; - - dst_ns = mailbox_get_namespace(dst_box); - src_ns = (src_box == NULL ? NULL : mailbox_get_namespace(src_box)); - - array_foreach_elem(&isuser->mbox_patterns, rule) { - struct imap_match_glob *glob; - - if (src_ns == NULL && rule->from != NULL) - continue; - if (!imap_sieve_mailbox_rule_match_cause(rule, cause)) - continue; - - if (strcmp(rule->mailbox, "*") != 0) { - glob = imap_match_init( - pool_datastack_create(), rule->mailbox, TRUE, - mail_namespace_get_sep(dst_ns)); - if (imap_match(glob, mailbox_get_vname(dst_box)) - != IMAP_MATCH_YES) - continue; - } - if (rule->from != NULL) { - glob = imap_match_init( - pool_datastack_create(), rule->from, TRUE, - mail_namespace_get_sep(src_ns)); - if (imap_match(glob, mailbox_get_vname(src_box)) != - IMAP_MATCH_YES) - continue; - } - - e_debug(isuser->event, "Matched static mailbox rule [%u]", - rule->index); - array_append(rules, &rule, 1); - } -} - -static void -imap_sieve_mailbox_rules_match(struct mail_user *user, - const char *dst_box, const char *src_box, - const char *cause, - ARRAY_TYPE(imap_sieve_mailbox_rule) *rules) -{ - struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); - struct imap_sieve_mailbox_rule lookup_rule; - struct imap_sieve_mailbox_rule *rule; - - i_zero(&lookup_rule); - lookup_rule.mailbox = dst_box; - lookup_rule.from = src_box; - rule = hash_table_lookup(isuser->mbox_rules, &lookup_rule); - - if (rule != NULL && imap_sieve_mailbox_rule_match_cause(rule, cause)) { - struct imap_sieve_mailbox_rule *const *rule_idx; - unsigned int insert_idx = array_count(rules); - - /* Insert sorted by rule index */ - array_foreach(rules, rule_idx) { - if (rule->index < (*rule_idx)->index) { - insert_idx = array_foreach_idx(rules, rule_idx); - break; - } - } - array_insert(rules, insert_idx, &rule, 1); - - e_debug(isuser->event, "Matched static mailbox rule [%u]", - rule->index); - } -} - -static void -imap_sieve_mailbox_rules_get(struct mail_user *user, - struct mailbox *dst_box, struct mailbox *src_box, - const char *cause, - ARRAY_TYPE(imap_sieve_mailbox_rule) *rules) -{ - const char *dst_name, *src_name; - - imap_sieve_mailbox_rules_init(user); - - imap_sieve_mailbox_rules_match_patterns(user, dst_box, src_box, - cause, rules); - - dst_name = mailbox_get_vname(dst_box); - src_name = (src_box == NULL ? NULL : mailbox_get_vname(src_box)); - - imap_sieve_mailbox_rules_match(user, dst_name, src_name, cause, rules); - imap_sieve_mailbox_rules_match(user, "*", src_name, cause, rules); - if (src_name != NULL) { - imap_sieve_mailbox_rules_match(user, dst_name, NULL, - cause, rules); - imap_sieve_mailbox_rules_match(user, "*", NULL, cause, rules); - } -} - /* * User */ @@ -1102,10 +763,6 @@ static void imap_sieve_user_deinit(struct mail_user *user) if (isuser->isieve != NULL) imap_sieve_deinit(&isuser->isieve); - hash_table_destroy(&isuser->mbox_rules); - if (array_is_created(&isuser->mbox_patterns)) - array_free(&isuser->mbox_patterns); - event_unref(&isuser->event); isuser->module_ctx.super.deinit(user); diff --git a/src/plugins/imapsieve/imap-sieve.c b/src/plugins/imapsieve/imap-sieve.c index 7f0cfa808..bb1a7e0a8 100644 --- a/src/plugins/imapsieve/imap-sieve.c +++ b/src/plugins/imapsieve/imap-sieve.c @@ -128,7 +128,7 @@ void imap_sieve_deinit(struct imap_sieve **_isieve) } static int -imap_sieve_get_storage(struct imap_sieve *isieve, +imap_sieve_get_storage(struct imap_sieve *isieve, const char *cause, struct sieve_storage **storage_r) { enum sieve_storage_flags storage_flags = 0; @@ -147,7 +147,8 @@ imap_sieve_get_storage(struct imap_sieve *isieve, return -1; } - if (sieve_storage_create_personal(isieve->svinst, user, storage_flags, + if (sieve_storage_create_personal(isieve->svinst, user, + cause, storage_flags, &isieve->storage, &error_code) < 0) { if (error_code == SIEVE_ERROR_TEMP_FAILURE) return -1; @@ -415,35 +416,94 @@ imap_sieve_run_init_trace_log(struct imap_sieve_run *isrun, *trace_log_r = isrun->trace_log; } +static int +imap_sieve_multiscript_get_scripts(struct sieve_instance *svinst, + struct event *event_parent, + const char *cause, const char *type, + ARRAY_TYPE(imap_sieve_run_script) *scripts, + enum sieve_error *error_code_r) +{ + struct sieve_script_sequence *sseq; + struct sieve_script *script; + bool found = FALSE; + int ret; + + ret = sieve_script_sequence_create(svinst, event_parent, cause, type, + &sseq, error_code_r, NULL); + if (ret < 0) + return (*error_code_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + + while (ret >= 0) { + ret = sieve_script_sequence_next(sseq, &script, + error_code_r, NULL); + if (ret < 0) { + if (*error_code_r == SIEVE_ERROR_TEMP_FAILURE) { + sieve_script_sequence_free(&sseq); + return -1; + } + continue; + } + if (ret == 0) + break; + + struct imap_sieve_run_script *rscript; + + rscript = array_append_space(scripts); + rscript->script = script; + found = TRUE; + } + + sieve_script_sequence_free(&sseq); + return (found ? 1 : 0); +} + +static int +imap_sieve_multiscript_get_scripts_for( + struct sieve_instance *svinst, struct event *dest_mbox_event, + struct mailbox *src_mailbox, const char *cause, const char *type, + ARRAY_TYPE(imap_sieve_run_script) *scripts, + enum sieve_error *error_code_r) +{ + struct event *event; + int ret; + + /* Restrict script storages to source mailbox if we have one. */ + event = event_create(dest_mbox_event); + if (strcasecmp(cause, "copy") == 0 && src_mailbox != NULL) { + event_add_str(event, "imapsieve_from", + mailbox_get_vname(src_mailbox)); + } + ret = imap_sieve_multiscript_get_scripts(svinst, event, + cause, type, scripts, + error_code_r); + event_unref(&event); + if (ret != 0) + return ret; + + /* None found */ + return 0; +} + static int imap_sieve_run_init_scripts(struct imap_sieve *isieve, + struct event *dest_mbox_event, ARRAY_TYPE(imap_sieve_run_script) *scripts, + struct mailbox *src_mailbox, const char *cause, struct sieve_storage *storage, const char *script_name, - const char *const *scripts_before, - const char *const *scripts_after) + const char *before_type, const char *after_type) { struct sieve_instance *svinst = isieve->svinst; enum sieve_error error_code; - const char *const *sp; /* Admin scripts before user script */ - if (scripts_before != NULL) { - for (sp = scripts_before; *sp != NULL; sp++) { - struct sieve_script *script; - - if (sieve_script_create_open(svinst, *sp, NULL, &script, - &error_code, NULL) < 0) { - if (error_code == SIEVE_ERROR_TEMP_FAILURE) - return -1; - continue; - } - - struct imap_sieve_run_script *rscript; - - rscript = array_append_space(scripts); - rscript->script = script; - } + if (before_type != NULL && + imap_sieve_multiscript_get_scripts_for(svinst, dest_mbox_event, + src_mailbox, cause, + before_type, scripts, + &error_code) < 0) { + if (error_code == SIEVE_ERROR_TEMP_FAILURE) + return -1; } /* The user script */ @@ -464,33 +524,23 @@ imap_sieve_run_init_scripts(struct imap_sieve *isieve, } /* Admin scripts after user script */ - if (scripts_after != NULL) { - for (sp = scripts_after; *sp != NULL; sp++) { - struct sieve_script *script; - - if (sieve_script_create_open(svinst, *sp, NULL, &script, - &error_code, NULL) < 0) { - if (error_code == SIEVE_ERROR_TEMP_FAILURE) - return -1; - continue; - } - - struct imap_sieve_run_script *rscript; - - rscript = array_append_space(scripts); - rscript->script = script; - } + if (imap_sieve_multiscript_get_scripts_for(svinst, dest_mbox_event, + src_mailbox, cause, + after_type, scripts, + &error_code) < 0) { + if (error_code == SIEVE_ERROR_TEMP_FAILURE) + return -1; } return 0; } int imap_sieve_run_init(struct imap_sieve *isieve, + struct event *dest_mbox_event, struct mailbox *dest_mailbox, struct mailbox *src_mailbox, const char *cause, const char *script_name, - const char *const *scripts_before, - const char *const *scripts_after, + const char *before_type, const char *after_type, struct imap_sieve_run **isrun_r) { struct imap_sieve_run *isrun; @@ -506,16 +556,17 @@ int imap_sieve_run_init(struct imap_sieve *isieve, /* Get storage for user script */ storage = NULL; if (script_name != NULL && *script_name != '\0' && - (ret = imap_sieve_get_storage(isieve, &storage)) < 0) + (ret = imap_sieve_get_storage(isieve, cause, &storage)) < 0) return ret; /* Open all scripts */ pool = pool_alloconly_create("imap_sieve_run", 256); p_array_init(&scripts, pool, 16); - ret = imap_sieve_run_init_scripts(isieve, &scripts, + ret = imap_sieve_run_init_scripts(isieve, dest_mbox_event, &scripts, + src_mailbox, cause, storage, script_name, - scripts_before, scripts_after); + before_type, after_type); if (ret < 0) { struct imap_sieve_run_script *rscript; diff --git a/src/plugins/imapsieve/imap-sieve.h b/src/plugins/imapsieve/imap-sieve.h index 12d0ac7b0..c3b95b3dd 100644 --- a/src/plugins/imapsieve/imap-sieve.h +++ b/src/plugins/imapsieve/imap-sieve.h @@ -1,6 +1,8 @@ #ifndef IMAP_SIEVE_H #define IMAP_SIEVE_H +#include "sieve-storage.h" + struct client; /* @@ -43,13 +45,13 @@ void imap_sieve_deinit(struct imap_sieve **_isieve); struct imap_sieve_run; -int imap_sieve_run_init(struct imap_sieve *isieve, struct mailbox *dest_mailbox, - struct mailbox *src_mailbox, const char *cause, - const char *script_name, - const char *const *scripts_before, - const char *const *scripts_after, - struct imap_sieve_run **isrun_r) - ATTR_NULL(4, 5, 6); +int imap_sieve_run_init(struct imap_sieve *isieve, + struct event *dest_mbox_event, + struct mailbox *dest_mailbox, + struct mailbox *src_mailbox, + const char *cause, const char *script_name, + const char *before_type, const char *after_type, + struct imap_sieve_run **isrun_r); int imap_sieve_run_mail(struct imap_sieve_run *isrun, struct mail *mail, const char *changed_flags, bool *fatal_r); diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index 5a4ad72a2..fd5e0a47c 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -257,7 +257,8 @@ lda_sieve_get_personal_storage(struct sieve_instance *svinst, struct sieve_storage **storage_r, enum sieve_error *error_code_r) { - if (sieve_storage_create_personal(svinst, user, 0, + if (sieve_storage_create_personal(svinst, user, + SIEVE_SCRIPT_CAUSE_DELIVERY, 0, storage_r, error_code_r) < 0) { switch (*error_code_r) { case SIEVE_ERROR_NOT_POSSIBLE: @@ -279,15 +280,13 @@ lda_sieve_get_personal_storage(struct sieve_instance *svinst, } static void -lda_sieve_multiscript_log_error(struct event *event, - const char *label, const char *location, +lda_sieve_multiscript_log_error(struct event *event, const char *type, enum sieve_error error_code) { switch (error_code) { case SIEVE_ERROR_TEMP_FAILURE: - e_error(event, "Failed to access %s script from '%s' " - "(temporary failure)", - label, location); + e_error(event, "Failed to access '%s' script sequence" + "(temporary failure)", type); break; default: break; @@ -296,7 +295,7 @@ lda_sieve_multiscript_log_error(struct event *event, static int lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst, - const char *label, const char *location, + const char *type, ARRAY_TYPE(sieve_script) *scripts, enum sieve_error *error_code_r) { @@ -304,14 +303,15 @@ lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst, struct sieve_script *script; int ret; - ret = sieve_script_sequence_create(svinst, location, + ret = sieve_script_sequence_create(svinst, svinst->event, + SIEVE_SCRIPT_CAUSE_DELIVERY, type, &sseq, error_code_r, NULL); if (ret < 0) { if (*error_code_r == SIEVE_ERROR_NOT_FOUND) { *error_code_r = SIEVE_ERROR_NONE; return 0; } - lda_sieve_multiscript_log_error(svinst->event, label, location, + lda_sieve_multiscript_log_error(svinst->event, type, *error_code_r); return -1; } @@ -322,7 +322,7 @@ lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst, sieve_script_sequence_free(&sseq); if (ret < 0) { - lda_sieve_multiscript_log_error(svinst->event, label, location, + lda_sieve_multiscript_log_error(svinst->event, type, *error_code_r); return -1; } @@ -718,8 +718,6 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) struct mail_deliver_context *mdctx = srctx->mdctx; struct sieve_instance *svinst = srctx->svinst; struct sieve_storage *main_storage; - const char *sieve_before, *sieve_after, *sieve_discard; - const char *setting_name; enum sieve_error error_code; ARRAY_TYPE(sieve_script) script_sequence; struct sieve_script *const *scripts; @@ -740,20 +738,22 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) switch (error_code) { case SIEVE_ERROR_NOT_FOUND: e_debug(sieve_get_event(svinst), - "User has no active script in storage '%s'", - sieve_storage_location(main_storage)); + "User has no active script in personal storage '%s'", + sieve_storage_name(main_storage)); break; case SIEVE_ERROR_TEMP_FAILURE: e_error(sieve_get_event(svinst), - "Failed to access active Sieve script in user storage '%s' " + "Failed to access active Sieve script in parsonal storage '%s': %s " "(temporary failure)", - sieve_storage_location(main_storage)); + sieve_storage_name(main_storage), + sieve_storage_get_last_error(main_storage, NULL)); ret = -1; break; default: e_error(sieve_get_event(svinst), - "Failed to access active Sieve script in user storage '%s'", - sieve_storage_location(main_storage)); + "Failed to access active Sieve script in personal storage '%s': %s", + sieve_storage_name(main_storage), + sieve_storage_get_last_error(main_storage, NULL)); break; } } else if (!sieve_script_is_default(srctx->main_script)) { @@ -772,29 +772,9 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) /* before */ if (ret >= 0) { - i = 2; - setting_name = "sieve_before"; - sieve_before = mail_user_plugin_getenv( - mdctx->rcpt_user, setting_name); - while (ret >= 0 && - sieve_before != NULL && *sieve_before != '\0') { - ret = lda_sieve_multiscript_get_scripts( - svinst, setting_name, sieve_before, - &script_sequence, &error_code); - if (ret < 0 && error_code == SIEVE_ERROR_TEMP_FAILURE) { - ret = -1; - break; - } else if (ret == 0) { - e_debug(sieve_get_event(svinst), - "Location for %s not found: %s", - setting_name, sieve_before); - } - ret = 0; - setting_name = t_strdup_printf("sieve_before%u", i++); - sieve_before = mail_user_plugin_getenv( - mdctx->rcpt_user, setting_name); - } - + ret = lda_sieve_multiscript_get_scripts( + svinst, SIEVE_STORAGE_TYPE_BEFORE, + &script_sequence, &error_code); if (ret >= 0) { scripts = array_get(&script_sequence, &count); for (i = 0; i < count; i ++) { @@ -820,27 +800,9 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) /* after */ if (ret >= 0) { - i = 2; - setting_name = "sieve_after"; - sieve_after = mail_user_plugin_getenv(mdctx->rcpt_user, setting_name); - while (sieve_after != NULL && *sieve_after != '\0') { - ret = lda_sieve_multiscript_get_scripts( - svinst, setting_name, sieve_after, - &script_sequence, &error_code); - if (ret < 0 && error_code == SIEVE_ERROR_TEMP_FAILURE) { - ret = -1; - break; - } else if (ret == 0) { - e_debug(sieve_get_event(svinst), - "Location for %s not found: %s", - setting_name, sieve_after); - } - ret = 0; - setting_name = t_strdup_printf("sieve_after%u", i++); - sieve_after = mail_user_plugin_getenv( - mdctx->rcpt_user, setting_name); - } - + ret = lda_sieve_multiscript_get_scripts( + svinst, SIEVE_STORAGE_TYPE_AFTER, + &script_sequence, &error_code); if (ret >= 0) { scripts = array_get(&script_sequence, &count); for ( i = after_index; i < count; i ++ ) { @@ -852,25 +814,13 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) } /* discard */ - sieve_discard = mail_user_plugin_getenv( - mdctx->rcpt_user, "sieve_discard"); - if (sieve_discard != NULL && *sieve_discard != '\0') { - if (sieve_script_create_open(svinst, sieve_discard, NULL, - &srctx->discard_script, - &error_code, NULL) < 0) { - switch (error_code) { - case SIEVE_ERROR_NOT_FOUND: - e_debug(sieve_get_event(svinst), - "Location for sieve_discard not found: %s", - sieve_discard); - break; - case SIEVE_ERROR_TEMP_FAILURE: - ret = -1; - break; - default: - break; - } - } + if (ret >= 0) { + ret = sieve_script_create_open( + svinst, SIEVE_SCRIPT_CAUSE_DELIVERY, + SIEVE_STORAGE_TYPE_DISCARD, NULL, + &srctx->discard_script, &error_code, NULL); + if (error_code == SIEVE_ERROR_NOT_FOUND) + ret = 0; } if (ret < 0) { diff --git a/src/plugins/settings/Makefile.am b/src/plugins/settings/Makefile.am index b55bb834e..8388b32fd 100644 --- a/src/plugins/settings/Makefile.am +++ b/src/plugins/settings/Makefile.am @@ -4,8 +4,12 @@ AM_CPPFLAGS = \ $(LIBDOVECOT_INCLUDE) \ -I$(top_srcdir) \ -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/storage/ldap \ -I$(top_srcdir)/src/lib-managesieve \ -DSETTINGS_PLUGIN +if LDAP_PLUGIN +AM_CPPFLAGS += -DPLUGIN_BUILD -DLDAP_PLUGIN +endif libpigeonhole_settings_la_LDFLAGS = -module -avoid-version @@ -17,10 +21,12 @@ libpigeonhole_settings_la_SOURCES = \ pigeonhole-settings.c libpigeonhole_settings_la_LIBADD = \ $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ - $(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la + $(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la \ + $(LDAP_LIBS) libpigeonhole_settings_la_DEPENDENCIES = \ $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ - $(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la + $(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la \ + $(LDAP_LIBS) noinst_HEADERS = \ pigeonhole-settings.h diff --git a/src/plugins/settings/settings-get.pl b/src/plugins/settings/settings-get.pl index edf7e8aa2..568df2483 100755 --- a/src/plugins/settings/settings-get.pl +++ b/src/plugins/settings/settings-get.pl @@ -14,6 +14,7 @@ print '#include "lib.h"'."\n"; print '#include "array.h"'."\n"; print '#include "str.h"'."\n"; print '#include "ipwd.h"'."\n"; +print '#include "env-util.h"'."\n"; print '#include "var-expand.h"'."\n"; print '#include "file-lock.h"'."\n"; print '#include "fsync-mode.h"'."\n"; @@ -29,6 +30,9 @@ print '#include "sieve-address-source.h"'."\n"; print '#include "managesieve-url.h"'."\n"; print '#include "pigeonhole-settings.h"'."\n"; print '#include <unistd.h>'."\n"; +print "#ifdef LDAP_PLUGIN\n"; +print '#include <ldap.h>'."\n"; +print "#endif\n"; print '#define CONFIG_BINARY'."\n"; my @services = (); diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c index 102909cbb..44f131474 100644 --- a/src/testsuite/testsuite-script.c +++ b/src/testsuite/testsuite-script.c @@ -2,6 +2,7 @@ */ #include "lib.h" +#include "settings.h" #include "sieve.h" #include "sieve-common.h" @@ -63,6 +64,7 @@ static struct sieve_binary * _testsuite_script_compile(const struct sieve_runtime_env *renv, const char *script) { + static unsigned int storage_id = 0; struct sieve_instance *svinst = testsuite_sieve_instance; struct sieve_binary *sbin; const char *script_path; @@ -75,8 +77,36 @@ _testsuite_script_compile(const struct sieve_runtime_env *renv, return NULL; script_path = t_strconcat(script_path, "/", script, NULL); - if (sieve_compile(svinst, script_path, NULL, - testsuite_log_ehandler, 0, &sbin, NULL) < 0) + + const char *storage_name = t_strdup_printf("testsuite-script%u", + storage_id++); + const char *script_name = testsuite_script_get_name(script_path); + + struct settings_instance *set_instance = + settings_instance_find(svinst->event); + settings_override(set_instance, "sieve_script+", storage_name, + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("sieve_script/%s/sieve_script_name", + storage_name), + script_name, + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("sieve_script/%s/sieve_script_type", + storage_name), + "testsuite", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("sieve_script/%s/sieve_script_driver", + storage_name), + "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + t_strdup_printf("sieve_script/%s/sieve_script_path", + storage_name), + script_path, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + + if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, + storage_name, script_name, testsuite_log_ehandler, 0, + &sbin, NULL) < 0) return NULL; return sbin; diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c index 6f65e3c5b..d556297ee 100644 --- a/src/testsuite/testsuite.c +++ b/src/testsuite/testsuite.c @@ -8,10 +8,12 @@ #include "ostream.h" #include "hostpid.h" #include "path-util.h" +#include "settings.h" #include "sieve.h" #include "sieve-extensions.h" #include "sieve-script.h" +#include "sieve-storage.h" #include "sieve-binary.h" #include "sieve-result.h" #include "sieve-interpreter.h" @@ -23,6 +25,7 @@ #include "testsuite-settings.h" #include "testsuite-result.h" #include "testsuite-message.h" +#include "testsuite-script.h" #include "testsuite-smtp.h" #include "testsuite-mailstore.h" @@ -156,21 +159,77 @@ int main(int argc, char **argv) else sieve_dir = t_strdup_until(abspath, sieve_dir); - testsuite_setting_set("sieve", - t_strdup_printf("file:%s/included;active=~/.dovecot.sieve", - sieve_dir)); - testsuite_setting_set("sieve_global", - t_strdup_printf("%s/included-global", sieve_dir)); - /* Finish testsuite initialization */ svinst = sieve_tool_init_finish(sieve_tool, FALSE, FALSE); testsuite_init(svinst, sieve_dir, log_stdout); printf("Test case: %s:\n\n", scriptfile); + struct settings_instance *set_instance = + settings_instance_find(svinst->event); + + /* Configure main test script */ + settings_override(set_instance, "sieve_script+", "testsuite-main", + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/testsuite-main/sieve_script_storage", + "testsuite-main", + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/testsuite-main/sieve_script_name", + testsuite_script_get_name(scriptfile), + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/testsuite-main/sieve_script_type", + "testsuite", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/testsuite-main/sieve_script_driver", + "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/testsuite-main/sieve_script_path", + scriptfile, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + + /* Configure personal storage */ + settings_override(set_instance, "sieve_script+", "included", + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included/sieve_script_storage", + "included", + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included/sieve_script_type", + SIEVE_STORAGE_TYPE_PERSONAL, + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included/sieve_script_driver", + "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included/sieve_script_path", + t_strdup_printf("%s/included", sieve_dir), + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + + /* Configure global storage */ + settings_override(set_instance, "sieve_script+", "included-global", + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included-global/sieve_script_storage", + "included-global", + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included-global/sieve_script_type", + SIEVE_STORAGE_TYPE_GLOBAL, + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included-global/sieve_script_driver", + "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + settings_override(set_instance, + "sieve_script/included-global/sieve_script_path", + t_strdup_printf("%s/included-global", sieve_dir), + SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); + /* Compile sieve script */ - if (sieve_compile(svinst, scriptfile, NULL, - testsuite_log_main_ehandler, 0, + if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, "testsuite-main", + NULL, testsuite_log_main_ehandler, 0, &sbin, NULL) < 0) { testsuite_testcase_fail("failed to compile testcase script"); } else { diff --git a/tests/extensions/include/execute.svtest b/tests/extensions/include/execute.svtest index 734ac6644..a1b849519 100644 --- a/tests/extensions/include/execute.svtest +++ b/tests/extensions/include/execute.svtest @@ -54,8 +54,29 @@ test "Namespace - file" { } test "Namespace - dict" { - test_config_set "sieve" "dict:file:${tst.path}/included/namespace.dict"; - test_config_set "sieve_global" "dict:file:${tst.path}/included-global/namespace.dict"; + test_config_set "sieve_script/included/sieve_script_type" "personal"; + test_config_set "sieve_script/included/sieve_script_driver" "dict"; + test_config_set "sieve_script/included/sieve_script_dict/dict" "file"; + test_config_set + "sieve_script/included/sieve_script_dict/dict/file/driver" + "file"; + test_config_set + "sieve_script/included/sieve_script_dict/dict/file/path" + "${tst.path}/included/namespace.dict"; + + test_config_set + "sieve_script/included-global/sieve_script_type" "global"; + test_config_set + "sieve_script/included-global/sieve_script_driver" "dict"; + test_config_set + "sieve_script/included-global/sieve_script_dict/dict" "file"; + test_config_set + "sieve_script/included-global/sieve_script_dict/dict/file/driver" + "file"; + test_config_set + "sieve_script/included-global/sieve_script_dict/dict/file/path" + "${tst.path}/included-global/namespace.dict"; + test_config_reload :extension "include"; if not test_script_compile "execute/namespace.sieve" { -- GitLab