diff --git a/TODO b/TODO index 7771e4386af3fb1ee37e627b4b36f7acff1bb143..e0483c575731d17e7bd3755121011b32e28daea7 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,6 @@ Current activities: -* Make the sieve storage a base class with (possibly) various implementations, - just like mail-storage. This aims to provide support for alternate types - of script storage like LDAP or SQL database. - - Implement read/write script storages for using ManageSieve with dict - database - - Implement infrastructure for loading a sequence of global scripts from - a database (for sieve_before/sieve_after). +* Implement LDAP Sieve script storage for read-only access. Parallel plugin-based efforts (on hold at the moment): @@ -17,8 +11,21 @@ Parallel plugin-based efforts (on hold at the moment): Next (mostly in order of descending priority/precedence): * Implement index extension +* Implement message modification and extraction API in order to: + - Implement replace, enclose, foreverypart, mime and extracttext extensions * Add normalize() method to comparators to normalize the string before matching (for efficiency). +* Properly implement Sieve internationalization support (utf-8 handling), + currently it is not complete: + - Make this implementation fully conform section 2.7.2 of RFC5228 (Comparisons + Across Character Sets). + - Verify validity of utf8 where necessary. + - Implement comparator-i;unicode-casemap. +* Add support for stream matching for handling large values, e.g. from the body + extension. +* Clean up doveadm sync implementation: + - Mostly comments referring specifically to the file storage type. + - API can be polished a lot as well. * Improve error handling. - Implement dropping errors in the user's mailbox as a mail message. * Further develop regex extension and update it to the latest draft: @@ -50,12 +57,6 @@ Next (mostly in order of descending priority/precedence): fails rather than an implicit keep. - Add a method to implicitly pass environment variables such as SENDER and RECIPIENT through the script socket service. -* Properly implement Sieve internationalization support (utf-8 handling), - currently it is not complete: - - Make this implementation fully conform section 2.7.2 of RFC5228 (Comparisons - Across Character Sets). - - Verify validity of utf8 where necessary. - - Implement comparator-i;unicode-casemap. * Make testsuite much more exhaustive: - Add support for testing the content of result actions - Test as many error/warning/info conditions as possible. @@ -79,10 +80,6 @@ Low priority items: * Implement extlists extension as a plugin * Enotify extension: detect use of variable values extracted from the message that are used in the method argument. RFC reports this as a security issue. -* Add support for stream matching for handling large values, e.g. from the body - extension. -* Implement message modification and extraction API in order to: - - Implement replace, enclose, foreverypart, mime and extracttext extensions * Provide a solution for mail_get_headers_utf8 reparsing the whole message each time it is called (header and address test; Timo might provide solution from within Dovecot) @@ -91,7 +88,6 @@ Low priority items: * Implement IMAP plugin for IMAPSieve support: - Requires some sort of Sieve transaction support. - - Requires (IMAP) METADATA support in Dovecot. - This may include support for manually running a script on a set of messages through IMAP (no specification for something like this is available; we will have to provide our own) diff --git a/configure.ac b/configure.ac index 729dbc3a1e151c4af4a541b0dd9811da386b162f..a5beb25de74d279af35449470a26f5cc905aefa9 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,9 @@ doc/rfc/Makefile src/Makefile src/lib-sieve/Makefile src/lib-sieve/util/Makefile +src/lib-sieve/storage/Makefile +src/lib-sieve/storage/file/Makefile +src/lib-sieve/storage/dict/Makefile src/lib-sieve/plugins/Makefile src/lib-sieve/plugins/vacation/Makefile src/lib-sieve/plugins/subaddress/Makefile @@ -126,7 +129,6 @@ src/lib-sieve/plugins/duplicate/Makefile src/lib-sieve/plugins/vnd.dovecot/Makefile src/lib-sieve/plugins/vnd.dovecot/debug/Makefile src/lib-sieve-tool/Makefile -src/lib-sievestorage/Makefile src/lib-managesieve/Makefile src/plugins/Makefile src/plugins/doveadm-sieve/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 6f0d3ef9eaf6a3f0f68e84138c1c37da637a1025..85c1e44642e9e5c62ea96994d3adf1b93093f2d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,6 @@ sieve_subdirs = \ lib-sieve \ - lib-sievestorage \ plugins \ lib-sieve-tool \ sieve-tools \ diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index d6007ca91c01bdea48db816afaeb3222bad425d2..6cc3aad8f20627ae1cf331ae8eb889aa07c982bc 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = util plugins +SUBDIRS = util storage plugins dovecot_pkglib_LTLIBRARIES = libdovecot-sieve.la @@ -43,11 +43,17 @@ comparators = \ cmp-i-ascii-casemap.c if BUILD_UNFINISHED +unfinished_storages = unfinished_plugins = \ $(extdir)/metadata/libsieve_ext_metadata.la endif -# These are not actual plugins just yet... +strgdir = $(top_builddir)/src/lib-sieve/storage +storages = \ + $(strgdir)/file/libsieve_storage_file.la \ + $(strgdir)/dict/libsieve_storage_dict.la \ + $(unfinished_storages) + extdir = $(top_builddir)/src/lib-sieve/plugins plugins = \ $(extdir)/vacation/libsieve_ext_vacation.la \ @@ -73,12 +79,14 @@ plugins = \ $(unfinished_plugins) libdovecot_sieve_la_DEPENDENCIES = \ + $(storages) \ $(plugins) \ $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ $(LIBDOVECOT_LDA_DEPS) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) libdovecot_sieve_la_LIBADD = \ + $(storages) \ $(plugins) \ $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ $(LIBDOVECOT_LDA) \ @@ -91,8 +99,8 @@ libdovecot_sieve_la_SOURCES = \ sieve-smtp.c \ sieve-lexer.c \ sieve-script.c \ - sieve-script-file.c \ - sieve-script-dict.c \ + sieve-storage.c \ + sieve-storage-sync.c \ sieve-ast.c \ sieve-binary.c \ sieve-binary-file.c \ @@ -137,7 +145,8 @@ headers = \ sieve-lexer.h \ sieve-script.h \ sieve-script-private.h \ - sieve-script-file.h \ + sieve-storage.h \ + sieve-storage-private.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 e8804d50ceac099fc4d40d6a5d3b040da40052f3..47949203c9c494a637e3008a8e796a9abf81d966 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -6,6 +6,7 @@ #include "sieve-common.h" #include "sieve-script.h" +#include "sieve-storage.h" #include "sieve-ast.h" #include "sieve-code.h" #include "sieve-extensions.h" @@ -213,8 +214,9 @@ static bool cmd_include_validate struct sieve_ast_argument *arg = cmd->first_positional; struct cmd_include_context_data *ctx_data = (struct cmd_include_context_data *) cmd->data; + struct sieve_storage *storage; struct sieve_script *script; - const char *script_location, *script_name; + const char *script_name; enum sieve_error error = SIEVE_ERROR_NONE; int ret; @@ -247,30 +249,41 @@ static bool cmd_include_validate return FALSE; } - script_location = ext_include_get_script_location - (this_ext, ctx_data->location, script_name); - if ( script_location == NULL ) { - sieve_argument_validate_error(valdtr, arg, - "include: %s location for included script '%s' is unavailable " - "(contact system administrator for more information)", - ext_include_script_location_name(ctx_data->location), - str_sanitize(script_name, 80)); + storage = ext_include_get_script_storage + (this_ext, ctx_data->location, script_name, &error); + if ( storage == NULL ) { + // FIXME: handle ':optional' in this case + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_argument_validate_error(valdtr, arg, + "include: %s location for included script `%s' is unavailable " + "(contact system administrator for more information)", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + } else { + sieve_argument_validate_error(valdtr, arg, + "include: failed to access %s location for included script `%s' " + "(contact system administrator for more information)", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + } return FALSE; } /* Create script object */ - script = sieve_script_create - (this_ext->svinst, script_location, script_name, - sieve_validator_error_handler(valdtr), &error); - - ret = 0; - if ( script != NULL ) - ret = sieve_script_open(script, &error); + script = sieve_storage_get_script + (storage, script_name, &error); + if ( script == NULL ) + return FALSE; - if ( script == NULL || ret < 0 ) { + ret = sieve_script_open(script, &error); + if ( ret < 0 ) { if ( error != SIEVE_ERROR_NOT_FOUND ) { - if ( script != NULL ) - sieve_script_unref(&script); + sieve_argument_validate_error(valdtr, arg, + "failed to access included %s script '%s': %s", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80), + sieve_script_get_last_error_lcase(script)); + sieve_script_unref(&script); return FALSE; /* Not found */ @@ -295,8 +308,7 @@ static bool cmd_include_validate "included %s script '%s' does not exist", ext_include_script_location_name(ctx_data->location), str_sanitize(script_name, 80)); - if ( script != NULL ) - sieve_script_unref(&script); + sieve_script_unref(&script); return FALSE; } } diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index f1a85f768f4fba13ad6ecfd59cfbb176c7b0812d..3f523a0b7587daef74f2e5edbbb3600565c1292e 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -7,6 +7,7 @@ #include "sieve-common.h" #include "sieve-error.h" #include "sieve-script.h" +#include "sieve-storage.h" #include "sieve-binary.h" #include "sieve-generator.h" #include "sieve-interpreter.h" @@ -277,7 +278,7 @@ static bool ext_include_binary_open struct sieve_binary_block *inc_block = NULL; unsigned int location, flags; string_t *script_name; - const char *script_location; + struct sieve_storage *storage; struct sieve_script *script; enum sieve_error error; int ret; @@ -314,24 +315,24 @@ static bool ext_include_binary_open } /* Can we find the script dependency ? */ - script_location = ext_include_get_script_location - (ext, location, str_c(script_name)); - if ( script_location == NULL ) { + storage = ext_include_get_script_storage + (ext, location, str_c(script_name), NULL); + if ( storage == NULL ) { /* No, recompile */ + // FIXME: handle ':optional' in this case return FALSE; } /* Can we open the script dependency ? */ - script = sieve_script_create - (ext->svinst, script_location, str_c(script_name), NULL, &error); + script = sieve_storage_get_script + (storage, str_c(script_name), &error); if ( script == NULL ) { /* No, recompile */ return FALSE; } - if ( sieve_script_open(script, &error) < 0 ) { + if ( sieve_script_open(script, &error) < 0 ) { if ( error != SIEVE_ERROR_NOT_FOUND ) { /* No, recompile */ - sieve_script_unref(&script); return FALSE; } @@ -342,7 +343,6 @@ static bool ext_include_binary_open "include: script '%s' included in binary %s is missing, " "so recompile", str_c(script_name), sieve_binary_path(sbin)); } - sieve_script_unref(&script); return FALSE; } diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index ad69325629134fb5bf38b2c44e391aa014fe3048..3a595d572202343b4b2d3a464c99bb11635ad29a 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -10,6 +10,7 @@ #include "sieve-settings.h" #include "sieve-error.h" #include "sieve-script.h" +#include "sieve-storage.h" #include "sieve-ast.h" #include "sieve-binary.h" #include "sieve-commands.h" @@ -76,36 +77,26 @@ bool ext_include_load { struct sieve_instance *svinst = ext->svinst; struct ext_include_context *ctx; - const char *sieve_dir; + const char *location; unsigned long long int uint_setting; if ( *context != NULL ) { ext_include_unload(ext); } - // FIXME: sieve_dir and sieve_global_dir settings have somewhat become - // misnomers; these are not necessary filesystem directories anymore. - ctx = i_new(struct ext_include_context, 1); - /* Get directory for :global scripts */ - sieve_dir = sieve_setting_get(svinst, "sieve_global_dir"); + /* Get location for :global scripts */ + location = sieve_setting_get(svinst, "sieve_global"); + if ( location == NULL ) + location = sieve_setting_get(svinst, "sieve_global_dir"); - if ( sieve_dir == NULL && svinst->debug ) { - sieve_sys_debug(svinst, "include: sieve_global_dir is not set; " + if ( location == NULL && svinst->debug ) { + sieve_sys_debug(svinst, "include: sieve_global is not set; " "it is currently not possible to include `:global' scripts."); } - ctx->global_dir = i_strdup(sieve_dir); - - /* Get directory for :personal scripts */ - sieve_dir = sieve_setting_get(svinst, "sieve_dir"); - - if ( sieve_dir == NULL ) { - sieve_dir = "~/sieve"; - } - - ctx->personal_dir = i_strdup(sieve_dir); + ctx->global_location = i_strdup(location); /* Get limits */ ctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH; @@ -135,8 +126,12 @@ void ext_include_unload struct ext_include_context *ctx = (struct ext_include_context *) ext->context; - i_free(ctx->personal_dir); - i_free(ctx->global_dir); + if ( ctx->global_storage != NULL ) + sieve_storage_unref(&ctx->global_storage); + if ( ctx->personal_storage != NULL ) + sieve_storage_unref(&ctx->personal_storage); + + i_free(ctx->global_location); i_free(ctx); } @@ -144,44 +139,42 @@ void ext_include_unload * Script access */ -const char *ext_include_get_script_location -(const struct sieve_extension *ext, enum ext_include_script_location location, - const char *script_name) +struct sieve_storage *ext_include_get_script_storage +(const struct sieve_extension *ext, + enum ext_include_script_location location, + const char *script_name, enum sieve_error *error_r) { struct sieve_instance *svinst = ext->svinst; struct ext_include_context *ctx = (struct ext_include_context *) ext->context; - const char *sieve_dir; switch ( location ) { case EXT_INCLUDE_LOCATION_PERSONAL: - - if ( ctx->personal_dir == NULL ) { - sieve_sys_error(svinst, "include: sieve_dir is unconfigured; " - "include of `:personal' script `%s' is therefore not possible", - str_sanitize(script_name, 80)); - return NULL; + if ( ctx->personal_storage == NULL ) { + ctx->personal_storage = sieve_storage_create_main + (svinst, NULL, 0, error_r); } - - sieve_dir = ctx->personal_dir; - break; + return ctx->personal_storage; case EXT_INCLUDE_LOCATION_GLOBAL: - - if ( ctx->global_dir == NULL ) { - sieve_sys_error(svinst, "include: sieve_global_dir is unconfigured; " + if ( ctx->global_location == NULL ) { + sieve_sys_info(svinst, "include: sieve_global is unconfigured; " "include of `:global' script `%s' is therefore not possible", str_sanitize(script_name, 80)); + *error_r = SIEVE_ERROR_NOT_FOUND; return NULL; } - - sieve_dir = ctx->global_dir; - break; + if ( ctx->global_storage == NULL ) { + ctx->global_storage = sieve_storage_create + (svinst, ctx->global_location, 0, error_r); + } + return ctx->global_storage; default: - i_unreached(); + break; } - return sieve_dir; + i_unreached(); + return NULL; } /* diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index 54edc46a40a91d3c82f060e08a80449312b7b476..5c5377ff8f5370078b1cb698b247a9f839c12344 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -93,10 +93,10 @@ extern const struct sieve_operation_def global_operation; * Script access */ -const char *ext_include_get_script_location +struct sieve_storage *ext_include_get_script_storage (const struct sieve_extension *ext, - enum ext_include_script_location location, const char *script_name); - + enum ext_include_script_location location, + const char *script_name, enum sieve_error *error_r); /* * Context */ @@ -108,8 +108,10 @@ struct ext_include_context { const struct sieve_extension *var_ext; /* Configuration */ - char *global_dir; - char *personal_dir; + char *global_location; + + struct sieve_storage *global_storage; + struct sieve_storage *personal_storage; unsigned int max_nesting_depth; unsigned int max_includes; diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h index 1e4fe5d5ef46ac2d35a17cfcdb80b0b92186df86..d2a172a797af64180a10eaf9ab3396f1fe49308c 100644 --- a/src/lib-sieve/sieve-binary.h +++ b/src/lib-sieve/sieve-binary.h @@ -13,7 +13,7 @@ */ #define SIEVE_BINARY_VERSION_MAJOR 1 -#define SIEVE_BINARY_VERSION_MINOR 0 +#define SIEVE_BINARY_VERSION_MINOR 1 /* * Binary object diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index 5c5c1820f5e8b8ff7620c4fa4466303e7f7959e3..6903dde92c36aeae26564c4577e6fc6424166947 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -132,6 +132,11 @@ struct sieve_side_effect_def; /* sieve-script.h */ struct sieve_script; +struct sieve_script_sequence; + +/* sieve-storage.h */ +struct sieve_storage_class_registry; +struct sieve_storage; /* sieve-message.h */ struct sieve_message_context; @@ -178,6 +183,9 @@ struct sieve_instance { /* Extension registry */ struct sieve_extension_registry *ext_reg; + /* Storage class registry */ + struct sieve_storage_class_registry *storage_reg; + /* System error handler */ struct sieve_error_handler *system_ehandler; diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c index 7d28bfa9f3cb003689289c06d3c7fb80219e25ed..02d8b10e611a93d6b38bf678048aea3aabeed8b5 100644 --- a/src/lib-sieve/sieve-error.c +++ b/src/lib-sieve/sieve-error.c @@ -468,9 +468,6 @@ void sieve_vcritical (struct sieve_instance *svinst, struct sieve_error_handler *ehandler, const char *location, const char *user_prefix, const char *fmt, va_list args) { - char str[256]; - struct tm *tm; - if ( location == NULL || *location == '\0' ) { sieve_direct_verror (svinst, svinst->system_ehandler, 0, NULL, fmt, args); @@ -479,19 +476,7 @@ void sieve_vcritical (svinst, svinst->system_ehandler, 0, location, fmt, args); } - if ( ehandler == NULL || ehandler == svinst->system_ehandler ) return; - - tm = localtime(&ioloop_time); - - if ( user_prefix == NULL || *user_prefix == '\0' ) { - sieve_direct_error(svinst, ehandler, 0, location, "%s", - ( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? - str : CRITICAL_MSG )); - } else { - sieve_direct_error(svinst, ehandler, 0, location, "%s: %s", user_prefix, - ( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? - str : CRITICAL_MSG )); - } + sieve_internal_error(ehandler, location, user_prefix); } void sieve_error @@ -556,6 +541,32 @@ void sieve_critical va_end(args); } +void sieve_internal_error +(struct sieve_error_handler *ehandler, const char *location, + const char *user_prefix) +{ + char str[256]; + struct tm *tm; + + if ( ehandler == NULL || + ehandler == ehandler->svinst->system_ehandler ) + return; + + tm = localtime(&ioloop_time); + + if ( user_prefix == NULL || *user_prefix == '\0' ) { + sieve_direct_error(ehandler->svinst, ehandler, 0, + location, "%s", + ( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + str : CRITICAL_MSG )); + } else { + sieve_direct_error(ehandler->svinst, ehandler, 0, + location, "%s: %s", user_prefix, + ( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + str : CRITICAL_MSG )); + } +} + /* * Error statistics */ diff --git a/src/lib-sieve/sieve-error.h b/src/lib-sieve/sieve-error.h index 2a5f92ffdd259d0fc29e4a196994af260c64a62d..4ae9bbbd2b051faaf3ebd2ecc7a42d2b8b1aefcf 100644 --- a/src/lib-sieve/sieve-error.h +++ b/src/lib-sieve/sieve-error.h @@ -143,6 +143,10 @@ void sieve_critical const char *location, const char *user_prefix, const char *fmt, ...) ATTR_FORMAT(5, 6); +void sieve_internal_error +(struct sieve_error_handler *ehandler, const char *location, + const char *user_prefix) ATTR_NULL(1, 2, 3); + /* * Error handler configuration */ diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c index 6d65c862bb3cd2422049dd7ddb9b3637c5107607..19abeb702b035c55630e1f860763844fec596351 100644 --- a/src/lib-sieve/sieve-script-dict.c +++ b/src/lib-sieve/sieve-script-dict.c @@ -263,9 +263,6 @@ static bool sieve_dict_script_equals struct sieve_dict_script *script = (struct sieve_dict_script *)_script; struct sieve_dict_script *other = (struct sieve_dict_script *)_other; - if ( script == NULL || other == NULL ) - return FALSE; - if ( strcmp(script->dict_uri, other->dict_uri) != 0 ) return FALSE; diff --git a/src/lib-sieve/sieve-script-file.h b/src/lib-sieve/sieve-script-file.h deleted file mode 100644 index cba6021003228af89b6d4b389b1b99a20e845a98..0000000000000000000000000000000000000000 --- a/src/lib-sieve/sieve-script-file.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_SCRIPT_FILE_H -#define __SIEVE_SCRIPT_FILE_H - -/* - * Sieve script filenames - */ - -bool sieve_scriptfile_has_extension(const char *filename); -const char *sieve_scriptfile_get_script_name(const char *filename); -const char *sieve_scriptfile_from_name(const char *name); - -/* - * File script specific functions - */ - - -/* Return directory where script resides in. Returns NULL if this is not a file - * script. - */ -const char *sieve_file_script_get_dirpath - (const struct sieve_script *script); - -/* Return full path to file script. Returns NULL if this is not a file script. - */ -const char *sieve_file_script_get_path - (const struct sieve_script *script); - -#endif /* __SIEVE_SCRIPT_FILE_H */ diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h index 3d80424e0f991a4b56c335e0fe5535e84d192012..0697511bc9763bb73108bead8cc65f57d7f6df8e 100644 --- a/src/lib-sieve/sieve-script-private.h +++ b/src/lib-sieve/sieve-script-private.h @@ -4,6 +4,7 @@ #ifndef __SIEVE_SCRIPT_PRIVATE_H #define __SIEVE_SCRIPT_PRIVATE_H +#include "sieve-common.h" #include "sieve-script.h" /* @@ -11,17 +12,16 @@ */ struct sieve_script_vfuncs { - struct sieve_script *(*alloc)(void); void (*destroy)(struct sieve_script *script); int (*open) - (struct sieve_script *script, const char *data, - const char *const *options, enum sieve_error *error_r); + (struct sieve_script *script, enum sieve_error *error_r); int (*get_stream) (struct sieve_script *script, struct istream **stream_r, enum sieve_error *error_r); + /* binary */ int (*binary_read_metadata) (struct sieve_script *_script, struct sieve_binary_block *sblock, sieve_size_t *offset); @@ -35,9 +35,18 @@ struct sieve_script_vfuncs { const char *(*binary_get_prefix) (struct sieve_script *script); + /* management */ + int (*rename) + (struct sieve_script *script, const char *newname); + int (*delete)(struct sieve_script *script); + int (*is_active)(struct sieve_script *script); + int (*activate)(struct sieve_script *script); + + /* properties */ int (*get_size) (const struct sieve_script *script, uoff_t *size_r); + /* matching */ bool (*equals) (const struct sieve_script *script, const struct sieve_script *other); }; @@ -45,18 +54,14 @@ struct sieve_script_vfuncs { struct sieve_script { pool_t pool; unsigned int refcount; + struct sieve_storage *storage; const char *driver_name; const struct sieve_script *script_class; struct sieve_script_vfuncs v; - struct sieve_instance *svinst; - struct sieve_error_handler *ehandler; - const char *name; - const char *data; const char *location; - const char *bin_dir; /* Stream */ struct istream *stream; @@ -65,40 +70,49 @@ struct sieve_script { }; void sieve_script_init - (struct sieve_script *script, struct sieve_instance *svinst, - const struct sieve_script *script_class, const char *data, - const char *name, struct sieve_error_handler *ehandler); - -int sieve_script_setup_bindir - (struct sieve_script *script, mode_t mode); +(struct sieve_script *script, struct sieve_storage *storage, + const struct sieve_script *script_class, const char *location, + const char *name); /* - * Built-in file script driver + * Built-in script drivers */ -#define SIEVE_FILE_SCRIPT_DRIVER_NAME "file" - -struct sieve_file_script { - struct sieve_script script; - - struct stat st; - struct stat lnk_st; - - const char *path; - const char *dirpath; - const char *filename; - const char *binpath; - const char *binprefix; -}; - extern const struct sieve_script sieve_file_script; +extern const struct sieve_script sieve_dict_script; +extern const struct sieve_script sieve_ldap_script; /* - * Built-in dict script driver + * Error handling */ -#define SIEVE_DICT_SCRIPT_DRIVER_NAME "dict" +void sieve_script_set_error + (struct sieve_script *script, enum sieve_error error, + const char *fmt, ...) ATTR_FORMAT(3, 4); +void sieve_script_set_internal_error + (struct sieve_script *script); +void sieve_script_set_critical + (struct sieve_script *script, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +void sieve_script_sys_error + (struct sieve_script *script, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_script_sys_warning + (struct sieve_script *script, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_script_sys_info + (struct sieve_script *script, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_script_sys_debug + (struct sieve_script *script, const char *fmt, ...) + ATTR_FORMAT(2, 3); -extern const struct sieve_script sieve_dict_script; +/* + * Script sequence + */ + +void sieve_script_sequence_init +(struct sieve_script_sequence *seq, struct sieve_storage *storage); #endif /* __SIEVE_SCRIPT_PRIVATE_H */ diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index 9e56951e08c06b710eb9cefd843884a18030be79..142c35530bcc35cbf9e747676841e2fd0270830f 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -5,10 +5,9 @@ #include "compat.h" #include "unichar.h" #include "str.h" +#include "str-sanitize.h" #include "hash.h" #include "array.h" -#include "home-expand.h" -#include "mkdir-parents.h" #include "eacces-error.h" #include "istream.h" @@ -18,8 +17,10 @@ #include "sieve-error.h" #include "sieve-binary.h" +#include "sieve-storage-private.h" #include "sieve-script-private.h" -#include "sieve-script-file.h" + +#include <ctype.h> /* * Script name @@ -89,200 +90,103 @@ bool sieve_script_name_is_valid(const char *scriptname) } /* - * Script object + * Script instance */ -static const char *split_next_arg(const char *const **_args) +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 *const *args = *_args; - const char *str = args[0]; + i_assert( storage != NULL ); - /* join arguments for escaped ";" separator */ + script->script_class = script_class; + script->refcount = 1; + script->storage = storage; + script->location = p_strdup_empty(script->pool, location); + script->name = p_strdup(script->pool, name); - 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; + sieve_storage_ref(storage); } -static bool sieve_script_location_parse -(struct sieve_script *script, const char *data, const char **location_r, - const char *const **options_r, const char **error_r) +struct sieve_script *sieve_script_create +(struct sieve_instance *svinst, const char *location, const char *name, + enum sieve_error *error_r) { - ARRAY_TYPE(const_string) options; - const char *const *tmp; - - if (*data == '\0') { - *options_r = NULL; - *location_r = data; - return TRUE; - } + struct sieve_storage *storage; + struct sieve_script *script; + enum sieve_error error; - /* <location> */ - tmp = t_strsplit(data, ";"); - *location_r = split_next_arg(&tmp); - - if ( options_r != NULL ) { - t_array_init(&options, 8); - - /* [<option> *(';' <option>)] */ - while (*tmp != NULL) { - const char *option = split_next_arg(&tmp); - - if ( strncasecmp(option, "name=", 5) == 0 ) { - if ( option[5] == '\0' ) { - *error_r = "empty name not allowed"; - return FALSE; - } - - if ( script->name == NULL ) - script->name = p_strdup(script->pool, option+5); - - } else if ( strncasecmp(option, "bindir=", 7) == 0 ) { - const char *bin_dir = option+7; - - if ( bin_dir[0] == '\0' ) { - *error_r = "empty bindir not allowed"; - return FALSE; - } - - if ( bin_dir[0] == '~' ) { - /* home-relative path. change to absolute. */ - const char *home = sieve_environment_get_homedir(script->svinst); - - if ( home != NULL ) { - bin_dir = home_expand_tilde(bin_dir, home); - } else if ( bin_dir[1] == '/' || bin_dir[1] == '\0' ) { - *error_r = "bindir is relative to home directory (~/), " - "but home directory cannot be determined"; - return FALSE; - } - } - - script->bin_dir = p_strdup(script->pool, bin_dir); - } else { - array_append(&options, &option, 1); - } - } + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; - (void)array_append_space(&options); - *options_r = array_idx(&options, 0); - } + storage = sieve_storage_create(svinst, location, 0, error_r); + if ( storage == NULL ) + return NULL; - return TRUE; + script = sieve_storage_get_script(storage, name, error_r); + + sieve_storage_unref(&storage); + return script; } -void sieve_script_init -(struct sieve_script *script, struct sieve_instance *svinst, - const struct sieve_script *script_class, const char *data, - const char *name, struct sieve_error_handler *ehandler) +void sieve_script_ref(struct sieve_script *script) { - script->script_class = script_class; - script->refcount = 1; - script->svinst = svinst; - script->ehandler = ehandler; - script->data = p_strdup_empty(script->pool, data); - script->name = p_strdup_empty(script->pool, name); - - sieve_error_handler_ref(ehandler); + script->refcount++; } -struct sieve_script *sieve_script_create -(struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r) +void sieve_script_unref(struct sieve_script **_script) { - struct sieve_script *script; - const struct sieve_script *script_class; - const char *data, *p; - - p = strchr(location, ':'); - if ( p == NULL ) { - /* Default script driver is "file" (meaning that location is a fs path) */ - data = location; - script_class = &sieve_file_script; - } else { - /* Lookup script driver */ - T_BEGIN { - const char *driver; - - data = p+1; - driver = t_strdup_until(location, p); - - /* FIXME - script_class = sieve_script_class_lookup(driver);*/ - if ( strcasecmp(driver, SIEVE_FILE_SCRIPT_DRIVER_NAME) == 0 ) - script_class = &sieve_file_script; - else if ( strcasecmp(driver, SIEVE_DICT_SCRIPT_DRIVER_NAME) == 0 ) - script_class = &sieve_dict_script; - else - script_class = NULL; - - if ( script_class == NULL ) { - sieve_sys_error(svinst, - "Unknown sieve script driver module: %s", driver); - } - } T_END; - } + struct sieve_script *script = *_script; - if ( script_class == NULL ) { - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAILURE; - return NULL; - } + i_assert( script->refcount > 0 ); - script = script_class->v.alloc(); - sieve_script_init(script, svinst, script_class, data, name, ehandler); - script->location = p_strdup(script->pool, location); - return script; + if ( --script->refcount != 0 ) + return; + + if ( script->stream != NULL ) + i_stream_unref(&script->stream); + + if ( script->v.destroy != NULL ) + script->v.destroy(script); + + sieve_storage_unref(&script->storage); + pool_unref(&script->pool); + *_script = NULL; } int sieve_script_open (struct sieve_script *script, enum sieve_error *error_r) { - struct sieve_instance *svinst = script->svinst; - struct sieve_error_handler *ehandler = script->ehandler; enum sieve_error error; - const char *const *options = NULL; - const char *location = NULL, *parse_error = NULL; if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; if ( script->open ) return 0; - if ( !sieve_script_location_parse - (script, script->data, &location, &options, &parse_error) ) { - sieve_critical(svinst, ehandler, NULL, - "failed to access sieve script", "failed to parse script location: %s", - parse_error); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAILURE; - return -1; - } - script->location = NULL; - if ( script->v.open(script, location, options, &error) < 0 ) { - if ( error_r == NULL ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) - sieve_error(ehandler, script->name, "sieve script does not exist"); - } else { - *error_r = error; - } + if ( script->v.open(script, error_r) < 0 ) return -1; - } i_assert( script->location != NULL ); i_assert( script->name != NULL ); script->open = TRUE; + + if ( *script->name != '\0' ) { + sieve_script_sys_debug(script, + "Opened script `%s' from `%s'", + script->name, script->location); + } else { + sieve_script_sys_debug(script, + "Opened nameless script from `%s'", + script->location); + } return 0; } @@ -299,11 +203,11 @@ int sieve_script_open_as struct sieve_script *sieve_script_create_open (struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r) + enum sieve_error *error_r) { struct sieve_script *script; - script = sieve_script_create(svinst, location, name, ehandler, error_r); + script = sieve_script_create(svinst, location, name, error_r); if ( script == NULL ) return NULL; @@ -315,51 +219,6 @@ struct sieve_script *sieve_script_create_open return script; } -struct sieve_script *sieve_script_create_open_as -(struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r) -{ - struct sieve_script *script; - - script = sieve_script_create(svinst, location, name, ehandler, error_r); - if ( script == NULL ) - return NULL; - - if ( sieve_script_open_as(script, name, error_r) < 0 ) { - sieve_script_unref(&script); - return NULL; - } - - return script; -} - -void sieve_script_ref(struct sieve_script *script) -{ - script->refcount++; -} - -void sieve_script_unref(struct sieve_script **_script) -{ - struct sieve_script *script = *_script; - - i_assert(script->refcount > 0); - - if (--script->refcount != 0) - return; - - if ( script->stream != NULL ) - i_stream_unref(&script->stream); - - if ( script->ehandler != NULL ) - sieve_error_handler_unref(&script->ehandler); - - if ( script->v.destroy != NULL ) - script->v.destroy(script); - - pool_unref(&script->pool); - *_script = NULL; -} - /* * Properties */ @@ -376,7 +235,7 @@ const char *sieve_script_location(const struct sieve_script *script) struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) { - return script->svinst; + return script->storage->svinst; } int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) @@ -415,27 +274,23 @@ int sieve_script_get_stream if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; if ( script->stream != NULL ) { *stream_r = script->stream; return 0; } + // FIXME: necessary? + i_assert( script->open ); + T_BEGIN { - ret = script->v.get_stream(script, &script->stream, &error); + ret = script->v.get_stream(script, &script->stream, error_r); } T_END; - if ( ret < 0 ) { - if ( error_r == NULL ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) { - sieve_error(script->ehandler, script->name, - "sieve script does not exist"); - } - } else { - *error_r = error; - } + if ( ret < 0 ) return -1; - } *stream_r = script->stream; return 0; @@ -481,23 +336,36 @@ int sieve_script_binary_read_metadata (struct sieve_script *script, struct sieve_binary_block *sblock, sieve_size_t *offset) { - struct sieve_instance *svinst = script->svinst; struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); - string_t *script_class; + string_t *storage_class, *location; if ( sieve_binary_block_get_size(sblock) - *offset == 0 ) return 0; - if ( !sieve_binary_read_string(sblock, offset, &script_class) ) { - sieve_sys_error(svinst, - "sieve script: binary %s has invalid metadata for script %s", + /* storage class */ + if ( !sieve_binary_read_string(sblock, offset, &storage_class) ) { + sieve_script_sys_error(script, + "Binary %s has invalid metadata for script %s: " + "Invalid storage class", sieve_binary_path(sbin), sieve_script_location(script)); return -1; } - - if ( strcmp(str_c(script_class), script->driver_name) != 0 ) + if ( strcmp(str_c(storage_class), script->driver_name) != 0 ) return 0; + /* location */ + if ( !sieve_binary_read_string(sblock, offset, &location) ) { + sieve_script_sys_error(script, + "Binary %s has invalid metadata for script %s: " + "Invalid location", + sieve_binary_path(sbin), sieve_script_location(script)); + return -1; + } + if ( script->location == NULL ) + return 0; + if ( strcmp(str_c(location), script->location) != 0 ) + return 0; + if ( script->v.binary_read_metadata == NULL ) return 1; @@ -508,6 +376,8 @@ void sieve_script_binary_write_metadata (struct sieve_script *script, struct sieve_binary_block *sblock) { sieve_binary_emit_cstring(sblock, script->driver_name); + sieve_binary_emit_cstring(sblock, + ( script->location == NULL ? "" : script->location )); if ( script->v.binary_write_metadata == NULL ) return; @@ -532,6 +402,7 @@ int sieve_script_binary_save { struct sieve_script *bin_script = sieve_binary_script(sbin); + i_assert(bin_script == NULL || sieve_script_equals(bin_script, script)); if ( script->v.binary_save == NULL ) { @@ -545,9 +416,11 @@ int sieve_script_binary_save const char *sieve_script_binary_get_prefix (struct sieve_script *script) { - if ( script->bin_dir != NULL && - sieve_script_setup_bindir(script, 0700) >= 0 ) { - return t_strconcat(script->bin_dir, "/", script->name, NULL); + struct sieve_storage *storage = script->storage; + + if ( storage->bin_dir != NULL && + sieve_storage_setup_bindir(storage, 0700) >= 0 ) { + return t_strconcat(storage->bin_dir, "/", script->name, NULL); } if ( script->v.binary_get_prefix == NULL ) @@ -556,54 +429,263 @@ const char *sieve_script_binary_get_prefix return script->v.binary_get_prefix(script); } -int sieve_script_setup_bindir -(struct sieve_script *script, mode_t mode) -{ - struct sieve_instance *svinst = script->svinst; - struct stat st; +/* + * Management + */ - if ( script->bin_dir == NULL ) - return -1; +int sieve_script_rename +(struct sieve_script *script, const char *newname) +{ + struct sieve_storage *storage = script->storage; + const char *oldname = script->name; + int ret; - if ( stat(script->bin_dir, &st) == 0 ) - return 0; + i_assert( newname != NULL ); - if ( errno == EACCES ) { - sieve_sys_error(svinst, "sieve script: " - "failed to setup directory for binaries: %s", - eacces_error_get("stat", script->bin_dir)); - return -1; - } else if ( errno != ENOENT ) { - sieve_sys_error(svinst, "sieve script: " - "failed to setup directory for binaries: stat(%s) failed: %m", - script->bin_dir); + /* Check script name */ + if ( !sieve_script_name_is_valid(newname) ) { + sieve_script_set_error(script, + SIEVE_ERROR_BAD_PARAMS, + "Invalid new Sieve script name `%s'.", + str_sanitize(newname, 80)); return -1; } - if ( mkdir_parents(script->bin_dir, mode) == 0 ) { - if ( svinst->debug ) - sieve_sys_debug(svinst, "sieve script: " - "created directory for binaries: %s", script->bin_dir); - return 1; + i_assert( (script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + i_assert( script->open ); // FIXME: auto-open? + + i_assert( script->v.rename != NULL ); + ret = script->v.rename(script, newname); + + /* rename INBOX mailbox attribute */ + if ( ret >= 0 && oldname != NULL ) + sieve_storage_sync_script_rename(storage, oldname, newname); + + return ret; +} + +int sieve_script_delete(struct sieve_script **_script) +{ + struct sieve_script *script = *_script; + struct sieve_storage *storage = script->storage; + int ret = 0; + + i_assert( (script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + i_assert( script->open ); // FIXME: auto-open? + + /* Is the requested script active? */ + if ( sieve_script_is_active(script) ) { + sieve_script_set_error(script, SIEVE_ERROR_ACTIVE, + "Cannot delete the active Sieve script."); + ret = -1; + } else { + i_assert( script->v.delete != NULL ); + ret = script->v.delete(script); } - switch ( errno ) { - case EEXIST: + /* unset INBOX mailbox attribute */ + if ( ret >= 0 ) + sieve_storage_sync_script_delete(storage, script->name); + + /* Always deinitialize the script object */ + sieve_script_unref(_script); + return ret; +} + +int sieve_script_is_active(struct sieve_script *script) +{ + if ( script->v.is_active == NULL ) return 0; - case ENOENT: - sieve_sys_error(svinst, "sieve script: " - "directory for binaries was deleted while it was being created"); - break; - case EACCES: - sieve_sys_error(svinst, "sieve script: %s", - eacces_error_get_creating("mkdir_parents_chgrp", script->bin_dir)); - break; - default: - sieve_sys_error(svinst, "sieve script: " - "mkdir_parents_chgrp(%s) failed: %m", script->bin_dir); - break; + return script->v.is_active(script); +} + +int sieve_script_activate(struct sieve_script *script, time_t mtime) +{ + int ret; + + i_assert( (script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + i_assert( script->open ); // FIXME: auto-open? + + i_assert( script->v.activate != NULL ); + ret = script->v.activate(script); + + sieve_storage_set_modified(script->storage, mtime); + + return ret; +} + +/* + * Error handling + */ + +void sieve_script_set_error +(struct sieve_script *script, enum sieve_error error, + const char *fmt, ...) +{ + struct sieve_storage *storage = script->storage; + va_list va; + + sieve_storage_clear_error(storage); + + if (fmt != NULL) { + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + } + storage->error_code = error; +} + +void sieve_script_set_internal_error +(struct sieve_script *script) +{ + sieve_storage_set_internal_error(script->storage); +} + +void sieve_script_set_critical +(struct sieve_script *script, const char *fmt, ...) +{ + struct sieve_storage *storage = script->storage; + + va_list va; + + sieve_storage_clear_error(storage); + if (fmt != NULL) { + if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { + va_start(va, fmt); + sieve_sys_error(storage->svinst, "%s script: %s", + storage->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); + + sieve_storage_set_internal_error(storage); + } else { + /* no user is involved while synchronizing, so do it the + normal way */ + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; + } } +} + +const char *sieve_script_get_last_error +(struct sieve_script *script, enum sieve_error *error_r) +{ + return sieve_storage_get_last_error(script->storage, error_r); +} + +const char *sieve_script_get_last_error_lcase +(struct sieve_script *script) +{ + char *errormsg = t_strdup_noconst(script->storage->error); + errormsg[0] = i_tolower(errormsg[0]); + return errormsg; +} + +void sieve_script_sys_error +(struct sieve_script *script, const char *fmt, ...) +{ + struct sieve_instance *svinst = script->storage->svinst; + va_list va; + + va_start(va, fmt); + sieve_sys_error(svinst, "%s script: %s", + script->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +void sieve_script_sys_warning +(struct sieve_script *script, const char *fmt, ...) +{ + struct sieve_instance *svinst = script->storage->svinst; + va_list va; + + va_start(va, fmt); + sieve_sys_warning(svinst, "%s script: %s", + script->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +void sieve_script_sys_info +(struct sieve_script *script, const char *fmt, ...) +{ + struct sieve_instance *svinst = script->storage->svinst; + va_list va; + + va_start(va, fmt); + sieve_sys_info(svinst, "%s script: %s", + script->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +void sieve_script_sys_debug +(struct sieve_script *script, const char *fmt, ...) +{ + struct sieve_instance *svinst = script->storage->svinst; + va_list va; + + if (!svinst->debug) + return; + + va_start(va, fmt); + sieve_sys_debug(svinst, "%s script: %s", + script->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +/* + * Script sequence + */ + +void sieve_script_sequence_init +(struct sieve_script_sequence *seq, struct sieve_storage *storage) +{ + seq->storage = storage; + sieve_storage_ref(storage); +} + +struct sieve_script_sequence *sieve_script_sequence_create +(struct sieve_instance *svinst, const char *location, + enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_script_sequence *seq; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + storage = sieve_storage_create(svinst, location, 0, error_r); + if ( storage == NULL ) + return NULL; + + seq = sieve_storage_get_script_sequence(storage, error_r); + + sieve_storage_unref(&storage); + return seq; +} + +struct sieve_script *sieve_script_sequence_next +(struct sieve_script_sequence *seq, enum sieve_error *error_r) +{ + struct sieve_storage *storage = seq->storage; + + i_assert( storage->v.script_sequence_next != NULL ); + return storage->v.script_sequence_next(seq, error_r); +} + +void sieve_script_sequence_free(struct sieve_script_sequence **_seq) +{ + struct sieve_script_sequence *seq = *_seq; + struct sieve_storage *storage = seq->storage; + + if ( storage->v.script_sequence_destroy != NULL ) + storage->v.script_sequence_destroy(seq); - return -1; + sieve_storage_unref(&storage); + *_seq = NULL; } diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h index b5cbc5482039ff154d1ded1cdb118b549b61b42f..ecf05558b6a4b321cd26ba56853a42b6f4657a34 100644 --- a/src/lib-sieve/sieve-script.h +++ b/src/lib-sieve/sieve-script.h @@ -16,44 +16,50 @@ bool sieve_script_name_is_valid(const char *scriptname); /* - * Sieve script object + * Sieve script file + */ + +bool sieve_script_file_has_extension(const char *filename); + +/* + * Sieve script class + */ + +void sieve_script_class_register + (struct sieve_instance *svinst, const struct sieve_script *script_class); +void sieve_script_class_unregister + (struct sieve_instance *svinst, const struct sieve_script *script_class); + +/* + * Sieve script instance */ struct sieve_script; -ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *); +ARRAY_DEFINE_TYPE(sieve_script, struct sieve_script *); struct sieve_script *sieve_script_create (struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r); + enum sieve_error *error_r) + ATTR_NULL(3,4); void sieve_script_ref(struct sieve_script *script); void sieve_script_unref(struct sieve_script **script); int sieve_script_open - (struct sieve_script *script, enum sieve_error *error_r); + (struct sieve_script *script, enum sieve_error *error_r) + ATTR_NULL(2); int sieve_script_open_as - (struct sieve_script *script, const char *name, enum sieve_error *error_r); + (struct sieve_script *script, const char *name, enum sieve_error *error_r) + ATTR_NULL(3); struct sieve_script *sieve_script_create_open (struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r); -struct sieve_script *sieve_script_create_open_as - (struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r); + enum sieve_error *error_r) + ATTR_NULL(3,4); /* - * Accessors - */ - -const char *sieve_script_name(const struct sieve_script *script); -const char *sieve_script_location(const struct sieve_script *script); -struct sieve_instance *sieve_script_svinst(const struct sieve_script *script); - -bool sieve_script_is_open(const struct sieve_script *script); - -/* - * Saving/loading Sieve binaries + * Binary */ int sieve_script_binary_read_metadata @@ -77,8 +83,39 @@ const char *sieve_script_binary_get_prefix int sieve_script_get_stream (struct sieve_script *script, struct istream **stream_r, - enum sieve_error *error_r); + enum sieve_error *error_r) ATTR_NULL(3); + +/* + * Management + */ + +// FIXME: check read/write flag! + +int sieve_script_rename + (struct sieve_script *script, const char *newname); +int sieve_script_is_active(struct sieve_script *script); +int sieve_script_activate(struct sieve_script *script, time_t mtime); +int sieve_script_delete(struct sieve_script **script); + +/* + * Properties + */ + +const char *sieve_script_name + (const struct sieve_script *script) ATTR_PURE; +const char *sieve_script_location + (const struct sieve_script *script) ATTR_PURE; +struct sieve_instance *sieve_script_svinst + (const struct sieve_script *script) ATTR_PURE; + int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r); +bool sieve_script_is_open + (const struct sieve_script *script) ATTR_PURE; + +const char *sieve_file_script_get_dirpath + (const struct sieve_script *script) ATTR_PURE; +const char *sieve_file_script_get_path + (const struct sieve_script *script) ATTR_PURE; /* * Comparison @@ -94,5 +131,27 @@ static inline int sieve_script_cmp return ( sieve_script_equals(script, other) ? 0 : -1 ); } +/* + * Error handling + */ + +const char *sieve_script_get_last_error + (struct sieve_script *script, enum sieve_error *error_r) + ATTR_NULL(2); +const char *sieve_script_get_last_error_lcase + (struct sieve_script *script); + +/* + * Script sequence + */ + +struct sieve_script_sequence; + +struct sieve_script_sequence *sieve_script_sequence_create +(struct sieve_instance *svinst, const char *location, + enum sieve_error *error_r); +struct sieve_script *sieve_script_sequence_next + (struct sieve_script_sequence *seq, enum sieve_error *error_r); +void sieve_script_sequence_free(struct sieve_script_sequence **_seq); #endif /* __SIEVE_SCRIPT_H */ diff --git a/src/lib-sieve/sieve-storage-private.h b/src/lib-sieve/sieve-storage-private.h new file mode 100644 index 0000000000000000000000000000000000000000..16352843368204f260eb4bf2d766085b804fc4c9 --- /dev/null +++ b/src/lib-sieve/sieve-storage-private.h @@ -0,0 +1,244 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SIEVE_STORAGE_PRIVATE_H +#define __SIEVE_STORAGE_PRIVATE_H + +#include "sieve.h" +#include "sieve-error-private.h" + +#include "sieve-storage.h" + +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" + +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' + +struct sieve_storage; + +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, + enum sieve_error *error_r); + + int (*get_last_change) + (struct sieve_storage *storage, time_t *last_change_r); + void (*set_modified) + (struct sieve_storage *storage, time_t mtime); + + int (*is_singular)(struct sieve_storage *storage); + + /* script access */ + struct sieve_script *(*get_script) + (struct sieve_storage *storage, const char *name); + + /* script sequence */ + struct sieve_script_sequence *(*get_script_sequence) + (struct sieve_storage *storage, enum sieve_error *error_r); + struct sieve_script *(*script_sequence_next) + (struct sieve_script_sequence *seq, enum sieve_error *error_r); + void (*script_sequence_destroy)(struct sieve_script_sequence *seq); + + /* active script */ + int (*active_script_get_name) + (struct sieve_storage *storage, const char **name_r); + struct sieve_script *(*active_script_open) + (struct sieve_storage *storage); + int (*deactivate) + (struct sieve_storage *storage); + int (*active_script_get_last_change) + (struct sieve_storage *storage, time_t *last_change_r); + + /* listing scripts */ + struct sieve_storage_list_context *(*list_init) + (struct sieve_storage *storage); + const char *(*list_next) + (struct sieve_storage_list_context *lctx, bool *active_r); + int (*list_deinit) + (struct sieve_storage_list_context *lctx); + + /* saving scripts */ + // FIXME: simplify this API; reduce this mostly to a single save function + struct sieve_storage_save_context *(*save_init) + (struct sieve_storage *storage, const char *scriptname, + struct istream *input); + int (*save_continue)(struct sieve_storage_save_context *sctx); + int (*save_finish)(struct sieve_storage_save_context *sctx); + struct sieve_script *(*save_get_tempscript) + (struct sieve_storage_save_context *sctx); + void (*save_cancel)(struct sieve_storage_save_context *sctx); + int (*save_commit)(struct sieve_storage_save_context *sctx); + int (*save_as_active) + (struct sieve_storage *storage, struct istream *input, + time_t mtime); + + /* checking quota */ + int (*quota_havespace) + (struct sieve_storage *storage, const char *scriptname, + size_t size, enum sieve_storage_quota *quota_r, uint64_t *limit_r); +}; + +struct sieve_storage { + pool_t pool; + unsigned int refcount; + struct sieve_instance *svinst; + + const char *driver_name; + const struct sieve_storage *storage_class; + struct sieve_storage_vfuncs v; + + uint64_t max_scripts; + uint64_t max_storage; + + char *error; + enum sieve_error error_code; + + const char *data; + const char *location; + const char *script_name; + const char *bin_dir; + + struct mailbox *sync_inbox; + + enum sieve_storage_flags flags; + + /* this is the main personal storage */ + unsigned int main_storage:1; + unsigned int allows_synchronization:1; +}; + +struct sieve_storage *sieve_storage_alloc + (struct sieve_instance *svinst, + const struct sieve_storage *storage_class, const char *data, + enum sieve_storage_flags flags, bool main); + +int sieve_storage_setup_bindir + (struct sieve_storage *storage, mode_t mode); + +/* + * Listing scripts + */ + +struct sieve_storage_list_context { + struct sieve_storage *storage; + + unsigned int seen_active:1; // Just present for assertions +}; + +/* + * Script sequence + */ + +struct sieve_script_sequence { + struct sieve_storage *storage; +}; + +/* + * Saving scripts + */ + +struct sieve_storage_save_context { + struct sieve_storage *storage; + const char *scriptname; + struct sieve_script *scriptobject; + + struct istream *input; + + time_t mtime; + + unsigned int failed:1; + unsigned int finished:1; +}; + +/* + * Storage class + */ + +struct sieve_storage_class_registry; + +void sieve_storages_init(struct sieve_instance *svinst); +void sieve_storages_deinit(struct sieve_instance *svinst); + +void sieve_storage_class_register + (struct sieve_instance *svinst, + const struct sieve_storage *storage_class); +void sieve_storage_class_unregister + (struct sieve_instance *svinst, + const struct sieve_storage *storage_class); +const struct sieve_storage *sieve_storage_find_class + (struct sieve_instance *svinst, const char *name); + +/* + * Built-in storage drivers + */ + +/* file */ + +#define SIEVE_FILE_STORAGE_DRIVER_NAME "file" + +extern const struct sieve_storage sieve_file_storage; + +struct sieve_storage *sieve_file_storage_init_legacy + (struct sieve_instance *svinst, const char *active_path, + const char *storage_path, enum sieve_storage_flags flags, + enum sieve_error *error_r) ATTR_NULL(6); + +/* dict */ + +#define SIEVE_DICT_STORAGE_DRIVER_NAME "dict" + +extern const struct sieve_storage sieve_dict_storage; + +/* ldap */ + +#define SIEVE_LDAP_STORAGE_DRIVER_NAME "ldap" + +extern const struct sieve_storage sieve_ldap_storage; + +/* + * Error handling + */ + +void sieve_storage_set_internal_error + (struct sieve_storage *storage); + +void sieve_storage_sys_error + (struct sieve_storage *storage, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_storage_sys_warning + (struct sieve_storage *storage, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_storage_sys_info + (struct sieve_storage *storage, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_storage_sys_debug + (struct sieve_storage *storage, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +/* + * Synchronization + */ + +int sieve_storage_sync_init + (struct sieve_storage *storage, struct mail_user *user); +void sieve_storage_sync_deinit + (struct sieve_storage *storage); + +void sieve_storage_sync_script_save + (struct sieve_storage *storage, const char *name); +void sieve_storage_sync_script_rename + (struct sieve_storage *storage, const char *oldname, + const char *newname); +void sieve_storage_sync_script_delete + (struct sieve_storage *storage, const char *name); + +#endif diff --git a/src/lib-sieve/sieve-storage-sync.c b/src/lib-sieve/sieve-storage-sync.c new file mode 100644 index 0000000000000000000000000000000000000000..a8b089aa8d85979a82e5c88f4ca22b628119a167 --- /dev/null +++ b/src/lib-sieve/sieve-storage-sync.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str-sanitize.h" +#include "home-expand.h" +#include "eacces-error.h" +#include "mkdir-parents.h" +#include "ioloop.h" +#include "mail-storage-private.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error-private.h" + +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +/* + * Synchronization + */ + +int sieve_storage_sync_init +(struct sieve_storage *storage, struct mail_user *user) +{ + struct mail_namespace *ns; + struct mailbox *box; + enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS; + enum mail_error error; + + if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && + (flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + return 0; + + if ( !storage->allows_synchronization ) { + if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 ) + return -1; + return 0; + } + + + ns = mail_namespace_find_inbox(user->namespaces); + storage->sync_inbox = box = mailbox_alloc(ns->list, "INBOX", flags); + if (mailbox_open(box) == 0) + return 0; + + sieve_storage_sys_warning(storage, "sync: " + "Failed to open user INBOX for attribute modifications: %s", + mailbox_get_last_error(box, &error)); + return -1; +} + +void sieve_storage_sync_deinit +(struct sieve_storage *storage) +{ + if (storage->sync_inbox != NULL) + mailbox_free(&storage->sync_inbox); +} + +/* + * Sync attributes + */ + +static void sieve_storage_sync_transaction_finish +(struct sieve_storage *storage, struct mailbox_transaction_context **t) +{ + struct mailbox *inbox = storage->sync_inbox; + + if (mailbox_transaction_commit(t) < 0) { + enum mail_error error; + + sieve_storage_sys_warning(storage, "sync: " + "Failed to update INBOX attributes: %s", + mail_storage_get_last_error(mailbox_get_storage(inbox), &error)); + } +} + +void sieve_storage_sync_script_save +(struct sieve_storage *storage, const char *name) +{ + struct mailbox_transaction_context *t; + const char *key; + + if (storage->sync_inbox == NULL) + return; + + key = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); + t = mailbox_transaction_begin(storage->sync_inbox, 0); + mail_index_attribute_set(t->itrans, TRUE, key, ioloop_time, 0); + sieve_storage_sync_transaction_finish(storage, &t); +} + +void sieve_storage_sync_script_rename +(struct sieve_storage *storage, const char *oldname, + const char *newname) +{ + struct mailbox_transaction_context *t; + const char *oldkey, *newkey; + + if (storage->sync_inbox == NULL) + return; + + oldkey = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL); + newkey = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL); + + t = mailbox_transaction_begin(storage->sync_inbox, 0); + mail_index_attribute_unset(t->itrans, TRUE, oldkey, ioloop_time); + mail_index_attribute_set(t->itrans, TRUE, newkey, ioloop_time, 0); + sieve_storage_sync_transaction_finish(storage, &t); +} + +void sieve_storage_sync_script_delete +(struct sieve_storage *storage, const char *name) +{ + struct mailbox_transaction_context *t; + const char *key; + + if (storage->sync_inbox == NULL) + return; + + key = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); + + t = mailbox_transaction_begin(storage->sync_inbox, 0); + mail_index_attribute_unset(t->itrans, TRUE, key, ioloop_time); + sieve_storage_sync_transaction_finish(storage, &t); +} diff --git a/src/lib-sieve/sieve-storage.c b/src/lib-sieve/sieve-storage.c new file mode 100644 index 0000000000000000000000000000000000000000..e9626d204c609cfb9499f60e86830090560039fa --- /dev/null +++ b/src/lib-sieve/sieve-storage.c @@ -0,0 +1,1078 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str-sanitize.h" +#include "home-expand.h" +#include "eacces-error.h" +#include "mkdir-parents.h" +#include "ioloop.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error-private.h" + +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <time.h> +#include <utime.h> + +#define CRITICAL_MSG \ + "Internal error occured. Refer to server log for more information." +#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" + +/* + * Storage class + */ + +struct sieve_storage_class_registry { + ARRAY_TYPE(sieve_storage_class) storage_classes; +}; + +void sieve_storages_init(struct sieve_instance *svinst) +{ + svinst->storage_reg = + p_new(svinst->pool, struct sieve_storage_class_registry, 1); + p_array_init(&svinst->storage_reg->storage_classes, svinst->pool, 8); + + sieve_storage_class_register(svinst, &sieve_file_storage); + sieve_storage_class_register(svinst, &sieve_dict_storage); +} + +void sieve_storages_deinit(struct sieve_instance *svinst ATTR_UNUSED) +{ + /* nothing yet */ +} + +void sieve_storage_class_register +(struct sieve_instance *svinst, const struct sieve_storage *storage_class) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + const struct sieve_storage *old_class; + + old_class = sieve_storage_find_class + (svinst, storage_class->driver_name); + if (old_class != NULL) { + if (old_class->v.alloc == NULL) { + /* replacing a "support not compiled in" storage class */ + sieve_storage_class_unregister(svinst, old_class); + } else { + i_panic("sieve_storage_class_register(%s): Already registered", + storage_class->driver_name); + } + } + + array_append(®->storage_classes, &storage_class, 1); +} + +void sieve_storage_class_unregister +(struct sieve_instance *svinst, const struct sieve_storage *storage_class) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + const struct sieve_storage *const *classes; + unsigned int i, count; + + classes = array_get(®->storage_classes, &count); + for ( i = 0; i < count; i++ ) { + if ( classes[i] == storage_class ) { + array_delete(®->storage_classes, i, 1); + break; + } + } +} + +const struct sieve_storage *sieve_storage_find_class +(struct sieve_instance *svinst, const char *name) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + const struct sieve_storage *const *classes; + unsigned int i, count; + + i_assert(name != NULL); + + classes = array_get(®->storage_classes, &count); + for ( i = 0; i < count; i++ ) { + if ( strcasecmp(classes[i]->driver_name, name) == 0 ) + return classes[i]; + } + return NULL; +} + +/* + * Storage instance + */ + +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_find_class(svinst, driver); + if ( storage_class == NULL ) { + sieve_sys_error(svinst, + "Unknown storage driver module `%s'", + driver); + } else if ( storage_class->v.alloc == NULL ) { + sieve_sys_error(svinst, + "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 bool sieve_storage_data_parse +(struct sieve_storage *storage, const char *data, const char **location_r, + const char *const **options_r, const char **error_r) +{ + ARRAY_TYPE(const_string) options; + const char *const *tmp; + + if (*data == '\0') { + *options_r = NULL; + *location_r = data; + return TRUE; + } + + /* <location> */ + tmp = t_strsplit(data, ";"); + *location_r = split_next_arg(&tmp); + + if ( options_r != NULL ) { + t_array_init(&options, 8); + + /* [<option> *(';' <option>)] */ + while (*tmp != NULL) { + const char *option = split_next_arg(&tmp); + + if ( strncasecmp(option, "name=", 5) == 0 ) { + if ( option[5] == '\0' ) { + *error_r = "empty name not allowed"; + return FALSE; + } + + if ( storage->script_name == NULL ) + storage->script_name = p_strdup(storage->pool, option+5); + + } else if ( strncasecmp(option, "bindir=", 7) == 0 ) { + const char *bin_dir = option+7; + + if ( bin_dir[0] == '\0' ) { + *error_r = "empty bindir not allowed"; + return FALSE; + } + + if ( bin_dir[0] == '~' ) { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(storage->svinst); + + if ( home != NULL ) { + bin_dir = home_expand_tilde(bin_dir, home); + } else if ( bin_dir[1] == '/' || bin_dir[1] == '\0' ) { + *error_r = "bindir is relative to home directory (~/), " + "but home directory cannot be determined"; + return FALSE; + } + } + + storage->bin_dir = p_strdup(storage->pool, bin_dir); + } else { + array_append(&options, &option, 1); + } + } + + (void)array_append_space(&options); + *options_r = array_idx(&options, 0); + } + + return TRUE; +} + +struct sieve_storage *sieve_storage_alloc +(struct sieve_instance *svinst, + const struct sieve_storage *storage_class, const char *data, + enum sieve_storage_flags flags, bool main) +{ + struct sieve_storage *storage; + + i_assert(storage_class->v.alloc != NULL); + storage = storage_class->v.alloc(); + + storage->storage_class = storage_class; + storage->refcount = 1; + storage->svinst = svinst; + storage->flags = flags; + storage->data = p_strdup_empty(storage->pool, data); + storage->main_storage = main; + + return storage; +} + +static struct sieve_storage *sieve_storage_init +(struct sieve_instance *svinst, + const struct sieve_storage *storage_class, const char *data, + enum sieve_storage_flags flags, bool main, enum sieve_error *error_r) +{ + struct sieve_storage *storage; + const char *const *options; + const char *location, *parse_error; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + i_assert( storage_class->v.init != NULL ); + + if ((flags & SIEVE_STORAGE_FLAG_READWRITE) && + storage_class->v.save_init == NULL) { + sieve_sys_error(svinst, "%s storage: " + "Storage does not support write access", + storage_class->driver_name); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + T_BEGIN { + storage = sieve_storage_alloc + (svinst, storage_class, data, flags, main); + + if ( !sieve_storage_data_parse + (storage, data, &location, &options, &parse_error) ) { + sieve_sys_error(svinst, "%s storage: " + "Failed to parse storage location: %s", + storage_class->driver_name, parse_error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + sieve_storage_unref(&storage); + storage = NULL; + } else { + storage->location = p_strdup(storage->pool, location); + + if ( storage_class->v.init + (storage, options, error_r) < 0 ) { + sieve_storage_unref(&storage); + storage = NULL; + } + } + } T_END; + + return storage; +} + +struct sieve_storage *sieve_storage_create +(struct sieve_instance *svinst, const char *location, + enum sieve_storage_flags flags, enum sieve_error *error_r) +{ + const struct sieve_storage *storage_class; + enum sieve_error error; + const char *data; + int ret; + + /* Dont use this function for creating a synchronizing storage */ + i_assert( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0); + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + data = location; + if ( (ret=sieve_storage_driver_parse + (svinst, &data, &storage_class)) < 0) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + if ( ret == 0 ) + storage_class = &sieve_file_storage; + + return sieve_storage_init + (svinst, storage_class, data, flags, FALSE, error_r); +} + +struct sieve_storage *sieve_storage_create_main +(struct sieve_instance *svinst, struct mail_user *user, + enum sieve_storage_flags flags, enum sieve_error *error_r) +{ + bool debug = svinst->debug; + struct sieve_storage *storage = NULL; + const struct sieve_storage + *sieve_class = NULL, + *sieve_dir_class = NULL; + const char *set_sieve, *set_sieve_dir; + const char *data, *storage_path; + unsigned long long int uint_setting; + size_t size_setting; + enum sieve_error error; + int ret; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + /* Sieve storage location */ + + set_sieve = sieve_setting_get(svinst, "sieve"); + + if ( set_sieve != NULL ) { + if ( *set_sieve == '\0' ) { + /* disabled */ + if ( debug ) { + sieve_sys_debug(svinst, "storage: " + "Sieve is disabled (sieve=\"\")"); + } + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + + data = set_sieve; + if ( (ret=sieve_storage_driver_parse + (svinst, &data, &sieve_class)) < 0 ) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + if ( ret > 0 ) { + /* The normal case: explicit driver name */ + storage = sieve_storage_init + (svinst, sieve_class, data, flags, TRUE, error_r); + if ( storage == NULL ) + return NULL; + } + + /* No driver name */ + } + + if ( storage == NULL ) { + /* Script storage directory configuration (deprecated) */ + + set_sieve_dir = sieve_setting_get(svinst, "sieve_dir"); + if ( set_sieve_dir == NULL ) + set_sieve_dir = sieve_setting_get(svinst, "sieve_storage"); + + if ( set_sieve_dir == NULL || *set_sieve_dir == '\0' ) { + storage_path = ""; + } else { + const char *p; + + /* Parse and check driver */ + storage_path = set_sieve_dir; + if ( (ret=sieve_storage_driver_parse + (svinst, &storage_path, &sieve_dir_class)) < 0) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + if ( ret > 0 && sieve_dir_class != &sieve_file_storage ) { + sieve_sys_error(svinst, "storage: " + "Cannot use deprecated sieve_dir= setting " + "with `%s' driver for main Sieve storage", + sieve_dir_class->driver_name); + } + + /* Ignore any options */ + p = strchr(storage_path, ';'); + if ( p != NULL ) + storage_path = t_strdup_until(storage_path, p); + } + + storage = sieve_file_storage_init_legacy + (svinst, data, storage_path, flags, error_r); + } + + if ( storage == NULL ) + return NULL; + + (void)sieve_storage_sync_init(storage, user); + + /* Get quota settings if storage driver provides none */ + + if ( storage->max_storage == 0 && + sieve_setting_get_size_value + (svinst, "sieve_quota_max_storage", &size_setting) ) { + storage->max_storage = size_setting; + } + + if ( storage->max_scripts == 0 && + sieve_setting_get_uint_value + (svinst, "sieve_quota_max_scripts", &uint_setting) ) { + storage->max_scripts = uint_setting; + } + + if ( debug ) { + if ( storage->max_storage > 0 ) { + sieve_storage_sys_debug(storage, "quota: " + "Storage limit: %llu bytes", + (unsigned long long int) storage->max_storage); + } + if ( storage->max_scripts > 0 ) { + sieve_storage_sys_debug(storage, "quota: " + "Script count limit: %llu scripts", + (unsigned long long int) storage->max_scripts); + } + } + return storage; +} + +void sieve_storage_ref(struct sieve_storage *storage) +{ + storage->refcount++; +} + +void sieve_storage_unref(struct sieve_storage **_storage) +{ + struct sieve_storage *storage = *_storage; + + i_assert(storage->refcount > 0); + + if (--storage->refcount != 0) + return; + + (void)sieve_storage_sync_deinit(storage); + + if ( storage->v.destroy != NULL ) + storage->v.destroy(storage); + + i_free(storage->error); + pool_unref(&storage->pool); + *_storage = NULL; +} + +int sieve_storage_setup_bindir +(struct sieve_storage *storage, mode_t mode) +{ + struct sieve_instance *svinst = storage->svinst; + const char *bin_dir = storage->bin_dir; + struct stat st; + + if ( bin_dir == NULL ) + return -1; + + if ( stat(bin_dir, &st) == 0 ) + return 0; + + if ( errno == EACCES ) { + sieve_storage_sys_error(storage, + "Failed to setup directory for binaries: " + "%s", eacces_error_get("stat", bin_dir)); + return -1; + } else if ( errno != ENOENT ) { + sieve_storage_sys_error(storage, + "Failed to setup directory for binaries: " + "stat(%s) failed: %m", + bin_dir); + return -1; + } + + if ( mkdir_parents(bin_dir, mode) == 0 ) { + if ( svinst->debug ) + sieve_storage_sys_debug(storage, + "Created directory for binaries: %s", bin_dir); + return 1; + } + + switch ( errno ) { + case EEXIST: + return 0; + case ENOENT: + sieve_storage_sys_error(storage, + "Directory for binaries was deleted while it was being created"); + break; + case EACCES: + sieve_storage_sys_error(storage, + "%s", eacces_error_get_creating("mkdir_parents_chgrp", bin_dir)); + break; + default: + sieve_storage_sys_error(storage, + "mkdir_parents_chgrp(%s) failed: %m", bin_dir); + break; + } + + return -1; +} + +int sieve_storage_is_singular +(struct sieve_storage *storage) +{ + if ( storage->v.is_singular == NULL ) + return 1; + return storage->v.is_singular(storage); +} + +int sieve_storage_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + i_assert(storage->v.get_last_change != NULL); + return storage->v.get_last_change(storage, last_change_r); +} + +void sieve_storage_set_modified +(struct sieve_storage *storage, time_t mtime) +{ + if (storage->v.set_modified == NULL) + return; + + storage->v.set_modified(storage, mtime); +} + +/* + * Script access + */ + +struct sieve_script *sieve_storage_get_script +(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) +{ + struct sieve_script *script; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + sieve_storage_clear_error(storage); + + /* Validate script name */ + if ( name != NULL && !sieve_script_name_is_valid(name) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_BAD_PARAMS, + "Invalid script name `%s'.", + str_sanitize(name, 80)); + if (error_r != NULL) + *error_r = storage->error_code; + return NULL; + } + + i_assert(storage->v.get_script != NULL); + script = storage->v.get_script(storage, name); + if (script == NULL && error_r != NULL) + *error_r = storage->error_code; + return script; +} + +struct sieve_script *sieve_storage_open_script +(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) +{ + struct sieve_script *script; + + script = sieve_storage_get_script(storage, name, error_r); + if ( script == NULL ) + return NULL; + + if ( sieve_script_open(script, error_r) < 0 ) { + sieve_script_unref(&script); + return NULL; + } + + return script; +} + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + i_assert( storage->v.get_script_sequence != NULL ); + return storage->v.get_script_sequence(storage, error_r); +} + +/* + * Active script + */ + +int sieve_storage_active_script_get_name +(struct sieve_storage *storage, const char **name_r) +{ + i_assert(storage->v.active_script_get_name != NULL); + return storage->v.active_script_get_name(storage, name_r); +} + +struct sieve_script *sieve_storage_active_script_open +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + struct sieve_script *script; + + i_assert(storage->v.active_script_open != NULL); + script = storage->v.active_script_open(storage); + + if ( error_r != NULL ) + *error_r = storage->error_code; + return script; +} + +int sieve_storage_deactivate +(struct sieve_storage *storage, time_t mtime) +{ + int ret; + + i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + + i_assert(storage->v.deactivate != NULL); + ret = storage->v.deactivate(storage); + + sieve_storage_set_modified(storage, mtime); + + return ret; +} + +int sieve_storage_active_script_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + i_assert( storage->v.active_script_get_last_change != NULL); + + return storage->v.active_script_get_last_change(storage, last_change_r); +} + +/* + * Listing scripts + */ + +struct sieve_storage_list_context *sieve_storage_list_init +(struct sieve_storage *storage) +{ + struct sieve_storage_list_context *lctx; + + i_assert(storage->v.list_init != NULL); + lctx = storage->v.list_init(storage); + + if (lctx != NULL) { + lctx->storage = storage; + } + + return lctx; +} + +const char *sieve_storage_list_next +(struct sieve_storage_list_context *lctx, bool *active_r) +{ + struct sieve_storage *storage = lctx->storage; + const char *scriptname; + bool script_active = FALSE; + + i_assert(storage->v.list_next != NULL); + scriptname = storage->v.list_next(lctx, &script_active); + + i_assert(!script_active || !lctx->seen_active); + if (script_active) + lctx->seen_active = TRUE; + + if (active_r != NULL) + *active_r = script_active; + return scriptname; +} + +int sieve_storage_list_deinit +(struct sieve_storage_list_context **_lctx) +{ + struct sieve_storage_list_context *lctx = *_lctx; + struct sieve_storage *storage = lctx->storage; + int ret; + + i_assert(storage->v.list_deinit != NULL); + ret = storage->v.list_deinit(lctx); + + _lctx = NULL; + return ret; +} + +/* + * Saving scripts + */ + +struct sieve_storage_save_context * +sieve_storage_save_init(struct sieve_storage *storage, + const char *scriptname, struct istream *input) +{ + struct sieve_storage_save_context *sctx; + + if ( scriptname != NULL ) { + /* Validate script name */ + if ( !sieve_script_name_is_valid(scriptname) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_BAD_PARAMS, + "Invalid Sieve script name `%s'.", + str_sanitize(scriptname, 80)); + return NULL; + } + } + + i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + + i_assert(storage->v.save_init != NULL); + if ((sctx=storage->v.save_init(storage, scriptname, input)) == NULL) + return NULL; + + sctx->storage = storage; + sctx->mtime = (time_t)-1; + + i_assert( sctx->input != NULL ); + + return sctx; +} + +int sieve_storage_save_continue(struct sieve_storage_save_context *sctx) +{ + struct sieve_storage *storage = sctx->storage; + int ret; + + i_assert(storage->v.save_continue != NULL); + ret = storage->v.save_continue(sctx); + if (ret < 0) + sctx->failed = TRUE; + return ret; +} + +int sieve_storage_save_finish(struct sieve_storage_save_context *sctx) +{ + struct sieve_storage *storage = sctx->storage; + + i_assert(!sctx->finished); + sctx->finished = TRUE; + + i_assert(storage->v.save_finish != NULL); + return storage->v.save_finish(sctx); +} + +void sieve_storage_save_set_mtime +(struct sieve_storage_save_context *sctx, time_t mtime) +{ + sctx->mtime = mtime; +} + +static void sieve_storage_save_deinit(struct sieve_storage_save_context *sctx) +{ + if (sctx->scriptobject != NULL) + sieve_script_unref(&sctx->scriptobject); +} + +struct sieve_script *sieve_storage_save_get_tempscript +(struct sieve_storage_save_context *sctx) +{ + struct sieve_storage *storage = sctx->storage; + + if ( sctx->failed ) + return NULL; + + if ( sctx->scriptobject != NULL ) + return sctx->scriptobject; + + i_assert( storage->v.save_get_tempscript != NULL ); + sctx->scriptobject = storage->v.save_get_tempscript(sctx); + + i_assert( sctx->scriptobject != NULL || + storage->error_code != SIEVE_ERROR_NONE ); + return sctx->scriptobject; +} + +bool sieve_storage_save_will_activate +(struct sieve_storage_save_context *sctx) +{ + bool result = FALSE; + + if ( sctx->scriptname != NULL ) T_BEGIN { + const char *scriptname; + int ret; + + ret = sieve_storage_active_script_get_name(sctx->storage, &scriptname); + if ( ret > 0 ) { + /* Is the requested script active? */ + result = ( strcmp(sctx->scriptname, scriptname) == 0 ); + } + } T_END; + + return result; +} + +int sieve_storage_save_commit(struct sieve_storage_save_context **_sctx) +{ + struct sieve_storage_save_context *sctx = *_sctx; + struct sieve_storage *storage = sctx->storage; + const char *scriptname; + int ret; + + i_assert(sctx->finished); + i_assert(sctx->scriptname != NULL); + + scriptname = t_strdup(sctx->scriptname); + sieve_storage_save_deinit(sctx); + + i_assert(storage->v.save_commit != NULL); + ret = storage->v.save_commit(sctx); + *_sctx = NULL; + + /* set INBOX mailbox attribute */ + if ( ret >= 0 ) + sieve_storage_sync_script_save(storage, scriptname); + + return ret; +} + +void sieve_storage_save_cancel(struct sieve_storage_save_context **_sctx) +{ + struct sieve_storage_save_context *sctx = *_sctx; + struct sieve_storage *storage = sctx->storage; + + sctx->failed = TRUE; + + sieve_storage_save_deinit(sctx); + + if (!sctx->finished) + (void)sieve_storage_save_finish(sctx); + + i_assert(storage->v.save_cancel != NULL); + storage->v.save_cancel(sctx); + *_sctx = NULL; +} + +int sieve_storage_save_as_active(struct sieve_storage *storage, + struct istream *input, time_t mtime) +{ + i_assert( storage->v.save_as_active != NULL ); + return storage->v.save_as_active(storage, input, mtime); +} + +/* + * Checking quota + */ + +bool sieve_storage_quota_validsize +(struct sieve_storage *storage, size_t size, uint64_t *limit_r) +{ + uint64_t max_size; + + max_size = sieve_max_script_size(storage->svinst); + if ( max_size > 0 && size > max_size ) { + *limit_r = max_size; + return FALSE; + } + + return TRUE; +} + +uint64_t sieve_storage_quota_max_script_size +(struct sieve_storage *storage) +{ + return sieve_max_script_size(storage->svinst); +} + +int sieve_storage_quota_havespace +(struct sieve_storage *storage, const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, uint64_t *limit_r) +{ + *quota_r = SIEVE_STORAGE_QUOTA_NONE; + *limit_r = 0; + + /* Check the script size */ + if ( !sieve_storage_quota_validsize(storage, size, limit_r) ) { + *quota_r = SIEVE_STORAGE_QUOTA_MAXSIZE; + return 0; + } + + /* Do we need to scan the storage (quota enabled) ? */ + if ( storage->max_scripts == 0 && storage->max_storage == 0 ) { + return 1; + } + + if (storage->v.quota_havespace == NULL) + return 1; + + return storage->v.quota_havespace + (storage, scriptname, size, quota_r, limit_r); +} + +/* + * Properties + */ + +const char *sieve_storage_location(const struct sieve_storage *storage) +{ + return storage->location; +} + +/* + * Error handling + */ + +void sieve_storage_clear_error(struct sieve_storage *storage) +{ + i_free(storage->error); + storage->error_code = SIEVE_ERROR_NONE; + storage->error = NULL; +} + +void sieve_storage_set_error +(struct sieve_storage *storage, enum sieve_error error, + const char *fmt, ...) +{ + va_list va; + + sieve_storage_clear_error(storage); + + if (fmt != NULL) { + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + } + + storage->error_code = error; +} + +void sieve_storage_set_internal_error +(struct sieve_storage *storage) +{ + struct tm *tm; + char str[256]; + + /* critical errors may contain sensitive data, so let user + see only "Internal error" with a timestamp to make it + easier to look from log files the actual error message. */ + tm = localtime(&ioloop_time); + + storage->error = + strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + i_strdup(str) : i_strdup(CRITICAL_MSG); + + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; +} + +void sieve_storage_set_critical +(struct sieve_storage *storage, const char *fmt, ...) +{ + va_list va; + + sieve_storage_clear_error(storage); + if (fmt != NULL) { + if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { + va_start(va, fmt); + sieve_sys_error(storage->svinst, "%s storage: %s", + storage->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); + + sieve_storage_set_internal_error(storage); + } else { + /* no user is involved while synchronizing, so do it the + normal way */ + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; + } + } +} + +const char *sieve_storage_get_last_error +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + /* We get here only in error situations, so we have to return some + error. If storage->error is NULL, it means we forgot to set it at + some point.. + */ + + if ( error_r != NULL ) + *error_r = storage->error_code; + + return storage->error != NULL ? storage->error : "Unknown error"; +} + +void sieve_storage_sys_error +(struct sieve_storage *storage, const char *fmt, ...) +{ + struct sieve_instance *svinst = storage->svinst; + va_list va; + + va_start(va, fmt); + sieve_sys_error(svinst, "%s storage: %s", + storage->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +void sieve_storage_sys_warning +(struct sieve_storage *storage, const char *fmt, ...) +{ + struct sieve_instance *svinst = storage->svinst; + va_list va; + + va_start(va, fmt); + sieve_sys_warning(svinst, "%s storage: %s", + storage->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +void sieve_storage_sys_info +(struct sieve_storage *storage, const char *fmt, ...) +{ + struct sieve_instance *svinst = storage->svinst; + va_list va; + + va_start(va, fmt); + sieve_sys_info(svinst, "%s storage: %s", + storage->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} + +void sieve_storage_sys_debug +(struct sieve_storage *storage, const char *fmt, ...) +{ + struct sieve_instance *svinst = storage->svinst; + va_list va; + + if (!svinst->debug) + return; + + va_start(va, fmt); + sieve_sys_debug(svinst, "%s storage: %s", + storage->driver_name, t_strdup_vprintf(fmt, va)); + va_end(va); +} diff --git a/src/lib-sieve/sieve-storage.h b/src/lib-sieve/sieve-storage.h new file mode 100644 index 0000000000000000000000000000000000000000..fc9c189f8ec86ab26de2fa16aa5967b4f4a59000 --- /dev/null +++ b/src/lib-sieve/sieve-storage.h @@ -0,0 +1,188 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SIEVE_STORAGE_H +#define __SIEVE_STORAGE_H + +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" + +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' + +/* + * Storage object + */ + +enum sieve_storage_flags { + /* Storage is opened for read/write access (e.g. ManageSieve) */ + SIEVE_STORAGE_FLAG_READWRITE = 0x01, + /* This storage is used for synchronization (and not normal ManageSieve) + */ + SIEVE_STORAGE_FLAG_SYNCHRONIZING = 0x02 +}; + +struct sieve_storage; + +struct sieve_storage *sieve_storage_create +(struct sieve_instance *svinst, const char *location, + enum sieve_storage_flags flags, enum sieve_error *error_r) + ATTR_NULL(4); +struct sieve_storage *sieve_storage_create_main +(struct sieve_instance *svinst, struct mail_user *user, + enum sieve_storage_flags flags, enum sieve_error *error_r) + ATTR_NULL(4); + +void sieve_storage_ref(struct sieve_storage *storage); +void sieve_storage_unref(struct sieve_storage **_storage); + +/* + * Script access + */ + +struct sieve_script *sieve_storage_get_script + (struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) ATTR_NULL(3); +struct sieve_script *sieve_storage_open_script + (struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) ATTR_NULL(3); + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r); + +/* + * Active script + */ + +int sieve_storage_active_script_get_name + (struct sieve_storage *storage, const char **name_r); + +struct sieve_script *sieve_storage_active_script_open + (struct sieve_storage *storage, enum sieve_error *error_r) + ATTR_NULL(2); + +int sieve_storage_active_script_get_last_change + (struct sieve_storage *storage, time_t *last_change_r); + +/* + * Listing scripts in storage + */ + +struct sieve_storage_list_context; + +/* Create a context for listing the scripts in the storage */ +struct sieve_storage_list_context *sieve_storage_list_init + (struct sieve_storage *storage); +/* Get the next script in the storage. */ +const char *sieve_storage_list_next + (struct sieve_storage_list_context *lctx, bool *active_r) + ATTR_NULL(2); +/* Destroy the listing context */ +int sieve_storage_list_deinit + (struct sieve_storage_list_context **lctx); + +/* + * Saving scripts in storage + */ + +struct sieve_storage_save_context; + +struct sieve_storage_save_context * +sieve_storage_save_init(struct sieve_storage *storage, + const char *scriptname, struct istream *input); + +int sieve_storage_save_continue(struct sieve_storage_save_context *sctx); + +int sieve_storage_save_finish(struct sieve_storage_save_context *sctx); + +struct sieve_script *sieve_storage_save_get_tempscript + (struct sieve_storage_save_context *sctx); + +bool sieve_storage_save_will_activate + (struct sieve_storage_save_context *sctx); + +void sieve_storage_save_set_mtime + (struct sieve_storage_save_context *sctx, time_t mtime); + +void sieve_storage_save_cancel(struct sieve_storage_save_context **sctx); + +int sieve_storage_save_commit(struct sieve_storage_save_context **sctx); + +/* Saves input directly as a regular file at the active script path. + * This is needed for the doveadm-sieve plugin. + */ +int sieve_storage_save_as_active + (struct sieve_storage *storage, struct istream *input, time_t mtime); + +/* + * Management + */ + +int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime); + +/* + * Storage quota + */ + +enum sieve_storage_quota { + SIEVE_STORAGE_QUOTA_NONE, + SIEVE_STORAGE_QUOTA_MAXSIZE, + SIEVE_STORAGE_QUOTA_MAXSCRIPTS, + SIEVE_STORAGE_QUOTA_MAXSTORAGE +}; + +bool sieve_storage_quota_validsize + (struct sieve_storage *storage, size_t size, uint64_t *limit_r); + +uint64_t sieve_storage_quota_max_script_size + (struct sieve_storage *storage); + +int sieve_storage_quota_havespace + (struct sieve_storage *storage, const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, uint64_t *limit_r); + +/* + * Properties + */ + +const char *sieve_storage_location + (const struct sieve_storage *storage) ATTR_PURE; + +int sieve_storage_is_singular + (struct sieve_storage *storage); + +/* + * Error handling + */ + +void sieve_storage_clear_error(struct sieve_storage *storage); + +void sieve_storage_set_error + (struct sieve_storage *storage, enum sieve_error error, + const char *fmt, ...) ATTR_FORMAT(3, 4); +void sieve_storage_set_critical + (struct sieve_storage *storage, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +const char *sieve_storage_get_last_error + (struct sieve_storage *storage, enum sieve_error *error_r) + ATTR_NULL(2); + +/* + * + */ + +int sieve_storage_get_last_change + (struct sieve_storage *storage, time_t *last_change_r); +void sieve_storage_set_modified + (struct sieve_storage *storage, time_t mtime); + +#endif diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index 6a2b671d76d9f2e48808c5fbd827ce090ad33b86..0bd041aaf88b22621bee8c0051e86f6bd560e0f1 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -14,7 +14,7 @@ #include "sieve-plugins.h" #include "sieve-script.h" -#include "sieve-script-file.h" +#include "sieve-storage-private.h" #include "sieve-ast.h" #include "sieve-binary.h" #include "sieve-actions.h" @@ -125,24 +125,29 @@ struct sieve_instance *sieve_init return NULL; } + /* Initialize storage classes */ + sieve_storages_init(svinst); + + /* Initialize plugins */ sieve_plugins_load(svinst, NULL, NULL); + /* Configure extensions */ sieve_extensions_configure(svinst); return svinst; } -void sieve_deinit(struct sieve_instance **svinst) +void sieve_deinit(struct sieve_instance **_svinst) { - sieve_plugins_unload(*svinst); - - sieve_extensions_deinit(*svinst); - - sieve_errors_deinit(*svinst); + struct sieve_instance *svinst = *_svinst; - pool_unref(&(*svinst)->pool); + sieve_plugins_unload(svinst); + sieve_storages_deinit(svinst); + sieve_extensions_deinit(svinst); + sieve_errors_deinit(svinst); - *svinst = NULL; + pool_unref(&(svinst)->pool); + *_svinst = NULL; } void sieve_set_extensions @@ -247,10 +252,22 @@ struct sieve_binary *sieve_compile_script { struct sieve_ast *ast; struct sieve_binary *sbin; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; /* Parse */ if ( (ast = sieve_parse(script, ehandler, error_r)) == NULL ) { - sieve_error(ehandler, sieve_script_name(script), "parse failed"); + switch ( *error_r ) { + case SIEVE_ERROR_NOT_FOUND: + sieve_error(ehandler, sieve_script_name(script), "script not found"); + break; + default: + sieve_error(ehandler, sieve_script_name(script), "parse failed"); + } return NULL; } @@ -286,15 +303,26 @@ struct sieve_binary *sieve_compile { struct sieve_script *script; struct sieve_binary *sbin; + enum sieve_error error; if ( (script = sieve_script_create_open - (svinst, script_location, script_name, ehandler, error_r)) == NULL ) + (svinst, script_location, script_name, &error)) == NULL ) { + if (error_r != NULL) + *error_r = error; + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + sieve_error(ehandler, script_name, "script not found"); + break; + default: + sieve_internal_error(ehandler, script_name, "failed to open script"); + } return NULL; + } sbin = sieve_compile_script(script, ehandler, flags, error_r); if ( svinst->debug && sbin != NULL ) { - sieve_sys_debug(svinst, "script `%s' from %s successfully compiled", + sieve_sys_debug(svinst, "Script `%s' from %s successfully compiled", sieve_script_name(script), sieve_script_location(script)); } @@ -366,9 +394,10 @@ struct sieve_binary *sieve_open_script /* Ok, it exists; now let's see if it is up to date */ if ( !sieve_binary_up_to_date(sbin, flags) ) { /* Not up to date */ - if ( svinst->debug ) - sieve_sys_debug(svinst, "script binary %s is not up-to-date", + if ( svinst->debug ) { + sieve_sys_debug(svinst, "Script binary %s is not up-to-date", sieve_binary_path(sbin)); + } sieve_binary_unref(&sbin); sbin = NULL; @@ -379,17 +408,21 @@ struct sieve_binary *sieve_open_script * to (re-)compile. */ if ( sbin != NULL ) { - if ( svinst->debug ) - sieve_sys_debug(svinst, "script binary %s successfully loaded", + if ( svinst->debug ) { + sieve_sys_debug(svinst, + "Script binary %s successfully loaded", sieve_binary_path(sbin)); + } } else { sbin = sieve_compile_script(script, ehandler, flags, error_r); if ( sbin != NULL ) { - if ( svinst->debug ) - sieve_sys_debug(svinst, "script `%s' from %s successfully compiled", + if ( svinst->debug ) { + sieve_sys_debug(svinst, + "Script `%s' from %s successfully compiled", sieve_script_name(script), sieve_script_location(script)); + } } } } T_END; @@ -404,11 +437,21 @@ struct sieve_binary *sieve_open { struct sieve_script *script; struct sieve_binary *sbin; + enum sieve_error error; /* First open the scriptfile itself */ if ( (script=sieve_script_create_open - (svinst, script_location, script_name, ehandler, error_r)) == NULL ) { + (svinst, script_location, script_name, &error)) == NULL ) { /* Failed */ + if (error_r != NULL) + *error_r = error; + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + sieve_error(ehandler, script_name, "script not found"); + break; + default: + sieve_internal_error(ehandler, script_name, "failed to open script"); + } return NULL; } @@ -771,160 +814,5 @@ size_t sieve_max_script_size(struct sieve_instance *svinst) return svinst->max_script_size; } -/* - * Script directory - */ - -struct sieve_directory { - struct sieve_instance *svinst; - DIR *dirp; - - const char *path; -}; - -struct sieve_directory *sieve_directory_open -(struct sieve_instance *svinst, const char *path, enum sieve_error *error_r) -{ - struct sieve_directory *sdir = NULL; - DIR *dirp; - struct stat st; - - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NONE; - - if ( (path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || - (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/') ) { - /* Home-relative path. change to absolute. */ - const char *home = sieve_environment_get_homedir(svinst); - - if ( home != NULL ) { - if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) - path = home_expand_tilde(path, home); - else - path = t_strconcat(home, "/", path, NULL); - } else { - sieve_sys_error(svinst, - "sieve dir path %s is relative to home directory, " - "but home directory is not available.", path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAILURE; - return NULL; - } - } - - /* Specified path can either be a regular file or a directory */ - if ( stat(path, &st) != 0 ) { - switch ( errno ) { - case ENOENT: - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NOT_FOUND; - break; - case EACCES: - sieve_sys_error(svinst, "failed to open sieve dir: %s", - eacces_error_get("stat", path)); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERMISSION; - break; - default: - sieve_sys_error(svinst, "failed to open sieve dir: stat(%s) failed: %m", - path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAILURE; - break; - } - return NULL; - } - - if ( S_ISDIR(st.st_mode) ) { - - /* Open the directory */ - if ( (dirp = opendir(path)) == NULL ) { - switch ( errno ) { - case ENOENT: - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NOT_FOUND; - break; - case EACCES: - sieve_sys_error(svinst, "failed to open sieve dir: %s", - eacces_error_get("opendir", path)); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERMISSION; - break; - default: - sieve_sys_error(svinst, "failed to open sieve dir: opendir(%s) failed: " - "%m", path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAILURE; - break; - } - return NULL; - } - - /* Create object */ - sdir = t_new(struct sieve_directory, 1); - sdir->path = path; - sdir->dirp = dirp; - } else { - sdir = t_new(struct sieve_directory, 1); - sdir->path = path; - sdir->dirp = NULL; - } - - sdir->svinst = svinst; - - return sdir; -} - -const char *sieve_directory_get_scriptfile(struct sieve_directory *sdir) -{ - const char *script = NULL; - struct dirent *dp; - - if ( sdir->dirp != NULL ) { - while ( script == NULL ) { - const char *file; - struct stat st; - - errno = 0; - if ( (dp = readdir(sdir->dirp)) == NULL ) { - if ( errno != 0 ) { - sieve_sys_error(sdir->svinst, "failed to read sieve dir: " - "readdir(%s) failed: %m", sdir->path); - } - - return NULL; - } - - if ( !sieve_scriptfile_has_extension(dp->d_name) ) - continue; - - if ( sdir->path[strlen(sdir->path)-1] == '/' ) - file = t_strconcat(sdir->path, dp->d_name, NULL); - else - file = t_strconcat(sdir->path, "/", dp->d_name, NULL); - - if ( stat(file, &st) != 0 || !S_ISREG(st.st_mode) ) - continue; - - script = file; - } - } else { - script = sdir->path; - sdir->path = NULL; - } - - return script; -} - -void sieve_directory_close(struct sieve_directory **sdir) -{ - /* Close the directory */ - if ( (*sdir)->dirp != NULL && closedir((*sdir)->dirp) < 0 ) - sieve_sys_error((*sdir)->svinst, "failed to close sieve dir: " - "closedir(%s) failed: %m", (*sdir)->path); - - *sdir = NULL; -} - diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index 6058d6838f5a8b2da7e91e2e307c9b214270fb7f..ea4aa3c309c39ce25d185045d74848e00ca7aee6 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -26,7 +26,7 @@ struct sieve_instance *sieve_init /* sieve_deinit(): * Frees all memory allocated by the sieve engine. */ -void sieve_deinit(struct sieve_instance **svinst); +void sieve_deinit(struct sieve_instance **_svinst); /* sieve_get_capabilities(): * @@ -48,7 +48,8 @@ void sieve_set_extensions */ struct sieve_binary *sieve_compile_script (struct sieve_script *script, struct sieve_error_handler *ehandler, - enum sieve_compile_flags flags, enum sieve_error *error_r); + enum sieve_compile_flags flags, enum sieve_error *error_r) + ATTR_NULL(2, 4); /* sieve_compile: * @@ -57,7 +58,8 @@ struct sieve_binary *sieve_compile_script struct sieve_binary *sieve_compile (struct sieve_instance *svinst, const char *script_location, const char *script_name, struct sieve_error_handler *ehandler, - enum sieve_compile_flags flags, enum sieve_error *error_r); + enum sieve_compile_flags flags, enum sieve_error *error_r) + ATTR_NULL(3, 4, 6); /* * Reading/writing Sieve binaries @@ -205,15 +207,4 @@ unsigned int sieve_max_redirects(struct sieve_instance *svinst); unsigned int sieve_max_actions(struct sieve_instance *svinst); size_t sieve_max_script_size(struct sieve_instance *svinst); -/* - * Script directory - */ - -struct sieve_directory; - -struct sieve_directory *sieve_directory_open - (struct sieve_instance *svinst, const char *path, enum sieve_error *error_r); -const char *sieve_directory_get_scriptfile(struct sieve_directory *sdir); -void sieve_directory_close(struct sieve_directory **sdir); - #endif diff --git a/src/lib-sieve/storage/Makefile.am b/src/lib-sieve/storage/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..f33b0bca4400cbead84801fc634e145c889cf870 --- /dev/null +++ b/src/lib-sieve/storage/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + file \ + dict diff --git a/src/lib-sieve/storage/dict/Makefile.am b/src/lib-sieve/storage/dict/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..2a73f4393f88bf997c8b9ad32c19810cd3528393 --- /dev/null +++ b/src/lib-sieve/storage/dict/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES = libsieve_storage_dict.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +libsieve_storage_dict_la_SOURCES = \ + sieve-dict-script.c \ + sieve-dict-storage.c + +noinst_HEADERS = \ + sieve-dict-storage.h diff --git a/src/lib-sieve/storage/dict/sieve-dict-script.c b/src/lib-sieve/storage/dict/sieve-dict-script.c new file mode 100644 index 0000000000000000000000000000000000000000..4d093f925d535ed8f490cd7c5458307e754d44ff --- /dev/null +++ b/src/lib-sieve/storage/dict/sieve-dict-script.c @@ -0,0 +1,315 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strfuncs.h" +#include "istream.h" +#include "dict.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-binary.h" + +#include "sieve-dict-storage.h" + +/* + * Script dict implementation + */ + +static struct sieve_dict_script *sieve_dict_script_alloc(void) +{ + struct sieve_dict_script *dscript; + pool_t pool; + + pool = pool_alloconly_create("sieve_dict_script", 1024); + dscript = p_new(pool, struct sieve_dict_script, 1); + dscript->script = sieve_dict_script; + dscript->script.pool = pool; + + return dscript; +} + +struct sieve_dict_script *sieve_dict_script_init +(struct sieve_dict_storage *dstorage, const char *name) +{ + struct sieve_storage *storage = &dstorage->storage; + struct sieve_dict_script *dscript = NULL; + + if ( name == NULL ) + name = SIEVE_DICT_SCRIPT_DEFAULT; + + dscript = sieve_dict_script_alloc(); + sieve_script_init(&dscript->script, + storage, &sieve_dict_script, storage->location, name); + + return dscript; +} + +static void sieve_dict_script_destroy(struct sieve_script *script) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + if ( dscript->data_pool != NULL ) + pool_unref(&dscript->data_pool); +} + +static int sieve_dict_script_open +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + const char *name = script->name; + const char *path; + int ret; + + if ( sieve_dict_storage_get_dict + (dstorage, &dscript->dict, error_r) < 0 ) + return -1; + + path = t_strconcat + (DICT_SIEVE_NAME_PATH, dict_escape_string(name), NULL); + + ret = dict_lookup + (dscript->dict, script->pool, path, &dscript->data_id); + if ( ret <= 0 ) { + if ( ret < 0 ) { + sieve_script_set_critical(script, + "Failed to lookup script id from path %s", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + } else { + sieve_script_sys_debug(script, + "Script `%s' not found at path %s", name, path); + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script `%s' not found", name); + *error_r = SIEVE_ERROR_NOT_FOUND; + } + return -1; + } + + if ( strcmp(name, SIEVE_DICT_SCRIPT_DEFAULT) == 0 ) { + script->location = p_strconcat(script->pool, + SIEVE_DICT_STORAGE_DRIVER_NAME, ":", storage->location, NULL); + } else { + script->location = p_strconcat(script->pool, + SIEVE_DICT_STORAGE_DRIVER_NAME, ":", storage->location, + ";name=", name, NULL); + } + + return 0; +} + +static int sieve_dict_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + const char *path, *name = script->name; + int ret; + + dscript->data_pool = + pool_alloconly_create("sieve_dict_script data pool", 1024); + + path = t_strconcat + (DICT_SIEVE_DATA_PATH, dict_escape_string(dscript->data_id), NULL); + + ret = dict_lookup + (dscript->dict, dscript->data_pool, path, &dscript->data); + if ( ret <= 0 ) { + if ( ret < 0 ) { + sieve_script_set_critical(script, + "Failed to lookup data with id `%s' " + "for script `%s' from path %s", + dscript->data_id, name, path); + } else { + sieve_script_set_critical(script, + "Data with id `%s' for script `%s' " + "not found at path %s", + dscript->data_id, name, path); + } + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + *stream_r = i_stream_create_from_data(dscript->data, strlen(dscript->data)); + return 0; +} + +static int sieve_dict_script_binary_read_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock, + sieve_size_t *offset) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + struct sieve_binary *sbin = + sieve_binary_block_get_binary(sblock); + string_t *data_id; + + if ( dscript->data_id == NULL && + sieve_script_open(script, NULL) < 0 ) + return 0; + + if ( !sieve_binary_read_string(sblock, offset, &data_id) ) { + sieve_script_sys_error(script, + "Binary `%s' has invalid metadata for script `%s'", + sieve_binary_path(sbin), sieve_script_location(script)); + return -1; + } + i_assert( dscript->data_id != NULL ); + if ( strcmp(str_c(data_id), dscript->data_id) != 0 ) + return 0; + return 1; +} + +static void sieve_dict_script_binary_write_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + sieve_binary_emit_cstring(sblock, dscript->data_id); +} + +static const char * sieve_dict_script_get_binpath +(struct sieve_dict_script *dscript) +{ + struct sieve_script *script = &dscript->script; + struct sieve_storage *storage = script->storage; + + if ( dscript->binpath == NULL ) { + if ( storage->bin_dir == NULL ) + return NULL; + dscript->binpath = p_strconcat(script->pool, + storage->bin_dir, "/", + sieve_binfile_from_name(script->name), NULL); + } + + return dscript->binpath; +} + +static struct sieve_binary *sieve_dict_script_binary_load +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + if ( sieve_dict_script_get_binpath(dscript) == NULL ) + return NULL; + + return sieve_binary_open(script->storage->svinst, + dscript->binpath, script, error_r); +} + +static int sieve_dict_script_binary_save +(struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + if ( sieve_dict_script_get_binpath(dscript) == NULL ) + return 0; + if ( sieve_storage_setup_bindir(script->storage, 0700) < 0 ) + return -1; + + return sieve_binary_save(sbin, + dscript->binpath, update, 0600, error_r); +} + +static bool sieve_dict_script_equals +(const struct sieve_script *script, const struct sieve_script *other) +{ + struct sieve_storage *storage = script->storage; + struct sieve_storage *sother = other->storage; + + if ( strcmp(storage->location, sother->location) != 0 ) + return FALSE; + + i_assert( script->name != NULL && other->name != NULL ); + + return ( strcmp(script->name, other->name) == 0 ); +} + +const struct sieve_script sieve_dict_script = { + .driver_name = SIEVE_DICT_STORAGE_DRIVER_NAME, + .v = { + .destroy = sieve_dict_script_destroy, + + .open = sieve_dict_script_open, + + .get_stream = sieve_dict_script_get_stream, + + .binary_read_metadata =sieve_dict_script_binary_read_metadata, + .binary_write_metadata = sieve_dict_script_binary_write_metadata, + .binary_load = sieve_dict_script_binary_load, + .binary_save = sieve_dict_script_binary_save, + + .equals = sieve_dict_script_equals + } +}; + +/* + * Script sequence + */ + +struct sieve_dict_script_sequence { + struct sieve_script_sequence seq; + + unsigned int done:1; +}; + +struct sieve_script_sequence *sieve_dict_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + struct sieve_dict_script_sequence *dseq = NULL; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + /* Create sequence object */ + dseq = i_new(struct sieve_dict_script_sequence, 1); + sieve_script_sequence_init(&dseq->seq, storage); + + return &dseq->seq; +} + +struct sieve_script *sieve_dict_script_sequence_next +(struct sieve_script_sequence *seq, enum sieve_error *error_r) +{ + struct sieve_dict_script_sequence *dseq = + (struct sieve_dict_script_sequence *)seq; + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)seq->storage; + struct sieve_dict_script *dscript; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + if ( dseq->done ) + return NULL; + dseq->done = TRUE; + + dscript = sieve_dict_script_init + (dstorage, seq->storage->script_name); + if ( sieve_script_open(&dscript->script, error_r) < 0 ) { + struct sieve_script *script = &dscript->script; + sieve_script_unref(&script); + return NULL; + } + + return &dscript->script; +} + +void sieve_dict_script_sequence_destroy(struct sieve_script_sequence *seq) +{ + struct sieve_dict_script_sequence *dseq = + (struct sieve_dict_script_sequence *)seq; + i_free(dseq); +} + diff --git a/src/lib-sieve/storage/dict/sieve-dict-storage.c b/src/lib-sieve/storage/dict/sieve-dict-storage.c new file mode 100644 index 0000000000000000000000000000000000000000..c89bee0edaba0a7a5f2d46daa356b967452dec9f --- /dev/null +++ b/src/lib-sieve/storage/dict/sieve-dict-storage.c @@ -0,0 +1,185 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "dict.h" + +#include "sieve-common.h" +#include "sieve-error.h" + +#include "sieve-dict-storage.h" + +/* + * Storage class + */ + +static struct sieve_storage *sieve_dict_storage_alloc(void) +{ + struct sieve_dict_storage *dstorage; + pool_t pool; + + pool = pool_alloconly_create("sieve_dict_storage", 1024); + dstorage = p_new(pool, struct sieve_dict_storage, 1); + dstorage->storage = sieve_dict_storage; + dstorage->storage.pool = pool; + + return &dstorage->storage; +} + +static int sieve_dict_storage_init +(struct sieve_storage *storage, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + struct sieve_instance *svinst = storage->svinst; + const char *username = NULL; + + if ( options != NULL ) { + while ( *options != NULL ) { + const char *option = *options; + + if ( strncasecmp(option, "user=", 5) == 0 && option[5] != '\0' ) { + username = option+5; + } else { + sieve_storage_set_critical(storage, + "Invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + options++; + } + } + + if ( username == NULL ) { + if ( svinst->username == NULL ) { + sieve_storage_set_critical(storage, + "No username specified"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + username = svinst->username; + } + + if ( svinst->base_dir == NULL ) { + sieve_storage_set_critical(storage, + "BUG: Sieve interpreter is initialized without a base_dir"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + sieve_storage_sys_debug(storage, + "user=%s, uri=%s", username, storage->location); + + dstorage->username = p_strdup(storage->pool, username); + return 0; +} + +int sieve_dict_storage_get_dict +(struct sieve_dict_storage *dstorage, struct dict **dict_r, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &dstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *error; + int ret; + + if ( dstorage->dict == NULL ) { + ret = dict_init(storage->location, DICT_DATA_TYPE_STRING, + dstorage->username, svinst->base_dir, &dstorage->dict, &error); + if ( ret < 0 ) { + sieve_storage_set_critical(storage, + "Failed to initialize dict with data `%s' for user `%s': %s", + storage->location, dstorage->username, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + + *dict_r = dstorage->dict; + return 0; +} + +static void sieve_dict_storage_destroy(struct sieve_storage *storage) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + + if ( dstorage->dict != NULL ) + dict_deinit(&dstorage->dict); +} + +/* + * Script access + */ + +static struct sieve_script *sieve_dict_storage_get_script +(struct sieve_storage *storage, const char *name) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + struct sieve_dict_script *dscript; + + T_BEGIN { + dscript = sieve_dict_script_init(dstorage, name); + } T_END; + + return &dscript->script; +} + +/* + * Active script + */ + +struct sieve_script *sieve_dict_storage_active_script_open +(struct sieve_storage *storage) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + struct sieve_dict_script *dscript; + + dscript = sieve_dict_script_init + (dstorage, storage->script_name); + if ( sieve_script_open(&dscript->script, NULL) < 0 ) { + struct sieve_script *script = &dscript->script; + sieve_script_unref(&script); + return NULL; + } + + return &dscript->script; +} + +int sieve_dict_storage_active_script_get_name +(struct sieve_storage *storage, const char **name_r) +{ + if ( storage->script_name != NULL ) + *name_r = storage->script_name; + else + *name_r = SIEVE_DICT_SCRIPT_DEFAULT; + return 0; +} + +/* + * Driver definition + */ + +const struct sieve_storage sieve_dict_storage = { + .driver_name = SIEVE_DICT_STORAGE_DRIVER_NAME, + .v = { + .alloc = sieve_dict_storage_alloc, + .destroy = sieve_dict_storage_destroy, + .init = sieve_dict_storage_init, + + .get_script = sieve_dict_storage_get_script, + + .get_script_sequence = sieve_dict_storage_get_script_sequence, + .script_sequence_next = sieve_dict_script_sequence_next, + .script_sequence_destroy = sieve_dict_script_sequence_destroy, + + .active_script_get_name = sieve_dict_storage_active_script_get_name, + .active_script_open = sieve_dict_storage_active_script_open, + + // FIXME: impement management interface + } +}; diff --git a/src/lib-sieve/storage/dict/sieve-dict-storage.h b/src/lib-sieve/storage/dict/sieve-dict-storage.h new file mode 100644 index 0000000000000000000000000000000000000000..30e9b778f96415684489f4852ee8062cbdddfe57 --- /dev/null +++ b/src/lib-sieve/storage/dict/sieve-dict-storage.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SIEVE_DICT_STORAGE_H +#define __SIEVE_DICT_STORAGE_H + +#include "sieve.h" +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#define DICT_SIEVE_PATH DICT_PATH_PRIVATE"sieve/" +#define DICT_SIEVE_NAME_PATH DICT_SIEVE_PATH"name/" +#define DICT_SIEVE_DATA_PATH DICT_SIEVE_PATH"data/" + +#define SIEVE_DICT_SCRIPT_DEFAULT "default" + +/* + * Storage class + */ + +struct sieve_dict_storage { + struct sieve_storage storage; + + const char *username; + + struct dict *dict; +}; + +int sieve_dict_storage_get_dict + (struct sieve_dict_storage *dstorage, struct dict **dict_r, + enum sieve_error *error_r); + +struct sieve_script *sieve_dict_storage_active_script_open + (struct sieve_storage *storage); +int sieve_dict_storage_active_script_get_name + (struct sieve_storage *storage, const char **name_r); + +/* + * Script class + */ + +struct sieve_dict_script { + struct sieve_script script; + + struct dict *dict; + + pool_t data_pool; + const char *data_id; + const char *data; + + const char *binpath; +}; + +struct sieve_dict_script *sieve_dict_script_init + (struct sieve_dict_storage *dstorage, const char *name); + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_dict_storage_get_script_sequence + (struct sieve_storage *storage, enum sieve_error *error_r); + +struct sieve_script *sieve_dict_script_sequence_next + (struct sieve_script_sequence *seq, enum sieve_error *error_r); +void sieve_dict_script_sequence_destroy(struct sieve_script_sequence *seq); + +#endif diff --git a/src/lib-sieve/storage/file/Makefile.am b/src/lib-sieve/storage/file/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..a6fda424fa10e69827efe1e17d0b6fc67f5234a5 --- /dev/null +++ b/src/lib-sieve/storage/file/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libsieve_storage_file.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +libsieve_storage_file_la_SOURCES = \ + sieve-file-script.c \ + sieve-file-script-sequence.c \ + sieve-file-storage-active.c \ + sieve-file-storage-save.c \ + sieve-file-storage-list.c \ + sieve-file-storage-quota.c \ + sieve-file-storage.c + +noinst_HEADERS = \ + sieve-file-storage.h diff --git a/src/lib-sieve/storage/file/sieve-file-script-sequence.c b/src/lib-sieve/storage/file/sieve-file-script-sequence.c new file mode 100644 index 0000000000000000000000000000000000000000..6822e71019375c72d94edcdfa5ee8334909f797f --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-script-sequence.c @@ -0,0 +1,239 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" +#include "eacces-error.h" + +#include "sieve-common.h" +#include "sieve-script-private.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> + +/* + * Script sequence + */ + +struct sieve_file_script_sequence { + struct sieve_script_sequence seq; + pool_t pool; + + ARRAY_TYPE(const_string) script_files; + unsigned int index; +}; + +static int sieve_file_script_sequence_read_dir +(struct sieve_file_script_sequence *fseq, const char *path) +{ + struct sieve_storage *storage = fseq->seq.storage; + DIR *dirp; + int ret = 0; + + /* Open the directory */ + if ( (dirp = opendir(path)) == NULL ) { + switch ( errno ) { + case ENOENT: + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script sequence location not found"); + break; + case EACCES: + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_PERMISSION, + "Script sequence location not accessible"); + sieve_storage_sys_error(storage, + "Failed to open sieve sequence: " + "%s", eacces_error_get("stat", path)); + break; + default: + sieve_storage_set_critical(storage, + "Failed to open sieve sequence: " + "opendir(%s) failed: %m", path); + break; + } + return -1; + } + + /* Read and sort script files */ + for (;;) { + const char *const *files; + unsigned int count, i; + const char *file; + struct dirent *dp; + struct stat st; + + errno = 0; + if ( (dp=readdir(dirp)) == NULL ) + break; + + if ( !sieve_script_file_has_extension(dp->d_name) ) + continue; + + file = NULL; + T_BEGIN { + if ( path[strlen(path)-1] == '/' ) + file = t_strconcat(path, dp->d_name, NULL); + else + file = t_strconcat(path, "/", dp->d_name, NULL); + + if ( stat(file, &st) == 0 && S_ISREG(st.st_mode) ) + file = p_strdup(fseq->pool, file); + else + file = NULL; + } T_END; + + if (file == NULL) + continue; + + /* Insert into sorted array */ + files = array_get(&fseq->script_files, &count); + for ( i = 0; i < count; i++ ) { + if ( strcmp(file, files[i]) < 0 ) + break; + } + + if ( i == count ) + array_append(&fseq->script_files, &file, 1); + else + array_insert(&fseq->script_files, i, &file, 1); + } + + if ( errno != 0 ) { + sieve_storage_set_critical(storage, + "Failed to read sequence directory: " + "readdir(%s) failed: %m", path); + ret = -1; + } + + /* Close the directory */ + if ( dirp != NULL && closedir(dirp) < 0 ) { + sieve_storage_sys_error(storage, + "Failed to close sequence directory: " + "closedir(%s) failed: %m", path); + } + return ret; +} + +struct sieve_script_sequence *sieve_file_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_script_sequence *fseq = NULL; + const char *name = storage->script_name; + const char *file; + pool_t pool; + struct stat st; + + /* Specified path can either be a regular file or a directory */ + if ( stat(fstorage->path, &st) != 0 ) { + switch ( errno ) { + case ENOENT: + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script sequence location not found"); + break; + case EACCES: + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_PERMISSION, + "Script sequence location not accessible"); + sieve_storage_sys_error(storage, + "Failed to open sieve sequence: " + "%s", eacces_error_get("stat", fstorage->path)); + break; + default: + sieve_storage_set_critical(storage, + "Failed to open sieve sequence: " + "stat(%s) failed: %m", fstorage->path); + break; + } + *error_r = storage->error_code; + return NULL; + } + + /* Create sequence object */ + pool = pool_alloconly_create("sieve_file_script_sequence", 1024); + fseq = p_new(pool, struct sieve_file_script_sequence, 1); + fseq->pool = pool; + sieve_script_sequence_init(&fseq->seq, storage); + i_array_init(&fseq->script_files, 16); + + if ( S_ISDIR(st.st_mode) ) { + /* Path is directory */ + if (name == 0 || *name == '\0') { + /* Read all '.sieve' files in directory */ + if (sieve_file_script_sequence_read_dir + (fseq, fstorage->path) < 0) { + *error_r = storage->error_code; + sieve_file_script_sequence_destroy(&fseq->seq); + return NULL; + } + + } else { + /* Read specific script file */ + file = sieve_script_file_from_name(name); + file = sieve_file_storage_path_extend(fstorage, file); + file = p_strdup(pool, file); + array_append(&fseq->script_files, &file, 1); + } + + } else { + /* Path is a file + (apparently; we'll see about that once it is opened) */ + file = p_strdup(pool, fstorage->path); + array_append(&fseq->script_files, &file, 1); + } + + return &fseq->seq; +} + +struct sieve_script *sieve_file_script_sequence_next +(struct sieve_script_sequence *seq, enum sieve_error *error_r) +{ + struct sieve_file_script_sequence *fseq = + (struct sieve_file_script_sequence *)seq; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)seq->storage; + struct sieve_file_script *fscript; + const char *const *files; + unsigned int count; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + files = array_get(&fseq->script_files, &count); + + fscript = NULL; + while ( fseq->index < count ) { + fscript = sieve_file_script_open_from_filename + (fstorage, files[fseq->index++], NULL); + if (fscript == NULL) { + if (seq->storage->error_code != SIEVE_ERROR_NOT_FOUND) + break; + sieve_storage_clear_error(seq->storage); + } + } + + if (fscript == NULL ) { + if ( error_r != NULL ) + *error_r = seq->storage->error_code; + return NULL; + } + return &fscript->script; +} + +void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq) +{ + struct sieve_file_script_sequence *fseq = + (struct sieve_file_script_sequence *)seq; + + array_free(&fseq->script_files); + pool_unref(&fseq->pool); +} diff --git a/src/lib-sieve/storage/file/sieve-file-script.c b/src/lib-sieve/storage/file/sieve-file-script.c new file mode 100644 index 0000000000000000000000000000000000000000..b32468d475e1dce8942b4e2ad9cc5b87abf66d9d --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-script.c @@ -0,0 +1,757 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "abspath.h" +#include "istream.h" +#include "eacces-error.h" + +#include "sieve-binary.h" +#include "sieve-script-private.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> + +/* + * Filename to name/name to filename + */ + +const char *sieve_script_file_get_scriptname(const char *filename) +{ + const char *ext; + + /* Extract the script name */ + ext = strrchr(filename, '.'); + if ( ext == NULL || ext == filename || + strcmp(ext, "."SIEVE_SCRIPT_FILEEXT) != 0 ) + return NULL; + + return t_strdup_until(filename, ext); +} + +bool sieve_script_file_has_extension(const char *filename) +{ + return ( sieve_script_file_get_scriptname(filename) != NULL ); +} + +const char *sieve_script_file_from_name(const char *name) +{ + return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL); +} + +/* + * Common error handling + */ + +static void sieve_file_script_handle_error +(struct sieve_file_script *fscript, const char *path, + enum sieve_error *error_r) +{ + struct sieve_script *script = &fscript->script; + + switch ( errno ) { + case ENOENT: + sieve_script_sys_debug(script, "File `%s' not found", t_abspath(path)); + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script `%s' not found", + fscript->script.name); + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + sieve_script_set_critical(script, + "Failed to stat sieve script: %s", eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERMISSION; + break; + default: + sieve_script_set_critical(script, + "Failed to stat sieve script: stat(%s) failed: %m", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } +} + +/* + * + */ + +static struct sieve_file_script *sieve_file_script_alloc(void) +{ + struct sieve_file_script *fscript; + pool_t pool; + + pool = pool_alloconly_create("sieve_file_script", 1024); + fscript = p_new(pool, struct sieve_file_script, 1); + fscript->script = sieve_file_script; + fscript->script.pool = pool; + + return fscript; +} + +struct sieve_file_script *sieve_file_script_init_from_filename +(struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_file_script *fscript = NULL; + + /* Prevent initializing the active script link as a script when it + * resides in the sieve storage directory. + */ + if ( scriptname != NULL && fstorage->link_path != NULL && + *(fstorage->link_path) == '\0' ) { + if ( strcmp(filename, fstorage->active_fname) == 0 ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script `%s' does not exist.", scriptname); + return NULL; + } + } + + 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); + return fscript; +} + +struct sieve_file_script *sieve_file_script_open_from_filename +(struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname) +{ + struct sieve_file_script *fscript; + enum sieve_error error; + + fscript = sieve_file_script_init_from_filename + (fstorage, filename, scriptname); + if ( fscript == NULL ) + return NULL; + + if ( sieve_script_open(&fscript->script, &error) < 0 ) { + struct sieve_script *script = &fscript->script; + sieve_script_unref(&script); + return NULL; + } + + return fscript; +} + +struct sieve_file_script *sieve_file_script_init_from_name +(struct sieve_file_storage *fstorage, const char *name) +{ + return sieve_file_script_init_from_filename + (fstorage, sieve_script_file_from_name(name), name); +} + +struct sieve_file_script *sieve_file_script_init_from_path +(struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) +{ + struct sieve_instance *svinst = fstorage->storage.svinst; + struct sieve_file_storage *fsubstorage; + struct sieve_file_script *fscript; + struct sieve_storage *substorage; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + fsubstorage = sieve_file_storage_init_from_path + (svinst, path, 0, error_r); + if (fsubstorage == NULL) + return NULL; + substorage = &fsubstorage->storage; + + fscript = sieve_file_script_alloc(); + sieve_script_init(&fscript->script, + substorage, &sieve_file_script, path, scriptname); + sieve_storage_unref(&substorage); + + return fscript; +} + +struct sieve_file_script *sieve_file_script_open_from_path +(struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_file_script *fscript; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + fscript = sieve_file_script_init_from_path + (fstorage, path, scriptname, error_r); + if (fscript == NULL) { + sieve_storage_set_error(storage, + *error_r, "Failed to open script"); + return NULL; + } + + if ( sieve_script_open(&fscript->script, error_r) < 0 ) { + struct sieve_script *script = &fscript->script; + const char *errormsg; + + errormsg = sieve_script_get_last_error(&fscript->script, error_r); + sieve_storage_set_error(storage, + *error_r, "%s", errormsg); + sieve_script_unref(&script); + return NULL; + } + + return fscript; +} + +/* + * Open + */ + +static int sieve_file_script_stat +(const char *path, struct stat *st, struct stat *lnk_st) +{ + if ( lstat(path, st) < 0 ) + return -1; + + *lnk_st = *st; + + if ( S_ISLNK(st->st_mode) && stat(path, st) < 0 ) + return -1; + + return 1; +} + +static int sieve_file_script_open +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + pool_t pool = script->pool; + const char *filename, *name, *path; + const char *dirpath, *basename, *binpath, *binprefix; + struct stat st; + struct stat lnk_st; + bool success = TRUE; + int ret; + + filename = fscript->filename; + basename = NULL; + name = script->name; + path = fstorage->path; + + if (name == NULL) + name = storage->script_name; + + T_BEGIN { + if ( (ret=sieve_file_script_stat(path, &st, &lnk_st)) > 0 ) { + if ( S_ISDIR(st.st_mode) ) { + /* Path is directory */ + if ( (filename == NULL || *filename == '\0') && + name != NULL && *name != '\0' ) { + /* Name is used to find actual filename */ + filename = sieve_script_file_from_name(name); + basename = name; + } + if ( filename == NULL && *filename == '\0' ) { + sieve_script_set_critical(script, + "Sieve script file path '%s' is a directory.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + success = FALSE; + } else { + /* Extend storage path with filename */ + if (name == NULL) { + if ( basename == NULL && + (basename=sieve_script_file_get_scriptname(filename)) == NULL ) + basename = filename; + name = basename; + } else if (basename == NULL) { + basename = name; + } + dirpath = path; + + path = sieve_file_storage_path_extend(fstorage, filename); + ret = sieve_file_script_stat(path, &st, &lnk_st); + } + + } else { + + /* Extract filename from path */ + filename = strrchr(path, '/'); + if ( filename == NULL ) { + dirpath = ""; + filename = path; + } else { + dirpath = t_strdup_until(path, filename); + filename++; + } + + if ( (basename=sieve_script_file_get_scriptname(filename)) == NULL ) + basename = filename; + + if ( name == NULL ) + name = basename; + } + } else { + basename = name; + } + + if ( success ) { + if ( ret <= 0 ) { + if ( fscript->script.name == NULL ) + fscript->script.name = p_strdup(pool, basename); + sieve_file_script_handle_error(fscript, path, error_r); + success = FALSE; + } else if (!S_ISREG(st.st_mode) ) { + sieve_script_set_critical(script, + "Sieve script file '%s' is not a regular file.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + success = FALSE; + } + } + + if ( success ) { + const char *bpath, *bfile, *bprefix; + + if ( storage->bin_dir != NULL ) { + bpath = storage->bin_dir; + bfile = sieve_binfile_from_name(name); + bprefix = name; + + } else { + bpath = dirpath; + bfile = sieve_binfile_from_name(basename); + bprefix = basename; + } + + if ( *bpath == '\0' ) { + binpath = bfile; + binprefix = bprefix; + } else if ( bpath[strlen(bpath)-1] == '/' ) { + binpath = t_strconcat(bpath, bfile, NULL); + binprefix = t_strconcat(bpath, bprefix, NULL); + } else { + binpath = t_strconcat(bpath, "/", bfile, NULL); + binprefix = t_strconcat(bpath, "/", bprefix, NULL); + } + + fscript->st = st; + fscript->lnk_st = lnk_st; + fscript->path = p_strdup(pool, path); + fscript->filename = p_strdup(pool, filename); + fscript->dirpath = p_strdup(pool, dirpath); + fscript->binpath = p_strdup(pool, binpath); + fscript->binprefix = p_strdup(pool, binprefix); + + if ( fscript->script.name == NULL || *fscript->script.name == '\0' || + strcmp(fscript->script.name, basename) == 0 ) + fscript->script.location = fscript->path; + else + fscript->script.location = p_strconcat + (pool, fscript->path, ";name=", fscript->script.name, NULL); + + if ( fscript->script.name == NULL ) + fscript->script.name = p_strdup(pool, basename); + } + } T_END; + + return ( success ? 0 : -1 ); +} + +static int sieve_file_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + struct stat st; + struct istream *result; + int fd; + + if ( (fd=open(fscript->path, O_RDONLY)) < 0 ) { + sieve_file_script_handle_error + (fscript, fscript->path, error_r); + return -1; + } + + if ( fstat(fd, &st) != 0 ) { + sieve_script_set_critical(script, + "Failed to open sieve script: fstat(fd=%s) failed: %m", + fscript->path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + result = NULL; + } else { + /* Re-check the file type just to be sure */ + if ( !S_ISREG(st.st_mode) ) { + sieve_script_set_critical(script, + "Sieve script file `%s' is not a regular file", fscript->path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + result = NULL; + } else { + result = i_stream_create_fd_autoclose(&fd, SIEVE_FILE_READ_BLOCK_SIZE); + fscript->st = fscript->lnk_st = st; + } + } + + if ( result == NULL ) { + /* Something went wrong, close the fd */ + if ( fd >= 0 && close(fd) != 0 ) { + sieve_script_sys_error(script, + "Failed to close sieve script: " + "close(fd=%s) failed: %m", fscript->path); + } + } + + *stream_r = result; + return 0; +} + +/* + * Binary + */ + +static int sieve_file_script_binary_read_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock, + sieve_size_t *offset ATTR_UNUSED) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + time_t time = ( fscript->st.st_mtime > fscript->lnk_st.st_mtime ? + fscript->st.st_mtime : fscript->lnk_st.st_mtime ); + + if ( sieve_binary_mtime(sbin) <= time ) + return 0; + + return 1; +} + +static struct sieve_binary *sieve_file_script_binary_load +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + struct sieve_instance *svinst = script->storage->svinst; + + return sieve_binary_open(svinst, fscript->binpath, script, error_r); +} + +static int sieve_file_script_binary_save +(struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = script->storage; + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + if ( storage->bin_dir != NULL && + sieve_storage_setup_bindir(storage, 0700) < 0 ) + return -1; + + return sieve_binary_save(sbin, fscript->binpath, update, + fscript->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), error_r); +} + +static const char *sieve_file_script_binary_get_prefix +(struct sieve_script *script) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + return fscript->binprefix; +} + +/* + * Management + */ + +static int sieve_file_storage_script_is_active(struct sieve_script *script) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *) script; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)script->storage; + const char *afile; + int ret = 0; + + T_BEGIN { + ret = sieve_file_storage_active_script_get_file(fstorage, &afile); + + if ( ret > 0 ) { + /* Is the requested script active? */ + ret = ( strcmp(fscript->filename, afile) == 0 ? 1 : 0 ); + } + } T_END; + + return ret; +} + +static int sieve_file_storage_script_delete(struct sieve_script *script) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + int ret = 0; + + if ( sieve_file_storage_pre_modify(script->storage) < 0 ) + return -1; + + ret = unlink(fscript->path); + if ( ret < 0 ) { + if ( errno == ENOENT ) { + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script does not exist."); + } else { + sieve_script_set_critical(script, + "Performing unlink() failed on sieve file `%s': %m", + fscript->path); + } + } + return ret; +} + +static int _sieve_file_storage_script_activate +(struct sieve_file_script *fscript) +{ + struct sieve_script *script = &fscript->script; + struct sieve_storage *storage = script->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + const char *link_path, *afile; + int activated = 0; + int ret; + + /* Find out whether there is an active script, but recreate + * the symlink either way. This way, any possible error in the symlink + * resolves automatically. This step is only necessary to provide a + * proper return value indicating whether the script was already active. + */ + ret = sieve_file_storage_active_script_get_file(fstorage, &afile); + + /* Is the requested script already active? */ + if ( ret <= 0 || strcmp(fscript->filename, afile) != 0 ) + activated = 1; + + /* Check the scriptfile we are trying to activate */ + if ( lstat(fscript->path, &st) != 0 ) { + sieve_script_set_critical(script, + "Failed to activate Sieve script: lstat(%s) failed: %m.", + fscript->path); + return -1; + } + + /* Rescue a possible .dovecot.sieve regular file remaining from old + * installations. + */ + if ( !sieve_file_storage_active_rescue_regular(fstorage) ) { + /* Rescue failed, manual intervention is necessary */ + return -1; + } + + /* Just try to create the symlink first */ + link_path = t_strconcat + ( fstorage->link_path, fscript->filename, NULL ); + + ret = symlink(link_path, fstorage->active_path); + if ( ret < 0 ) { + if ( errno == EEXIST ) { + ret = sieve_file_storage_active_replace_link(fstorage, link_path); + if ( ret < 0 ) { + return ret; + } + } else { + /* Other error, critical */ + sieve_script_set_critical(script, + "Failed to activate Sieve script: " + "symlink(%s, %s) failed: %m", + link_path, fstorage->active_path); + return -1; + } + } + return activated; +} + +static int sieve_file_storage_script_activate +(struct sieve_script *script) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + int ret; + + if ( sieve_file_storage_pre_modify(script->storage) < 0 ) + return -1; + + T_BEGIN { + ret = _sieve_file_storage_script_activate(fscript); + } T_END; + + return ret; +} + +static int sieve_file_storage_script_rename +(struct sieve_script *script, const char *newname) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + const char *newpath, *newfile, *link_path; + int ret = 0; + + if ( sieve_file_storage_pre_modify(storage) < 0 ) + return -1; + + T_BEGIN { + newfile = sieve_script_file_from_name(newname); + newpath = t_strconcat( fstorage->path, "/", newfile, NULL ); + + /* The normal rename() system call overwrites the existing file without + * notice. Also, active scripts must not be disrupted by renaming a script. + * That is why we use a link(newpath) [activate newpath] unlink(oldpath) + */ + + /* Link to the new path */ + ret = link(fscript->path, newpath); + if ( ret >= 0 ) { + /* Is the requested script active? */ + if ( sieve_script_is_active(script) ) { + /* Active; make active link point to the new copy */ + link_path = t_strconcat + ( fstorage->link_path, newfile, NULL ); + + ret = sieve_file_storage_active_replace_link(fstorage, link_path); + } + + if ( ret >= 0 ) { + /* If all is good, remove the old link */ + if ( unlink(fscript->path) < 0 ) { + sieve_script_sys_error(script, + "Failed to clean up after rename: " + "unlink(%s) failed: %m", fscript->path); + } + + if ( script->name != NULL && *script->name != '\0' ) + script->name = p_strdup(script->pool, newname); + fscript->path = p_strdup(script->pool, newpath); + fscript->filename = p_strdup(script->pool, newfile); + } else { + /* If something went wrong, remove the new link to restore previous + * state + */ + if ( unlink(newpath) < 0 ) { + sieve_script_sys_error(script, + "Failed to clean up after failed rename: " + "unlink(%s) failed: %m", newpath); + } + } + } else { + /* Our efforts failed right away */ + switch ( errno ) { + case ENOENT: + sieve_script_set_error(script, SIEVE_ERROR_NOT_FOUND, + "Sieve script does not exist."); + break; + case EEXIST: + sieve_script_set_error(script, SIEVE_ERROR_EXISTS, + "A sieve script with that name already exists."); + break; + default: + sieve_script_set_critical(script, + "Failed to rename Sieve script: " + "link(%s, %s) failed: %m", fscript->path, newpath); + } + } + } T_END; + + return ret; +} + +/* + * Properties + */ + +static int sieve_file_script_get_size +(const struct sieve_script *script, uoff_t *size_r) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + *size_r = fscript->st.st_size; + return 1; +} + +const char *sieve_file_script_get_dirpath +(const struct sieve_script *script) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + if ( script->driver_name != sieve_file_script.driver_name ) + return NULL; + + return fscript->dirpath; +} + +const char *sieve_file_script_get_path +(const struct sieve_script *script) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + if ( script->driver_name != sieve_file_script.driver_name ) + return NULL; + + return fscript->path; +} + +/* + * Matching + */ + +static bool sieve_file_script_equals +(const struct sieve_script *script, const struct sieve_script *other) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + struct sieve_file_script *fother = + (struct sieve_file_script *)other; + + return ( fscript->st.st_ino == fother->st.st_ino ); +} + +/* + * Driver definition + */ + +const struct sieve_script sieve_file_script = { + .driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME, + .v = { + .open = sieve_file_script_open, + + .get_stream = sieve_file_script_get_stream, + + .binary_read_metadata = sieve_file_script_binary_read_metadata, + .binary_load = sieve_file_script_binary_load, + .binary_save = sieve_file_script_binary_save, + .binary_get_prefix = sieve_file_script_binary_get_prefix, + + .rename = sieve_file_storage_script_rename, + .delete = sieve_file_storage_script_delete, + .is_active = sieve_file_storage_script_is_active, + .activate = sieve_file_storage_script_activate, + + .get_size = sieve_file_script_get_size, + + .equals = sieve_file_script_equals + } +}; + diff --git a/src/lib-sieve/storage/file/sieve-file-storage-active.c b/src/lib-sieve/storage/file/sieve-file-storage-active.c new file mode 100644 index 0000000000000000000000000000000000000000..0ba2d95578143ad957be51d0becc5b26c6d1bc5f --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-storage-active.c @@ -0,0 +1,428 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "abspath.h" +#include "ioloop.h" +#include "hostpid.h" +#include "file-copy.h" + +#include "sieve-file-storage.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +static int _file_path_cmp(const char *path1, const char *path2) +{ + const char *p1, *p2; + int ret; + + p1 = path1; p2 = path2; + if (*p2 == '\0' && *p1 != '\0') + return 1; + if (*p1 == '\0' && *p2 != '\0') + return -1; + if (*p1 == '/' && *p2 != '/') + return 1; + if (*p2 == '/' && *p1 != '/') + return -1; + for (;;) { + const char *s1, *s2; + size_t size1, size2; + + /* skip repeated slashes */ + for (; *p1 == '/'; p1++); + for (; *p2 == '/'; p2++); + /* check for end of comparison */ + if (*p1 == '\0' || *p2 == '\0') + break; + /* mark start of path element */ + s1 = p1; + s2 = p2; + /* scan to end of path elements */ + for (; *p1 != '\0' && *p1 != '/'; p1++); + for (; *p2 != '\0' && *p2 != '/'; p2++); + /* compare sizes */ + size1 = p1 - s1; + size2 = p2 - s2; + if (size1 != size2) + return size1 - size2; + /* compare */ + if (size1 > 0 && (ret=memcmp(s1, s2, size1)) != 0) + return ret; + } + if (*p1 == '\0') { + if (*p2 == '\0') + return 0; + return -1; + } + return 1; +} + +/* + * Symlink manipulation + */ + +static int sieve_file_storage_active_read_link +(struct sieve_file_storage *fstorage, const char **link_r) +{ + struct sieve_storage *storage = &fstorage->storage; + int ret; + + ret = t_readlink(fstorage->active_path, link_r); + + if ( ret < 0 ) { + *link_r = NULL; + + if ( errno == EINVAL ) { + /* Our symlink is no symlink. Report 'no active script'. + * Activating a script will automatically resolve this, so + * there is no need to panic on this one. + */ + if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { + sieve_storage_sys_warning(storage, + "Active sieve script symlink %s is no symlink.", + fstorage->active_path); + } + return 0; + } + + if ( errno == ENOENT ) { + /* Symlink not found */ + return 0; + } + + /* We do need to panic otherwise */ + sieve_storage_set_critical(storage, + "Performing readlink() on active sieve symlink '%s' failed: %m", + fstorage->active_path); + return -1; + } + + /* ret is now assured to be valid, i.e. > 0 */ + return 1; +} + +static const char *sieve_file_storage_active_parse_link +(struct sieve_file_storage *fstorage, const char *link, + const char **scriptname_r) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *fname, *scriptname, *scriptpath; + + /* Split link into path and filename */ + fname = strrchr(link, '/'); + if ( fname != NULL ) { + scriptpath = t_strdup_until(link, fname+1); + fname++; + } else { + scriptpath = ""; + fname = link; + } + + /* Check the script name */ + scriptname = sieve_script_file_get_scriptname(fname); + + /* Warn if link is deemed to be invalid */ + if ( scriptname == NULL ) { + sieve_storage_sys_warning(storage, + "Active sieve script symlink %s is broken: " + "invalid scriptname (points to %s).", + fstorage->active_path, link); + return NULL; + } + + /* Check whether the path is any good */ + if ( _file_path_cmp(scriptpath, fstorage->link_path) != 0 && + _file_path_cmp(scriptpath, fstorage->path) != 0 ) { + sieve_storage_sys_warning(storage, + "Active sieve script symlink %s is broken: " + "invalid/unknown path to storage (points to %s).", + fstorage->active_path, link); + return NULL; + } + + if ( scriptname_r != NULL ) + *scriptname_r = scriptname; + + return fname; +} + +int sieve_file_storage_active_replace_link +(struct sieve_file_storage *fstorage, const char *link_path) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *active_path_new; + struct timeval *tv, tv_now; + int ret = 0; + + tv = &ioloop_timeval; + + for (;;) { + /* First the new symlink is created with a different filename */ + active_path_new = t_strdup_printf + ("%s-new.%s.P%sM%s.%s", + fstorage->active_path, + dec2str(tv->tv_sec), my_pid, + dec2str(tv->tv_usec), my_hostname); + + ret = symlink(link_path, active_path_new); + + if ( ret < 0 ) { + /* If link exists we try again later */ + if ( errno == EEXIST ) { + /* Wait and try again - very unlikely */ + sleep(2); + tv = &tv_now; + if (gettimeofday(&tv_now, NULL) < 0) + i_fatal("gettimeofday(): %m"); + continue; + } + + /* Other error, critical */ + sieve_storage_set_critical(storage, + "Creating symlink() %s to %s failed: %m", + active_path_new, link_path); + return -1; + } + + /* Link created */ + break; + } + + /* Replace the existing link. This activates the new script */ + ret = rename(active_path_new, fstorage->active_path); + + if ( ret < 0 ) { + /* Failed; created symlink must be deleted */ + (void)unlink(active_path_new); + sieve_storage_set_critical(storage, + "Performing rename() %s to %s failed: %m", + active_path_new, fstorage->active_path); + return -1; + } + + return 1; +} + +/* + * Active script properties + */ + +int sieve_file_storage_active_script_get_file +(struct sieve_file_storage *fstorage, const char **file_r) +{ + const char *link, *scriptfile; + int ret; + + *file_r = NULL; + + /* Read the active link */ + if ( (ret=sieve_file_storage_active_read_link(fstorage, &link)) <= 0 ) + return ret; + + /* Parse the link */ + scriptfile = sieve_file_storage_active_parse_link(fstorage, link, NULL); + + if (scriptfile == NULL) { + /* Obviously, someone has been playing with our symlink: + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + return 0; + } + + *file_r = scriptfile; + return 1; +} + +int sieve_file_storage_active_script_get_name +(struct sieve_storage *storage, const char **name_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + const char *link; + int ret; + + *name_r = NULL; + + /* Read the active link */ + if ( (ret=sieve_file_storage_active_read_link + (fstorage, &link)) <= 0 ) + return ret; + + if ( sieve_file_storage_active_parse_link + (fstorage, link, name_r) == NULL ) { + /* Obviously, someone has been playing with our symlink: + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + return 0; + } + + return 1; +} + +/* + * Active script + */ + +struct sieve_script *sieve_file_storage_active_script_open +(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_script *fscript; + const char *scriptfile, *link; + int ret; + + sieve_storage_clear_error(storage); + + /* Read the active link */ + if ( (ret=sieve_file_storage_active_read_link(fstorage, &link)) <= 0 ) { + if ( ret < 0 ) + return NULL; + + /* Try to open the active_path as a regular file */ + fscript = sieve_file_script_open_from_path(fstorage, + fstorage->active_path, NULL, NULL); + if ( fscript == NULL ) { + sieve_storage_set_critical(storage, + "Failed to open active path `%s' as regular file", + fstorage->active_path); + return NULL; + } + + return &fscript->script; + } + + /* Parse the link */ + scriptfile = sieve_file_storage_active_parse_link(fstorage, link, NULL); + if (scriptfile == NULL) { + /* Obviously someone has been playing with our symlink, + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + return NULL; + } + + fscript = sieve_file_script_open_from_path(fstorage, + fstorage->active_path, + sieve_script_file_get_scriptname(scriptfile), + NULL); + if ( fscript == NULL && storage->error_code == SIEVE_ERROR_NOT_FOUND ) { + sieve_storage_sys_warning(storage, + "Active sieve script symlink %s points to non-existent script " + "(points to %s).", fstorage->active_path, link); + } + return (fscript != NULL ? &fscript->script : NULL); +} + +int sieve_file_storage_active_script_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + + /* Try direct lstat first */ + if ( lstat(fstorage->active_path, &st) == 0 ) { + if ( !S_ISLNK(st.st_mode) ) { + *last_change_r = st.st_mtime; + return 0; + } + } + /* Check error */ + else if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "lstat(%s) failed: %m", fstorage->active_path); + } + + /* Fall back to statting storage directory */ + return sieve_storage_get_last_change(storage, last_change_r); +} + +bool sieve_file_storage_active_rescue_regular +(struct sieve_file_storage *fstorage) +{ + struct sieve_storage *storage = &fstorage->storage; + struct stat st; + + /* Stat the file */ + if ( lstat(fstorage->active_path, &st) != 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to stat active sieve script symlink (%s): %m.", + fstorage->active_path); + return FALSE; + } + return TRUE; + } + + if ( S_ISLNK( st.st_mode ) ) { + sieve_storage_sys_debug(storage, + "Nothing to rescue %s.", fstorage->active_path); + return TRUE; /* Nothing to rescue */ + } + + /* Only regular files can be rescued */ + if ( S_ISREG( st.st_mode ) ) { + const char *dstpath; + bool result = TRUE; + + T_BEGIN { + + dstpath = t_strconcat( fstorage->path, "/", + sieve_script_file_from_name("dovecot.orig"), NULL ); + if ( file_copy(fstorage->active_path, dstpath, 1) < 1 ) { + sieve_storage_set_critical(storage, + "Active sieve script file '%s' is a regular file " + "and copying it to the script storage as '%s' failed. " + "This needs to be fixed manually.", + fstorage->active_path, dstpath); + result = FALSE; + } else { + sieve_storage_sys_info(storage, + "Moved active sieve script file '%s' " + "to script storage as '%s'.", + fstorage->active_path, dstpath); + } + } T_END; + + return result; + } + + sieve_storage_set_critical(storage, + "Active sieve script file '%s' is no symlink nor a regular file. " + "This needs to be fixed manually.", fstorage->active_path); + return FALSE; +} + +int sieve_file_storage_deactivate(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + int ret; + + if ( sieve_file_storage_pre_modify(storage) < 0 ) + return -1; + + if ( !sieve_file_storage_active_rescue_regular(fstorage) ) + return -1; + + /* Delete the symlink, so no script is active */ + ret = unlink(fstorage->active_path); + + if ( ret < 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to deactivate Sieve: " + "unlink(%s) failed: %m", fstorage->active_path); + return -1; + } else { + return 0; + } + } + return 1; +} diff --git a/src/lib-sieve/storage/file/sieve-file-storage-list.c b/src/lib-sieve/storage/file/sieve-file-storage-list.c new file mode 100644 index 0000000000000000000000000000000000000000..e01acc3a34bd1317a3abdb09496010f3e52068a7 --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-storage-list.c @@ -0,0 +1,125 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-script-private.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> + +struct sieve_file_list_context { + struct sieve_storage_list_context context; + pool_t pool; + + const char *active; + const char *dir; + DIR *dirp; +}; + +struct sieve_storage_list_context *sieve_file_storage_list_init +(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_list_context *flctx; + const char *active = NULL; + pool_t pool; + DIR *dirp; + + /* Open the directory */ + if ( (dirp = opendir(fstorage->path)) == NULL ) { + sieve_storage_set_critical(storage, + "Failed to list scripts: " + "opendir(%s) failed: %m", fstorage->path); + return NULL; + } + + T_BEGIN { + /* Get the name of the active script */ + if ( sieve_file_storage_active_script_get_file(fstorage, &active) < 0) { + flctx = NULL; + } else { + pool = pool_alloconly_create("sieve_file_list_context", 1024); + flctx = p_new(pool, struct sieve_file_list_context, 1); + flctx->pool = pool; + flctx->dirp = dirp; + flctx->active = ( active != NULL ? p_strdup(pool, active) : NULL ); + } + } T_END; + + if ( flctx == NULL ) { + if ( closedir(dirp) < 0) { + sieve_storage_sys_error(storage, + "closedir(%s) failed: %m", fstorage->path); + } + return NULL; + } + return &flctx->context; +} + +const char *sieve_file_storage_list_next +(struct sieve_storage_list_context *ctx, bool *active) +{ + struct sieve_file_list_context *flctx = + (struct sieve_file_list_context *)ctx; + const struct sieve_file_storage *fstorage = + (const struct sieve_file_storage *)ctx->storage; + struct dirent *dp; + const char *scriptname; + + *active = FALSE; + + for (;;) { + if ( (dp = readdir(flctx->dirp)) == NULL ) + return NULL; + + scriptname = sieve_script_file_get_scriptname(dp->d_name); + if (scriptname != NULL ) { + /* Don't list our active sieve script link if the link + * resides in the script dir (generally a bad idea). + */ + if ( *(fstorage->link_path) == '\0' && + strcmp(fstorage->active_fname, dp->d_name) == 0 ) + continue; + + break; + } + } + + if ( flctx->active != NULL && strcmp(dp->d_name, flctx->active) == 0 ) { + *active = TRUE; + flctx->active = NULL; + } + + return scriptname; +} + +int sieve_file_storage_list_deinit(struct sieve_storage_list_context *lctx) +{ + struct sieve_file_list_context *flctx = + (struct sieve_file_list_context *)lctx; + const struct sieve_file_storage *fstorage = + (const struct sieve_file_storage *)lctx->storage; + + if (closedir(flctx->dirp) < 0) { + sieve_storage_sys_error(lctx->storage, + "closedir(%s) failed: %m", fstorage->path); + } + + pool_unref(&flctx->pool); + + // FIXME: return error here if something went wrong during listing + return 0; +} + + + + diff --git a/src/lib-sievestorage/sieve-storage-quota.c b/src/lib-sieve/storage/file/sieve-file-storage-quota.c similarity index 54% rename from src/lib-sievestorage/sieve-storage-quota.c rename to src/lib-sieve/storage/file/sieve-file-storage-quota.c index efc5e0a68dde21802fd00c0321e52f92dfa0bf7a..2054445d63ef96f8034880278f6c313b1b9f363a 100644 --- a/src/lib-sievestorage/sieve-storage-quota.c +++ b/src/lib-sieve/storage/file/sieve-file-storage-quota.c @@ -6,11 +6,8 @@ #include "sieve.h" #include "sieve-script.h" -#include "sieve-script-file.h" -#include "sieve-storage-private.h" -#include "sieve-storage-script.h" -#include "sieve-storage-quota.h" +#include "sieve-file-storage.h" #include <stdio.h> #include <stdlib.h> @@ -21,54 +18,22 @@ #include <unistd.h> #include <fcntl.h> -bool sieve_storage_quota_validsize -(struct sieve_storage *storage, size_t size, uint64_t *limit_r) -{ - uint64_t max_size; - - max_size = sieve_max_script_size(storage->svinst); - if ( max_size > 0 && size > max_size ) { - *limit_r = max_size; - return FALSE; - } - - return TRUE; -} - -uint64_t sieve_storage_quota_max_script_size -(struct sieve_storage *storage) -{ - return sieve_max_script_size(storage->svinst); -} - -int sieve_storage_quota_havespace +int sieve_file_storage_quota_havespace (struct sieve_storage *storage, const char *scriptname, size_t size, enum sieve_storage_quota *quota_r, uint64_t *limit_r) { + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; struct dirent *dp; DIR *dirp; uint64_t script_count = 1; uint64_t script_storage = size; int result = 1; - *limit_r = 0; - *quota_r = SIEVE_STORAGE_QUOTA_NONE; - - /* Check the script size */ - if ( !sieve_storage_quota_validsize(storage, size, limit_r) ) { - *quota_r = SIEVE_STORAGE_QUOTA_MAXSIZE; - return 0; - } - - /* Do we need to scan the storage (quota enabled) ? */ - if ( storage->max_scripts == 0 && storage->max_storage == 0 ) { - return 1; - } - /* Open the directory */ - if ( (dirp = opendir(storage->dir)) == NULL ) { - sieve_storage_set_critical - (storage, "quota: opendir(%s) failed: %m", storage->dir); + if ( (dirp = opendir(fstorage->path)) == NULL ) { + sieve_storage_set_critical(storage, + "quota: opendir(%s) failed: %m", fstorage->path); return -1; } @@ -81,15 +46,15 @@ int sieve_storage_quota_havespace errno = 0; if ( (dp = readdir(dirp)) == NULL ) { if ( errno != 0 ) { - sieve_storage_set_critical - (storage, "quota: readdir(%s) failed: %m", storage->dir); + sieve_storage_set_critical(storage, + "quota: readdir(%s) failed: %m", fstorage->path); result = -1; } break; } /* Parse filename */ - name = sieve_scriptfile_get_script_name(dp->d_name); + name = sieve_script_file_get_scriptname(dp->d_name); /* Ignore non-script files */ if ( name == NULL ) @@ -98,14 +63,14 @@ int sieve_storage_quota_havespace /* Don't list our active sieve script link if the link * resides in the script dir (generally a bad idea). */ - if ( *(storage->link_path) == '\0' && - strcmp(storage->active_fname, dp->d_name) == 0 ) + if ( *(fstorage->link_path) == '\0' && + strcmp(fstorage->active_fname, dp->d_name) == 0 ) continue; if ( strcmp(name, scriptname) == 0 ) replaced = TRUE; - /* Check cont quota if necessary */ + /* Check count quota if necessary */ if ( storage->max_scripts > 0 ) { if ( !replaced ) { script_count++; @@ -124,11 +89,11 @@ int sieve_storage_quota_havespace const char *path; struct stat st; - path = t_strconcat(storage->dir, "/", dp->d_name, NULL); + path = t_strconcat(fstorage->path, "/", dp->d_name, NULL); if ( stat(path, &st) < 0 ) { - i_warning - ("sieve-storage: quota: stat(%s) failed: %m", path); + sieve_storage_sys_warning(storage, + "quota: stat(%s) failed: %m", path); continue; } @@ -147,10 +112,9 @@ int sieve_storage_quota_havespace /* Close directory */ if ( closedir(dirp) < 0 ) { - sieve_storage_set_critical - (storage, "quota: closedir(%s) failed: %m", storage->dir); + sieve_storage_set_critical(storage, + "quota: closedir(%s) failed: %m", fstorage->path); } - return result; } diff --git a/src/lib-sieve/storage/file/sieve-file-storage-save.c b/src/lib-sieve/storage/file/sieve-file-storage-save.c new file mode 100644 index 0000000000000000000000000000000000000000..93da6ff4d538ff49086b33b1aacaa1c88ac1efef --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-storage-save.c @@ -0,0 +1,452 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "hostpid.h" +#include "ioloop.h" +#include "array.h" +#include "buffer.h" +#include "ostream.h" +#include "str.h" +#include "eacces-error.h" +#include "safe-mkstemp.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <utime.h> +#include <sys/stat.h> + +struct sieve_file_save_context { + struct sieve_storage_save_context context; + + pool_t pool; + + struct ostream *output; + int fd; + const char *tmp_path; + + time_t mtime; + + unsigned int failed:1; + unsigned int finished:1; +}; + +static const char *sieve_generate_tmp_filename(const char *scriptname) +{ + static struct timeval last_tv = { 0, 0 }; + struct timeval tv; + + /* use secs + usecs to guarantee uniqueness within this process. */ + if (ioloop_timeval.tv_sec > last_tv.tv_sec || + (ioloop_timeval.tv_sec == last_tv.tv_sec && + ioloop_timeval.tv_usec > last_tv.tv_usec)) { + tv = ioloop_timeval; + } else { + tv = last_tv; + if (++tv.tv_usec == 1000000) { + tv.tv_sec++; + tv.tv_usec = 0; + } + } + last_tv = tv; + + if ( scriptname == NULL ) { + return t_strdup_printf("%s.M%sP%s.%s.tmp", + dec2str(tv.tv_sec), dec2str(tv.tv_usec), + my_pid, my_hostname); + } + + scriptname = t_strdup_printf("%s_%s.M%sP%s.%s", + scriptname, dec2str(tv.tv_sec), dec2str(tv.tv_usec), + my_pid, my_hostname); + return sieve_script_file_from_name(scriptname); +} + +static int sieve_file_storage_create_tmp +(struct sieve_file_storage *fstorage, const char *scriptname, + const char **fpath_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct stat st; + unsigned int prefix_len; + const char *tmp_fname = NULL; + string_t *path; + int fd; + + path = t_str_new(256); + str_append(path, fstorage->path); + str_append(path, "/tmp/"); + prefix_len = str_len(path); + + for (;;) { + tmp_fname = sieve_generate_tmp_filename(scriptname); + str_truncate(path, prefix_len); + str_append(path, tmp_fname); + + /* stat() first to see if it exists. pretty much the only + possibility of that happening is if time had moved + backwards, but even then it's highly unlikely. */ + if (stat(str_c(path), &st) == 0) { + /* try another file name */ + } else if (errno != ENOENT) { + sieve_storage_set_critical(storage, "save: " + "stat(%s) failed: %m", str_c(path)); + return -1; + } else { + /* doesn't exist */ + mode_t old_mask = umask(0777 & ~(fstorage->file_create_mode)); + fd = open(str_c(path), + O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777); + umask(old_mask); + + if (fd != -1 || errno != EEXIST) + break; + /* race condition between stat() and open(). + highly unlikely. */ + } + } + + *fpath_r = str_c(path); + if (fd == -1) { + if (ENOSPACE(errno)) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_SPACE, + "Not enough disk space"); + } else { + sieve_storage_set_critical(storage, "save: " + "open(%s) failed: %m", str_c(path)); + } + } + + return fd; +} + +static int sieve_file_storage_script_move +(struct sieve_file_save_context *fsctx, const char *dst) +{ + struct sieve_storage_save_context *sctx = &fsctx->context; + struct sieve_storage *storage = sctx->storage; + int result = 0; + + T_BEGIN { + + /* Using rename() to ensure existing files are replaced + * without conflicts with other processes using the same + * file. The kernel wont fully delete the original until + * all processes have closed the file. + */ + if (rename(fsctx->tmp_path, dst) == 0) + result = 0; + else { + result = -1; + if ( ENOSPACE(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_SPACE, + "Not enough disk space"); + } else if ( errno == EACCES ) { + sieve_storage_set_critical(storage, "save: " + "Failed to save Sieve script: " + "%s", eacces_error_get("rename", dst)); + } else { + sieve_storage_set_critical(storage, "save: " + "rename(%s, %s) failed: %m", fsctx->tmp_path, dst); + } + } + + /* Always destroy temp file */ + if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) { + sieve_storage_sys_warning(storage, "save: " + "unlink(%s) failed: %m", fsctx->tmp_path); + } + } T_END; + + return result; +} + +struct sieve_storage_save_context * +sieve_file_storage_save_init(struct sieve_storage *storage, + const char *scriptname, struct istream *input) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_save_context *fsctx; + pool_t pool; + const char *path; + int fd; + + if ( sieve_file_storage_pre_modify(storage) < 0 ) + return NULL; + + if ( scriptname != NULL ) { + /* Prevent overwriting the active script link when it resides in the + * sieve storage directory. + */ + if ( *(fstorage->link_path) == '\0' ) { + const char *svext; + size_t namelen; + + svext = strrchr(fstorage->active_fname, '.'); + namelen = svext - fstorage->active_fname; + if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 && + strlen(scriptname) == namelen && + strncmp(scriptname, fstorage->active_fname, namelen) == 0 ) + { + sieve_storage_set_error(storage, + SIEVE_ERROR_BAD_PARAMS, + "Script name `%s' is reserved for internal use.", + scriptname); + return NULL; + } + } + } + + T_BEGIN { + fd = sieve_file_storage_create_tmp(fstorage, scriptname, &path); + if (fd == -1) { + fsctx = NULL; + } else { + pool = pool_alloconly_create("sieve_file_save_context", 1024); + fsctx = p_new(pool, struct sieve_file_save_context, 1); + fsctx->context.scriptname = p_strdup(pool, scriptname); + fsctx->context.input = input; + fsctx->pool = pool; + fsctx->fd = fd; + fsctx->output = o_stream_create_fd(fsctx->fd, 0, FALSE); + fsctx->tmp_path = p_strdup(pool, path); + } + } T_END; + + return &fsctx->context; +} + +int sieve_file_storage_save_continue +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + + if (o_stream_send_istream(fsctx->output, sctx->input) < 0) { + sieve_storage_set_critical(sctx->storage, "save: " + "o_stream_send_istream(%s) failed: %m", fsctx->tmp_path); + return -1; + } + return 0; +} + +int sieve_file_storage_save_finish +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_storage *storage = sctx->storage; + int output_errno; + + if ( sctx->failed && fsctx->fd == -1 ) { + /* tmp file creation failed */ + return -1; + } + + T_BEGIN { + output_errno = fsctx->output->stream_errno; + o_stream_destroy(&fsctx->output); + + if ( fsync(fsctx->fd) < 0 ) { + sieve_storage_set_critical(storage, "save: " + "fsync(%s) failed: %m", fsctx->tmp_path); + sctx->failed = TRUE; + } + if ( close(fsctx->fd) < 0 ) { + sieve_storage_set_critical(storage, "save: " + "close(%s) failed: %m", fsctx->tmp_path); + sctx->failed = TRUE; + } + fsctx->fd = -1; + + if ( sctx->failed ) { + /* delete the tmp file */ + if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) { + sieve_storage_sys_warning(storage, "save: " + "unlink(%s) failed: %m", fsctx->tmp_path); + } + + fsctx->tmp_path = NULL; + + errno = output_errno; + if ( ENOSPACE(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_SPACE, + "Not enough disk space"); + } else if ( errno != 0 ) { + sieve_storage_set_critical(storage, "save: " + "write(%s) failed: %m", fsctx->tmp_path); + } + } + } T_END; + + return ( sctx->failed ? -1 : 0 ); +} + +struct sieve_script *sieve_file_storage_save_get_tempscript +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)sctx->storage; + struct sieve_file_script *tmpscript; + enum sieve_error error; + const char *scriptname; + + if (sctx->failed) + return NULL; + + if ( sctx->scriptobject != NULL ) + return sctx->scriptobject; + + scriptname = + ( sctx->scriptname == NULL ? "" : sctx->scriptname ); + tmpscript = sieve_file_script_open_from_path + (fstorage, fsctx->tmp_path, scriptname, &error); + + if ( tmpscript == NULL ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) { + sieve_storage_set_critical(sctx->storage, "save: " + "Temporary script file `%s' got lost, " + "which should not happen (possibly deleted externally).", + fsctx->tmp_path); + } else { + sieve_storage_set_critical(sctx->storage, "save: " + "Failed to open temporary script file `%s'", + fsctx->tmp_path); + } + return NULL; + } + + return &tmpscript->script; +} + +static void sieve_file_storage_update_mtime +(struct sieve_storage *storage, const char *path, time_t mtime) +{ + struct utimbuf times = { .actime = mtime, .modtime = mtime }; + + if ( utime(path, ×) < 0 ) { + switch ( errno ) { + case ENOENT: + break; + case EACCES: + sieve_storage_sys_error(storage, "save: " + "%s", eacces_error_get("utime", path)); + break; + default: + sieve_storage_sys_error(storage, "save: " + "utime(%s) failed: %m", path); + } + } +} + +int sieve_file_storage_save_commit +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_storage *storage = sctx->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)sctx->storage; + const char *dest_path; + bool failed = FALSE; + + i_assert(fsctx->output == NULL); + + T_BEGIN { + dest_path = t_strconcat(fstorage->path, "/", + sieve_script_file_from_name(sctx->scriptname), NULL); + + failed = ( sieve_file_storage_script_move(fsctx, dest_path) < 0 ); + if ( sctx->mtime != (time_t)-1 ) + sieve_file_storage_update_mtime(storage, dest_path, sctx->mtime); + } T_END; + + pool_unref(&fsctx->pool); + + return ( failed ? -1 : 0 ); +} + +void sieve_file_storage_save_cancel(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_storage *storage = sctx->storage; + + if (fsctx->tmp_path != NULL && + unlink(fsctx->tmp_path) < 0 && errno != ENOENT) { + sieve_storage_sys_warning(storage, "save: " + "unlink(%s) failed: %m", fsctx->tmp_path); + } + + i_assert(fsctx->output == NULL); +} + +int sieve_file_storage_save_as_active(struct sieve_storage *storage, + struct istream *input, time_t mtime) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + string_t *temp_path; + struct ostream *output; + int fd; + + temp_path = t_str_new(256); + str_append(temp_path, fstorage->active_path); + str_append_c(temp_path, '.'); + fd = safe_mkstemp_hostpid + (temp_path, fstorage->file_create_mode, (uid_t)-1, (gid_t)-1); + if ( fd < 0 ) { + if ( errno == EACCES ) { + sieve_storage_set_critical(storage, + "Failed to create temporary file: %s", + eacces_error_get_creating("open", str_c(temp_path))); + } else { + sieve_storage_set_critical(storage, + "Failed to create temporary file: open(%s) failed: %m", + str_c(temp_path)); + } + return -1; + } + + output = o_stream_create_fd(fd, 0, FALSE); + if (o_stream_send_istream(output, input) < 0) { + sieve_storage_set_critical(storage, + "o_stream_send_istream(%s) failed: %m", str_c(temp_path)); + o_stream_destroy(&output); + (void)unlink(str_c(temp_path)); + return -1; + } + o_stream_destroy(&output); + + if (rename(str_c(temp_path), fstorage->active_path) < 0) { + if ( ENOSPACE(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_SPACE, + "Not enough disk space"); + } else if ( errno == EACCES ) { + sieve_storage_set_critical(storage, + "%s", eacces_error_get("rename", fstorage->active_path)); + } else { + sieve_storage_set_critical(storage, + "rename(%s, %s) failed: %m", + str_c(temp_path), fstorage->active_path); + } + } else { + sieve_file_storage_update_mtime + (storage, fstorage->active_path, mtime); + } + + (void)unlink(str_c(temp_path)); + return 0; +} + diff --git a/src/lib-sieve/storage/file/sieve-file-storage.c b/src/lib-sieve/storage/file/sieve-file-storage.c new file mode 100644 index 0000000000000000000000000000000000000000..fd75db1efe76ac134915519d93ccdd6e385a48f6 --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-storage.c @@ -0,0 +1,681 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "home-expand.h" +#include "ioloop.h" +#include "mkdir-parents.h" +#include "eacces-error.h" +#include "unlink-old-files.h" +#include "mail-storage-private.h" + +#include "sieve.h" +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error-private.h" +#include "sieve-settings.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <ctype.h> +#include <sys/types.h> +#include <utime.h> +#include <sys/time.h> + + +#define MAX_DIR_CREATE_MODE 0770 + +/* + * Utility + */ + +const char *sieve_file_storage_path_extend +(struct sieve_file_storage *fstorage, const char *filename) +{ + const char *path = fstorage->path; + + if ( path[strlen(path)-1] == '/' ) + return t_strconcat(path, filename, NULL); + + return t_strconcat(path, "/", filename , NULL); +} + +/* + * + */ + +static const char *sieve_storage_get_relative_link_path + (const char *active_path, const char *storage_dir) +{ + const char *link_path, *p; + size_t pathlen; + + /* Determine to what extent the sieve storage and active script + * paths match up. This enables the managed symlink to be short and the + * sieve storages can be moved around without trouble (if the active + * script path is common to the script storage). + */ + p = strrchr(active_path, '/'); + if ( p == NULL ) { + link_path = storage_dir; + } else { + pathlen = p - active_path; + + if ( strncmp( active_path, storage_dir, pathlen ) == 0 && + (storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') ) + { + if ( storage_dir[pathlen] == '\0' ) + link_path = ""; + else + link_path = storage_dir + pathlen + 1; + } else + link_path = storage_dir; + } + + /* Add trailing '/' when link path is not empty + */ + pathlen = strlen(link_path); + if ( pathlen != 0 && link_path[pathlen-1] != '/') + return t_strconcat(link_path, "/", NULL); + + return t_strdup(link_path); +} + +static mode_t get_dir_mode(mode_t mode) +{ + /* Add the execute bit if either read or write bit is set */ + + if ((mode & 0600) != 0) mode |= 0100; + if ((mode & 0060) != 0) mode |= 0010; + if ((mode & 0006) != 0) mode |= 0001; + + return mode; +} + +static void sieve_file_storage_get_permissions +(struct sieve_storage *storage, const char *path, +mode_t *file_mode_r, mode_t *dir_mode_r, gid_t *gid_r, + const char **gid_origin_r) +{ + struct stat st; + + /* Use safe defaults */ + *file_mode_r = 0600; + *dir_mode_r = 0700; + *gid_r = (gid_t)-1; + *gid_origin_r = "defaults"; + + if ( stat(path, &st) < 0 ) { + if ( !ENOTFOUND(errno) ) { + sieve_storage_sys_error(storage, + "stat(%s) failed: %m", path); + } else { + sieve_storage_sys_debug(storage, + "Permission lookup failed from %s", path); + } + return; + + } else { + *file_mode_r = (st.st_mode & 0666) | 0600; + *dir_mode_r = (st.st_mode & 0777) | 0700; + *gid_origin_r = path; + + if ( !S_ISDIR(st.st_mode) ) { + /* We're getting permissions from a file. Apply +x modes as necessary. */ + *dir_mode_r = get_dir_mode(*dir_mode_r); + } + + if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) { + /* Directory's GID is used automatically for new files */ + *gid_r = (gid_t)-1; + } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) { + /* Group has same permissions as world, so don't bother changing it */ + *gid_r = (gid_t)-1; + } else if (getegid() == st.st_gid) { + /* Using our own gid, no need to change it */ + *gid_r = (gid_t)-1; + } else { + *gid_r = st.st_gid; + } + } + + sieve_storage_sys_debug(storage, + "Using permissions from %s: mode=0%o gid=%ld", + path, (int)*dir_mode_r, *gid_r == (gid_t)-1 ? -1L : (long)*gid_r); +} + +static int mkdir_verify +(struct sieve_storage *storage, const char *dir, + mode_t mode, gid_t gid, const char *gid_origin) +{ + struct stat st; + + if ( stat(dir, &st) == 0 ) + return 0; + + if ( errno == EACCES ) { + sieve_storage_sys_error(storage, + "mkdir_verify: %s", eacces_error_get("stat", dir)); + return -1; + } else if ( errno != ENOENT ) { + sieve_storage_sys_error(storage, + "mkdir_verify: stat(%s) failed: %m", dir); + return -1; + } + + if ( mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0 ) { + sieve_storage_sys_debug(storage, + "Created storage directory %s", dir); + return 0; + } + + switch ( errno ) { + case EEXIST: + return 0; + case ENOENT: + sieve_storage_sys_error(storage, + "Storage was deleted while it was being created"); + break; + case EACCES: + sieve_storage_sys_error(storage, + "%s", eacces_error_get_creating("mkdir_parents_chgrp", dir)); + break; + default: + sieve_storage_sys_error(storage, + "mkdir_parents_chgrp(%s) failed: %m", dir); + break; + } + + return -1; +} + +static int check_tmp(struct sieve_storage *storage, const char *path) +{ + struct stat st; + + /* If tmp/ directory exists, we need to clean it up once in a while */ + if ( stat(path, &st) < 0 ) { + if ( errno == ENOENT ) + return 0; + if ( errno == EACCES ) { + sieve_storage_sys_error(storage, + "check_tmp: %s", eacces_error_get("stat", path)); + return -1; + } + sieve_storage_sys_error(storage, + "check_tmp: stat(%s) failed: %m", path); + return -1; + } + + if ( st.st_atime > st.st_ctime + SIEVE_FILE_STORAGE_TMP_DELETE_SECS ) { + /* The directory should be empty. we won't do anything + until ctime changes. */ + } else if ( st.st_atime < ioloop_time - SIEVE_FILE_STORAGE_TMP_SCAN_SECS ) { + /* Time to scan */ + (void)unlink_old_files(path, "", + ioloop_time - SIEVE_FILE_STORAGE_TMP_DELETE_SECS); + } + return 1; +} + +static struct sieve_storage *sieve_file_storage_alloc(void) +{ + struct sieve_file_storage *fstorage; + pool_t pool; + + pool = pool_alloconly_create("sieve_file_storage", 1024); + fstorage = p_new(pool, struct sieve_file_storage, 1); + fstorage->storage = sieve_file_storage; + fstorage->storage.pool = pool; + + return &fstorage->storage; +} + +static int sieve_file_storage_init_paths +(struct sieve_file_storage *fstorage, const char *active_path, + const char *storage_path, enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *tmp_dir, *link_path, *path, *active_fname; + mode_t dir_create_mode, file_create_mode; + gid_t file_create_gid; + const char *file_create_gid_origin; + int ret; + + fstorage->prev_mtime = (time_t)-1; + + /* Active script path */ + + active_path = NULL; + if ( storage->main_storage || + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + if ( active_path == NULL || *active_path == '\0' ) { + sieve_storage_sys_debug(storage, + "Active script path is unconfigured; " + "using default (path=%s)", SIEVE_FILE_DEFAULT_PATH); + active_path = SIEVE_FILE_DEFAULT_PATH; + } + } + + path = active_path; + if ( path != NULL && *path != '\0' && + ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || + (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) + ) { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(svinst); + + if ( home != NULL ) { + if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) + path = home_expand_tilde(path, home); + else + path = t_strconcat(home, "/", path, NULL); + } else { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' is relative to home directory, " + "but home directory is not available.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + active_path = path; + + /* Get the filename for the active script link */ + + active_fname = NULL; + if ( active_path != NULL && *active_path != '\0' ) { + active_fname = strrchr(active_path, '/'); + if ( active_fname == NULL ) + active_fname = active_path; + else + active_fname++; + + if ( *active_fname == '\0' ) { + /* Link cannot be just a path ending in '/' */ + sieve_storage_set_critical(storage, + "Path to active symlink must include the link's filename " + "(path=%s)", active_path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + + /* Storage path */ + + path = storage_path; + if ( path != NULL && + ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || + (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) ) { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(svinst); + + if ( home != NULL ) { + if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) + path = home_expand_tilde(path, home); + else + path = t_strconcat(home, "/", path, NULL); + } else { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' is relative to home directory, " + "but home directory is not available.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + storage_path = path; + + if (storage_path == NULL || *storage_path == '\0') { + sieve_storage_set_critical(storage, + "Storage path cannot be empty"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + sieve_storage_sys_debug(storage, + "Using script storage path: %s", storage_path); + + fstorage->path = p_strdup(storage->pool, storage_path); + + if ( active_path != NULL && *active_path != '\0' ) { + sieve_storage_sys_debug(storage, + "Using active Sieve script path: %s", active_path); + + fstorage->active_path = p_strdup(storage->pool, active_path); + fstorage->active_fname = p_strdup(storage->pool, active_fname); + + /* Get the path to be prefixed to the script name in the symlink pointing + * to the active script. + */ + link_path = sieve_storage_get_relative_link_path + (fstorage->active_path, fstorage->path); + + sieve_storage_sys_debug(storage, + "Relative path to sieve storage in active link: %s", + link_path); + + fstorage->link_path = p_strdup(storage->pool, link_path); + } + + /* Prepare for write access */ + + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + /* Get permissions */ + sieve_file_storage_get_permissions(storage, + fstorage->path, &file_create_mode, &dir_create_mode, &file_create_gid, + &file_create_gid_origin); + + /* + * Ensure sieve local directory structure exists (full autocreate): + * This currently only consists of a ./tmp direcory + */ + + tmp_dir = t_strconcat(fstorage->path, "/tmp", NULL); + + /* Try to find and clean up tmp dir */ + if ( (ret=check_tmp(storage, tmp_dir)) < 0 ) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + /* Auto-create if necessary */ + if ( ret == 0 && mkdir_verify(storage, tmp_dir, + dir_create_mode, file_create_gid, file_create_gid_origin) < 0 ) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + fstorage->dir_create_mode = dir_create_mode; + fstorage->file_create_mode = file_create_mode; + fstorage->file_create_gid = file_create_gid; + } + + return 0; +} + +static int sieve_file_storage_init +(struct sieve_storage *storage, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + const char *active_path = ""; + + if ( options != NULL ) { + while ( *options != NULL ) { + const char *option = *options; + + if ( strncasecmp(option, "active=", 7) == 0 && option[7] != '\0' ) { + active_path = option+7; + } else { + sieve_storage_set_critical(storage, + "Invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + options++; + } + } + + return sieve_file_storage_init_paths + (fstorage, active_path, storage->location, error_r); +} + +struct sieve_storage *sieve_file_storage_init_legacy +(struct sieve_instance *svinst, const char *active_path, + const char *storage_path, enum sieve_storage_flags flags, + enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_file_storage *fstorage; + + storage = sieve_storage_alloc + (svinst, &sieve_file_storage, "", flags, TRUE); + storage->location = p_strdup(storage->pool, storage_path); + fstorage = (struct sieve_file_storage *)storage; + + T_BEGIN { + if ( storage_path == NULL || *storage_path == '\0' ) { + const char *home = sieve_environment_get_homedir(svinst); + + sieve_storage_sys_debug(storage, + "Performing auto-detection"); + + /* We'll need to figure out the storage location ourself. + * + * It's $HOME/sieve or /sieve when (presumed to be) chrooted. + */ + if ( home != NULL && *home != '\0' ) { + if (access(home, R_OK|W_OK|X_OK) == 0) { + /* Use default ~/sieve */ + sieve_storage_sys_debug(storage, + "Root exists (%s)", home); + + storage_path = t_strconcat(home, "/sieve", NULL); + } else { + /* Don't have required access on the home directory */ + + sieve_storage_sys_debug(storage, + "access(%s, rwx) failed: %m", home); + } + } else { + sieve_storage_sys_debug(storage, + "HOME is not set"); + + if (access("/sieve", R_OK|W_OK|X_OK) == 0) { + storage_path = "/sieve"; + sieve_storage_sys_debug(storage, + "Directory `/sieve' exists, assuming chroot"); + } + } + } + + if (storage_path == NULL || *storage_path == '\0') { + sieve_storage_set_critical(storage, + "Could not find storage root directory; " + "path was left unconfigured and autodetection failed"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + sieve_storage_unref(&storage); + storage = NULL; + } else if (sieve_file_storage_init_paths + (fstorage, active_path, storage_path, error_r) < 0) { + sieve_storage_unref(&storage); + storage = NULL; + } + } T_END; + + return storage; +} + +struct sieve_file_storage *sieve_file_storage_init_from_path +(struct sieve_instance *svinst, const char *path, + enum sieve_storage_flags flags, enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_file_storage *fstorage; + + storage = sieve_storage_alloc + (svinst, &sieve_file_storage, "", flags, FALSE); + storage->location = p_strdup(storage->pool, path); + fstorage = (struct sieve_file_storage *)storage; + + T_BEGIN { + if ( sieve_file_storage_init_paths + (fstorage, NULL, path, error_r) < 0 ) { + sieve_storage_unref(&storage); + storage = NULL; + } + } T_END; + + return fstorage; +} + +static int sieve_file_storage_is_singular +(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + + /* Stat the file */ + if ( lstat(fstorage->active_path, &st) != 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to stat active sieve script symlink (%s): %m.", + fstorage->active_path); + return -1; + } + return 0; + } + + if ( S_ISLNK( st.st_mode ) ) + return 0; + if ( !S_ISREG( st.st_mode ) ) { + sieve_storage_set_critical(storage, + "Active sieve script file '%s' is no symlink nor a regular file.", + fstorage->active_path); + return -1; + } + return 1; +} + + +/* + * + */ + +static int sieve_file_storage_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + + if ( fstorage->prev_mtime == (time_t)-1 ) { + /* Get the storage mtime before we modify it ourself */ + if ( stat(fstorage->path, &st) < 0 ) { + if ( errno != ENOENT ) { + sieve_storage_sys_error(storage, + "stat(%s) failed: %m", fstorage->path); + return -1; + } + st.st_mtime = 0; + } + + fstorage->prev_mtime = st.st_mtime; + } + + if ( last_change_r != NULL ) + *last_change_r = fstorage->prev_mtime; + return 0; +} + +int sieve_file_storage_pre_modify +(struct sieve_storage *storage) +{ + i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + + return sieve_storage_get_last_change(storage, NULL); +} + +static void sieve_file_storage_set_modified +(struct sieve_storage *storage, time_t mtime) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct utimbuf times; + time_t cur_mtime; + + if ( mtime != (time_t)-1 ) { + if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 && + cur_mtime > mtime ) + return; + } else { + mtime = ioloop_time; + } + + times.actime = mtime; + times.modtime = mtime; + if ( utime(fstorage->path, ×) < 0 ) { + switch ( errno ) { + case ENOENT: + break; + case EACCES: + sieve_storage_sys_error(storage, + "%s", eacces_error_get("utime", fstorage->path)); + break; + default: + sieve_storage_sys_error(storage, + "utime(%s) failed: %m", fstorage->path); + } + } else { + fstorage->prev_mtime = mtime; + } +} + +/* + * Script access + */ + +static struct sieve_script *sieve_file_storage_get_script +(struct sieve_storage *storage, const char *name) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_script *fscript; + + T_BEGIN { + fscript = sieve_file_script_init_from_name(fstorage, name); + } T_END; + + return &fscript->script; +} + +/* + * Driver definition + */ + +const struct sieve_storage sieve_file_storage = { + .driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME, + .allows_synchronization = TRUE, + .v = { + .alloc = sieve_file_storage_alloc, + .init = sieve_file_storage_init, + + .get_last_change = sieve_file_storage_get_last_change, + .set_modified = sieve_file_storage_set_modified, + + .is_singular = sieve_file_storage_is_singular, + + .get_script = sieve_file_storage_get_script, + + .get_script_sequence = sieve_file_storage_get_script_sequence, + .script_sequence_next = sieve_file_script_sequence_next, + .script_sequence_destroy = sieve_file_script_sequence_destroy, + + .active_script_get_name = sieve_file_storage_active_script_get_name, + .active_script_open = sieve_file_storage_active_script_open, + .deactivate = sieve_file_storage_deactivate, + .active_script_get_last_change = + sieve_file_storage_active_script_get_last_change, + + .list_init = sieve_file_storage_list_init, + .list_next = sieve_file_storage_list_next, + .list_deinit = sieve_file_storage_list_deinit, + + .save_init = sieve_file_storage_save_init, + .save_continue = sieve_file_storage_save_continue, + .save_finish = sieve_file_storage_save_finish, + .save_get_tempscript = sieve_file_storage_save_get_tempscript, + .save_cancel = sieve_file_storage_save_cancel, + .save_commit = sieve_file_storage_save_commit, + .save_as_active = sieve_file_storage_save_as_active, + + .quota_havespace = sieve_file_storage_quota_havespace + } +}; diff --git a/src/lib-sieve/storage/file/sieve-file-storage.h b/src/lib-sieve/storage/file/sieve-file-storage.h new file mode 100644 index 0000000000000000000000000000000000000000..4a4e3182463b472e5894b30b72a9c68eb06b94b5 --- /dev/null +++ b/src/lib-sieve/storage/file/sieve-file-storage.h @@ -0,0 +1,177 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SIEVE_FILE_STORAGE_H +#define __SIEVE_FILE_STORAGE_H + +#include "lib.h" +#include "mail-storage.h" +#include "mail-user.h" + +#include "sieve.h" +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#define SIEVE_FILE_READ_BLOCK_SIZE (1024*8) + +#define SIEVE_FILE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT + +/* How often to scan tmp/ directory for old files (based on dir's atime) */ +#define SIEVE_FILE_STORAGE_TMP_SCAN_SECS (8*60*60) +/* Delete files having ctime older than this from tmp/. 36h is standard. */ +#define SIEVE_FILE_STORAGE_TMP_DELETE_SECS (36*60*60) + +/* + * Storage class + */ + +struct sieve_file_storage { + struct sieve_storage storage; + + const char *path; + const char *active_path; + const char *active_fname; + const char *link_path; + + mode_t dir_create_mode; + mode_t file_create_mode; + gid_t file_create_gid; + + time_t prev_mtime; +}; + +const char *sieve_file_storage_path_extend + (struct sieve_file_storage *fstorage, const char *filename); + +struct sieve_file_storage *sieve_file_storage_init_from_path +(struct sieve_instance *svinst, const char *path, + enum sieve_storage_flags flags, enum sieve_error *error_r) + ATTR_NULL(4); + +int sieve_file_storage_pre_modify + (struct sieve_storage *storage); + +/* Active script */ + +int sieve_file_storage_active_replace_link + (struct sieve_file_storage *fstorage, const char *link_path); +bool sieve_file_storage_active_rescue_regular + (struct sieve_file_storage *fstorage); + +int sieve_file_storage_active_script_get_name + (struct sieve_storage *storage, const char **name_r); +struct sieve_script *sieve_file_storage_active_script_open + (struct sieve_storage *storage); + +int sieve_file_storage_active_script_get_file + (struct sieve_file_storage *fstorage, const char **file_r); +int sieve_file_storage_active_script_is_no_link + (struct sieve_file_storage *fstorage); + +int sieve_file_storage_deactivate + (struct sieve_storage *storage); + +int sieve_file_storage_active_script_get_last_change + (struct sieve_storage *storage, time_t *last_change_r); +int sieve_file_storage_save_as_active + (struct sieve_storage *storage, struct istream *input, + time_t mtime); + +/* Listing */ + +struct sieve_storage_list_context *sieve_file_storage_list_init + (struct sieve_storage *storage); +const char *sieve_file_storage_list_next + (struct sieve_storage_list_context *ctx, bool *active); +int sieve_file_storage_list_deinit + (struct sieve_storage_list_context *lctx); + +/* Saving */ + +struct sieve_storage_save_context *sieve_file_storage_save_init + (struct sieve_storage *storage, const char *scriptname, + struct istream *input); +int sieve_file_storage_save_continue + (struct sieve_storage_save_context *sctx); +int sieve_file_storage_save_finish + (struct sieve_storage_save_context *sctx); +struct sieve_script *sieve_file_storage_save_get_tempscript + (struct sieve_storage_save_context *sctx); +int sieve_file_storage_save_commit + (struct sieve_storage_save_context *sctx); +void sieve_file_storage_save_cancel + (struct sieve_storage_save_context *sctx); + +/* Quota */ + +int sieve_file_storage_quota_havespace +(struct sieve_storage *storage, const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, uint64_t *limit_r); + +/* + * Sieve script filenames + */ + +const char *sieve_script_file_get_scriptname(const char *filename); +const char *sieve_script_file_from_name(const char *name); + +/* + * Script class + */ + +struct sieve_file_script { + struct sieve_script script; + + struct stat st; + struct stat lnk_st; + + const char *path; + const char *dirpath; + const char *filename; + const char *binpath; + const char *binprefix; + + time_t prev_mtime; +}; + +struct sieve_file_script *sieve_file_script_init_from_filename + (struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname); +struct sieve_file_script *sieve_file_script_open_from_filename + (struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname); +struct sieve_file_script *sieve_file_script_init_from_name + (struct sieve_file_storage *fstorage, const char *name); + +struct sieve_file_script *sieve_file_script_init_from_path + (struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) + ATTR_NULL(4); +struct sieve_file_script *sieve_file_script_open_from_path + (struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) + ATTR_NULL(4); + +/* Return directory where script resides in. Returns NULL if this is not a file + * script. + */ +const char *sieve_file_script_get_dirpath + (const struct sieve_script *script); + +/* Return full path to file script. Returns NULL if this is not a file script. + */ +const char *sieve_file_script_get_path + (const struct sieve_script *script); + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_file_storage_get_script_sequence + (struct sieve_storage *storage, enum sieve_error *error_r); + +struct sieve_script *sieve_file_script_sequence_next + (struct sieve_script_sequence *seq, enum sieve_error *error_r); +void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq); + +#endif diff --git a/src/lib-sievestorage/Makefile.am b/src/lib-sievestorage/Makefile.am deleted file mode 100644 index 67ff30a2ff808b580222e8d9acfb65fcfc44e1b1..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -noinst_LTLIBRARIES = libsievestorage.la - -AM_CPPFLAGS = \ - $(LIBDOVECOT_INCLUDE) \ - -I$(top_srcdir) \ - -I$(top_srcdir)/src/lib-sieve - -libsievestorage_la_SOURCES = \ - sieve-storage-save.c \ - sieve-storage-script.c \ - sieve-storage-list.c \ - sieve-storage-quota.c \ - sieve-storage.c - -noinst_HEADERS = \ - sieve-storage-save.h \ - sieve-storage-script.h \ - sieve-storage-list.h \ - sieve-storage-quota.h \ - sieve-storage-private.h \ - sieve-storage.h diff --git a/src/lib-sievestorage/sieve-storage-list.c b/src/lib-sievestorage/sieve-storage-list.c deleted file mode 100644 index 9d5f2dd727b508e6db4cbc523d2f0aea3c28fa3d..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-list.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "str.h" - -#include "sieve-common.h" -#include "sieve-script.h" -#include "sieve-script-file.h" - -#include "sieve-storage-private.h" -#include "sieve-storage-script.h" -#include "sieve-storage-list.h" - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <dirent.h> -#include <sys/stat.h> - -struct sieve_list_context { - pool_t pool; - struct sieve_storage *storage; - - const char *active; - const char *dir; - DIR *dirp; - - unsigned int seen_active:1; // Just present for assertions -}; - -struct sieve_list_context *sieve_storage_list_init -(struct sieve_storage *storage) -{ - struct sieve_list_context *ctx; - const char *active = NULL; - pool_t pool; - DIR *dirp; - - /* Open the directory */ - if ( (dirp = opendir(storage->dir)) == NULL ) { - sieve_storage_set_critical(storage, "opendir(%s) failed: %m", storage->dir); - return NULL; - } - - T_BEGIN { - /* Get the name of the active script */ - if ( sieve_storage_active_script_get_file(storage, &active) < 0) { - ctx = NULL; - } else { - pool = pool_alloconly_create("sieve_list_context", 4096); - ctx = p_new(pool, struct sieve_list_context, 1); - ctx->pool = pool; - ctx->storage = storage; - ctx->dirp = dirp; - ctx->active = ( active != NULL ? p_strdup(pool, active) : NULL ); - ctx->seen_active = FALSE; - } - } T_END; - - if ( ctx == NULL && closedir(dirp) < 0) - i_error("sieve-storage: closedir(%s) failed: %m", storage->dir); - - return ctx; -} - -const char *sieve_storage_list_next -(struct sieve_list_context *ctx, bool *active) -{ - const struct sieve_storage *storage = ctx->storage; - struct dirent *dp; - const char *scriptname; - - *active = FALSE; - - for (;;) { - if ( (dp = readdir(ctx->dirp)) == NULL ) - return NULL; - - scriptname = sieve_scriptfile_get_script_name(dp->d_name); - if (scriptname != NULL ) { - /* Don't list our active sieve script link if the link - * resides in the script dir (generally a bad idea). - */ - if ( *(storage->link_path) == '\0' && - strcmp(storage->active_fname, dp->d_name) == 0 ) - continue; - - break; - } - } - - if ( ctx->active != NULL && strcmp(dp->d_name, ctx->active) == 0 ) { - *active = TRUE; - ctx->active = NULL; - } - - return scriptname; -} - -int sieve_storage_list_deinit(struct sieve_list_context **ctx) -{ - if (closedir((*ctx)->dirp) < 0) { - sieve_storage_set_critical((*ctx)->storage, "closedir(%s) failed: %m", - (*ctx)->storage->dir); - } - - pool_unref(&(*ctx)->pool); - *ctx = NULL; - return 1; -} - - - - diff --git a/src/lib-sievestorage/sieve-storage-list.h b/src/lib-sievestorage/sieve-storage-list.h deleted file mode 100644 index b7eeab52584e1a61887e2f47e06e827d3bbc274f..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-list.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_STORAGE_LIST_H -#define __SIEVE_STORAGE_LIST_H - -#include "lib.h" -#include "str.h" -#include "sieve-storage.h" - -struct sieve_list_context; - -/* Create a context for listing the scripts in the storage */ -struct sieve_list_context *sieve_storage_list_init - (struct sieve_storage *storage); - -/* Get the next script in the storage. */ -const char *sieve_storage_list_next - (struct sieve_list_context *ctx, bool *active); - -/* Destroy the listing context */ -int sieve_storage_list_deinit - (struct sieve_list_context **ctx); - -#endif - - - - diff --git a/src/lib-sievestorage/sieve-storage-private.h b/src/lib-sievestorage/sieve-storage-private.h deleted file mode 100644 index d848d56967002aad40aeaab8450447bde69e52fd..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-private.h +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_STORAGE_PRIVATE_H -#define __SIEVE_STORAGE_PRIVATE_H - -#include "sieve.h" -#include "sieve-error-private.h" - -#include "sieve-storage.h" - -#define SIEVE_READ_BLOCK_SIZE (1024*8) - -/* How often to scan tmp/ directory for old files (based on dir's atime) */ -#define SIEVE_STORAGE_TMP_SCAN_SECS (8*60*60) -/* Delete files having ctime older than this from tmp/. 36h is standard. */ -#define SIEVE_STORAGE_TMP_DELETE_SECS (36*60*60) - -struct sieve_storage; - -struct sieve_storage_ehandler { - struct sieve_error_handler handler; - struct sieve_storage *storage; -}; - -/* All methods returning int return either TRUE or FALSE. */ -struct sieve_storage { - pool_t pool; - struct sieve_instance *svinst; - - char *name; - char *dir; - - /* Private */ - char *active_path; - char *active_fname; - char *link_path; - char *error; - char *username; /* name of user accessing the storage */ - - mode_t dir_create_mode; - mode_t file_create_mode; - gid_t file_create_gid; - - struct mailbox *inbox; - - uint64_t max_scripts; - uint64_t max_storage; - - enum sieve_error error_code; - struct sieve_error_handler *ehandler; - - enum sieve_storage_flags flags; - time_t prev_mtime; -}; - -struct sieve_script *sieve_storage_script_init_from_path - (struct sieve_storage *storage, const char *path, const char *scriptname); - -void sieve_storage_inbox_script_attribute_set - (struct sieve_storage *storage, const char *name); -void sieve_storage_inbox_script_attribute_rename - (struct sieve_storage *storage, const char *oldname, const char *newname); -void sieve_storage_inbox_script_attribute_unset - (struct sieve_storage *storage, const char *name); - - -#endif - diff --git a/src/lib-sievestorage/sieve-storage-quota.h b/src/lib-sievestorage/sieve-storage-quota.h deleted file mode 100644 index 4c43b8afe1efe46f3d1ddcb0ae3abda3541b86f3..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-quota.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_STORAGE_QUOTA_H -#define __SIEVE_STORAGE_QUOTA_H - -#include "lib.h" - -#include "sieve-storage.h" - -enum sieve_storage_quota { - SIEVE_STORAGE_QUOTA_NONE, - SIEVE_STORAGE_QUOTA_MAXSIZE, - SIEVE_STORAGE_QUOTA_MAXSCRIPTS, - SIEVE_STORAGE_QUOTA_MAXSTORAGE -}; - -bool sieve_storage_quota_validsize - (struct sieve_storage *storage, size_t size, uint64_t *limit_r); - -uint64_t sieve_storage_quota_max_script_size - (struct sieve_storage *storage); - -int sieve_storage_quota_havespace - (struct sieve_storage *storage, const char *scriptname, size_t size, - enum sieve_storage_quota *quota_r, uint64_t *limit_r); - -#endif /* __SIEVE_STORAGE_QUOTA_H */ diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c deleted file mode 100644 index 3b54b66dec6c8c9099746d7a27f3e3ae59abcfdf..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-save.c +++ /dev/null @@ -1,459 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "hostpid.h" -#include "ioloop.h" -#include "array.h" -#include "buffer.h" -#include "ostream.h" -#include "str.h" -#include "eacces-error.h" -#include "safe-mkstemp.h" - -#include "sieve-script.h" -#include "sieve-script-file.h" - -#include "sieve-storage-private.h" -#include "sieve-storage-script.h" -#include "sieve-storage-save.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <utime.h> -#include <sys/stat.h> - -struct sieve_save_context { - pool_t pool; - - struct sieve_storage *storage; - const char *scriptname; - struct sieve_script *scriptobject; - - struct istream *input; - struct ostream *output; - int fd; - const char *tmp_path; - - time_t mtime; - - unsigned int failed:1; - unsigned int moving:1; - unsigned int finished:1; -}; - -static const char *sieve_generate_tmp_filename(const char *scriptname) -{ - static struct timeval last_tv = { 0, 0 }; - struct timeval tv; - - /* use secs + usecs to guarantee uniqueness within this process. */ - if (ioloop_timeval.tv_sec > last_tv.tv_sec || - (ioloop_timeval.tv_sec == last_tv.tv_sec && - ioloop_timeval.tv_usec > last_tv.tv_usec)) { - tv = ioloop_timeval; - } else { - tv = last_tv; - if (++tv.tv_usec == 1000000) { - tv.tv_sec++; - tv.tv_usec = 0; - } - } - last_tv = tv; - - scriptname = t_strdup_printf("%s_%s.M%sP%s.%s", - ( scriptname == NULL ? "NULL" : scriptname ), - dec2str(tv.tv_sec), dec2str(tv.tv_usec), my_pid, my_hostname); - - return sieve_scriptfile_from_name(scriptname); -} - -static int sieve_storage_create_tmp -(struct sieve_storage *storage, const char *scriptname, const char **fpath_r) -{ - struct stat st; - unsigned int prefix_len; - const char *tmp_fname = NULL; - string_t *path; - int fd; - - path = t_str_new(256); - str_append(path, storage->dir); - str_append(path, "/tmp/"); - prefix_len = str_len(path); - - for (;;) { - tmp_fname = sieve_generate_tmp_filename(scriptname); - str_truncate(path, prefix_len); - str_append(path, tmp_fname); - - /* stat() first to see if it exists. pretty much the only - possibility of that happening is if time had moved - backwards, but even then it's highly unlikely. */ - if (stat(str_c(path), &st) == 0) { - /* try another file name */ - } else if (errno != ENOENT) { - sieve_storage_set_critical(storage, - "stat(%s) failed: %m", str_c(path)); - return -1; - } else { - /* doesn't exist */ - mode_t old_mask = umask(0777 & ~(storage->file_create_mode)); - fd = open(str_c(path), - O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777); - umask(old_mask); - - if (fd != -1 || errno != EEXIST) - break; - /* race condition between stat() and open(). - highly unlikely. */ - } - } - - *fpath_r = str_c(path); - if (fd == -1) { - if (ENOSPACE(errno)) { - sieve_storage_set_error(storage, SIEVE_ERROR_NO_SPACE, - "Not enough disk space"); - } else { - sieve_storage_set_critical(storage, - "open(%s) failed: %m", str_c(path)); - } - } - - return fd; -} - - -static int sieve_storage_script_move(struct sieve_save_context *ctx, - const char *dst) -{ - int failed; - - T_BEGIN { - - /* Using rename() to ensure existing files are replaced - * without conflicts with other processes using the same - * file. The kernel wont fully delete the original until - * all processes have closed the file. - */ - if (rename(ctx->tmp_path, dst) == 0) - failed = FALSE; - else { - failed = TRUE; - if ( ENOSPACE(errno) ) { - sieve_storage_set_error - (ctx->storage, SIEVE_ERROR_NO_SPACE, "Not enough disk space"); - } else if ( errno == EACCES ) { - sieve_storage_set_critical - (ctx->storage, "%s", eacces_error_get("rename", dst)); - } else { - sieve_storage_set_critical - (ctx->storage, "rename(%s, %s) failed: %m", ctx->tmp_path, dst); - } - } - - /* Always destroy temp file */ - (void)unlink(ctx->tmp_path); - - } T_END; - - return !failed; -} - -struct sieve_save_context * -sieve_storage_save_init(struct sieve_storage *storage, - const char *scriptname, struct istream *input) -{ - struct sieve_save_context *ctx; - pool_t pool; - const char *path; - - if ( scriptname != NULL ) { - /* Validate script name */ - if ( !sieve_script_name_is_valid(scriptname) ) { - sieve_storage_set_error(storage, - SIEVE_ERROR_BAD_PARAMS, - "Invalid script name '%s'.", scriptname); - return NULL; - } - - /* Prevent overwriting the active script link when it resides in the - * sieve storage directory. - */ - if ( *(storage->link_path) == '\0' ) { - const char *svext; - size_t namelen; - - svext = strrchr(storage->active_fname, '.'); - namelen = svext - storage->active_fname; - if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 && - strlen(scriptname) == namelen && - strncmp(scriptname, storage->active_fname, namelen) == 0 ) - { - sieve_storage_set_error( - storage, SIEVE_ERROR_BAD_PARAMS, - "Script name '%s' is reserved for internal use.", scriptname); - return NULL; - } - } - } - - pool = pool_alloconly_create("sieve_save_context", 4096); - ctx = p_new(pool, struct sieve_save_context, 1); - ctx->pool = pool; - ctx->storage = storage; - ctx->scriptname = p_strdup(pool, scriptname); - ctx->scriptobject = NULL; - ctx->mtime = (time_t)-1; - - T_BEGIN { - ctx->fd = sieve_storage_create_tmp(storage, scriptname, &path); - if (ctx->fd == -1) { - ctx->failed = TRUE; - pool_unref(&pool); - ctx = NULL; - } else { - ctx->input = input; - ctx->output = o_stream_create_fd(ctx->fd, 0, FALSE); - ctx->tmp_path = p_strdup(pool, path); - ctx->failed = FALSE; - } - } T_END; - - return ctx; -} - -int sieve_storage_save_continue(struct sieve_save_context *ctx) -{ - if (o_stream_send_istream(ctx->output, ctx->input) < 0) { - sieve_storage_set_critical(ctx->storage, - "o_stream_send_istream(%s) failed: %m", ctx->tmp_path); - ctx->failed = TRUE; - return -1; - } - return 0; -} - -int sieve_storage_save_finish(struct sieve_save_context *ctx) -{ - int output_errno; - - ctx->finished = TRUE; - if ( ctx->failed && ctx->fd == -1 ) { - /* tmp file creation failed */ - return -1; - } - - T_BEGIN { - output_errno = ctx->output->stream_errno; - o_stream_destroy(&ctx->output); - - if ( fsync(ctx->fd) < 0 ) { - sieve_storage_set_critical(ctx->storage, - "fsync(%s) failed: %m", ctx->tmp_path); - ctx->failed = TRUE; - } - if ( close(ctx->fd) < 0 ) { - sieve_storage_set_critical(ctx->storage, - "close(%s) failed: %m", ctx->tmp_path); - ctx->failed = TRUE; - } - ctx->fd = -1; - - if ( ctx->failed ) { - /* delete the tmp file */ - if (unlink(ctx->tmp_path) < 0 && errno != ENOENT) - i_warning("sieve-storage: Unlink(%s) failed: %m", ctx->tmp_path); - - errno = output_errno; - if ( ENOSPACE(errno) ) { - sieve_storage_set_error(ctx->storage, SIEVE_ERROR_NO_SPACE, - "Not enough disk space"); - } else if ( errno != 0 ) { - sieve_storage_set_critical(ctx->storage, - "write(%s) failed: %m", ctx->tmp_path); - } - } - } T_END; - - return ( ctx->failed ? -1 : 0 ); -} - -void sieve_storage_save_set_mtime -(struct sieve_save_context *ctx, time_t mtime) -{ - ctx->mtime = mtime; -} - -static void sieve_storage_save_destroy(struct sieve_save_context **ctx) -{ - if ((*ctx)->scriptobject != NULL) - sieve_script_unref(&((*ctx)->scriptobject)); - - pool_unref(&(*ctx)->pool); - *ctx = NULL; -} - -struct sieve_script *sieve_storage_save_get_tempscript -(struct sieve_save_context *ctx) -{ - const char *scriptname = - ( ctx->scriptname == NULL ? "" : ctx->scriptname ); - - if (ctx->failed) - return NULL; - - if ( ctx->scriptobject != NULL ) - return ctx->scriptobject; - - ctx->scriptobject = sieve_storage_script_init_from_path - (ctx->storage, ctx->tmp_path, scriptname); - - if ( ctx->scriptobject == NULL ) { - if ( ctx->storage->error_code == SIEVE_ERROR_NOT_FOUND ) { - sieve_storage_set_critical(ctx->storage, - "save: Temporary script file '%s' got lost, " - "which should not happen (possibly deleted externally).", - ctx->tmp_path); - } - return NULL; - } - - return ctx->scriptobject; -} - -bool sieve_storage_save_will_activate -(struct sieve_save_context *ctx) -{ - bool result = FALSE; - - if ( ctx->scriptname != NULL ) T_BEGIN { - const char *scriptname; - int ret; - - ret = sieve_storage_active_script_get_name(ctx->storage, &scriptname); - if ( ret > 0 ) { - /* Is the requested script active? */ - result = ( strcmp(ctx->scriptname, scriptname) == 0 ); - } - } T_END; - - return result; -} - -static void sieve_storage_update_mtime(const char *path, time_t mtime) -{ - struct utimbuf times = { .actime = mtime, .modtime = mtime }; - - if ( utime(path, ×) < 0 ) { - switch ( errno ) { - case ENOENT: - break; - case EACCES: - i_error("sieve-storage: %s", eacces_error_get("utime", path)); - break; - default: - i_error("sieve-storage: utime(%s) failed: %m", path); - } - } -} - -int sieve_storage_save_commit(struct sieve_save_context **_ctx) -{ - struct sieve_save_context *ctx = *_ctx; - struct sieve_storage *storage = ctx->storage; - const char *dest_path; - bool failed = FALSE; - - i_assert(ctx->output == NULL); - i_assert(ctx->finished); - i_assert(ctx->scriptname != NULL); - - T_BEGIN { - dest_path = t_strconcat(storage->dir, "/", - sieve_scriptfile_from_name(ctx->scriptname), NULL); - - failed = !sieve_storage_script_move(ctx, dest_path); - if ( ctx->mtime != (time_t)-1 ) - sieve_storage_update_mtime(dest_path, ctx->mtime); - } T_END; - - /* set INBOX mailbox attribute */ - if ( !failed ) - sieve_storage_inbox_script_attribute_set(storage, ctx->scriptname); - - sieve_storage_save_destroy(_ctx); - - return ( failed ? -1 : 0 ); -} - -void sieve_storage_save_cancel(struct sieve_save_context **ctx) -{ - (*ctx)->failed = TRUE; - - if (!(*ctx)->finished) - (void)sieve_storage_save_finish(*ctx); - else - (void)unlink((*ctx)->tmp_path); - - i_assert((*ctx)->output == NULL); - - sieve_storage_save_destroy(ctx); -} - -int sieve_storage_save_as_active_script(struct sieve_storage *storage, - struct istream *input, time_t mtime) -{ - int fd; - string_t *temp_path; - struct ostream *output; - - temp_path = t_str_new(256); - str_append(temp_path, storage->active_path); - str_append_c(temp_path, '.'); - fd = safe_mkstemp_hostpid - (temp_path, storage->file_create_mode, (uid_t)-1, (gid_t)-1); - if ( fd < 0 ) { - if ( errno == EACCES ) { - sieve_storage_set_critical(storage, - "failed to create temporary file: %s", - eacces_error_get_creating("open", str_c(temp_path))); - } else { - sieve_storage_set_critical(storage, - "failed to create temporary file: open(%s) failed: %m", - str_c(temp_path)); - } - return -1; - } - - output = o_stream_create_fd(fd, 0, FALSE); - if (o_stream_send_istream(output, input) < 0) { - sieve_storage_set_critical(storage, - "o_stream_send_istream(%s) failed: %m", str_c(temp_path)); - o_stream_destroy(&output); - (void)unlink(str_c(temp_path)); - return -1; - } - o_stream_destroy(&output); - - if (rename(str_c(temp_path), storage->active_path) < 0) { - if ( ENOSPACE(errno) ) { - sieve_storage_set_error(storage, - SIEVE_ERROR_NO_SPACE, "Not enough disk space"); - } else if ( errno == EACCES ) { - sieve_storage_set_critical(storage, - "%s", eacces_error_get("rename", storage->active_path)); - } else { - sieve_storage_set_critical(storage, - "rename(%s, %s) failed: %m", str_c(temp_path), storage->active_path); - } - } else { - sieve_storage_update_mtime(storage->active_path, mtime); - } - - (void)unlink(str_c(temp_path)); - return 0; -} diff --git a/src/lib-sievestorage/sieve-storage-save.h b/src/lib-sievestorage/sieve-storage-save.h deleted file mode 100644 index c9f84f2427ed40ec0671211f7de5ba852d7d28a9..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-save.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_SAVE_H -#define __SIEVE_SAVE_H - -#include "sieve-storage.h" - -struct sieve_save_context; - -struct sieve_save_context * -sieve_storage_save_init(struct sieve_storage *storage, - const char *scriptname, struct istream *input); - -int sieve_storage_save_continue(struct sieve_save_context *ctx); - -int sieve_storage_save_finish(struct sieve_save_context *ctx); - -struct sieve_script *sieve_storage_save_get_tempscript - (struct sieve_save_context *ctx); - -bool sieve_storage_save_will_activate - (struct sieve_save_context *ctx); - -void sieve_storage_save_set_mtime - (struct sieve_save_context *ctx, time_t mtime); - -void sieve_storage_save_cancel(struct sieve_save_context **ctx); - -int sieve_storage_save_commit(struct sieve_save_context **ctx); - -/* Saves input directly as a regular file at the active script path. - * This is needed for the doveadm-sieve plugin. - */ -int sieve_storage_save_as_active_script(struct sieve_storage *storage, - struct istream *input, time_t mtime); - -#endif - diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c deleted file mode 100644 index 04c7e3d527ded1c3ac3ea8a6141935e93ab90d11..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-script.c +++ /dev/null @@ -1,740 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "abspath.h" -#include "mempool.h" -#include "hostpid.h" -#include "ioloop.h" -#include "istream.h" -#include "file-copy.h" -#include "eacces-error.h" - -#include "sieve-script-private.h" -#include "sieve-script-file.h" - -#include "sieve-storage.h" -#include "sieve-storage-private.h" -#include "sieve-storage-script.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/stat.h> -#include <ctype.h> -#include <time.h> -#include <fcntl.h> - -struct sieve_storage_script { - struct sieve_file_script file; - - struct sieve_storage *storage; -}; - -static int _file_path_cmp(const char *path1, const char *path2) -{ - const char *p1, *p2; - int ret; - - p1 = path1; p2 = path2; - if (*p2 == '\0' && *p1 != '\0') - return 1; - if (*p1 == '\0' && *p2 != '\0') - return -1; - if (*p1 == '/' && *p2 != '/') - return 1; - if (*p2 == '/' && *p1 != '/') - return -1; - for (;;) { - const char *s1, *s2; - size_t size1, size2; - - /* skip repeated slashes */ - for (; *p1 == '/'; p1++); - for (; *p2 == '/'; p2++); - /* check for end of comparison */ - if (*p1 == '\0' || *p2 == '\0') - break; - /* mark start of path element */ - s1 = p1; - s2 = p2; - /* scan to end of path elements */ - for (; *p1 != '\0' && *p1 != '/'; p1++); - for (; *p2 != '\0' && *p2 != '/'; p2++); - /* compare sizes */ - size1 = p1 - s1; - size2 = p2 - s2; - if (size1 != size2) - return size1 - size2; - /* compare */ - if (size1 > 0 && (ret=memcmp(s1, s2, size1)) != 0) - return ret; - } - if (*p1 == '\0') { - if (*p2 == '\0') - return 0; - return -1; - } - return 1; -} - -struct sieve_script *sieve_storage_script_init_from_path -(struct sieve_storage *storage, const char *path, - const char *scriptname) -{ - pool_t pool; - struct sieve_storage_script *st_script = NULL; - enum sieve_error error; - - /* Prevent initializing the active script link as a script when it - * resides in the sieve storage directory. - */ - if ( scriptname != NULL && *(storage->link_path) == '\0' ) { - const char *fname; - - fname = strrchr(path, '/'); - if ( fname == NULL ) - fname = path; - else - fname++; - - if ( strcmp(fname, storage->active_fname) == 0 ) { - sieve_storage_set_error - (storage, SIEVE_ERROR_NOT_FOUND, "Script does not exist."); - return NULL; - } - } - - pool = pool_alloconly_create("sieve_storage_script", 4096); - st_script = p_new(pool, struct sieve_storage_script, 1); - st_script->file.script = sieve_file_script; - st_script->file.script.pool = pool; - st_script->storage = storage; - - sieve_script_init - (&st_script->file.script, storage->svinst, &sieve_file_script, path, - scriptname, sieve_storage_get_error_handler(storage)); - - if ( sieve_script_open(&st_script->file.script, &error) < 0 ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) - sieve_storage_set_error(storage, error, "Script does not exist."); - pool_unref(&pool); - return NULL; - } - - return &st_script->file.script; -} - -struct sieve_script *sieve_storage_script_init -(struct sieve_storage *storage, const char *scriptname) -{ - struct sieve_script *script; - const char *path; - - /* Validate script name */ - if ( !sieve_script_name_is_valid(scriptname) ) { - sieve_storage_set_error(storage, SIEVE_ERROR_BAD_PARAMS, - "Invalid script name '%s'.", scriptname); - return NULL; - } - - T_BEGIN { - path = t_strconcat - ( storage->dir, "/", sieve_scriptfile_from_name(scriptname), NULL ); - - script = sieve_storage_script_init_from_path(storage, path, NULL); - } T_END; - - return script; -} - -static struct sieve_script *sieve_storage_script_init_from_file -(struct sieve_storage *storage, const char *scriptfile) -{ - struct sieve_script *script; - const char *path; - - T_BEGIN { - path = t_strconcat( storage->dir, "/", scriptfile, NULL ); - - script = sieve_storage_script_init_from_path(storage, path, NULL); - } T_END; - - return script; -} - -static int sieve_storage_read_active_link -(struct sieve_storage *storage, const char **link_r) -{ - int ret; - - ret = t_readlink(storage->active_path, link_r); - - if ( ret < 0 ) { - *link_r = NULL; - - if ( errno == EINVAL ) { - /* Our symlink is no symlink. Report 'no active script'. - * Activating a script will automatically resolve this, so - * there is no need to panic on this one. - */ - if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { - i_warning - ("sieve-storage: Active sieve script symlink %s is no symlink.", - storage->active_path); - } - return 0; - } - - if ( errno == ENOENT ) { - /* Symlink not found */ - return 0; - } - - /* We do need to panic otherwise */ - sieve_storage_set_critical(storage, - "Performing readlink() on active sieve symlink '%s' failed: %m", - storage->active_path); - return -1; - } - - /* ret is now assured to be valid, i.e. > 0 */ - return 1; -} - -static const char *sieve_storage_parse_active_link -(struct sieve_storage *storage, const char *link, const char **scriptname_r) -{ - const char *fname, *scriptname, *scriptpath; - - /* Split link into path and filename */ - fname = strrchr(link, '/'); - if ( fname != NULL ) { - scriptpath = t_strdup_until(link, fname+1); - fname++; - } else { - scriptpath = ""; - fname = link; - } - - /* Check the script name */ - scriptname = sieve_scriptfile_get_script_name(fname); - - /* Warn if link is deemed to be invalid */ - if ( scriptname == NULL ) { - i_warning - ("sieve-storage: Active sieve script symlink %s is broken: " - "invalid scriptname (points to %s).", - storage->active_path, link); - return NULL; - } - - /* Check whether the path is any good */ - if ( _file_path_cmp(scriptpath, storage->link_path) != 0 && - _file_path_cmp(scriptpath, storage->dir) != 0 ) { - i_warning - ("sieve-storage: Active sieve script symlink %s is broken: " - "invalid/unknown path to storage (points to %s).", - storage->active_path, link); - return NULL; - } - - if ( scriptname_r != NULL ) - *scriptname_r = scriptname; - - return fname; -} - -int sieve_storage_active_script_get_file -(struct sieve_storage *storage, const char **file_r) -{ - const char *link, *scriptfile; - int ret; - - *file_r = NULL; - - /* Read the active link */ - if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) - return ret; - - /* Parse the link */ - scriptfile = sieve_storage_parse_active_link(storage, link, NULL); - - if (scriptfile == NULL) { - /* Obviously someone has been playing with our symlink, - * ignore this situation and report 'no active script'. - * Activation should fix this situation. - */ - return 0; - } - - *file_r = scriptfile; - return 1; -} - -int sieve_storage_active_script_get_name -(struct sieve_storage *storage, const char **name_r) -{ - const char *link; - int ret; - - *name_r = NULL; - - /* Read the active link */ - if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) - return ret; - - if ( sieve_storage_parse_active_link(storage, link, name_r) == NULL ) { - /* Obviously someone has been playing with our symlink, - * ignore this situation and report 'no active script'. - * Activation should fix this situation. - */ - return 0; - } - - return 1; -} - -const char *sieve_storage_active_script_get_path - (struct sieve_storage *storage) -{ - return storage->active_path; -} - -struct sieve_script *sieve_storage_active_script_get -(struct sieve_storage *storage) -{ - struct sieve_script *script; - const char *scriptfile, *link; - int ret; - - sieve_storage_clear_error(storage); - - /* Read the active link */ - if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) { - if ( ret == 0 ) { - /* Try to open the active_path as a regular file */ - return sieve_storage_script_init_from_path - (storage, storage->active_path, NULL); - } - - return NULL; - } - - /* Parse the link */ - scriptfile = sieve_storage_parse_active_link(storage, link, NULL); - - if (scriptfile == NULL) { - /* Obviously someone has been playing with our symlink, - * ignore this situation and report. - */ - return NULL; - } - - script = sieve_storage_script_init_from_file(storage, scriptfile); - - if ( script == NULL && storage->error_code == SIEVE_ERROR_NOT_FOUND ) { - i_warning - ("sieve-storage: Active sieve script symlink %s " - "points to non-existent script (points to %s).", - storage->active_path, link); - } - - return script; -} - -int sieve_storage_active_script_get_last_change -(struct sieve_storage *storage, time_t *last_change_r) -{ - struct stat st; - - /* Try direct lstat first */ - if (lstat(storage->active_path, &st) == 0) { - if (!S_ISLNK(st.st_mode)) { - *last_change_r = st.st_mtime; - return 0; - } - } - /* Check error */ - else if (errno != ENOENT) { - sieve_storage_set_critical(storage, "lstat(%s) failed: %m", - storage->active_path); - } - - /* Fall back to statting storage directory */ - return sieve_storage_get_last_change(storage, last_change_r); -} - -int -sieve_storage_active_script_is_no_link(struct sieve_storage *storage) -{ - struct stat st; - - /* Stat the file */ - if ( lstat(storage->active_path, &st) != 0 ) { - if ( errno != ENOENT ) { - sieve_storage_set_critical(storage, - "Failed to stat active sieve script symlink (%s): %m.", - storage->active_path); - return -1; - } - return 0; - } - - if ( S_ISLNK( st.st_mode ) ) - return 0; - if ( !S_ISREG( st.st_mode ) ) { - sieve_storage_set_critical( storage, - "Active sieve script file '%s' is no symlink nor a regular file.", - storage->active_path ); - return -1; - } - return 1; -} - -int sieve_storage_script_is_active(struct sieve_script *script) -{ - struct sieve_storage_script *st_script = - (struct sieve_storage_script *) script; - const char *afile; - int ret = 0; - - T_BEGIN { - ret = sieve_storage_active_script_get_file(st_script->storage, &afile); - - if ( ret > 0 ) { - /* Is the requested script active? */ - ret = ( strcmp(st_script->file.filename, afile) == 0 ? 1 : 0 ); - } - } T_END; - - return ret; -} - -int sieve_storage_script_delete(struct sieve_script **script) -{ - struct sieve_storage_script *st_script = - (struct sieve_storage_script *) *script; - struct sieve_storage *storage = st_script->storage; - int ret = 0; - - /* Is the requested script active? */ - if ( sieve_storage_script_is_active(*script) ) { - sieve_storage_set_error(storage, SIEVE_ERROR_ACTIVE, - "Cannot delete the active sieve script."); - ret = -1; - } else { - ret = unlink(st_script->file.path); - - if ( ret < 0 ) { - if ( errno == ENOENT ) - sieve_storage_set_error(storage, SIEVE_ERROR_NOT_FOUND, - "Sieve script does not exist."); - else - sieve_storage_set_critical( - storage, "Performing unlink() failed on sieve file '%s': %m", - st_script->file.path); - } - } - - /* unset INBOX mailbox attribute */ - if ( ret >= 0 ) - sieve_storage_inbox_script_attribute_unset(storage, (*script)->name); - - /* Always deinitialize the script object */ - sieve_script_unref(script); - return ret; -} - -static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) -{ - bool debug = ( (storage->flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 ); - struct stat st; - - /* Stat the file */ - if ( lstat(storage->active_path, &st) != 0 ) { - if ( errno != ENOENT ) { - sieve_storage_set_critical(storage, - "Failed to stat active sieve script symlink (%s): %m.", - storage->active_path); - return FALSE; - } - return TRUE; - } - - if ( S_ISLNK( st.st_mode ) ) { - if ( debug ) - i_debug( "sieve-storage: nothing to rescue %s.", storage->active_path); - return TRUE; /* Nothing to rescue */ - } - - /* Only regular files can be rescued */ - if ( S_ISREG( st.st_mode ) ) { - const char *dstpath; - bool result = TRUE; - - T_BEGIN { - - dstpath = t_strconcat - ( storage->dir, "/", sieve_scriptfile_from_name("dovecot.orig"), NULL ); - if ( file_copy(storage->active_path, dstpath, 1) < 1 ) { - sieve_storage_set_critical(storage, - "Active sieve script file '%s' is a regular file and copying it to " - "the script storage as '%s' failed. This needs to be fixed manually.", - storage->active_path, dstpath); - result = FALSE; - } else { - i_info("sieve-storage: Moved active sieve script file '%s' " - "to script storage as '%s'.", - storage->active_path, dstpath); - } - } T_END; - - return result; - } - - sieve_storage_set_critical( storage, - "Active sieve script file '%s' is no symlink nor a regular file. " - "This needs to be fixed manually.", storage->active_path ); - return FALSE; -} - -int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime) -{ - int ret; - - if ( !sieve_storage_rescue_regular_file(storage) ) - return -1; - - /* Delete the symlink, so no script is active */ - ret = unlink(storage->active_path); - - if ( ret < 0 ) { - if ( errno != ENOENT ) { - sieve_storage_set_critical(storage, "sieve_storage_deactivate(): " - "error on unlink(%s): %m", storage->active_path); - return -1; - } else { - return 0; - } - } - - sieve_storage_set_modified(storage, mtime); - return 1; -} - -static int sieve_storage_replace_active_link - (struct sieve_storage *storage, const char *link_path) -{ - const char *active_path_new; - struct timeval *tv, tv_now; - int ret = 0; - - tv = &ioloop_timeval; - - for (;;) { - /* First the new symlink is created with a different filename */ - active_path_new = t_strdup_printf - ("%s-new.%s.P%sM%s.%s", - storage->active_path, - dec2str(tv->tv_sec), my_pid, - dec2str(tv->tv_usec), my_hostname); - - ret = symlink(link_path, active_path_new); - - if ( ret < 0 ) { - /* If link exists we try again later */ - if ( errno == EEXIST ) { - /* Wait and try again - very unlikely */ - sleep(2); - tv = &tv_now; - if (gettimeofday(&tv_now, NULL) < 0) - i_fatal("gettimeofday(): %m"); - continue; - } - - /* Other error, critical */ - sieve_storage_set_critical - (storage, "Creating symlink() %s to %s failed: %m", - active_path_new, link_path); - return -1; - } - - /* Link created */ - break; - } - - /* Replace the existing link. This activates the new script */ - ret = rename(active_path_new, storage->active_path); - - if ( ret < 0 ) { - /* Failed; created symlink must be deleted */ - (void)unlink(active_path_new); - sieve_storage_set_critical - (storage, "Performing rename() %s to %s failed: %m", - active_path_new, storage->active_path); - return -1; - } - - return 1; -} - -static int _sieve_storage_script_activate(struct sieve_script *script, time_t mtime) -{ - struct sieve_storage_script *st_script = - (struct sieve_storage_script *) script; - struct sieve_storage *storage = st_script->storage; - struct stat st; - const char *link_path, *afile; - int activated = 0; - int ret; - - /* Find out whether there is an active script, but recreate - * the symlink either way. This way, any possible error in the symlink - * resolves automatically. This step is only necessary to provide a - * proper return value indicating whether the script was already active. - */ - ret = sieve_storage_active_script_get_file(storage, &afile); - - /* Is the requested script already active? */ - if ( ret <= 0 || strcmp(st_script->file.filename, afile) != 0 ) - activated = 1; - - /* Check the scriptfile we are trying to activate */ - if ( lstat(st_script->file.path, &st) != 0 ) { - sieve_storage_set_critical(storage, - "Stat on sieve script %s failed, but it is to be activated: %m.", - st_script->file.path); - return -1; - } - - /* Rescue a possible .dovecot.sieve regular file remaining from old - * installations. - */ - if ( !sieve_storage_rescue_regular_file(storage) ) { - /* Rescue failed, manual intervention is necessary */ - return -1; - } - - /* Just try to create the symlink first */ - link_path = t_strconcat - ( storage->link_path, st_script->file.filename, NULL ); - - ret = symlink(link_path, storage->active_path); - - if ( ret < 0 ) { - if ( errno == EEXIST ) { - ret = sieve_storage_replace_active_link(storage, link_path); - if ( ret < 0 ) { - return ret; - } - } else { - /* Other error, critical */ - sieve_storage_set_critical - (storage, - "Creating symlink() %s to %s failed: %m", - storage->active_path, link_path); - return -1; - } - } - - sieve_storage_set_modified(storage, mtime); - return activated; -} - -int sieve_storage_script_activate(struct sieve_script *script, time_t mtime) -{ - int ret; - - T_BEGIN { - ret = _sieve_storage_script_activate(script, mtime); - } T_END; - - return ret; -} - -int sieve_storage_script_rename -(struct sieve_script *script, const char *newname) -{ - struct sieve_storage_script *st_script = - (struct sieve_storage_script *) script; - struct sieve_storage *storage = st_script->storage; - const char *oldname = script->name, *newpath, *newfile, *link_path; - int ret = 0; - - /* Check script name */ - if ( !sieve_script_name_is_valid(newname) ) { - sieve_storage_set_error(storage, - SIEVE_ERROR_BAD_PARAMS, - "Invalid new script name '%s'.", newname); - return -1; - } - - T_BEGIN { - newfile = sieve_scriptfile_from_name(newname); - newpath = t_strconcat( storage->dir, "/", newfile, NULL ); - - /* The normal rename() system call overwrites the existing file without - * notice. Also, active scripts must not be disrupted by renaming a script. - * That is why we use a link(newpath) [activate newpath] unlink(oldpath) - */ - - /* Link to the new path */ - ret = link(st_script->file.path, newpath); - if ( ret >= 0 ) { - /* Is the requested script active? */ - if ( sieve_storage_script_is_active(script) ) { - /* Active; make active link point to the new copy */ - link_path = t_strconcat - ( storage->link_path, newfile, NULL ); - - ret = sieve_storage_replace_active_link(storage, link_path); - } - - if ( ret >= 0 ) { - /* If all is good, remove the old link */ - if ( unlink(st_script->file.path) < 0 ) { - i_error("Failed to clean up old file link '%s' after rename: %m", - st_script->file.path); - } - - if ( script->name != NULL && *script->name != '\0' ) - script->name = p_strdup(script->pool, newname); - st_script->file.path = p_strdup(script->pool, newpath); - st_script->file.filename = p_strdup(script->pool, newfile); - } else { - /* If something went wrong, remove the new link to restore previous - * state - */ - if ( unlink(newpath) < 0 ) { - i_error("Failed to clean up new file link '%s'" - " after failed rename: %m", newpath); - } - } - } else { - /* Our efforts failed right away */ - switch ( errno ) { - case ENOENT: - sieve_storage_set_error(storage, SIEVE_ERROR_NOT_FOUND, - "Sieve script does not exist."); - break; - case EEXIST: - sieve_storage_set_error(storage, SIEVE_ERROR_EXISTS, - "A sieve script with that name already exists."); - break; - default: - sieve_storage_set_critical( - storage, "Performing link(%s, %s) failed: %m", - st_script->file.path, newpath); - } - } - } T_END; - - /* rename INBOX mailbox attribute */ - if ( ret >= 0 && oldname != NULL ) - sieve_storage_inbox_script_attribute_rename(storage, oldname, newname); - - return ret; -} - - diff --git a/src/lib-sievestorage/sieve-storage-script.h b/src/lib-sievestorage/sieve-storage-script.h deleted file mode 100644 index 63f958d5faf87913e5d6e1419169142a16844277..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage-script.h +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_STORAGE_SCRIPT_H -#define __SIEVE_STORAGE_SCRIPT_H - -#include "sieve-script.h" - -#include "sieve-storage.h" - -struct sieve_script *sieve_storage_script_init - (struct sieve_storage *storage, const char *scriptname); - -int sieve_storage_active_script_get_file - (struct sieve_storage *storage, const char **file_r); -int sieve_storage_active_script_get_name - (struct sieve_storage *storage, const char **name_r); -const char *sieve_storage_active_script_get_path - (struct sieve_storage *storage); -int sieve_storage_active_script_is_no_link(struct sieve_storage *storage); -struct sieve_script *sieve_storage_active_script_get - (struct sieve_storage *storage); -int sieve_storage_active_script_get_last_change - (struct sieve_storage *storage, time_t *last_change_r); - -int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime); -int sieve_storage_script_is_active(struct sieve_script *script); -int sieve_storage_script_activate(struct sieve_script *script, time_t mtime); -int sieve_storage_script_delete(struct sieve_script **script); -int sieve_storage_script_rename - (struct sieve_script *script, const char *newname); - -#endif - diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c deleted file mode 100644 index 8082be5132981f9003efe063c69b302c7fe55bcf..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage.c +++ /dev/null @@ -1,744 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "home-expand.h" -#include "ioloop.h" -#include "mkdir-parents.h" -#include "eacces-error.h" -#include "unlink-old-files.h" -#include "mail-storage-private.h" - -#include "sieve.h" -#include "sieve-common.h" -#include "sieve-settings.h" -#include "sieve-error-private.h" -#include "sieve-settings.h" - -#include "sieve-storage-private.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/stat.h> -#include <ctype.h> -#include <time.h> -#include <utime.h> - -#define SIEVE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT - -#define MAX_DIR_CREATE_MODE 0770 - -#define CRITICAL_MSG \ - "Internal error occured. Refer to server log for more information." -#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" - -static void sieve_storage_verror - (struct sieve_error_handler *ehandler ATTR_UNUSED, - unsigned int flags ATTR_UNUSED, const char *location ATTR_UNUSED, - const char *fmt, va_list args); - -static const char *sieve_storage_get_relative_link_path - (const char *active_path, const char *storage_dir) -{ - const char *link_path, *p; - size_t pathlen; - - /* Determine to what extent the sieve storage and active script - * paths match up. This enables the managed symlink to be short and the - * sieve storages can be moved around without trouble (if the active - * script path is common to the script storage). - */ - p = strrchr(active_path, '/'); - if ( p == NULL ) { - link_path = storage_dir; - } else { - pathlen = p - active_path; - - if ( strncmp( active_path, storage_dir, pathlen ) == 0 && - (storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') ) - { - if ( storage_dir[pathlen] == '\0' ) - link_path = ""; - else - link_path = storage_dir + pathlen + 1; - } else - link_path = storage_dir; - } - - /* Add trailing '/' when link path is not empty - */ - pathlen = strlen(link_path); - if ( pathlen != 0 && link_path[pathlen-1] != '/') - return t_strconcat(link_path, "/", NULL); - - return t_strdup(link_path); -} - -static mode_t get_dir_mode(mode_t mode) -{ - /* Add the execute bit if either read or write bit is set */ - - if ((mode & 0600) != 0) mode |= 0100; - if ((mode & 0060) != 0) mode |= 0010; - if ((mode & 0006) != 0) mode |= 0001; - - return mode; -} - -static void sieve_storage_get_permissions -(const char *path, mode_t *file_mode_r, mode_t *dir_mode_r, gid_t *gid_r, - const char **gid_origin_r, bool debug) -{ - struct stat st; - - /* Use safe defaults */ - *file_mode_r = 0600; - *dir_mode_r = 0700; - *gid_r = (gid_t)-1; - *gid_origin_r = "defaults"; - - if ( stat(path, &st) < 0 ) { - if ( !ENOTFOUND(errno) ) { - i_error("sieve-storage: stat(%s) failed: %m", path); - } else if ( debug ) { - i_debug("sieve-storage: permission lookup failed from %s", path); - } - return; - - } else { - *file_mode_r = (st.st_mode & 0666) | 0600; - *dir_mode_r = (st.st_mode & 0777) | 0700; - *gid_origin_r = path; - - if ( !S_ISDIR(st.st_mode) ) { - /* We're getting permissions from a file. Apply +x modes as necessary. */ - *dir_mode_r = get_dir_mode(*dir_mode_r); - } - - if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) { - /* Directory's GID is used automatically for new files */ - *gid_r = (gid_t)-1; - } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) { - /* Group has same permissions as world, so don't bother changing it */ - *gid_r = (gid_t)-1; - } else if (getegid() == st.st_gid) { - /* Using our own gid, no need to change it */ - *gid_r = (gid_t)-1; - } else { - *gid_r = st.st_gid; - } - } - - if ( debug ) { - i_debug("sieve-storage: using permissions from %s: mode=0%o gid=%ld", - path, (int)*dir_mode_r, *gid_r == (gid_t)-1 ? -1L : (long)*gid_r); - } -} - -static int mkdir_verify -(const char *dir, mode_t mode, gid_t gid, const char *gid_origin, bool debug) -{ - struct stat st; - - if ( stat(dir, &st) == 0 ) - return 0; - - if ( errno == EACCES ) { - i_error("sieve-storage: mkdir_verify: %s", eacces_error_get("stat", dir)); - return -1; - } else if ( errno != ENOENT ) { - i_error("sieve-storage: mkdir_verify: stat(%s) failed: %m", dir); - return -1; - } - - if ( mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0 ) { - if ( debug ) - i_debug("sieve-storage: created storage directory %s", dir); - return 0; - } - - switch ( errno ) { - case EEXIST: - return 0; - case ENOENT: - i_error("sieve-storage: storage was deleted while it was being created"); - break; - case EACCES: - i_error("sieve-storage: %s", - eacces_error_get_creating("mkdir_parents_chgrp", dir)); - break; - default: - i_error("sieve-storage: mkdir_parents_chgrp(%s) failed: %m", dir); - break; - } - - return -1; -} - -static int check_tmp(const char *path) -{ - struct stat st; - - /* If tmp/ directory exists, we need to clean it up once in a while */ - if ( stat(path, &st) < 0 ) { - if ( errno == ENOENT ) - return 0; - if ( errno == EACCES ) { - i_error("sieve-storage: check_tmp: %s", eacces_error_get("stat", path)); - return -1; - } - i_error("sieve-storage: check_tmp: stat(%s) failed: %m", path); - return -1; - } - - if ( st.st_atime > st.st_ctime + SIEVE_STORAGE_TMP_DELETE_SECS ) { - /* The directory should be empty. we won't do anything - until ctime changes. */ - } else if ( st.st_atime < ioloop_time - SIEVE_STORAGE_TMP_SCAN_SECS ) { - /* Time to scan */ - (void)unlink_old_files(path, "", - ioloop_time - SIEVE_STORAGE_TMP_DELETE_SECS); - } - return 1; -} - -static int _sieve_storage_open_inbox -(struct mail_user *user, struct mailbox **box_r) -{ - struct mail_namespace *ns; - struct mailbox *box; - enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS; - enum mail_error error; - - ns = mail_namespace_find_inbox(user->namespaces); - *box_r = box = mailbox_alloc(ns->list, "INBOX", flags); - if (mailbox_open(box) == 0) - return 0; - - i_warning("sieve-storage: " - "Failed to open user INBOX for attribute modifications: %s", - mailbox_get_last_error(box, &error)); - return -1; -} - -static struct sieve_storage *_sieve_storage_create -(struct sieve_instance *svinst, struct mail_user *user, const char *home, - enum sieve_storage_flags flags) -{ - pool_t pool; - struct sieve_storage *storage; - struct mailbox *inbox = NULL; - bool debug = ( (flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 ); - const char *tmp_dir, *link_path, *path; - const char *sieve_data, *active_path, *active_fname, *storage_dir; - const char *username = user->username; - mode_t dir_create_mode, file_create_mode; - gid_t file_create_gid; - const char *file_create_gid_origin; - unsigned long long int uint_setting; - size_t size_setting; - int ret; - - /* - * Configure active script path - */ - - active_path = sieve_setting_get(svinst, "sieve"); - - /* Get path to active Sieve script */ - - if ( active_path != NULL ) { - const char *p; - - if ( *active_path == '\0' ) { - /* disabled */ - if ( debug ) - i_debug("sieve-storage: sieve is disabled (sieve=\"\")"); - return NULL; - } - - /* Parse full location into a file path */ - p = strchr(active_path, ':'); - if ( p != NULL ) { - if ( strncmp(active_path, "file", p-active_path) != 0 ) { - i_error("sieve-storage: Cannot open non-file script location " - "for active script `%s'", active_path); - return NULL; - } - active_path = p+1; - - p = strchr(active_path, ';'); - if ( p != NULL ) - active_path = t_strdup_until(active_path, p); - } - - } else { - if ( debug ) { - i_debug("sieve-storage: sieve active script path is unconfigured; " - "using default (sieve=%s)", SIEVE_DEFAULT_PATH); - } - - active_path = SIEVE_DEFAULT_PATH; - } - - /* Substitute home dir if necessary */ - - path = home_expand_tilde(active_path, home); - if ( path == NULL ) { - i_error("sieve-storage: userdb(%s) didn't return a home directory " - "for substitition in active script path (sieve=%s)", - username, active_path); - return NULL; - } - - active_path = path; - - /* Get the filename for the active script link */ - - active_fname = strrchr(active_path, '/'); - if ( active_fname == NULL ) - active_fname = active_path; - else - active_fname++; - - if ( *active_fname == '\0' ) { - /* Link cannot be just a path */ - i_error("sieve-storage: " - "path to active symlink must include the link's filename. Path is: %s", - active_path); - - return NULL; - } - - /* - * Configure script storage directory - */ - - storage_dir = NULL; - - /* Read setting */ - - sieve_data = sieve_setting_get(svinst, "sieve_dir"); - - if ( sieve_data == NULL ) - sieve_data = sieve_setting_get(svinst, "sieve_storage"); - - /* Determine location */ - - if ( sieve_data == NULL || *sieve_data == '\0' ) { - /* We'll need to figure out the storage location ourself. - * - * It's $HOME/sieve or /sieve when (presumed to be) chrooted. - */ - if ( home != NULL && *home != '\0' ) { - if (access(home, R_OK|W_OK|X_OK) == 0) { - /* Use default ~/sieve */ - - if ( debug ) { - i_debug("sieve-storage: root exists (%s)", home); - } - - storage_dir = t_strconcat(home, "/sieve", NULL); - } else { - /* Don't have required access on the home directory */ - - if ( debug ) { - i_debug("sieve-storage: access(%s, rwx): " - "failed: %m", home); - } - } - } else { - if ( debug ) - i_debug("sieve-storage: HOME not set"); - - if (access("/sieve", R_OK|W_OK|X_OK) == 0) { - storage_dir = "/sieve"; - if ( debug ) - i_debug("sieve-storage: /sieve exists, assuming chroot"); - } - } - } else { - const char *p; - - /* Parse full location into a file path */ - p = strchr(sieve_data, ':'); - if ( p != NULL ) { - if (strncmp(sieve_data, "file", p-sieve_data) != 0 ) { - i_error("sieve-storage: Cannot open non-file script storage `%s'", - sieve_data); - return NULL; - } - sieve_data = p+1; - - p = strchr(sieve_data, ';'); - if ( p != NULL ) - sieve_data = t_strdup_until(sieve_data, p); - } - - storage_dir = sieve_data; - } - - if (storage_dir == NULL || *storage_dir == '\0') { - i_error("sieve-storage: couldn't find storage root directory; " - "sieve_dir was left unconfigured and autodetection failed"); - return NULL; - } - - /* Expand home directory in path */ - - path = home_expand_tilde(storage_dir, home); - if ( path == NULL ) { - i_error("sieve-storage: userdb(%s) didn't return a home directory " - "for substitition in storage root directory (sieve_dir=%s)", - username, storage_dir); - return NULL; - } - - storage_dir = path; - - if ( debug ) { - i_debug("sieve-storage: " - "using active sieve script path: %s", active_path); - i_debug("sieve-storage: " - "using sieve script storage directory: %s", storage_dir); - } - - /* get the storage mtime before we modify it ourself. FIXME: do this - later, only just before modifying the sieve dir */ - struct stat st; - if (stat(storage_dir, &st) < 0) { - if (errno != ENOENT) { - i_error("stat(%s) failed: %m", storage_dir); - return NULL; - } - st.st_mtime = 0; - } - - /* Get permissions */ - - sieve_storage_get_permissions - (storage_dir, &file_create_mode, &dir_create_mode, &file_create_gid, - &file_create_gid_origin, debug); - - /* - * Ensure sieve local directory structure exists (full autocreate): - * This currently only consists of a ./tmp direcory - */ - - tmp_dir = t_strconcat(storage_dir, "/tmp", NULL); - - /* Try to find and clean up tmp dir */ - if ( (ret=check_tmp(tmp_dir)) < 0 ) - return NULL; - - /* Auto-create if necessary */ - if ( ret == 0 && mkdir_verify(tmp_dir, dir_create_mode, file_create_gid, - file_create_gid_origin, debug) < 0 ) - return NULL; - - /* Open user's INBOX for attribute updates if necessary */ - if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) - (void)_sieve_storage_open_inbox(user, &inbox); - - /* - * Create storage object - */ - - pool = pool_alloconly_create("sieve-storage", 512+256); - storage = p_new(pool, struct sieve_storage, 1); - storage->svinst = svinst; - storage->flags = flags; - storage->pool = pool; - storage->dir = p_strdup(pool, storage_dir); - storage->username = p_strdup(pool, username); - storage->active_path = p_strdup(pool, active_path); - storage->active_fname = p_strdup(pool, active_fname); - storage->prev_mtime = st.st_mtime; - - storage->dir_create_mode = dir_create_mode; - storage->file_create_mode = file_create_mode; - storage->file_create_gid = file_create_gid; - - storage->inbox = inbox; - - /* Get the path to be prefixed to the script name in the symlink pointing - * to the active script. - */ - link_path = sieve_storage_get_relative_link_path - (storage->active_path, storage->dir); - - if ( debug ) - i_debug("sieve-storage: " - "relative path to sieve storage in active link: %s", link_path); - - storage->link_path = p_strdup(pool, link_path); - - /* Get quota settings */ - - storage->max_storage = 0; - storage->max_scripts = 0; - - if ( sieve_setting_get_size_value - (svinst, "sieve_quota_max_storage", &size_setting) ) { - storage->max_storage = size_setting; - } - - if ( sieve_setting_get_uint_value - (svinst, "sieve_quota_max_scripts", &uint_setting) ) { - storage->max_scripts = uint_setting; - } - - if ( debug ) { - if ( storage->max_storage > 0 ) { - i_debug("sieve-storage: quota: storage limit: %llu bytes", - (unsigned long long int) storage->max_storage); - } - if ( storage->max_scripts > 0 ) { - i_debug("sieve-storage: quota: script count limit: %llu scripts", - (unsigned long long int) storage->max_scripts); - } - } - return storage; -} - -struct sieve_storage *sieve_storage_create -(struct sieve_instance *svinst, struct mail_user *user, const char *home, - enum sieve_storage_flags flags) -{ - struct sieve_storage *storage; - - T_BEGIN { - storage = _sieve_storage_create(svinst, user, home, flags); - } T_END; - - return storage; -} - -void sieve_storage_free(struct sieve_storage *storage) -{ - if (storage->inbox != NULL) - mailbox_free(&storage->inbox); - sieve_error_handler_unref(&storage->ehandler); - - pool_unref(&storage->pool); -} - -int sieve_storage_get_last_change -(struct sieve_storage *storage, time_t *last_change_r) -{ - *last_change_r = storage->prev_mtime; - return 0; -} - -void sieve_storage_set_modified -(struct sieve_storage *storage, time_t mtime) -{ - struct utimbuf times; - time_t cur_mtime; - - if ( mtime != (time_t)-1 ) { - if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 && - cur_mtime > mtime ) - return; - } else { - mtime = ioloop_time; - } - - times.actime = mtime; - times.modtime = mtime; - if ( utime(storage->dir, ×) < 0 ) { - switch ( errno ) { - case ENOENT: - break; - case EACCES: - i_error("sieve-storage: %s", eacces_error_get("utime", storage->dir)); - break; - default: - i_error("sieve-storage: utime(%s) failed: %m", storage->dir); - } - } else { - storage->prev_mtime = mtime; - } -} - -/* Error handling */ - -struct sieve_error_handler *sieve_storage_get_error_handler -(struct sieve_storage *storage) -{ - struct sieve_storage_ehandler *ehandler; - - if ( storage->ehandler == NULL ) { - pool_t pool = pool_alloconly_create("sieve_storage_ehandler", 512); - ehandler = p_new(pool, struct sieve_storage_ehandler,1); - sieve_error_handler_init(&ehandler->handler, storage->svinst, pool, 1); - - ehandler->handler.verror = sieve_storage_verror; - ehandler->storage = storage; - - storage->ehandler = (struct sieve_error_handler *) ehandler; - } - - return storage->ehandler; -} - -static void ATTR_FORMAT(4, 0) sieve_storage_verror -(struct sieve_error_handler *ehandler, unsigned int flags ATTR_UNUSED, - const char *location ATTR_UNUSED, const char *fmt, va_list args) -{ - struct sieve_storage_ehandler *sehandler = - (struct sieve_storage_ehandler *) ehandler; - struct sieve_storage *storage = sehandler->storage; - - sieve_storage_clear_error(storage); - - if (fmt != NULL) { - storage->error = i_strdup_vprintf(fmt, args); - } - storage->error_code = SIEVE_ERROR_TEMP_FAILURE; -} - -void sieve_storage_clear_error(struct sieve_storage *storage) -{ - i_free(storage->error); - storage->error_code = SIEVE_ERROR_NONE; - storage->error = NULL; -} - -void sieve_storage_set_error -(struct sieve_storage *storage, enum sieve_error error, - const char *fmt, ...) -{ - va_list va; - - sieve_storage_clear_error(storage); - - if (fmt != NULL) { - va_start(va, fmt); - storage->error = i_strdup_vprintf(fmt, va); - va_end(va); - } - - storage->error_code = error; -} - -void sieve_storage_set_critical -(struct sieve_storage *storage, const char *fmt, ...) -{ - va_list va; - - sieve_storage_clear_error(storage); - if (fmt != NULL) { - i_free(storage->error); - storage->error_code = SIEVE_ERROR_TEMP_FAILURE; - - if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { - struct tm *tm; - char str[256]; - - va_start(va, fmt); - i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va)); - va_end(va); - - /* critical errors may contain sensitive data, so let user - see only "Internal error" with a timestamp to make it - easier to look from log files the actual error message. */ - tm = localtime(&ioloop_time); - storage->error = - strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? - i_strdup(str) : i_strdup(CRITICAL_MSG); - } else { - /* no user is involved while synchronizing, so do it the - normal way */ - va_start(va, fmt); - storage->error = i_strdup_vprintf(fmt, va); - va_end(va); - } - } -} - -const char *sieve_storage_get_last_error -(struct sieve_storage *storage, enum sieve_error *error_r) -{ - /* We get here only in error situations, so we have to return some - error. If storage->error is NULL, it means we forgot to set it at - some point.. - */ - - if ( error_r != NULL ) - *error_r = storage->error_code; - - return storage->error != NULL ? storage->error : "Unknown error"; -} - -/* - * INBOX attributes - */ - -static void sieve_storage_inbox_transaction_finish -(struct sieve_storage *storage, struct mailbox_transaction_context **t) -{ - struct mailbox *inbox = storage->inbox; - - if (mailbox_transaction_commit(t) < 0) { - enum mail_error error; - - i_warning("sieve-storage: Failed to update INBOX attributes: %s", - mail_storage_get_last_error(mailbox_get_storage(inbox), &error)); - } -} - -void sieve_storage_inbox_script_attribute_set -(struct sieve_storage *storage, const char *name) -{ - struct mailbox_transaction_context *t; - const char *key; - - if (storage->inbox == NULL) - return; - - key = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); - t = mailbox_transaction_begin(storage->inbox, 0); - mail_index_attribute_set(t->itrans, TRUE, key, ioloop_time, 0); - sieve_storage_inbox_transaction_finish(storage, &t); -} - -void sieve_storage_inbox_script_attribute_rename -(struct sieve_storage *storage, const char *oldname, const char *newname) -{ - struct mailbox_transaction_context *t; - const char *oldkey, *newkey; - - if (storage->inbox == NULL) - return; - - oldkey = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL); - newkey = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL); - - t = mailbox_transaction_begin(storage->inbox, 0); - mail_index_attribute_unset(t->itrans, TRUE, oldkey, ioloop_time); - mail_index_attribute_set(t->itrans, TRUE, newkey, ioloop_time, 0); - sieve_storage_inbox_transaction_finish(storage, &t); -} - -void sieve_storage_inbox_script_attribute_unset -(struct sieve_storage *storage, const char *name) -{ - struct mailbox_transaction_context *t; - const char *key; - - if (storage->inbox == NULL) - return; - - key = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); - - t = mailbox_transaction_begin(storage->inbox, 0); - mail_index_attribute_unset(t->itrans, TRUE, key, ioloop_time); - sieve_storage_inbox_transaction_finish(storage, &t); -} - diff --git a/src/lib-sievestorage/sieve-storage.h b/src/lib-sievestorage/sieve-storage.h deleted file mode 100644 index 45060125fa2c6ad5dbf8841d1013bb6ecf6d41c5..0000000000000000000000000000000000000000 --- a/src/lib-sievestorage/sieve-storage.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_STORAGE_H -#define __SIEVE_STORAGE_H - -#include "lib.h" -#include "mail-storage.h" -#include "mail-user.h" - -#include "sieve.h" - -#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ - MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" -#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ - MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" -#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ - MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" - -#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' -#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' - -enum sieve_storage_flags { - /* Print debugging information */ - SIEVE_STORAGE_FLAG_DEBUG = 0x01, - /* This storage is used for synchronization (and not normal ManageSieve) - */ - SIEVE_STORAGE_FLAG_SYNCHRONIZING = 0x02 -}; - -struct sieve_storage *sieve_storage_create - (struct sieve_instance *svinst, struct mail_user *user, const char *home, - enum sieve_storage_flags flags); -void sieve_storage_free(struct sieve_storage *storage); - -struct sieve_error_handler *sieve_storage_get_error_handler - (struct sieve_storage *storage); - -/* Set error message in storage. Critical errors are logged with i_error(), - but user sees only "internal error" message. */ -void sieve_storage_clear_error(struct sieve_storage *storage); - -void sieve_storage_set_error - (struct sieve_storage *storage, enum sieve_error error, - const char *fmt, ...) ATTR_FORMAT(3, 4); - -void sieve_storage_set_critical(struct sieve_storage *storage, - const char *fmt, ...) ATTR_FORMAT(2, 3); - -const char *sieve_storage_get_last_error - (struct sieve_storage *storage, enum sieve_error *error_r); - -int sieve_storage_get_last_change - (struct sieve_storage *storage, time_t *last_change_r); -void sieve_storage_set_modified - (struct sieve_storage *storage, time_t mtime); - -#endif diff --git a/src/managesieve/Makefile.am b/src/managesieve/Makefile.am index a523ffeac8f9d7de7fab21c851d1370b70bf96e2..e160088f0a0c7a1af6a4d1308c0cf3dfe834c324 100644 --- a/src/managesieve/Makefile.am +++ b/src/managesieve/Makefile.am @@ -8,8 +8,7 @@ AM_CPPFLAGS = \ -DMODULEDIR=\""$(dovecot_moduledir)"\" \ -I$(top_srcdir) \ -I$(top_srcdir)/src/lib-sieve \ - -I$(top_srcdir)/src/lib-managesieve \ - -I$(top_srcdir)/src/lib-sievestorage + -I$(top_srcdir)/src/lib-managesieve libmanagesieve_settings_la_LDFLAGS = -module -avoid-version @@ -24,7 +23,6 @@ managesieve_LDFLAGS = -export-dynamic libs = \ managesieve-settings.lo \ $(top_builddir)/src/lib-managesieve/libmanagesieve.a \ - $(top_builddir)/src/lib-sievestorage/libsievestorage.la \ $(top_builddir)/src/lib-sieve/libdovecot-sieve.la managesieve_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) diff --git a/src/managesieve/cmd-deletescript.c b/src/managesieve/cmd-deletescript.c index 0c76053cc900d878528446cf7156b1acaf5a322b..16c6766c838886dd12df8ed98b77462624e5292b 100644 --- a/src/managesieve/cmd-deletescript.c +++ b/src/managesieve/cmd-deletescript.c @@ -3,8 +3,9 @@ #include "lib.h" +#include "sieve.h" +#include "sieve-script.h" #include "sieve-storage.h" -#include "sieve-storage-script.h" #include "managesieve-common.h" #include "managesieve-commands.h" @@ -20,14 +21,14 @@ bool cmd_deletescript(struct client_command_context *cmd) if ( !client_read_string_args(cmd, 1, TRUE, &scriptname) ) return FALSE; - script = sieve_storage_script_init(storage, scriptname); - + script = sieve_storage_open_script + (storage, scriptname, NULL); if (script == NULL) { client_send_storage_error(client, storage); return TRUE; } - if (sieve_storage_script_delete(&script) < 0) { + if (sieve_script_delete(&script) < 0) { client_send_storage_error(client, storage); } else { client_send_ok(client, "Deletescript completed."); @@ -36,6 +37,5 @@ bool cmd_deletescript(struct client_command_context *cmd) /* Script object is deleted no matter what in * sieve_script_delete() */ - return TRUE; } diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c index edc56cc84df7c3252af507fc1ad07bf082c37af3..7498dff1f039a377da1e64c75bcb6fcfb794cc01 100644 --- a/src/managesieve/cmd-getscript.c +++ b/src/managesieve/cmd-getscript.c @@ -5,7 +5,8 @@ #include "ostream.h" #include "istream.h" -#include "sieve-storage-script.h" +#include "sieve-script.h" +#include "sieve-storage.h" #include "managesieve-common.h" #include "managesieve-commands.h" @@ -98,8 +99,9 @@ bool cmd_getscript(struct client_command_context *cmd) ctx->client = client; ctx->storage = client->storage; ctx->failed = FALSE; - ctx->script = sieve_storage_script_init(client->storage, scriptname); + ctx->script = sieve_storage_open_script + (client->storage, scriptname, NULL); if (ctx->script == NULL) { ctx->failed = TRUE; return cmd_getscript_finish(ctx); diff --git a/src/managesieve/cmd-havespace.c b/src/managesieve/cmd-havespace.c index 237160bee925e0b3aab9e1e89793bbfa73450487..e00b31784a424a9378cba8741091c125ecb4e4d3 100644 --- a/src/managesieve/cmd-havespace.c +++ b/src/managesieve/cmd-havespace.c @@ -8,8 +8,7 @@ #include "sieve.h" #include "sieve-script.h" - -#include "sieve-storage-quota.h" +#include "sieve-storage.h" #include "managesieve-client.h" #include "managesieve-quota.h" diff --git a/src/managesieve/cmd-listscripts.c b/src/managesieve/cmd-listscripts.c index 93e7e6882613a54454350158d0dee01e7bb22cfb..75373b0514f9ec6d14e2a36be81e39156cf92f12 100644 --- a/src/managesieve/cmd-listscripts.c +++ b/src/managesieve/cmd-listscripts.c @@ -4,8 +4,8 @@ #include "lib.h" #include "str.h" +#include "sieve.h" #include "sieve-storage.h" -#include "sieve-storage-list.h" #include "managesieve-quote.h" @@ -15,7 +15,7 @@ bool cmd_listscripts(struct client_command_context *cmd) { struct client *client = cmd->client; - struct sieve_list_context *ctx; + struct sieve_storage_list_context *ctx; const char *scriptname; bool active; string_t *str; diff --git a/src/managesieve/cmd-putscript.c b/src/managesieve/cmd-putscript.c index 17f48b89cdca6907f85cf3e58c26e088770d8334..45c0a65ef7180261f4fec02f14aa0d5195c8c861 100644 --- a/src/managesieve/cmd-putscript.c +++ b/src/managesieve/cmd-putscript.c @@ -12,10 +12,8 @@ #include "str.h" #include "sieve.h" - +#include "sieve-script.h" #include "sieve-storage.h" -#include "sieve-storage-script.h" -#include "sieve-storage-save.h" #include "managesieve-parser.h" @@ -37,7 +35,7 @@ struct cmd_putscript_context { uoff_t script_size, max_script_size; struct managesieve_parser *save_parser; - struct sieve_save_context *save_ctx; + struct sieve_storage_save_context *save_ctx; }; static void cmd_putscript_finish(struct cmd_putscript_context *ctx); @@ -59,7 +57,7 @@ static void client_input_putscript(struct client *client) /* Reset command so that client_destroy() doesn't try to call cmd_putscript_continue_script() anymore. */ _client_reset_command(client); - client_destroy(client, "Disconnected in PUTSCRIPT/SCRIPT"); + client_destroy(client, "Disconnected in PUTSCRIPT/CHECKSCRIPT"); return; case -2: cmd_putscript_finish(cmd->context); @@ -199,6 +197,7 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) enum sieve_compile_flags cpflags = SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED; struct sieve_binary *sbin; + enum sieve_error error; string_t *errors; /* Mark this as an activation when we are replacing the active script */ @@ -213,8 +212,17 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) /* Compile */ if ( (sbin=sieve_compile_script - (script, ehandler, cpflags, NULL)) == NULL ) { - client_send_no(client, str_c(errors)); + (script, ehandler, cpflags, &error)) == NULL ) { + if ( error != SIEVE_ERROR_NOT_VALID ) { + const char *errormsg = + sieve_script_get_last_error(script, &error); + if ( error != SIEVE_ERROR_NONE ) + client_send_no(client, errormsg); + else + client_send_no(client, str_c(errors)); + } else { + client_send_no(client, str_c(errors)); + } success = FALSE; } else { sieve_close(&sbin); diff --git a/src/managesieve/cmd-renamescript.c b/src/managesieve/cmd-renamescript.c index 1d1d6a84e7884392e94f02310b957c2480ebb9e9..bf3373500bc106d9c51b8797b11c16dd0b5502c8 100644 --- a/src/managesieve/cmd-renamescript.c +++ b/src/managesieve/cmd-renamescript.c @@ -4,8 +4,9 @@ #include "lib.h" #include "str.h" +#include "sieve.h" +#include "sieve-script.h" #include "sieve-storage.h" -#include "sieve-storage-script.h" #include "managesieve-common.h" #include "managesieve-commands.h" @@ -21,14 +22,14 @@ bool cmd_renamescript(struct client_command_context *cmd) if (!client_read_string_args(cmd, 2, TRUE, &scriptname, &newname)) return FALSE; - script = sieve_storage_script_init(storage, scriptname); - + script = sieve_storage_open_script + (storage, scriptname, NULL); if (script == NULL) { client_send_storage_error(client, storage); return TRUE; } - if (sieve_storage_script_rename(script, newname) < 0) + if (sieve_script_rename(script, newname) < 0) client_send_storage_error(client, storage); else client_send_ok(client, "Renamescript completed."); diff --git a/src/managesieve/cmd-setactive.c b/src/managesieve/cmd-setactive.c index b39e7f328ec4929256a5a746b855917c11a35c46..247b2b56aac373d86b529490aa6f4318724ad59d 100644 --- a/src/managesieve/cmd-setactive.c +++ b/src/managesieve/cmd-setactive.c @@ -5,8 +5,8 @@ #include "str.h" #include "sieve.h" +#include "sieve-script.h" #include "sieve-storage.h" -#include "sieve-storage-script.h" #include "managesieve-common.h" #include "managesieve-commands.h" @@ -26,16 +26,18 @@ bool cmd_setactive(struct client_command_context *cmd) /* Activate, or .. */ if ( *scriptname != '\0' ) { string_t *errors = NULL; + const char *errormsg = NULL; bool warnings = FALSE; bool success = TRUE; - script = sieve_storage_script_init(storage, scriptname); + script = sieve_storage_open_script + (storage, scriptname, NULL); if ( script == NULL ) { client_send_storage_error(client, storage); return TRUE; } - if ( sieve_storage_script_is_active(script) <= 0 ) { + if ( sieve_script_is_active(script) <= 0 ) { /* Script is first being activated; compile it again without the UPLOAD * flag. */ @@ -44,6 +46,7 @@ bool cmd_setactive(struct client_command_context *cmd) enum sieve_compile_flags cpflags = SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_ACTIVATED; struct sieve_binary *sbin; + enum sieve_error error; /* Prepare error handler */ errors = str_new(default_pool, 1024); @@ -52,7 +55,12 @@ bool cmd_setactive(struct client_command_context *cmd) /* Compile */ if ( (sbin=sieve_compile_script - (script, ehandler, cpflags, NULL)) == NULL ) { + (script, ehandler, cpflags, &error)) == NULL ) { + if (error != SIEVE_ERROR_NOT_VALID) { + errormsg = sieve_script_get_last_error(script, &error); + if ( error == SIEVE_ERROR_NONE ) + errormsg = NULL; + } success = FALSE; } else { sieve_close(&sbin); @@ -68,7 +76,7 @@ bool cmd_setactive(struct client_command_context *cmd) /* Refresh activation no matter what; this can also resolve some erroneous * situations. */ - ret = sieve_storage_script_activate(script, (time_t)-1); + ret = sieve_script_activate(script, (time_t)-1); if ( ret < 0 ) { client_send_storage_error(client, storage); } else { @@ -80,8 +88,10 @@ bool cmd_setactive(struct client_command_context *cmd) "Script is already active." )); } } - } else { + } else if ( errormsg == NULL ) { client_send_no(client, str_c(errors)); + } else { + client_send_no(client, errormsg); } if ( errors != NULL ) diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c index fdb59db5f97a836070d5b3c16d648fc01b74c1f4..84aa3146e5aa37be3448881ae1d62dd17803dc8e 100644 --- a/src/managesieve/managesieve-client.c +++ b/src/managesieve/managesieve-client.c @@ -14,6 +14,7 @@ #include "mail-storage-service.h" #include "mail-namespace.h" +#include "sieve.h" #include "sieve-storage.h" #include "managesieve-quote.h" @@ -58,34 +59,35 @@ static void client_idle_timeout(struct client *client) } static struct sieve_storage *client_get_storage -(struct sieve_instance *svinst, struct mail_user *user, - const struct managesieve_settings *set) +(struct sieve_instance *svinst, struct mail_user *user) { struct sieve_storage *storage; - enum sieve_storage_flags flags = 0; - const char *home; - - if ( mail_user_get_home(user, &home) <= 0 ) - home = NULL; - - if ( set->mail_debug ) - flags |= SIEVE_STORAGE_FLAG_DEBUG; - - storage = sieve_storage_create - (svinst, user, home, flags); + enum sieve_error error; + storage = sieve_storage_create_main + (svinst, user, SIEVE_STORAGE_FLAG_READWRITE, &error); if (storage == NULL) { - struct tm *tm; - char str[256]; + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + printf("BYE \"Sieve filtering is disabled for this user.\"\n"); + + i_fatal("Failed to open Sieve storage: Sieve disabled for user"); + break; + default: + { + struct tm *tm; + char str[256]; - tm = localtime(&ioloop_time); + tm = localtime(&ioloop_time); - printf("BYE \"%s\"\n", - strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? - i_strdup(str) : i_strdup(CRITICAL_MSG)); + printf("BYE \"%s\"\n", + strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + i_strdup(str) : i_strdup(CRITICAL_MSG)); - i_fatal("Failed to open Sieve storage."); - } + i_fatal("Failed to open Sieve storage."); + } + } + } return storage; } @@ -102,7 +104,7 @@ struct client *client_create struct sieve_storage *storage; pool_t pool; - /* Initialize Sieve instance */ + /* Initialize Sieve */ memset((void*)&svenv, 0, sizeof(svenv)); svenv.username = user->username; @@ -115,7 +117,7 @@ struct client *client_create /* Get Sieve storage */ - storage = client_get_storage(svinst, user, set); + storage = client_get_storage(svinst, user); /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); @@ -252,7 +254,7 @@ void client_destroy(struct client *client, const char *reason) if (client->fd_in != client->fd_out) net_disconnect(client->fd_out); - sieve_storage_free(client->storage); + sieve_storage_unref(&client->storage); sieve_deinit(&client->svinst); pool_unref(&client->cmd.pool); diff --git a/src/managesieve/managesieve-quota.c b/src/managesieve/managesieve-quota.c index 467caa56547282e49c0adf5d1f69a00c5e48a590..41f3e5a0bd396e1e237868a2b75211673feabf22 100644 --- a/src/managesieve/managesieve-quota.c +++ b/src/managesieve/managesieve-quota.c @@ -4,8 +4,8 @@ #include "lib.h" #include "strfuncs.h" +#include "sieve.h" #include "sieve-storage.h" -#include "sieve-storage-quota.h" #include "managesieve-client.h" #include "managesieve-quota.h" diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index c8f8d976ad39a084cc59abda4cf9f62af9b733a9..b1f4d858b012902852d7b1b0d7f781ef270f0f87 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1 +1,4 @@ -SUBDIRS = doveadm-sieve lda-sieve sieve-extprograms +SUBDIRS = \ + doveadm-sieve \ + lda-sieve \ + sieve-extprograms diff --git a/src/plugins/doveadm-sieve/Makefile.am b/src/plugins/doveadm-sieve/Makefile.am index ea1d992e30b6e4e38eaeaf2ff04b4185f812137f..48f7009d3b969544dccaf021dacab2d5a21e3e84 100644 --- a/src/plugins/doveadm-sieve/Makefile.am +++ b/src/plugins/doveadm-sieve/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib-sieve \ - -I$(top_srcdir)/src/lib-sievestorage \ $(LIBDOVECOT_INCLUDE) doveadm_moduledir = $(dovecot_moduledir)/doveadm @@ -11,5 +10,4 @@ doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la lib10_doveadm_sieve_plugin_la_SOURCES = \ doveadm-sieve-plugin.c lib10_doveadm_sieve_plugin_la_LIBADD = \ - $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ - $(top_builddir)/src/lib-sievestorage/libsievestorage.la + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c index 6647360a94bfffb7f77ba181abc147a200abf0e8..d9a9e2f5aa0c738636fc8a887c986efcefed46ab 100644 --- a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c +++ b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c @@ -2,16 +2,15 @@ */ #include "lib.h" +#include "str.h" #include "ioloop.h" #include "istream.h" #include "istream-concat.h" +#include "mail-storage-private.h" + +#include "sieve.h" #include "sieve-script.h" -#include "sieve-script-file.h" #include "sieve-storage.h" -#include "sieve-storage-list.h" -#include "sieve-storage-save.h" -#include "sieve-storage-script.h" -#include "mail-storage-private.h" #define SIEVE_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, sieve_storage_module) @@ -29,7 +28,7 @@ struct sieve_mailbox_attribute_iter { struct mailbox_attribute_iter iter; struct mailbox_attribute_iter *super; - struct sieve_list_context *sieve_list; + struct sieve_storage_list_context *sieve_list; string_t *name; bool failed; @@ -63,7 +62,7 @@ static void mail_sieve_user_deinit(struct mail_user *user) { struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); - sieve_storage_free(suser->sieve_storage); + sieve_storage_unref(&suser->sieve_storage); sieve_deinit(&suser->svinst); suser->module_ctx.super.deinit(user); @@ -74,7 +73,9 @@ mail_sieve_user_init (struct mail_user *user, struct sieve_storage **svstorage_r) { struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); - enum sieve_storage_flags storage_flags = SIEVE_STORAGE_FLAG_SYNCHRONIZING; + enum sieve_storage_flags storage_flags = + SIEVE_STORAGE_FLAG_READWRITE | + SIEVE_STORAGE_FLAG_SYNCHRONIZING; struct mail_user_vfuncs *v = user->vlast; struct sieve_environment svenv; @@ -95,13 +96,10 @@ mail_sieve_user_init user->vlast = &suser->module_ctx.super; v->deinit = mail_sieve_user_deinit; - if (user->mail_debug) - storage_flags |= SIEVE_STORAGE_FLAG_DEBUG; - suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks, user, user->mail_debug); - suser->sieve_storage = sieve_storage_create(suser->svinst, user, - svenv.home_dir, storage_flags); + suser->sieve_storage = sieve_storage_create_main + (suser->svinst, user, storage_flags, NULL); MODULE_CONTEXT_SET(user, sieve_user_module, suser); *svstorage_r = suser->sieve_storage; @@ -117,9 +115,8 @@ static int sieve_attribute_unset_script(struct mail_storage *storage, enum sieve_error error; int ret = 0; - script = sieve_storage_script_init(svstorage, scriptname); - ret = script == NULL ? -1 : - sieve_storage_script_delete(&script); + script = sieve_storage_open_script(svstorage, scriptname, NULL); + ret = script == NULL ? -1 : sieve_script_delete(&script); if (ret < 0) { errstr = sieve_storage_get_last_error(svstorage, &error); if (error == SIEVE_ERROR_NOT_FOUND) { @@ -148,7 +145,7 @@ sieve_attribute_set_active(struct mail_storage *storage, if (scriptname == NULL) { /* don't affect non-link active script */ - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) != 0) { + if ((ret=sieve_storage_is_singular(svstorage)) != 0) { if (ret < 0) { mail_storage_set_internal_error(storage); return -1; @@ -169,9 +166,9 @@ sieve_attribute_set_active(struct mail_storage *storage, scriptname++; /* activate specified script */ - script = sieve_storage_script_init(svstorage, scriptname); + script = sieve_storage_open_script(svstorage, scriptname, NULL); ret = script == NULL ? -1 : - sieve_storage_script_activate(script, value->last_change); + sieve_script_activate(script, value->last_change); if (ret < 0) { mail_storage_set_critical(storage, "Failed to activate Sieve script '%s': %s", scriptname, @@ -189,7 +186,7 @@ sieve_attribute_unset_active_script(struct mail_storage *storage, { int ret; - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) { + if ((ret=sieve_storage_is_singular(svstorage)) <= 0) { if (ret < 0) mail_storage_set_internal_error(storage); return ret; @@ -222,7 +219,7 @@ sieve_attribute_set_active_script(struct mail_storage *storage, /* skip over the 'S' type */ i_stream_skip(input, 1); - if (sieve_storage_save_as_active_script + if (sieve_storage_save_as_active (svstorage, input, value->last_change) < 0) { mail_storage_set_critical(storage, "Failed to save active sieve script: %s", @@ -275,7 +272,7 @@ sieve_attribute_set_sieve(struct mail_storage *storage, const struct mail_attribute_value *value) { struct sieve_storage *svstorage; - struct sieve_save_context *save_ctx; + struct sieve_storage_save_context *save_ctx; struct istream *input; const char *scriptname; int ret; @@ -437,7 +434,7 @@ sieve_attribute_get_active_script(struct mail_storage *storage, const char *errstr; int ret; - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) { + if ((ret=sieve_storage_is_singular(svstorage)) <= 0) { if (ret == 0 && sieve_storage_active_script_get_last_change (svstorage, &value_r->last_change) < 0) { ret = -1; @@ -447,8 +444,10 @@ sieve_attribute_get_active_script(struct mail_storage *storage, return ret; } - if ((script=sieve_storage_active_script_get(svstorage)) == NULL) + if ((script=sieve_storage_active_script_open + (svstorage, NULL)) == NULL) return 0; + if ((ret=sieve_attribute_retrieve_script (storage, svstorage, script, TRUE, value_r, &errstr)) < 0) { mail_storage_set_critical(storage, @@ -504,7 +503,7 @@ sieve_attribute_get_sieve(struct mail_storage *storage, const char *key, return -1; } scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); - script = sieve_storage_script_init(svstorage, scriptname); + script = sieve_storage_open_script(svstorage, scriptname, NULL); if ((ret=sieve_attribute_retrieve_script (storage, svstorage, script, FALSE, value_r, &errstr)) < 0) { mail_storage_set_critical(storage, @@ -628,7 +627,7 @@ sieve_attribute_iter_next_script(struct sieve_mailbox_attribute_iter *siter) } /* Check whether active script is a proper symlink or a regular file */ - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) < 0) { + if ((ret=sieve_storage_is_singular(svstorage)) < 0) { mail_storage_set_critical(siter->iter.box->storage, "Failed to iterate sieve scripts: %s", sieve_storage_get_last_error(svstorage, NULL)); diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index b48edb0fdc3fbe0d052f7b6c86913954bb70649f..62a4cdf69e95a83e57a85729e88f60b45804763b 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -15,7 +15,7 @@ #include "sieve.h" #include "sieve-script.h" -#include "sieve-script-file.h" +#include "sieve-storage.h" #include "lda-sieve-plugin.h" @@ -156,109 +156,118 @@ struct lda_sieve_run_context { const char *userlog; }; -static const char *lda_sieve_get_personal_location -(struct sieve_instance *svinst, struct mail_user *user) +static int lda_sieve_get_personal_storage +(struct sieve_instance *svinst, struct mail_user *user, + struct sieve_storage **storage_r) { - const char *script_location; - - script_location = mail_user_plugin_getenv(user, "sieve"); - - /* userdb may specify Sieve location */ - if (script_location != NULL) { + enum sieve_error error; - if (*script_location == '\0') { - /* disabled */ - if ( user->mail_debug ) - sieve_sys_debug(svinst, "empty script location, disabled"); - return NULL; + *storage_r = sieve_storage_create_main(svinst, user, 0, &error); + if (*storage_r == NULL) { + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + break; + case SIEVE_ERROR_TEMP_FAILURE: + sieve_sys_error(svinst, + "Failed to access user storage " + "(temporary failure)"); + return -1; + default: + sieve_sys_error(svinst, + "Failed to access user storage"); + break; } - } else { - script_location = LDA_SIEVE_DEFAULT_LOCATION; + return 0; } - - return script_location; + return 1; } -static const char *lda_sieve_get_default_location -(struct mail_user *user) +static int lda_sieve_get_default_storage +(struct sieve_instance *svinst, struct mail_user *user, + struct sieve_storage **storage_r) { - const char *script_location; + bool debug = user->mail_debug; + enum sieve_error error; + const char *location; /* Use default script location, if one exists */ - script_location = mail_user_plugin_getenv(user, "sieve_default"); - if ( script_location == NULL ) { + location = mail_user_plugin_getenv(user, "sieve_default"); + if ( location == NULL ) { /* For backwards compatibility */ - script_location = mail_user_plugin_getenv(user, "sieve_global_path"); + location = mail_user_plugin_getenv(user, "sieve_global_path"); } - return script_location; + if ( location == NULL ) { + if ( debug ) { + sieve_sys_debug(svinst, + "No default script configured for user"); + } + return 0; + } + + *storage_r = sieve_storage_create(svinst, location, 0, &error); + if ( *storage_r == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + if ( debug ) { + sieve_sys_debug(svinst, + "Default user script location `%s' does not exist", + location); + } + break; + case SIEVE_ERROR_TEMP_FAILURE: + sieve_sys_error(svinst, + "Failed to access default user script location `%s' " + "(temporary failure)", + location); + return -1; + default: + sieve_sys_error(svinst, + "Failed to access default user script location `%s'", + location); + break; + } + return 0; + } + return 1; } static int lda_sieve_multiscript_get_scripts (struct sieve_instance *svinst, const char *label, const char *location, - struct sieve_error_handler *ehandler, ARRAY_TYPE(sieve_scripts) *scripts, - enum sieve_error *error_r) + ARRAY_TYPE(sieve_script) *scripts, enum sieve_error *error_r) { - struct sieve_directory *sdir; - ARRAY_TYPE(const_string) script_files; - const char *const *files; - unsigned int count, i; - const char *file; - - // FIXME: make this a generic iteration API - if ( (sdir=sieve_directory_open(svinst, location, error_r)) == NULL ) - return ( *error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); - - t_array_init(&script_files, 16); - - while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) { - /* Insert into sorted array */ - - files = array_get(&script_files, &count); - for ( i = 0; i < count; i++ ) { - if ( strcmp(file, files[i]) < 0 ) - break; - } - - if ( i == count ) - array_append(&script_files, &file, 1); - else - array_insert(&script_files, i, &file, 1); - } - - sieve_directory_close(&sdir); + struct sieve_script_sequence *seq; + struct sieve_script *script; + bool finished = FALSE; + int ret = 1; - files = array_get(&script_files, &count); - for ( i = 0; i < count; i++ ) { - struct sieve_script *script = sieve_script_create_open - (svinst, files[i], NULL, ehandler, error_r); + seq = sieve_script_sequence_create(svinst, location, error_r); + if ( seq == NULL ) + return ( *error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); + while ( ret > 0 && !finished ) { + script = sieve_script_sequence_next(seq, error_r); if ( script == NULL ) { switch ( *error_r ) { - case SIEVE_ERROR_NOT_FOUND: - /* Shouldn't normally happen, but the script could have disappeared */ - sieve_sys_warning(svinst, - "%s script %s doesn't exist", label, files[i]); + case SIEVE_ERROR_NONE: + finished = TRUE; break; case SIEVE_ERROR_TEMP_FAILURE: sieve_sys_error(svinst, - "failed to access %s script %s (temporary failure)", - label, files[i]); - return -1; + "Failed to access %s script from `%s' (temporary failure)", + label, location); + ret = -1; default: - sieve_sys_error(svinst, - "failed to access %s script %s", label, files[i]); break; } - continue; } array_append(scripts, &script, 1); } - *error_r = SIEVE_ERROR_NONE; - return 1; + sieve_script_sequence_free(&seq); + return ret; } static void lda_sieve_binary_save @@ -273,10 +282,11 @@ static void lda_sieve_binary_save /* Cannot save binary for global script */ sieve_sys_error(srctx->svinst, - "the lda sieve plugin does not have permission " - "to save global sieve script binaries; " - "global sieve scripts like %s need to be " - "pre-compiled using the sievec tool", sieve_script_location(script)); + "The LDA Sieve plugin does not have permission " + "to save global Sieve script binaries; " + "global Sieve scripts like `%s' need to be " + "pre-compiled using the sievec tool", + sieve_script_location(script)); } } @@ -293,12 +303,12 @@ static struct sieve_binary *lda_sieve_open if ( recompile ) { /* Warn */ sieve_sys_warning(svinst, - "encountered corrupt binary: re-compiling script %s", + "Encountered corrupt binary: re-compiling script %s", sieve_script_location(script)); compile_name = "re-compile"; } else if ( debug ) { sieve_sys_debug(svinst, - "loading script %s", sieve_script_location(script)); + "Loading script %s", sieve_script_location(script)); } if ( script == srctx->user_script ) @@ -319,30 +329,31 @@ static struct sieve_binary *lda_sieve_open /* Script not found */ case SIEVE_ERROR_NOT_FOUND: if ( debug ) { - sieve_sys_debug(svinst, "script file %s is missing for %s", + sieve_sys_debug(svinst, "Script `%s' is missing for %s", sieve_script_location(script), compile_name); } break; /* Temporary failure */ case SIEVE_ERROR_TEMP_FAILURE: sieve_sys_error(svinst, - "failed to open script %s for %s (temporary failure)", + "Failed to open script `%s' for %s (temporary failure)", sieve_script_location(script), compile_name); break; /* Compile failed */ case SIEVE_ERROR_NOT_VALID: if (script == srctx->user_script && srctx->userlog != NULL ) { - sieve_sys_info(svinst, "failed to %s script %s " - "(view user logfile %s for more information)", + sieve_sys_info(svinst, + "Failed to %s script `%s' " + "(view user logfile `%s' for more information)", compile_name, sieve_script_location(script), srctx->userlog); break; } - sieve_sys_error(svinst, "failed to %s script %s", + sieve_sys_error(svinst, "Failed to %s script `%s'", compile_name, sieve_script_location(script)); break; /* Something else */ default: - sieve_sys_error(svinst, "failed to open script %s for %s", + sieve_sys_error(svinst, "Failed to open script `%s' for %s", sieve_script_location(script), compile_name); break; } @@ -387,13 +398,13 @@ static int lda_sieve_handle_exec_status switch ( status ) { case SIEVE_EXEC_FAILURE: user_error_func(svinst, - "execution of script %s failed, but implicit keep was successful%s", + "Execution of script %s failed, but implicit keep was successful%s", sieve_script_location(script), userlog_notice); ret = 1; break; case SIEVE_EXEC_TEMP_FAILURE: error_func(svinst, - "execution of script %s was aborted due to temporary failure%s", + "Execution of script %s was aborted due to temporary failure%s", sieve_script_location(script), userlog_notice); if ( mail_error != MAIL_ERROR_TEMP && mdctx->tempfail_error == NULL ) { mdctx->tempfail_error = @@ -403,14 +414,14 @@ static int lda_sieve_handle_exec_status break; case SIEVE_EXEC_BIN_CORRUPT: sieve_sys_error(svinst, - "!!BUG!!: binary compiled from %s is still corrupt; " + "!!BUG!!: Binary compiled from %s is still corrupt; " "bailing out and reverting to default delivery", sieve_script_location(script)); ret = -1; break; case SIEVE_EXEC_KEEP_FAILED: error_func(svinst, - "execution of script %s failed with unsuccessful implicit keep%s", + "Execution of script %s failed with unsuccessful implicit keep%s", sieve_script_location(script), userlog_notice); ret = -1; break; @@ -461,8 +472,10 @@ static int lda_sieve_singlescript_execute /* Execute */ - if ( debug ) - sieve_sys_debug(svinst, "executing script from %s", sieve_get_source(sbin)); + if ( debug ) { + sieve_sys_debug(svinst, + "Executing script from `%s'", sieve_get_source(sbin)); + } ret = sieve_execute (sbin, srctx->msgdata, srctx->scriptenv, ehandler, rtflags, NULL); @@ -491,9 +504,10 @@ static int lda_sieve_singlescript_execute /* Execute again */ - if ( debug ) + if ( debug ) { sieve_sys_debug - (svinst, "executing script from %s", sieve_get_source(sbin)); + (svinst, "Executing script from `%s'", sieve_get_source(sbin)); + } ret = sieve_execute (sbin, srctx->msgdata, srctx->scriptenv, ehandler, rtflags, NULL); @@ -554,7 +568,7 @@ static int lda_sieve_multiscript_execute if ( debug ) { sieve_sys_debug - (svinst, "opening script %d of %d from %s", i+1, count, + (svinst, "Opening script %d of %d from `%s'", i+1, count, sieve_script_location(script)); } @@ -568,7 +582,7 @@ static int lda_sieve_multiscript_execute if ( debug ) { sieve_sys_debug - (svinst, "executing script from %s", sieve_get_source(sbin)); + (svinst, "Executing script from `%s'", sieve_get_source(sbin)); } more = sieve_multiscript_run(mscript, sbin, ehandler, rtflags, final); @@ -613,7 +627,7 @@ static int lda_sieve_multiscript_execute /* Don't log additional messages about compile failure */ if ( compile_error && ret == SIEVE_EXEC_FAILURE ) { sieve_sys_info(svinst, - "aborted script execution sequence with successful implicit keep"); + "Aborted script execution sequence with successful implicit keep"); return 1; } @@ -624,11 +638,11 @@ 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_error_handler *master_ehandler = srctx->master_ehandler; - const char *user_location, *default_location, *sieve_before, *sieve_after; + struct sieve_storage *user_storage, *default_storage; + const char *sieve_before, *sieve_after; const char *setting_name; enum sieve_error error; - ARRAY_TYPE(sieve_scripts) script_sequence; + ARRAY_TYPE(sieve_script) script_sequence; struct sieve_script *const *scripts; bool debug = mdctx->dest_user->mail_debug; unsigned int after_index, count, i; @@ -636,71 +650,79 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) /* Find the personal script to execute */ - user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user); - if ( user_location != NULL ) { - srctx->user_script = sieve_script_create_open_as - (svinst, user_location, "main_script", master_ehandler, &error); + ret = lda_sieve_get_personal_storage + (svinst, mdctx->dest_user, &user_storage); + if ( ret > 0 ) { + srctx->user_script = + sieve_storage_active_script_open(user_storage, &error); if ( srctx->user_script == NULL ) { switch ( error ) { case SIEVE_ERROR_NOT_FOUND: if ( debug ) { - sieve_sys_debug(svinst, "user's script %s doesn't exist " - "(trying default script location instead)", user_location); + sieve_sys_debug(svinst, + "No active Sieve script exists in user storage `%s' " + "(trying default script location instead)", + sieve_storage_location(user_storage)); } break; case SIEVE_ERROR_TEMP_FAILURE: sieve_sys_error(svinst, - "failed to access user's Sieve script %s (temporary failure)", - user_location); + "Failed to access active Sieve script in user storage `%s' " + "(temporary failure)", + sieve_storage_location(user_storage)); ret = -1; break; default: sieve_sys_error(svinst, - "failed to access user's Sieve script %s " + "Failed to access active Sieve script in user storage `%s' " "(trying default script location instead)", - user_location); + sieve_storage_location(user_storage)); break; } } else { srctx->main_script = srctx->user_script; } + sieve_storage_unref(&user_storage); } if ( ret >= 0 && srctx->user_script == NULL ) { - default_location = lda_sieve_get_default_location(mdctx->dest_user); - if ( default_location != NULL ) { - srctx->main_script = sieve_script_create_open_as - (svinst, default_location, "main script", master_ehandler, &error); + ret = lda_sieve_get_default_storage + (svinst, mdctx->dest_user, &default_storage); + if ( ret > 0 ) { + srctx->main_script = sieve_storage_open_script + (default_storage, NULL, &error); if ( srctx->main_script == NULL ) { switch ( error ) { case SIEVE_ERROR_NOT_FOUND: if ( debug ) { - sieve_sys_debug(svinst, "default user script %s doesn't exist", - default_location); + sieve_sys_debug(svinst, + "Default user script `%s' does not exist", + sieve_storage_location(default_storage)); } break; case SIEVE_ERROR_TEMP_FAILURE: sieve_sys_error(svinst, - "failed to access default user script %s (temporary failure)", - default_location); + "Failed to access default user script `%s'" + "(temporary failure)", + sieve_storage_location(default_storage)); ret = -1; break; default: - sieve_sys_error(svinst, "failed to access default user script %s", - default_location); + sieve_sys_error(svinst, + "failed to access default user script %s", + sieve_storage_location(default_storage)); break; } } - } else { - sieve_sys_debug(svinst, "no default script configured for user"); } + sieve_storage_unref(&default_storage); } if ( debug && ret >= 0 && srctx->main_script == NULL ) { sieve_sys_debug(svinst, - "user has no valid location for a personal script"); + "User has no personal script"); } /* Compose script array */ @@ -714,12 +736,12 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) sieve_before = mail_user_plugin_getenv(mdctx->dest_user, setting_name); while ( ret >= 0 && sieve_before != NULL && *sieve_before != '\0' ) { ret = lda_sieve_multiscript_get_scripts(svinst, setting_name, - sieve_before, master_ehandler, &script_sequence, &error); + sieve_before, &script_sequence, &error); if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) { ret = -1; break; } else if (ret == 0 && debug ) { - sieve_sys_debug(svinst, "%s location not found: %s", + sieve_sys_debug(svinst, "Location for %s not found: %s", setting_name, sieve_before); } ret = 0; @@ -731,7 +753,7 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) scripts = array_get(&script_sequence, &count); for ( i = 0; i < count; i ++ ) { sieve_sys_debug(svinst, - "executed before user's personal Sieve script(%d): %s", + "Executed before user's personal Sieve script(%d): %s", i+1, sieve_script_location(scripts[i])); } } @@ -743,7 +765,7 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) if ( ret >= 0 && debug ) { sieve_sys_debug(svinst, - "using the following location for user's Sieve script: %s", + "Using the following location for user's Sieve script: %s", sieve_script_location(srctx->main_script)); } } @@ -757,12 +779,12 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) sieve_after = mail_user_plugin_getenv(mdctx->dest_user, setting_name); while ( sieve_after != NULL && *sieve_after != '\0' ) { ret = lda_sieve_multiscript_get_scripts(svinst, setting_name, - sieve_after, master_ehandler, &script_sequence, &error); + sieve_after, &script_sequence, &error); if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) { ret = -1; break; } else if (ret == 0 && debug ) { - sieve_sys_debug(svinst, "%s location not found: %s", + sieve_sys_debug(svinst, "Location for %s not found: %s", setting_name, sieve_after); } ret = 0; @@ -804,7 +826,7 @@ static int lda_sieve_execute if ( srctx->script_count == 0 ) { if ( debug ) { sieve_sys_debug(svinst, - "no scripts to execute: reverting to default delivery."); + "No scripts to execute: reverting to default delivery."); } /* No error, but no delivery by this plugin either. A return value of <= 0 diff --git a/src/sieve-tools/sievec.c b/src/sieve-tools/sievec.c index 616314e950d363a95878f5da386d8b29a15f04db..87396e7eecbce95068c4782bb6974dfc242b1968 100644 --- a/src/sieve-tools/sievec.c +++ b/src/sieve-tools/sievec.c @@ -11,7 +11,6 @@ #include "sieve.h" #include "sieve-extensions.h" #include "sieve-script.h" -#include "sieve-script-file.h" #include "sieve-tool.h" #include <stdio.h> @@ -113,7 +112,7 @@ int main(int argc, char **argv) break; } - if ( sieve_scriptfile_has_extension(dp->d_name) ) { + if ( sieve_script_file_has_extension(dp->d_name) ) { const char *file; if ( scriptfile[strlen(scriptfile)-1] == '/' ) diff --git a/src/testsuite/testsuite-binary.c b/src/testsuite/testsuite-binary.c index 99847d508e8de5216f52627ae1c561a68ca3188a..3a2cc1504cb597a8041ec20b4f41c7e0e9ab3d16 100644 --- a/src/testsuite/testsuite-binary.c +++ b/src/testsuite/testsuite-binary.c @@ -11,7 +11,6 @@ #include "sieve.h" #include "sieve-common.h" #include "sieve-script.h" -#include "sieve-script-file.h" #include "sieve-binary.h" #include "sieve-error.h" diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c index 7b3aa586203c43d3de0ae00a7127bd73ab50ef04..c8108253277346d2f0581e24b380e02cc9a3f945 100644 --- a/src/testsuite/testsuite-script.c +++ b/src/testsuite/testsuite-script.c @@ -6,7 +6,6 @@ #include "sieve.h" #include "sieve-common.h" #include "sieve-script.h" -#include "sieve-script-file.h" #include "sieve-binary.h" #include "sieve-interpreter.h" #include "sieve-runtime-trace.h" diff --git a/tests/extensions/include/execute.svtest b/tests/extensions/include/execute.svtest index 3d91423321258ccb4b52b485f31893fbc93fcb40..734ac66441060db6c7a35c0ece90b5aa9b532d18 100644 --- a/tests/extensions/include/execute.svtest +++ b/tests/extensions/include/execute.svtest @@ -44,25 +44,25 @@ test "Actions Fileinto" { } test "Namespace - file" { - if not test_script_compile "execute/namespace.sieve" { - test_fail "failed to compile sub-test"; - } + if not test_script_compile "execute/namespace.sieve" { + test_fail "failed to compile sub-test"; + } - if not test_script_run { - test_fail "failed to execute sub-test"; - } + if not test_script_run { + test_fail "failed to execute sub-test"; + } } test "Namespace - dict" { - test_config_set "sieve_dir" "dict:file:${tst.path}/included/namespace.dict"; - test_config_set "sieve_global_dir" "dict:file:${tst.path}/included-global/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_reload :extension "include"; - if not test_script_compile "execute/namespace.sieve" { - test_fail "failed to compile sub-test"; - } - - if not test_script_run { - test_fail "failed to execute sub-test"; - } + if not test_script_compile "execute/namespace.sieve" { + test_fail "failed to compile sub-test"; + } + + if not test_script_run { + test_fail "failed to execute sub-test"; + } }