diff --git a/INSTALL b/INSTALL index 3d41903d17b2f9941f014b588507d266b8332b05..43afdb3c9db7449a0dcf196052c2d99b330b1cbe 100644 --- a/INSTALL +++ b/INSTALL @@ -85,23 +85,21 @@ The Sieve interpreter recognizes the following configuration options in the plugin section of the config file (default values are shown if applicable): sieve = ~/.dovecot.sieve - The path to the user's main active script. + The location of the user's main active script. sieve_default = - The path to the default personal sieve script file, which gets executed ONLY - if user's private Sieve script does no exist, e.g. - /var/lib/dovecot/default.sieve. This is usually a global script, so sure to - pre-compile this script manually using the sievec command line tool, as - explained in the README file. - - sieve_global_path = - This is the DEPRECATED former name of the sieve_default setting. + The location of the default personal sieve script file, which gets executed + ONLY if user's private Sieve script does no exist, e.g. + /var/lib/dovecot/default.sieve. This is usually a global script, so be sure + to pre-compile this script manually using the sievec command line tool, as + explained in the README file. This setting used to be called + `sieve_global_path', but that name is now deprecated. sieve_global_dir = - Directory for :global include scripts for the Sieve include extension. + Location for :global include scripts for the Sieve include extension. - sieve_dir = ~/ - Directory for :personal include scripts for the Sieve include extension. + sieve_dir = ~/sieve + Location for :personal include scripts for the Sieve include extension. sieve_extensions = Which Sieve language extensions are available to users. By default, all @@ -131,6 +129,12 @@ plugin section of the config file (default values are shown if applicable): (wiki2.dovecot.org) or the pigeonhole website (http://pigeonhole.dovecot.org) for available plugins. + sieve_user_log = + The path to the file where the user log file is written. If not configured, a + default location is used. If the main user's personal Sieve (as configured + with sieve=) is a file, the logfile is set to <filename>.log by default. If + it is not a file, the default user log file is ~/.dovecot.sieve.log. + recipient_delimiter = + The separator that is expected between the :user and :detail address parts introduced by the subaddress extension. This may also be a sequence of @@ -176,7 +180,48 @@ Sieve Interpreter - Configurable Limits The maximum number of redirect actions that can be performed during a single script execution. If set to 0, no redirect actions are allowed. -Sieve Interpreter - Per-user Sieve script location +Sieve Interpreter - Script Locations +------------------------------------ + +The location of Sieve scripts is not limited to the file system. The Sieve +interpreter can be extended to retrieve Sieve scripts from other sources as +well, such as a database. Currently, all settings that are used to obtain the +location of a single Sieve script, such as sieve=, sieve_default=, sieve_dir= +and sieve_global_dir= accept the following extended syntax: + +location = [<type>:]path[;<option>[=<value>][;...]] + +The following script location types are implemented by default: + + file - The location path is a file system path pointing to the script file + or a directory containing script files with names structured as + `<script-name>.sieve'. + dict - Dovecot dict lookup. The location path is a dict uri. Read + doc/scipt-location-dict.txt for more information and examples. + +If the type prefix is omitted, the script location type is 'file'. + +The following options are defined for all location types: + + name=<script-name> + Set the name of the Sieve script that this location points to. If the name + of the Sieve script is not contained in the location path, this option is + required (e.g. for dict locations that must point to a particular script). + If the name of the script is contained in the location, the value of the + name option overrides the name retrieved from the location. If the Sieve + interpreter explicitly queries for a specific name (e.g. to include a script + from the sieve_dir= location), this option has no effect. + + bindir=<dirpath> + Points to the directory where the compiled binaries for this script location + are stored. If this option is omitted, the behavior depends on the location + type. For `file' type locations, the binary is then stored in the same + directory as where the script file was found if possible. For `dict' type + locations, the binary is not stored at all in that case. Don't specify the + same directory for different script locations, as this will result in + undefined behavior. + +Sieve Interpreter - Per-user Sieve Script Location -------------------------------------------------- By default, the Pigeonhole LDA Sieve plugin looks for the user's Sieve script @@ -184,8 +229,8 @@ file in the user's home directory (~/.dovecot.sieve). This requires that the home directory is set for the user. If you want to store the script elsewhere, you can override the default using -the sieve setting, which specifies the path to the user's script file. This can -be done in two ways: +the sieve= setting, which specifies the location of the user's script file. This +can be done in two ways: 1. Define the sieve setting in the plugin section of dovecot.conf. 2. Return sieve extra field from userdb extra fields. diff --git a/TODO b/TODO index 25704b2657de699d53efa68d7e519e6aa6415366..552647464d2f6404656aeeed3ddfeb788bb6e201 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,9 @@ Current activities: -* Update include extension to latest draft (v13 currently): - - Implement :optional tag. +* Implement generic Sieve script object that abstracts from its location and + add support for retrieving scripts from dict database. + - Implement infrastructure for loading a sequence of global scripts from + a database (for sieve_before/sieve_after). Parallel plugin-based efforts: @@ -14,6 +16,13 @@ Parallel plugin-based efforts: Next (mostly in order of descending priority/precedence): +* 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 +* Update include extension to latest draft (v13 currently): + - Implement :optional tag. * Implement index extension * Add normalize() method to comparators to normalize the string before matching (for efficiency). @@ -69,9 +78,6 @@ Next (mostly in order of descending priority/precedence): * 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. -* 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. * Add support for stream matching for handling large values, e.g. from the body extension. * Implement message modification and extraction API in order to: diff --git a/doc/script-location-dict.txt b/doc/script-location-dict.txt new file mode 100644 index 0000000000000000000000000000000000000000..3ee5a894d7f867e241874c7c70b3ef399451604a --- /dev/null +++ b/doc/script-location-dict.txt @@ -0,0 +1,145 @@ +DICT Sieve Script Location Type + +Description +=========== + +This location type is used to retrieve Sieve scripts from a Dovecot dict lookup. +Such dictionaries use a file or an SQL database as backend. Refer to the Dovecot +dict documentation for more information on dict lookups. + +To retrieve a Sieve script from the dict database, two lookups are performed. +First, the name of the Sieve script is queried from the dict path +`/priv/sieve/name/<name>'. If the Sieve script exists, this yields a data ID +which in turn points to the actual script text. The script text is subsequently +queried from the dict path '/priv/sieve/data/<dict-id>'. + +The second query is only necessary when no compiled binary is available or when +the script has changed and needs to be recompiled. The data ID is used to detect +changes in the dict's underlying database. Changing a Sieve script in the +database must be done by first making a new script data item with a new data ID. +Then, the mapping from name to data ID must be changed to point to the new +script text, thereby changing the data ID returned from the name lookup, i.e. +the first query mentioned above. Script binaries compiled from Sieve scripts +contained in a dict database record the data ID. While the data ID contained in +the binary is identical to the one returned from the dict lookup, the binary is +assumed up-to-date. When the returned data ID is different, the new script text +is retrieved using the second query and compiled into a new binary containing +the updated data ID. + +Note that, by default, compiled binaries are not stored at all for Sieve scripts +retrieved from a dict database. The bindir= option needs to be specified in the +location specification. Refer to the INSTALL file for more general information +about configuration of script locations. + +Configuration +============= + +The script location syntax is specialized as follows: + +location = dict:<dict-uri>[;<option>[=<value>][;...]] + +The following additional options are recognized: + + user=<username> + Overrides the user name used for the dict lookup. Normally, the name of the + user running the Sieve interpreter is used. + +If the name of the Script is left unspecified and not otherwise provided by the +Sieve interpreter, the name defaults to `default'. + +Examples +======== + +Example 1 +--------- + +This example is mainly useful for performing a quick test of the dict location +configuration without configuring an actual (SQL) database. For this example, a +very simple file dict is assumed to be contained in the file +/etc/dovecot/sieve.dict: + +priv/sieve/name/keep +1 +priv/sieve/name/discard +2 +priv/sieve/name/spam +3 +priv/sieve/data/1 +keep; +priv/sieve/data/2 +discard; +priv/sieve/data/3 +require ["fileinto", "mailbox"]; fileinto :create "spam"; + +To use this file dict for the main active script, you can change the +configuration as follows (e.g. in /etc/dovecot/conf.d/90-sieve.conf): + +plugin { + sieve = dict:file:/etc/dovecot/sieve.dict;name=keep;bindir=~/.sieve-bin +} + +The Sieve script named "keep" is retrieved from the file dict as the main +script. Binaries are stored in the ~/.sieve-bin directory. + +Example 2 +--------- + +This example uses a PostgreSQL database. Our database contains the following +table: + +CREATE TABLE user_sieve_scripts ( + id integer, + username varchar(40), + script_name varchar(256), + script_data varchar(10240), + + PRIMARY KEY (id), + UNIQUE(username, script_name) +); + +We create a file /etc/dovecot/dict-sieve-sql.conf with the following content: + +connect = host=localhost dbname=dovecot user=dovecot password=password +map { + pattern = priv/sieve/name/$script_name + table = user_sieve_scripts + username_field = username + value_field = id + fields { + script_name = $script_name + } +} +map { + pattern = priv/sieve/data/$id + table = user_sieve_scripts + username_field = username + value_field = script_data + fields { + id = $id + } +} + +These are the mappings used by the SQL dict. The first mapping is the name query +that yields the id of the Sieve script. The second mapping is the query used to +retrieve the Sieve script itself. + +Much like the dict configuration for mailbox quota, it is often not possible to +directly use an SQL dict because the SQL drivers are not linked to binaries such +as dovecot-lda and lmtp. You need to use the dict proxy service. Add the dict +URI to the dict section (typically located in your main dovecot.conf): + +dict { + sieve = pgsql:/etc/dovecot/dict-sieve-sql.conf.ext +} + +To use this SQL dict for the main active script, you can change the +configuration as follows (e.g. in /etc/dovecot/conf.d/90-sieve.conf): + +plugin { + sieve = dict:proxy::sieve;name=active;bindir=~/.sieve-bin +} + +This uses the proxy dict uri `proxy::sieve'. This refers to the `sieve =' entry +in the dict {...} section above. With this configuration, a Sieve script called +"main" is retrieved from the SQL dict. Binaries are stored in the ~/.sieve-bin +directory. diff --git a/m4/dovecot.m4 b/m4/dovecot.m4 index 02e6e015352b661232fa8fd366491b046aacc90d..3ce503692e4cec2e8fcf66025bb7c5a9a9b9527b 100644 --- a/m4/dovecot.m4 +++ b/m4/dovecot.m4 @@ -6,7 +6,7 @@ # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. -# serial 4 +# serial 5 AC_DEFUN([DC_DOVECOT_MODULEDIR],[ AC_ARG_WITH(moduledir, @@ -48,13 +48,13 @@ AC_DEFUN([DC_DOVECOT],[ AC_ARG_WITH(dovecot-install-dirs, [AC_HELP_STRING([--with-dovecot-install-dirs], - [Use install directories configured for Dovecot (default)])], - if test x$withval = xno; then - use_install_dirs=no - else - use_install_dirs=yes - fi, - use_install_dirs=yes) + [Use install directories configured for Dovecot (default)])], + if test x$withval = xno; then + use_install_dirs=no + else + use_install_dirs=yes + fi, + use_install_dirs=yes) AC_MSG_CHECKING([for dovecot-config in "$dovecotdir"]) if test -f "$dovecotdir/dovecot-config"; then @@ -72,7 +72,7 @@ AC_DEFUN([DC_DOVECOT],[ cd $old DISTCHECK_CONFIGURE_FLAGS="--with-dovecot=$abs_dovecotdir --without-dovecot-install-dirs" - eval `grep -i '^dovecot_[[a-z]]*=' "$dovecotdir"/dovecot-config` + eval `grep -i '^dovecot_[[a-z_]]*=' "$dovecotdir"/dovecot-config` eval `grep '^LIBDOVECOT[[A-Z_]]*=' "$dovecotdir"/dovecot-config` if test "$use_install_dirs" = "no"; then @@ -83,8 +83,8 @@ AC_DEFUN([DC_DOVECOT],[ dovecot_moduledir='$(moduledir)' fi - AX_SUBST_L([DISTCHECK_CONFIGURE_FLAGS], [dovecot_moduledir], [dovecot_pkgincludedir], [dovecot_pkglibexecdir], [dovecot_pkglibdir], [dovecot_docdir]) - AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS]) + AX_SUBST_L([DISTCHECK_CONFIGURE_FLAGS], [dovecotdir], [dovecot_moduledir], [dovecot_pkgincludedir], [dovecot_pkglibexecdir], [dovecot_pkglibdir], [dovecot_docdir]) + AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS], [DOVECOT_SQL_LIBS]) AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE]) AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS]) AX_SUBST_L([LIBDOVECOT_INCLUDE], [LIBDOVECOT_LDA_INCLUDE], [LIBDOVECOT_SERVICE_INCLUDE], [LIBDOVECOT_STORAGE_INCLUDE], [LIBDOVECOT_LOGIN_INCLUDE], [LIBDOVECOT_CONFIG_INCLUDE]) diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c index 2627e28cb0ee0694996e9140ef2e22ca9251582d..d242cb89c4f23e21ee5b646e8afed9f4333816f5 100644 --- a/src/lib-sieve-tool/sieve-tool.c +++ b/src/lib-sieve-tool/sieve-tool.c @@ -89,7 +89,7 @@ static const char *sieve_tool_sieve_get_homedir return sieve_tool_get_homedir(tool); } -const struct sieve_environment sieve_tool_sieve_env = { +const struct sieve_callbacks sieve_tool_callbacks = { sieve_tool_sieve_get_homedir, sieve_tool_sieve_get_setting }; @@ -223,6 +223,7 @@ struct sieve_instance *sieve_tool_init_finish MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS; struct mail_storage_service_input service_input; + struct sieve_environment svenv; const char *username = tool->username; const char *homedir = tool->homedir; const char *errstr; @@ -261,9 +262,15 @@ struct sieve_instance *sieve_tool_init_finish (master_service, "mail_full_filesystem_access=yes") < 0 ) i_unreached(); + memset((void *)&svenv, 0, sizeof(svenv)); + svenv.username = username; + (void)mail_user_get_home(tool->mail_user_dovecot, &svenv.home_dir); + svenv.hostname = "host.example.com"; + svenv.base_dir = tool->mail_user_dovecot->set->base_dir; + /* Initialize Sieve Engine */ - if ( (tool->svinst=sieve_init(&sieve_tool_sieve_env, tool, tool->debug)) - == NULL ) + if ( (tool->svinst=sieve_init + (&svenv, &sieve_tool_callbacks, tool, tool->debug)) == NULL ) i_fatal("failed to initialize sieve implementation"); /* Load Sieve plugins */ @@ -545,7 +552,7 @@ struct sieve_binary *sieve_tool_script_open sieve_error_handler_unref(&ehandler); - sieve_save(sbin, NULL, FALSE, NULL); + sieve_save(sbin, FALSE, NULL); return sbin; } diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 0b3321ce3ab2a4df40f38581e1506bc0a3183e47..5d4c163676282c875de2d7136c1779a3b7fbed57 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -80,6 +80,8 @@ libdovecot_sieve_la_SOURCES = \ sieve-smtp.c \ sieve-lexer.c \ sieve-script.c \ + sieve-script-file.c \ + sieve-script-dict.c \ sieve-ast.c \ sieve-binary.c \ sieve-binary-file.c \ @@ -126,6 +128,7 @@ headers = \ sieve-lexer.h \ sieve-script.h \ sieve-script-private.h \ + sieve-script-file.h \ sieve-ast.h \ sieve-binary.h \ sieve-binary-private.h \ diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c index 953080760707bb48816dc9c17042b93d1bb26a4f..f9279ded1ad052c4cd078a4b21bb90da74f9961b 100644 --- a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c +++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c @@ -465,7 +465,7 @@ static bool ntfy_mailto_send } msg = t_str_new(512); - outmsgid = sieve_message_get_new_id(senv); + outmsgid = sieve_message_get_new_id(nenv->svinst); rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); rfc2822_header_write(msg, "Message-ID", outmsgid); diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c index 0afd6c664f9e20a38d5c7d465c229a7bb26273ea..7657dc24c94ee2270d7e8030e45eda9f4e7f781d 100644 --- a/src/lib-sieve/plugins/environment/ext-environment-common.c +++ b/src/lib-sieve/plugins/environment/ext-environment-common.c @@ -103,8 +103,11 @@ const char *ext_environment_item_get_value if ( item->value != NULL ) return item->value; - if ( item->get_value != NULL ) - return item->get_value(senv); + if ( item->get_value != NULL ) { + const char *value = item->get_value(ext->svinst, senv); + + return ( value == NULL ? "" : value ); + } return NULL; } @@ -130,9 +133,10 @@ const struct sieve_environment_item domain_env_item = { * executing. */ -static const char *envit_host_get_value(const struct sieve_script_env *senv) +static const char *envit_host_get_value +(struct sieve_instance *svinst, const struct sieve_script_env *senv ATTR_UNUSED) { - return senv->hostname != NULL ? senv->hostname : ""; + return svinst->hostname; } const struct sieve_environment_item host_env_item = { diff --git a/src/lib-sieve/plugins/environment/sieve-ext-environment.h b/src/lib-sieve/plugins/environment/sieve-ext-environment.h index cb2ef1ea4f6c3975df5b72e38a1b3bfaee99ddf3..cede06d3f6e96f0d04365191ceb03d3b42412825 100644 --- a/src/lib-sieve/plugins/environment/sieve-ext-environment.h +++ b/src/lib-sieve/plugins/environment/sieve-ext-environment.h @@ -10,7 +10,8 @@ struct sieve_environment_item { const char *name; const char *value; - const char *(*get_value)(const struct sieve_script_env *senv); + const char *(*get_value) + (struct sieve_instance *svinst, const struct sieve_script_env *senv); }; void sieve_ext_environment_item_register diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c index 9e7ab2157615800823a4260c97a35de682883881..23193387bb17717e4a6fd5492fe85bb0a615abea 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -202,7 +202,7 @@ static bool cmd_include_validate struct cmd_include_context_data *ctx_data = (struct cmd_include_context_data *) cmd->data; struct sieve_script *script; - const char *script_path, *script_name; + const char *script_location, *script_name; enum sieve_error error = SIEVE_ERROR_NONE; bool include = TRUE; @@ -235,9 +235,9 @@ static bool cmd_include_validate return FALSE; } - script_path = ext_include_get_script_directory + script_location = ext_include_get_script_location (this_ext, ctx_data->location, script_name); - if ( script_path == NULL ) { + 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)", @@ -247,8 +247,8 @@ static bool cmd_include_validate } /* Create script object */ - script = sieve_script_create_in_directory - (this_ext->svinst, script_path, script_name, + script = sieve_script_create + (this_ext->svinst, script_location, script_name, sieve_validator_error_handler(valdtr), &error); if ( script == NULL ) { @@ -343,11 +343,12 @@ static bool opc_include_dump included = ext_include_binary_script_get_included(binctx, include_id); if ( included == NULL ) return FALSE; - + sieve_code_descend(denv); - sieve_code_dumpf(denv, "script: %s %s[ID: %d, BLOCK: %d]", - sieve_script_filename(included->script), (flags & 0x01 ? "(once) " : ""), - include_id, sieve_binary_block_get_id(included->block)); + sieve_code_dumpf(denv, "script: `%s' from %s %s[ID: %d, BLOCK: %d]", + sieve_script_name(included->script), sieve_script_location(included->script), + (flags & 0x01 ? "(once) " : ""), include_id, + sieve_binary_block_get_id(included->block)); return TRUE; } diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index dd0fb988e4af05e842cf4028f5a94149d304e859..da520c84b36d2707c9a446f17fb5eb007bd7962a 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -57,9 +57,10 @@ struct ext_include_binary_context { ARRAY_DEFINE(include_index, struct ext_include_script_info *); struct sieve_variable_scope_binary *global_vars; + + unsigned int outdated:1; }; - static struct ext_include_binary_context *ext_include_binary_create_context (const struct sieve_extension *this_ext, struct sieve_binary *sbin) { @@ -224,6 +225,7 @@ static bool ext_include_binary_save sieve_binary_emit_unsigned(sblock, sieve_binary_block_get_id(incscript->block)); sieve_binary_emit_byte(sblock, incscript->location); sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script)); + sieve_script_binary_write_metadata(incscript->script, sblock); } result = ext_include_variables_save(sblock, binctx->global_vars); @@ -270,8 +272,9 @@ static bool ext_include_binary_open struct sieve_binary_block *inc_block; unsigned int location; string_t *script_name; - const char *script_dir; + const char *script_location; struct sieve_script *script; + int ret; if ( !sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) || @@ -298,20 +301,33 @@ static bool ext_include_binary_open /* Binary is corrupt, recompile */ sieve_sys_error(svinst, "include: dependency block %d of binary %s " - "reports invalid script location (id %d)", + "uses invalid script location (id %d)", block_id, sieve_binary_path(sbin), location); return FALSE; } /* Can we find/open the script dependency ? */ - script_dir = ext_include_get_script_directory + script_location = ext_include_get_script_location (ext, location, str_c(script_name)); - if ( script_dir == NULL || - !(script=sieve_script_create_in_directory - (ext->svinst, script_dir, str_c(script_name), NULL, NULL)) ) { + if ( script_location == NULL || (script=sieve_script_create + (ext->svinst, script_location, str_c(script_name), NULL, NULL)) == NULL ) + { /* No, recompile */ return FALSE; } + + if ( (ret=sieve_script_binary_read_metadata(script, sblock, &offset)) + < 0 ) { + /* Binary is corrupt, recompile */ + sieve_sys_error(svinst, + "include: dependency block %d of binary %s " + "contains invalid script metadata for script %s", + block_id, sieve_binary_path(sbin), sieve_script_location(script)); + return FALSE; + } + + if ( ret == 0 ) + binctx->outdated = TRUE; (void)ext_include_binary_script_include (binctx, script, location, inc_block); @@ -327,29 +343,14 @@ static bool ext_include_binary_open } static bool ext_include_binary_up_to_date -(const struct sieve_extension *ext ATTR_UNUSED, struct sieve_binary *sbin, - void *context, enum sieve_compile_flags cpflags ATTR_UNUSED) +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context, + enum sieve_compile_flags cpflags ATTR_UNUSED) { struct ext_include_binary_context *binctx = (struct ext_include_binary_context *) context; - struct hash_iterate_context *hctx; - void *key, *value; - - /* Check all included scripts for changes */ - hctx = hash_table_iterate_init(binctx->included_scripts); - while ( hash_table_iterate(hctx, &key, &value) ) { - struct ext_include_script_info *incscript = - (struct ext_include_script_info *) value; - - /* Is the binary old than this dependency? */ - if ( sieve_binary_script_newer(sbin, incscript->script) ) { - /* No, recompile */ - return FALSE; - } - } - hash_table_iterate_deinit(&hctx); - return TRUE; + return !binctx->outdated; } static void ext_include_binary_free diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index e9d3bc22fb3645a73d923c3a3bd70698ab1b6b55..1c2e68a27e7f752f2eab2f6ff66d8e92b28d2cbe 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -76,13 +76,16 @@ bool ext_include_load { struct sieve_instance *svinst = ext->svinst; struct ext_include_context *ctx; - const char *sieve_dir, *home; + const char *sieve_dir; 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 */ @@ -98,23 +101,10 @@ bool ext_include_load /* Get directory for :personal scripts */ sieve_dir = sieve_setting_get(svinst, "sieve_dir"); - home = sieve_environment_get_homedir(svinst); - if ( sieve_dir == NULL ) { - if ( home == NULL ) { - if ( svinst->debug ) { - sieve_sys_debug(svinst, "include: sieve_dir is not set " - "and no home directory is set for the default `~/sieve'; " - "it is currently not possible to include `:personal' scripts."); - } - } else { - sieve_dir = "~/sieve"; - } + sieve_dir = "~/sieve"; } - if ( home != NULL ) - sieve_dir = home_expand_tilde(sieve_dir, home); - ctx->personal_dir = i_strdup(sieve_dir); /* Get limits */ @@ -154,7 +144,7 @@ void ext_include_unload * Script access */ -const char *ext_include_get_script_directory +const char *ext_include_get_script_location (const struct sieve_extension *ext, enum ext_include_script_location location, const char *script_name) { diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index 71b7435726e07a0ee553d676a263a70c116155de..d62503743f79c79bda4095a49202cb6eeb72db3f 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -87,7 +87,7 @@ extern const struct sieve_operation_def global_operation; * Script access */ -const char *ext_include_get_script_directory +const char *ext_include_get_script_location (const struct sieve_extension *ext, enum ext_include_script_location location, const char *script_name); diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c index c09644559a7472c783e95578eee5f4d4a7a361f8..35e564435bec019e505b488f1d93459ddf0dbb28 100644 --- a/src/lib-sieve/plugins/notify/cmd-notify.c +++ b/src/lib-sieve/plugins/notify/cmd-notify.c @@ -765,7 +765,7 @@ static bool act_notify_send str_truncate(msg, hdr_size); - outmsgid = sieve_message_get_new_id(senv); + outmsgid = sieve_message_get_new_id(aenv->svinst); rfc2822_header_write(msg, "Message-ID", outmsgid); rfc2822_header_write(msg, "To", recipients[i].full); diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c index b30f18b2c1197a2905bffbc4ad7aa13e9d360fda..cd1bc141b8e3067f55dd5b1bd41deebfdf022776 100644 --- a/src/lib-sieve/plugins/vacation/cmd-vacation.c +++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c @@ -935,7 +935,7 @@ static bool act_vacation_send /* Open smtp session */ smtp_handle = sieve_smtp_open(senv, reply_to, NULL, &output); - outmsgid = sieve_message_get_new_id(senv); + outmsgid = sieve_message_get_new_id(aenv->svinst); /* Produce a proper reply */ diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index e69312a6ed7f63a527641a6c82539daab677630a..b1832238aa56fceb42e338c07c5a304621f38067 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -723,8 +723,7 @@ int sieve_action_duplicate_check if ( senv->duplicate_check == NULL || senv->duplicate_mark == NULL) return 0; - return senv->duplicate_check - (senv->script_context, id, id_size, senv->username); + return senv->duplicate_check(senv, id, id_size); } void sieve_action_duplicate_mark @@ -734,8 +733,7 @@ void sieve_action_duplicate_mark if ( senv->duplicate_check == NULL || senv->duplicate_mark == NULL) return; - senv->duplicate_mark - (senv->script_context, id, id_size, senv->username, time); + senv->duplicate_mark(senv, id, id_size, time); } /* Rejecting the mail */ @@ -744,6 +742,7 @@ static bool sieve_action_do_reject_mail (const struct sieve_action_exec_env *aenv, const char *sender, const char *recipient, const char *reason) { + struct sieve_instance *svinst = aenv->svinst; const struct sieve_script_env *senv = aenv->scriptenv; const struct sieve_message_data *msgdata = aenv->msgdata; struct istream *input; @@ -763,8 +762,8 @@ static bool sieve_action_do_reject_mail smtp_handle = sieve_smtp_open(senv, sender, NULL, &output); - new_msgid = sieve_message_get_new_id(senv); - boundary = t_strdup_printf("%s/%s", my_pid, senv->hostname); + new_msgid = sieve_message_get_new_id(svinst); + boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname); hdr = t_str_new(512); rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); @@ -798,7 +797,7 @@ static bool sieve_action_do_reject_mail rfc2822_header_write(hdr, "Content-Type", "message/disposition-notification"); str_append(hdr, "\r\n"); rfc2822_header_write(hdr, "Reporting-UA: %s; Dovecot Mail Delivery Agent", - senv->hostname); + svinst->hostname); if (mail_get_first_header(msgdata->mail, "Original-Recipient", &header) > 0) rfc2822_header_printf(hdr, "Original-Recipient", "rfc822; %s", header); rfc2822_header_printf(hdr, "Final-Recipient", "rfc822; %s", recipient); diff --git a/src/lib-sieve/sieve-binary-file.c b/src/lib-sieve/sieve-binary-file.c index 16dfe0060aa523d50123d6c0b0c0d3cf3c40b689..f03bdd1cb8fbc6fd037200cf5e18cfb5ef1a003c 100644 --- a/src/lib-sieve/sieve-binary-file.c +++ b/src/lib-sieve/sieve-binary-file.c @@ -269,30 +269,15 @@ static bool _sieve_binary_save } int sieve_binary_save -(struct sieve_binary *sbin, const char *path, bool update, +(struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode, enum sieve_error *error_r) { int result, fd; string_t *temp_path; struct ostream *stream; - mode_t save_mode = - sbin->script == NULL ? 0600 : sieve_script_permissions(sbin->script); if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; - - /* Use default path if none is specified */ - if ( path == NULL ) { - if ( sbin->script == NULL ) { - sieve_sys_error(sbin->svinst, - "binary save: cannot determine default path " - "with missing script object"); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - return -1; - } - path = sieve_script_binpath(sbin->script); - } /* Check whether saving is necessary */ if ( !update && sbin->path != NULL && strcmp(sbin->path, path) == 0 ) { @@ -853,13 +838,11 @@ static bool _sieve_binary_open(struct sieve_binary *sbin) ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); if ( ext_block == NULL ) { result = FALSE; - } else { - if ( !_read_extensions(ext_block) ) { - sieve_sys_error(sbin->svinst, - "binary open: binary %s is corrupt: failed to load extension block", - sbin->path); - result = FALSE; - } + } else if ( !_read_extensions(ext_block) ) { + sieve_sys_error(sbin->svinst, + "binary open: binary %s is corrupt: failed to load extension block", + sbin->path); + result = FALSE; } } T_END; diff --git a/src/lib-sieve/sieve-binary.c b/src/lib-sieve/sieve-binary.c index 3f34efb31225e690209ea519ad0ded9ed9f40fb3..d6c730a1d55d3ea8184a9f1e8fecabb426ad101d 100644 --- a/src/lib-sieve/sieve-binary.c +++ b/src/lib-sieve/sieve-binary.c @@ -59,7 +59,7 @@ struct sieve_binary *sieve_binary_create p_array_init(&sbin->extensions, pool, ext_count); p_array_init(&sbin->extension_index, pool, ext_count); - p_array_init(&sbin->blocks, pool, 3); + p_array_init(&sbin->blocks, pool, 16); /* Pre-load core language features implemented as 'extensions' */ ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count); @@ -76,13 +76,18 @@ struct sieve_binary *sieve_binary_create struct sieve_binary *sieve_binary_create_new(struct sieve_script *script) { struct sieve_binary *sbin = sieve_binary_create - (sieve_script_svinst(script), script); - - /* Extensions block */ - (void) sieve_binary_block_create(sbin); + (sieve_script_svinst(script), script); + struct sieve_binary_block *sblock; + unsigned int i; - /* Main program block */ - (void) sieve_binary_block_create(sbin); + /* Create script metadata block */ + sblock = sieve_binary_block_create(sbin); + sieve_script_binary_write_metadata(script, sblock); + + /* Create other system blocks */ + for ( i = 1; i < SBIN_SYSBLOCK_LAST; i++ ) { + (void) sieve_binary_block_create(sbin); + } return sbin; } @@ -127,6 +132,10 @@ void sieve_binary_unref(struct sieve_binary **sbin) *sbin = NULL; } +/* + * Accessors + */ + pool_t sieve_binary_pool(struct sieve_binary *sbin) { return sbin->pool; @@ -155,7 +164,7 @@ bool sieve_binary_loaded(struct sieve_binary *sbin) const char *sieve_binary_source(struct sieve_binary *sbin) { if ( sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL) ) - return sieve_script_path(sbin->script); + return sieve_script_location(sbin->script); return sbin->path; } @@ -165,11 +174,11 @@ struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin) return sbin->svinst; } -bool sieve_binary_script_newer -(struct sieve_binary *sbin, struct sieve_script *script) +time_t sieve_binary_mtime +(struct sieve_binary *sbin) { i_assert(sbin->file != NULL); - return ( sieve_script_newer(script, sbin->file->st.st_mtime) ); + return sbin->file->st.st_mtime; } const char *sieve_binary_script_name(struct sieve_binary *sbin) @@ -177,9 +186,18 @@ const char *sieve_binary_script_name(struct sieve_binary *sbin) return ( sbin->script == NULL ? NULL : sieve_script_name(sbin->script) ); } -const char *sieve_binary_script_path(struct sieve_binary *sbin) +const char *sieve_binary_script_location(struct sieve_binary *sbin) +{ + return ( sbin->script == NULL ? NULL : sieve_script_location(sbin->script) ); +} + +/* + * Utility + */ + +const char *sieve_binfile_from_name(const char *name) { - return ( sbin->script == NULL ? NULL : sieve_script_path(sbin->script) ); + return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL); } /* @@ -294,12 +312,15 @@ bool sieve_binary_up_to_date (struct sieve_binary *sbin, enum sieve_compile_flags cpflags) { struct sieve_binary_extension_reg *const *regs; + struct sieve_binary_block *sblock; + sieve_size_t offset = 0; unsigned int ext_count, i; i_assert(sbin->file != NULL); - if ( sbin->script == NULL || sieve_script_newer - (sbin->script, sbin->file->st.st_mtime) ) + sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA); + if ( sblock == NULL || sbin->script == NULL || + sieve_script_binary_read_metadata(sbin->script, sblock, &offset) <= 0 ) return FALSE; regs = array_get(&sbin->extensions, &ext_count); diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h index b675013aed9fb3d4ba254972e5be248e3d6bf485..20d12cf894270b3a7ed13e5cfeb399a622965167 100644 --- a/src/lib-sieve/sieve-binary.h +++ b/src/lib-sieve/sieve-binary.h @@ -13,7 +13,7 @@ */ #define SIEVE_BINARY_VERSION_MAJOR 0 -#define SIEVE_BINARY_VERSION_MINOR 3 +#define SIEVE_BINARY_VERSION_MINOR 4 /* * Binary object @@ -34,15 +34,20 @@ struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin); const char *sieve_binary_path(struct sieve_binary *sbin); struct sieve_script *sieve_binary_script(struct sieve_binary *sbin); -bool sieve_binary_script_newer - (struct sieve_binary *sbin, struct sieve_script *script); +time_t sieve_binary_mtime(struct sieve_binary *sbin); const char *sieve_binary_script_name(struct sieve_binary *sbin); -const char *sieve_binary_script_path(struct sieve_binary *sbin); +const char *sieve_binary_script_location(struct sieve_binary *sbin); const char *sieve_binary_source(struct sieve_binary *sbin); bool sieve_binary_loaded(struct sieve_binary *sbin); bool sieve_binary_saved(struct sieve_binary *sbin); +/* + * Utility + */ + +const char *sieve_binfile_from_name(const char *name); + /* * Activation after code generation */ @@ -54,8 +59,8 @@ void sieve_binary_activate(struct sieve_binary *sbin); */ int sieve_binary_save -(struct sieve_binary *sbin, const char *path, bool update, - enum sieve_error *error_r); + (struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode, + enum sieve_error *error_r); /* * Loading the binary @@ -72,6 +77,7 @@ bool sieve_binary_up_to_date */ enum sieve_binary_system_block { + SBIN_SYSBLOCK_SCRIPT_DATA, SBIN_SYSBLOCK_EXTENSIONS, SBIN_SYSBLOCK_MAIN_PROGRAM, SBIN_SYSBLOCK_LAST diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index f7f9f500adfb3389a74dff3c60c7eafefd5c8a52..92ad7b9069cbc2ab5537bf3cc94e37fe615b4a51 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -155,8 +155,19 @@ struct sieve_instance { /* Main engine pool */ pool_t pool; + /* System environment */ + const char *hostname; + const char *base_dir; + + /* User environment */ + const char *username; + const char *home_dir; + + /* Flags */ + enum sieve_flag flags; + /* Callbacks */ - const struct sieve_environment *env; + const struct sieve_callbacks *callbacks; void *context; /* Engine debug */ diff --git a/src/lib-sieve/sieve-message.c b/src/lib-sieve/sieve-message.c index 3d7e90ccbb6f528ec6a1bcb8ae977bf6eb53757c..242611dc775344c1ff7d2f8048bd432ad8e40ec2 100644 --- a/src/lib-sieve/sieve-message.c +++ b/src/lib-sieve/sieve-message.c @@ -30,14 +30,13 @@ * Message transmission */ -const char *sieve_message_get_new_id -(const struct sieve_script_env *senv) +const char *sieve_message_get_new_id(const struct sieve_instance *svinst) { static int count = 0; return t_strdup_printf("<dovecot-sieve-%s-%s-%d@%s>", dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec), - count++, senv->hostname); + count++, svinst->hostname); } /* diff --git a/src/lib-sieve/sieve-message.h b/src/lib-sieve/sieve-message.h index deb6063d83790d3effd43278f7540cf55e7c23b1..4fc1549b6bd424c0dd1e32dc2af73e7b19d71314 100644 --- a/src/lib-sieve/sieve-message.h +++ b/src/lib-sieve/sieve-message.h @@ -8,8 +8,7 @@ * Message transmission */ -const char *sieve_message_get_new_id - (const struct sieve_script_env *senv); +const char *sieve_message_get_new_id(const struct sieve_instance *svinst); /* * Message context diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c new file mode 100644 index 0000000000000000000000000000000000000000..d761155ac04aff8a082f9484e9e3afc899f33ae5 --- /dev/null +++ b/src/lib-sieve/sieve-script-dict.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2002-2012 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-script-private.h" + +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; +}; + +#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" + +/* + * Script file implementation + */ + +static struct sieve_script *sieve_dict_script_alloc(void) +{ + struct sieve_dict_script *script; + pool_t pool; + + pool = pool_alloconly_create("sieve_dict_script", 1024); + script = p_new(pool, struct sieve_dict_script, 1); + script->script = sieve_dict_script; + script->script.pool = pool; + + return &script->script; +} + +static int sieve_dict_script_create +(struct sieve_script *_script, const char *data, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + struct sieve_instance *svinst = _script->svinst; + struct sieve_error_handler *ehandler = _script->ehandler; + const char *username = NULL, *name = _script->name; + const char *path; + int ret; + + if ( options != NULL ) { + while ( *options != NULL ) { + const char *option = *options; + + if ( strncasecmp(option, "user=", 5) == 0 && option[5] != '\0' ) { + username = option+5; + } else { + sieve_critical(svinst, ehandler, NULL, "failed to open sieve script", + "sieve dict backend: invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAIL; + return -1; + } + + options++; + } + } + + if ( name == NULL ) { + name = _script->name = SIEVE_DICT_SCRIPT_DEFAULT; + } + + if ( username == NULL ) { + if ( svinst->username == NULL ) { + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "sieve dict backend: no username specified"); + *error_r = SIEVE_ERROR_TEMP_FAIL; + return -1; + } + username = svinst->username; + } + + if ( svinst->base_dir == NULL ) { + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "sieve dict backend: BUG: Sieve interpreter is initialized without " + "a base_dir"); + *error_r = SIEVE_ERROR_TEMP_FAIL; + return -1; + } + + if ( _script->svinst->debug ) { + sieve_sys_debug(_script->svinst, "sieve dict backend: " + "user=%s, uri=%s, script=%s", username, data, name); + } + + script->dict = dict_init + (data, DICT_DATA_TYPE_STRING, username, svinst->base_dir); + if ( script->dict == NULL ) { + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "sieve dict backend: failed to initialize dict with data `%s' " + "for user `%s'", data, username); + *error_r = SIEVE_ERROR_TEMP_FAIL; + dict_deinit(&script->dict); + return -1; + } + + path = t_strconcat + (DICT_SIEVE_NAME_PATH, dict_escape_string(name), NULL); + + ret = dict_lookup + (script->dict, script->script.pool, path, &script->data_id); + if ( ret <= 0 ) { + if ( ret < 0 ) { + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "sieve dict backend: failed to lookup script id from path %s", path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + } else { + if ( svinst->debug ) { + sieve_sys_debug(svinst, "sieve dict backend: " + "script `%s' not found at path %s", name, path); + } + *error_r = SIEVE_ERROR_NOT_FOUND; + } + + dict_deinit(&script->dict); + return -1; + } + + if ( _script->bin_dir != NULL ) { + script->binpath = p_strconcat(_script->pool, _script->bin_dir, "/", + sieve_binfile_from_name(name), NULL); + } + + if ( strcmp(name, SIEVE_DICT_SCRIPT_DEFAULT) == 0 ) { + _script->location = p_strconcat(_script->pool, + SIEVE_DICT_SCRIPT_DRIVER_NAME, ":", data, NULL); + } else { + _script->location = p_strconcat(_script->pool, + SIEVE_DICT_SCRIPT_DRIVER_NAME, ":", data, ";name=", name, NULL); + } + + return 0; +} + +static void sieve_dict_script_destroy(struct sieve_script *_script) +{ + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + + if ( script->dict != NULL ) + dict_deinit(&script->dict); + + if ( script->data_pool != NULL ) + pool_unref(&script->data_pool); +} + +static struct istream *sieve_dict_script_open +(struct sieve_script *_script, enum sieve_error *error_r) +{ + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + struct sieve_instance *svinst = _script->svinst; + struct sieve_error_handler *ehandler = _script->ehandler; + const char *path, *name = _script->name; + int ret; + + script->data_pool = + pool_alloconly_create("sieve_dict_script data pool", 1024); + + path = t_strconcat + (DICT_SIEVE_DATA_PATH, dict_escape_string(script->data_id), NULL); + + ret = dict_lookup + (script->dict, script->data_pool, path, &script->data); + if ( ret <= 0 ) { + if ( ret < 0 ) { + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "sieve dict backend: failed to lookup data with id `%s' " + "for script `%s' from path %s", script->data_id, name, + path); + } else { + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "sieve dict backend: data with id `%s' for script `%s' " + "not found at path %s", script->data_id, name, path); + } + *error_r = SIEVE_ERROR_TEMP_FAIL; + return NULL; + } + + return i_stream_create_from_data(script->data, strlen(script->data)); +} + +static void sieve_dict_script_close(struct sieve_script *_script) +{ + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + + pool_unref(&script->data_pool); + + script->data_pool = NULL; + script->data_id = NULL; + script->data = NULL; +} + +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 *script = (struct sieve_dict_script *)_script; + struct sieve_instance *svinst = _script->svinst; + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + string_t *data_id; + + if ( !sieve_binary_read_string(sblock, offset, &data_id) ) { + sieve_sys_error(svinst, + "sieve dict script: binary %s has invalid metadata for script %s", + sieve_binary_path(sbin), sieve_script_location(_script)); + return -1; + } + + if ( strcmp(str_c(data_id), script->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 *script = (struct sieve_dict_script *)_script; + + sieve_binary_emit_cstring(sblock, script->data_id); +} + +static struct sieve_binary *sieve_dict_script_binary_load +(struct sieve_script *_script, enum sieve_error *error_r) +{ + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + + if (script->binpath == NULL) + return NULL; + + return sieve_binary_open(_script->svinst, script->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 *script = (struct sieve_dict_script *)_script; + + if (script->binpath == NULL) + return 0; + + if ( sieve_script_setup_bindir(_script, 0700) < 0 ) + return -1; + + return sieve_binary_save(sbin, script->binpath, update, 0600, error_r); +} + +const struct sieve_script sieve_dict_script = { + .driver_name = SIEVE_DICT_SCRIPT_DRIVER_NAME, + .v = { + sieve_dict_script_alloc, + sieve_dict_script_create, + sieve_dict_script_destroy, + + sieve_dict_script_open, + sieve_dict_script_close, + + sieve_dict_script_binary_read_metadata, + sieve_dict_script_binary_write_metadata, + sieve_dict_script_binary_load, + sieve_dict_script_binary_save, + + NULL, NULL + } +}; diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c new file mode 100644 index 0000000000000000000000000000000000000000..1ba30ef22f6f84e49ce39e807f5bfe0504b21ca6 --- /dev/null +++ b/src/lib-sieve/sieve-script-file.c @@ -0,0 +1,397 @@ +/* Copyright (c) 2002-2012 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "strfuncs.h" +#include "abspath.h" +#include "eacces-error.h" +#include "istream.h" +#include "home-expand.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-binary.h" +#include "sieve-script-private.h" +#include "sieve-script-file.h" + +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> + +/* + * Configuration + */ + +#define SIEVE_FILE_READ_BLOCK_SIZE (1024*8) + +/* + * Filename to name/name to filename + */ + +const char *sieve_scriptfile_get_script_name(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_scriptfile_has_extension(const char *filename) +{ + return ( sieve_scriptfile_get_script_name(filename) != NULL ); +} + +const char *sieve_scriptfile_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_script *script, const char *path, const char *name, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = script->svinst; + struct sieve_error_handler *ehandler = script->ehandler; + + switch ( errno ) { + case ENOENT: + if ( svinst->debug ) + sieve_sys_debug(svinst, "script file %s not found", t_abspath(path)); + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "failed to stat sieve script: %s", eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERM; + break; + default: + sieve_critical(svinst, ehandler, name, "failed to open sieve script", + "failed to stat sieve script: stat(%s) failed: %m", path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + break; + } +} + +/* + * Script file implementation + */ + +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 struct sieve_script *sieve_file_script_alloc(void) +{ + struct sieve_file_script *script; + pool_t pool; + + pool = pool_alloconly_create("sieve_file_script", 1024); + script = p_new(pool, struct sieve_file_script, 1); + script->script = sieve_file_script; + script->script.pool = pool; + + return &script->script; +} + +static int sieve_file_script_create +(struct sieve_script *_script, const char *path, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_file_script *script = (struct sieve_file_script *)_script; + struct sieve_instance *svinst = _script->svinst; + struct sieve_error_handler *ehandler = _script->ehandler; + pool_t pool = _script->pool; + const char *name = _script->name; + const char *filename, *dirpath, *basename, *binpath; + struct stat st; + struct stat lnk_st; + bool success = TRUE; + int ret; + + if ( options != NULL && *options != NULL ) { + const char *option = *options; + + sieve_critical(svinst, ehandler, NULL, "failed to open sieve script", + "sieve file backend: invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAIL; + return -1; + } + + T_BEGIN { + 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_critical(svinst, ehandler, NULL, + "failed to open sieve script", + "sieve script file path %s is relative to home directory, " + "but home directory is not available.", path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + success = FALSE; + } + } + + if ( success && (ret=sieve_file_script_stat(path, &st, &lnk_st)) > 0 ) { + if ( S_ISDIR(st.st_mode) ) { + /* Path is directory; name is used to find actual file */ + if (name == 0 || *name == '\0') { + sieve_critical(svinst, ehandler, NULL, + "failed to open sieve script", + "sieve script file path '%s' is a directory.", path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + success = FALSE; + } else { + /* Extend path with filename */ + filename = sieve_scriptfile_from_name(name); + basename = name; + dirpath = path; + + if ( path[strlen(path)-1] == '/' ) + path = t_strconcat(dirpath, filename, NULL); + else + path = t_strconcat(dirpath, "/", filename , NULL); + + 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_scriptfile_get_script_name(filename)) == NULL ) + basename = filename; + + if ( name == NULL ) + name = basename; + } + } else { + basename = name; + } + + if ( success ) { + if ( ret <= 0 ) { + sieve_file_script_handle_error(_script, path, name, error_r); + success = FALSE; + } else if (!S_ISREG(st.st_mode) ) { + sieve_critical(svinst, ehandler, name, + "failed to open sieve script", + "sieve script file '%s' is not a regular file.", path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + success = FALSE; + } + } + + if ( success ) { + if ( _script->bin_dir != NULL ) { + binpath = sieve_binfile_from_name(name); + binpath = t_strconcat(_script->bin_dir, "/", binpath, NULL); + } else { + binpath = sieve_binfile_from_name(basename); + if ( *dirpath != '\0' ) + binpath = t_strconcat(dirpath, "/", binpath, NULL); + } + + script->st = st; + script->lnk_st = lnk_st; + script->path = p_strdup(pool, path); + script->filename = p_strdup(pool, filename); + script->dirpath = p_strdup(pool, dirpath); + script->binpath = p_strdup(pool, binpath); + + if ( script->script.name == NULL || + strcmp(script->script.name, basename) == 0 ) + script->script.location = script->path; + else + script->script.location = p_strconcat + (pool, script->path, ";name=", script->script.name, NULL); + + if ( script->script.name == NULL ) + script->script.name = p_strdup(pool, basename); + } + } T_END; + + return ( success ? 0 : -1 ); +} + +static struct istream *sieve_file_script_open +(struct sieve_script *_script, enum sieve_error *error_r) +{ + struct sieve_file_script *script = (struct sieve_file_script *)_script; + struct sieve_instance *svinst = _script->svinst; + struct sieve_error_handler *ehandler = _script->ehandler; + const char *name = _script->name; + struct stat st; + struct istream *result; + int fd; + + if ( (fd=open(script->path, O_RDONLY)) < 0 ) { + sieve_file_script_handle_error(_script, script->path, name, error_r); + return NULL; + } + + if ( fstat(fd, &st) != 0 ) { + sieve_critical(svinst, ehandler, name, + "failed to open sieve script", + "failed to open sieve script: fstat(fd=%s) failed: %m", script->path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + result = NULL; + } else { + /* Re-check the file type just to be sure */ + if ( !S_ISREG(st.st_mode) ) { + sieve_critical(svinst, ehandler, name, + "failed to open sieve script", + "sieve script file '%s' is not a regular file", script->path); + *error_r = SIEVE_ERROR_TEMP_FAIL; + result = NULL; + } else { + result = i_stream_create_fd(fd, SIEVE_FILE_READ_BLOCK_SIZE, TRUE); + script->st = script->lnk_st = st; + } + } + + if ( result == NULL ) { + /* Something went wrong, close the fd */ + if ( close(fd) != 0 ) { + sieve_sys_error(svinst, + "failed to close sieve script: close(fd=%s) failed: %m", script->path); + } + } + + return result; +} + +static int sieve_file_script_get_size +(const struct sieve_script *_script, uoff_t *size_r) +{ + struct sieve_file_script *script = (struct sieve_file_script *)_script; + + *size_r = script->st.st_size; + return 1; +} + +static bool sieve_file_script_equals +(const struct sieve_script *_script, const struct sieve_script *_other) +{ + struct sieve_file_script *script = (struct sieve_file_script *)_script; + struct sieve_file_script *other = (struct sieve_file_script *)_other; + + if ( script == NULL || other == NULL ) + return -1; + + return ( script->st.st_ino == other->st.st_ino ); +} + +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 *script = (struct sieve_file_script *)_script; + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + time_t time = ( script->st.st_mtime > script->lnk_st.st_mtime ? + script->st.st_mtime : script->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 *script = (struct sieve_file_script *)_script; + + return sieve_binary_open(_script->svinst, script->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_file_script *script = (struct sieve_file_script *)_script; + + if ( sieve_script_setup_bindir(_script, 0700) < 0 ) + return -1; + + return sieve_binary_save(sbin, script->binpath, update, + script->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), error_r); +} + +const struct sieve_script sieve_file_script = { + .driver_name = SIEVE_FILE_SCRIPT_DRIVER_NAME, + .v = { + sieve_file_script_alloc, + sieve_file_script_create, + NULL, + + sieve_file_script_open, + NULL, + + sieve_file_script_binary_read_metadata, + NULL, + sieve_file_script_binary_load, + sieve_file_script_binary_save, + + sieve_file_script_get_size, + + sieve_file_script_equals + } +}; + +const char *sieve_file_script_get_dirpath +(const struct sieve_script *_script) +{ + struct sieve_file_script *script = (struct sieve_file_script *)_script; + + if ( _script->driver_name != sieve_file_script.driver_name ) + return NULL; + + return script->dirpath; +} + +const char *sieve_file_script_get_path +(const struct sieve_script *_script) +{ + struct sieve_file_script *script = (struct sieve_file_script *)_script; + + if ( _script->driver_name != sieve_file_script.driver_name ) + return NULL; + + return script->path; +} + diff --git a/src/lib-sieve/sieve-script-file.h b/src/lib-sieve/sieve-script-file.h new file mode 100644 index 0000000000000000000000000000000000000000..c71fa87eeffd66271397c4d69f8acd0e944cef42 --- /dev/null +++ b/src/lib-sieve/sieve-script-file.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2002-2012 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 970484831f5ebdf90877a20bc671c7dd81af93f5..49e1ca3720839a002036d291e072f360fa995c07 100644 --- a/src/lib-sieve/sieve-script-private.h +++ b/src/lib-sieve/sieve-script-private.h @@ -10,34 +10,93 @@ * Script object */ +struct sieve_script_vfuncs { + struct sieve_script *(*alloc)(void); + int (*create) + (struct sieve_script *script, const char *data, const char *const *options, + enum sieve_error *error_r); + void (*destroy) + (struct sieve_script *script); + + struct istream *(*open) + (struct sieve_script *script, enum sieve_error *error_r); + void (*close) + (struct sieve_script *script); + + int (*binary_read_metadata) + (struct sieve_script *_script, struct sieve_binary_block *sblock, + sieve_size_t *offset); + void (*binary_write_metadata) + (struct sieve_script *script, struct sieve_binary_block *sblock); + struct sieve_binary *(*binary_load) + (struct sieve_script *script, enum sieve_error *error_r); + int (*binary_save) + (struct sieve_script *script, struct sieve_binary *sbin, + bool update, enum sieve_error *error_r); + + int (*get_size) + (const struct sieve_script *script, uoff_t *size_r); + + bool (*equals) + (const struct sieve_script *script, const struct sieve_script *other); +}; + struct sieve_script { pool_t pool; unsigned int refcount; + 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 *location; + const char *bin_dir; + + /* Stream */ + struct istream *stream; +}; + +struct sieve_script *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, + enum sieve_error *error_r); + +int sieve_script_setup_bindir + (struct sieve_script *script, mode_t mode); + +/* + * Built-in file script driver + */ + +#define SIEVE_FILE_SCRIPT_DRIVER_NAME "file" + +struct sieve_file_script { + struct sieve_script script; struct stat st; struct stat lnk_st; - time_t mtime; - struct sieve_error_handler *ehandler; - - /* Parameters */ - const char *name; - const char *basename; - const char *filename; const char *path; const char *dirpath; + const char *filename; const char *binpath; - /* Stream */ - int fd; /* FIXME: we could use the stream's autoclose facility */ - struct istream *stream; + int fd; }; -struct sieve_script *sieve_script_init -(struct sieve_script *script, struct sieve_instance *svinst, - const char *path, const char *name, struct sieve_error_handler *ehandler, - enum sieve_error *error_r); +extern const struct sieve_script sieve_file_script; + +/* + * Built-in dict script driver + */ + +#define SIEVE_DICT_SCRIPT_DRIVER_NAME "dict" + +extern const struct sieve_script sieve_dict_script; #endif /* __SIEVE_SCRIPT_PRIVATE_H */ diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index a529165165a7b71c8ae0b4386b7e699384f67023..b5017d604b840a1d820d34bcf0ec5998470b8b49 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -4,26 +4,22 @@ #include "lib.h" #include "compat.h" #include "unichar.h" +#include "str.h" +#include "hash.h" #include "array.h" -#include "abspath.h" -#include "istream.h" +#include "home-expand.h" +#include "mkdir-parents.h" #include "eacces-error.h" +#include "istream.h" #include "sieve-common.h" #include "sieve-limits.h" +#include "sieve-settings.h" #include "sieve-error.h" +#include "sieve-binary.h" #include "sieve-script-private.h" - -#include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> - -/* - * Configuration - */ - -#define SIEVE_READ_BLOCK_SIZE (1024*8) +#include "sieve-script-file.h" /* * Script name @@ -92,201 +88,202 @@ bool sieve_script_name_is_valid(const char *scriptname) return TRUE; } -/* - * Filename to name/name to filename +/* + * Script object */ -const char *sieve_scriptfile_get_script_name(const char *filename) +static const char *split_next_arg(const char *const **_args) { - 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); -} + const char *const *args = *_args; + const char *str = args[0]; -bool sieve_scriptfile_has_extension(const char *filename) -{ - return ( sieve_scriptfile_get_script_name(filename) != NULL ); -} + /* join arguments for escaped ";" separator */ -const char *sieve_scriptfile_from_name(const char *name) -{ - return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL); + 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; } -const char *sieve_binfile_from_name(const char *name) +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) { - return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL); -} + ARRAY_TYPE(const_string) options; + const char *const *tmp; + if (*data == '\0') { + *options_r = NULL; + *location_r = data; + return TRUE; + } -/* - * Common error handling - */ + /* <location> */ + tmp = t_strsplit(data, ";"); + *location_r = split_next_arg(&tmp); -static void sieve_script_handle_file_error -(struct sieve_instance *svinst, const char *path, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r) -{ - switch ( errno ) { - case ENOENT: - if ( svinst->debug ) - sieve_sys_debug(svinst, "script file %s not found", t_abspath(path)); - if ( error_r == NULL ) - sieve_error(ehandler, name, "sieve script does not exist"); - else { - *error_r = SIEVE_ERROR_NOT_FOUND; + 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); + } } - break; - case EACCES: - sieve_critical(svinst, ehandler, name, "failed to open sieve script", - "failed to stat sieve script: %s", eacces_error_get("stat", path)); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERM; - break; - default: - sieve_critical(svinst, ehandler, name, "failed to open sieve script", - "failed to stat sieve script: stat(%s) failed: %m", path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; - break; + + (void)array_append_space(&options); + *options_r = array_idx(&options, 0); } + + return TRUE; } -/* - * Script object - */ - struct sieve_script *sieve_script_init -(struct sieve_script *script, struct sieve_instance *svinst, - const char *path, const char *name, struct sieve_error_handler *ehandler, - enum sieve_error *error_r) +(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, enum sieve_error *error_r) { - int ret; - pool_t pool; - struct stat st; - struct stat lnk_st; - const char *filename, *dirpath, *basename, *binpath; + 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; - T_BEGIN { - - /* Extract filename from path */ + script->script_class = script_class; + script->refcount = 1; + script->svinst = svinst; - filename = strrchr(path, '/'); - if ( filename == NULL ) { - dirpath = ""; - filename = path; - } else { - dirpath = t_strdup_until(path, filename); - filename++; - } + script->ehandler = ehandler; + + script->name = p_strdup_empty(script->pool, name); + + if ( !sieve_script_location_parse + (script, data, &location, &options, &parse_error) ) { + sieve_critical(svinst, ehandler, NULL, + "failed to access sieve script", "failed to parse script location: %s", + parse_error); + *error_r = SIEVE_ERROR_TEMP_FAIL; + return NULL; + } - if ( (basename=sieve_scriptfile_get_script_name(filename)) == NULL ) - basename = filename; - - binpath = sieve_binfile_from_name(basename); - if ( *dirpath != '\0' ) - binpath = t_strconcat(dirpath, "/", binpath, NULL); - - if ( name == NULL ) { - name = basename; - } else if ( *name == '\0' ) { - name = NULL; - } else { - basename = name; - } - - /* First obtain stat data from the system */ - - if ( (ret=lstat(path, &st)) < 0 ) { - sieve_script_handle_file_error(svinst, path, basename, ehandler, error_r); - script = NULL; - ret = 1; + if ( script->v.create(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 { - /* Record stat information from the symlink */ - lnk_st = st; - - /* Only create/init the object if it stat()s without problems */ - if ( S_ISLNK(st.st_mode) && (ret=stat(path, &st)) < 0 ) { - sieve_script_handle_file_error - (svinst, path, basename, ehandler, error_r); - script = NULL; - ret = 1; - } - - if ( ret == 0 && !S_ISREG(st.st_mode) ) { - sieve_critical(svinst, ehandler, basename, - "failed to open sieve script", - "sieve script file '%s' is not a regular file.", path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - script = NULL; - ret = 1; - } + *error_r = error; } + return NULL; + } - if ( ret <= 0 ) { - if ( script == NULL ) { - pool = pool_alloconly_create("sieve_script", 1024); - script = p_new(pool, struct sieve_script, 1); - script->pool = pool; - } else - pool = script->pool; - - script->refcount = 1; - script->svinst = svinst; - - script->ehandler = ehandler; - sieve_error_handler_ref(ehandler); - - script->st = st; - script->lnk_st = lnk_st; - script->path = p_strdup(pool, path); - script->filename = p_strdup(pool, filename); - script->dirpath = p_strdup(pool, dirpath); - script->binpath = p_strdup(pool, binpath); - script->basename = p_strdup(pool, basename); - - if ( name != NULL ) - script->name = p_strdup(pool, name); - else - script->name = NULL; - } - } T_END; + i_assert( script->location != NULL ); + sieve_error_handler_ref(ehandler); return script; } struct sieve_script *sieve_script_create -(struct sieve_instance *svinst, const char *path, const char *name, +(struct sieve_instance *svinst, const char *location, const char *name, struct sieve_error_handler *ehandler, enum sieve_error *error_r) { - return sieve_script_init(NULL, svinst, path, name, ehandler, error_r); + 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 ) + i_error("Unknown sieve script driver module: %s", driver); + } T_END; + } + + script = script_class->v.alloc(); + if ( sieve_script_init + (script, svinst, script_class, data, name, ehandler, error_r) == NULL ) { + pool_unref(&script->pool); + return NULL; + } + + return script; } -struct sieve_script *sieve_script_create_in_directory -(struct sieve_instance *svinst, const char *dirpath, const char *name, +struct sieve_script *sieve_script_create_as +(struct sieve_instance *svinst, const char *location, const char *name, struct sieve_error_handler *ehandler, enum sieve_error *error_r) { - const char *path; + struct sieve_script *script; - if ( dirpath[strlen(dirpath)-1] == '/' ) - path = t_strconcat(dirpath, - sieve_scriptfile_from_name(name), NULL); - else - path = t_strconcat(dirpath, "/", - sieve_scriptfile_from_name(name), NULL); + if ( (script=sieve_script_create(svinst, location, NULL, ehandler, error_r)) + == NULL ) + return NULL; - return sieve_script_init(NULL, svinst, path, name, ehandler, error_r); + /* override name */ + script->name = p_strdup(script->pool, name); + return script; } void sieve_script_ref(struct sieve_script *script) @@ -294,25 +291,30 @@ void sieve_script_ref(struct sieve_script *script) script->refcount++; } -void sieve_script_unref(struct sieve_script **script) +void sieve_script_unref(struct sieve_script **_script) { - i_assert((*script)->refcount > 0); + struct sieve_script *script = *_script; - if (--(*script)->refcount != 0) + i_assert(script->refcount > 0); + + if (--script->refcount != 0) return; - if ( (*script)->stream != NULL ) - i_stream_destroy(&(*script)->stream); + if ( script->stream != NULL ) + i_stream_unref(&script->stream); - sieve_error_handler_unref(&(*script)->ehandler); + if ( script->ehandler != NULL ) + sieve_error_handler_unref(&script->ehandler); - pool_unref(&(*script)->pool); + if ( script->v.destroy != NULL ) + script->v.destroy(script); - *script = NULL; + pool_unref(&script->pool); + *_script = NULL; } /* - * Accessors + * Properties */ const char *sieve_script_name(const struct sieve_script *script) @@ -320,127 +322,226 @@ const char *sieve_script_name(const struct sieve_script *script) return script->name; } -const char *sieve_script_filename(const struct sieve_script *script) +const char *sieve_script_location(const struct sieve_script *script) { - return script->filename; + return script->location; } -const char *sieve_script_path(const struct sieve_script *script) -{ - return script->path; -} - -const char *sieve_script_dirpath(const struct sieve_script *script) +struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) { - return script->dirpath; + return script->svinst; } -const char *sieve_script_binpath(const struct sieve_script *script) +int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) { - return script->binpath; -} + int ret; -mode_t sieve_script_permissions(const struct sieve_script *script) -{ - return script->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); -} + if ( script->v.get_size != NULL ) { + if ( (ret=script->v.get_size(script, size_r)) != 0) + return ret; + } -struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) -{ - return script->svinst; -} + /* Try getting size from the stream */ + if ( script->stream == NULL && sieve_script_open(script, NULL) == NULL ) + return -1; -size_t sieve_script_size(const struct sieve_script *script) -{ - return script->st.st_size; + return i_stream_get_size(script->stream, TRUE, size_r); } /* - * Stream manageement + * Stream management */ struct istream *sieve_script_open (struct sieve_script *script, enum sieve_error *error_r) { - int fd; - struct stat st; - struct istream *result; + enum sieve_error error; if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; - if ( (fd=open(script->path, O_RDONLY)) < 0 ) { - sieve_script_handle_file_error - (script->svinst, script->path, script->basename, script->ehandler, - error_r); - return NULL; - } - - if ( fstat(fd, &st) != 0 ) { - sieve_critical(script->svinst, script->ehandler, script->basename, - "failed to open sieve script", - "failed to open sieve script: fstat(fd=%s) failed: %m", script->path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; - result = NULL; - } else { - /* Re-check the file type just to be sure */ - if ( !S_ISREG(st.st_mode) ) { - sieve_critical(script->svinst, script->ehandler, script->basename, - "failed to open sieve script", - "sieve script file '%s' is not a regular file", script->path); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - result = NULL; - } else { - result = script->stream = - i_stream_create_fd(fd, SIEVE_READ_BLOCK_SIZE, TRUE); - script->st = script->lnk_st = st; - } + if ( script->stream == NULL ) { + T_BEGIN { + script->stream = script->v.open(script, &error); + } T_END; } - if ( result == NULL ) { - /* Something went wrong, close the fd */ - if ( close(fd) != 0 ) { - sieve_sys_error(script->svinst, - "failed to close sieve script: close(fd=%s) failed: %m", - script->path); + if ( script->stream == NULL ) { + 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; } } - - return result; + + return script->stream; } void sieve_script_close(struct sieve_script *script) { - i_stream_destroy(&script->stream); -} + if ( script->stream != NULL ) + return; -uoff_t sieve_script_get_size(const struct sieve_script *script) -{ - return script->st.st_size; + i_stream_unref(&script->stream); + + if ( script->v.close != NULL ) { + T_BEGIN { + script->v.close(script); + } T_END; + } } /* * Comparison */ -int sieve_script_cmp -(const struct sieve_script *script1, const struct sieve_script *script2) +bool sieve_script_equals +(const struct sieve_script *script, const struct sieve_script *other) { - if ( script1 == NULL || script2 == NULL ) - return -1; + if ( script == other ) + return TRUE; + + if ( script == NULL || other == NULL ) + return FALSE; + + if ( script->script_class != other->script_class ) + return FALSE; + + if ( script->name != NULL && other->name != NULL && + strcmp(script->name, other->name) == 0 ) + return TRUE; - return ( script1->st.st_ino == script2->st.st_ino ) ? 0 : -1; + if ( script->v.equals == NULL ) + return FALSE; + + return script->v.equals(script, other); } unsigned int sieve_script_hash(const struct sieve_script *script) { - return (unsigned int) script->st.st_ino; + return str_hash(script->name); +} + +/* + * Binary + */ + +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; + + 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", + sieve_binary_path(sbin), sieve_script_location(script)); + return -1; + } + + if ( strcmp(str_c(script_class), script->driver_name) != 0 ) + return 0; + + if ( script->v.binary_read_metadata == NULL ) + return 1; + + return script->v.binary_read_metadata(script, sblock, offset); +} + +void sieve_script_binary_write_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock) +{ + sieve_binary_emit_cstring(sblock, script->driver_name); + + if ( script->v.binary_write_metadata == NULL ) + return; + + script->v.binary_write_metadata(script, sblock); +} + +struct sieve_binary *sieve_script_binary_load +(struct sieve_script *script, enum sieve_error *error_r) +{ + if ( script->v.binary_load == NULL ) { + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + + return script->v.binary_load(script, error_r); } -bool sieve_script_newer -(const struct sieve_script *script, time_t time) +int sieve_script_binary_save +(struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) { - return ( script->st.st_mtime > time || script->lnk_st.st_mtime > time ); + 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 ) { + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return -1; + } + + return script->v.binary_save(script, sbin, update, error_r); } + +int sieve_script_setup_bindir +(struct sieve_script *script, mode_t mode) +{ + struct sieve_instance *svinst = script->svinst; + struct stat st; + + if ( script->bin_dir == NULL ) + return -1; + + if ( stat(script->bin_dir, &st) == 0 ) + return 0; + + 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); + 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; + } + + switch ( errno ) { + case EEXIST: + 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 -1; +} + diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h index dc1e052f049ec110ca2ca886d6bf634678355d5e..896cfdfad182da3defbd981b6b09502d3d67b7c6 100644 --- a/src/lib-sieve/sieve-script.h +++ b/src/lib-sieve/sieve-script.h @@ -15,28 +15,20 @@ bool sieve_script_name_is_valid(const char *scriptname); -/* - * 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); -const char *sieve_binfile_from_name(const char *name); - /* * Sieve script object */ struct sieve_script; +ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *); + struct sieve_script *sieve_script_create - (struct sieve_instance *svinst, const char *path, const char *name, + (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_as + (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_in_directory - (struct sieve_instance *svinst, const char *dirpath, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r); void sieve_script_ref(struct sieve_script *script); void sieve_script_unref(struct sieve_script **script); @@ -46,15 +38,24 @@ void sieve_script_unref(struct sieve_script **script); */ const char *sieve_script_name(const struct sieve_script *script); -const char *sieve_script_filename(const struct sieve_script *script); -const char *sieve_script_path(const struct sieve_script *script); -const char *sieve_script_binpath(const struct sieve_script *script); -const char *sieve_script_dirpath(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); -mode_t sieve_script_permissions(const struct sieve_script *script); +/* + * Saving/loading Sieve binaries + */ -struct sieve_instance *sieve_script_svinst(const struct sieve_script *script); -size_t sieve_script_size(const struct sieve_script *script); +int sieve_script_binary_read_metadata + (struct sieve_script *script, struct sieve_binary_block *sblock, + sieve_size_t *offset); +void sieve_script_binary_write_metadata + (struct sieve_script *script, struct sieve_binary_block *sblock); + +struct sieve_binary *sieve_script_binary_load + (struct sieve_script *script, enum sieve_error *error_r); +int sieve_script_binary_save + (struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r); /* * Stream management @@ -64,21 +65,21 @@ struct istream *sieve_script_open (struct sieve_script *script, enum sieve_error *error_r); void sieve_script_close(struct sieve_script *script); -uoff_t sieve_script_get_size(const struct sieve_script *script); +int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r); /* * Comparison */ -int sieve_script_cmp - (const struct sieve_script *script1, const struct sieve_script *script2); -unsigned int sieve_script_hash(const struct sieve_script *script); -bool sieve_script_newer(const struct sieve_script *script, time_t time); +bool sieve_script_equals + (const struct sieve_script *script, const struct sieve_script *other); -static inline bool sieve_script_equals - (const struct sieve_script *script1, const struct sieve_script *script2) +unsigned int sieve_script_hash(const struct sieve_script *script); +static inline int sieve_script_cmp +(const struct sieve_script *script, const struct sieve_script *other) { - return ( sieve_script_cmp(script1, script2) == 0 ); + return ( sieve_script_equals(script, other) ? 0 : -1 ); } + #endif /* __SIEVE_SCRIPT_H */ diff --git a/src/lib-sieve/sieve-settings.h b/src/lib-sieve/sieve-settings.h index be588d99e588c31d9c0815bb71d2b62955f783f5..c835d686ed765339e0846a4eb3805d5733035f0e 100644 --- a/src/lib-sieve/sieve-settings.h +++ b/src/lib-sieve/sieve-settings.h @@ -13,12 +13,12 @@ static inline const char *sieve_setting_get (struct sieve_instance *svinst, const char *identifier) { - const struct sieve_environment *env = svinst->env; + const struct sieve_callbacks *callbacks = svinst->callbacks; - if ( env == NULL || env->get_setting == NULL ) + if ( callbacks == NULL || callbacks->get_setting == NULL ) return NULL; - return env->get_setting(svinst->context, identifier); + return callbacks->get_setting(svinst->context, identifier); } bool sieve_setting_get_uint_value @@ -44,12 +44,15 @@ bool sieve_setting_get_duration_value static inline const char *sieve_environment_get_homedir (struct sieve_instance *svinst) { - const struct sieve_environment *env = svinst->env; + const struct sieve_callbacks *callbacks = svinst->callbacks; - if ( env == NULL || env->get_homedir == NULL ) + if ( svinst->home_dir != NULL ) + return svinst->home_dir; + + if ( callbacks == NULL || callbacks->get_homedir == NULL ) return NULL; - return env->get_homedir(svinst->context); + return callbacks->get_homedir(svinst->context); } #endif /* __SIEVE_SETTINGS_H */ diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h index 5f75b08e60b3cf45fbcc2fa7deea4d2f19fcb430..2c9e7a731c680936d1ca87467a44e7ddbaee4980 100644 --- a/src/lib-sieve/sieve-types.h +++ b/src/lib-sieve/sieve-types.h @@ -23,10 +23,29 @@ struct sieve_script_env; struct sieve_exec_status; /* - * Callbacks + * System environment */ +enum sieve_flag { + /* Relative paths are resolved to HOME */ + SIEVE_FLAG_HOME_RELATIVE = (1 << 0), +}; + struct sieve_environment { + const char *hostname; + const char *base_dir; + + const char *username; + const char *home_dir; + + enum sieve_flag flags; +}; + +/* + * Callbacks + */ + +struct sieve_callbacks { const char *(*get_homedir)(void *context); const char *(*get_setting)(void *context, const char *identifier); }; @@ -136,13 +155,9 @@ struct sieve_script_env { /* Mail-related */ struct mail_user *user; const char *default_mailbox; + const char *postmaster_address; bool mailbox_autocreate; bool mailbox_autosubscribe; - - /* System-related */ - const char *username; - const char *hostname; - const char *postmaster_address; /* External context data */ @@ -152,19 +167,19 @@ struct sieve_script_env { /* Interface for sending mail */ void *(*smtp_open) - (void *script_ctx, const char *destination, + (const struct sieve_script_env *senv, const char *destination, const char *return_path, struct ostream **output_r); - bool (*smtp_close)(void *script_ctx, void *handle); + bool (*smtp_close)(const struct sieve_script_env *senv, void *handle); /* Interface for marking and checking duplicates */ int (*duplicate_check) - (void *script_ctx, const void *id, size_t id_size, const char *user); + (const struct sieve_script_env *senv, const void *id, size_t id_size); void (*duplicate_mark) - (void *script_ctx, const void *id, size_t id_size, const char *user, + (const struct sieve_script_env *senv, const void *id, size_t id_size, time_t time); /* Interface for rejecting mail */ - int (*reject_mail)(void *script_ctx, const char *recipient, + int (*reject_mail)(const struct sieve_script_env *senv, const char *recipient, const char *reason); /* Execution status record */ diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index 14c9b4e02403e394135fd9b33a3ea2a2ef831f03..b6c2f24e424d0e6bba62bf4e53286a0d535faea6 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -13,6 +13,7 @@ #include "sieve-plugins.h" #include "sieve-script.h" +#include "sieve-script-file.h" #include "sieve-ast.h" #include "sieve-binary.h" #include "sieve-actions.h" @@ -41,7 +42,8 @@ */ struct sieve_instance *sieve_init -(const struct sieve_environment *env, void *context, bool debug) +(const struct sieve_environment *env, + const struct sieve_callbacks *callbacks, void *context, bool debug) { struct sieve_instance *svinst; unsigned long long int uint_setting; @@ -52,9 +54,14 @@ struct sieve_instance *sieve_init pool = pool_alloconly_create("sieve", 8192); svinst = p_new(pool, struct sieve_instance, 1); svinst->pool = pool; - svinst->env = env; + svinst->callbacks = callbacks; svinst->context = context; svinst->debug = debug; + svinst->hostname = p_strdup_empty(pool, env->hostname); + svinst->base_dir = p_strdup_empty(pool, env->base_dir); + svinst->username = p_strdup_empty(pool, env->username); + svinst->home_dir = p_strdup_empty(pool, env->home_dir); + svinst->flags = env->flags; sieve_errors_init(svinst); @@ -240,7 +247,7 @@ struct sieve_binary *sieve_compile_script } struct sieve_binary *sieve_compile -(struct sieve_instance *svinst, const char *script_path, +(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) { @@ -248,17 +255,17 @@ struct sieve_binary *sieve_compile struct sieve_binary *sbin; if ( (script = sieve_script_create - (svinst, script_path, script_name, ehandler, error_r)) == NULL ) + (svinst, script_location, script_name, ehandler, error_r)) == NULL ) return NULL; sbin = sieve_compile_script(script, ehandler, flags, error_r); - - sieve_script_unref(&script); if ( svinst->debug && sbin != NULL ) { - sieve_sys_debug(svinst, "script file %s successfully compiled", - script_path); + sieve_sys_debug(svinst, "script `%s' from %s successfully compiled", + sieve_script_name(script), sieve_script_location(script)); } + + sieve_script_unref(&script); return sbin; } @@ -311,28 +318,16 @@ struct sieve_binary *sieve_load return sieve_binary_open(svinst, bin_path, NULL, error_r); } -struct sieve_binary *sieve_open -(struct sieve_instance *svinst, const char *script_path, - const char *script_name, struct sieve_error_handler *ehandler, +struct sieve_binary *sieve_open_script +(struct sieve_script *script, struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, enum sieve_error *error_r) { - struct sieve_script *script; + struct sieve_instance *svinst = sieve_script_svinst(script); struct sieve_binary *sbin; - const char *bin_path; - /* First open the scriptfile itself */ - script = sieve_script_create - (svinst, script_path, script_name, ehandler, error_r); - - if ( script == NULL ) { - /* Failed */ - return NULL; - } - T_BEGIN { /* Then try to open the matching binary */ - bin_path = sieve_script_binpath(script); - sbin = sieve_binary_open(svinst, bin_path, script, error_r); + sbin = sieve_script_binary_load(script, error_r); if (sbin != NULL) { /* Ok, it exists; now let's see if it is up to date */ @@ -340,7 +335,7 @@ struct sieve_binary *sieve_open /* Not up to date */ if ( svinst->debug ) sieve_sys_debug(svinst, "script binary %s is not up-to-date", - bin_path); + sieve_binary_path(sbin)); sieve_binary_unref(&sbin); sbin = NULL; @@ -353,7 +348,7 @@ struct sieve_binary *sieve_open if ( sbin != NULL ) { if ( svinst->debug ) sieve_sys_debug(svinst, "script binary %s successfully loaded", - bin_path); + sieve_binary_path(sbin)); } else { sbin = sieve_compile_script(script, ehandler, flags, error_r); @@ -361,12 +356,34 @@ struct sieve_binary *sieve_open /* Save the binary if compile was successful */ if ( sbin != NULL ) { if ( svinst->debug ) - sieve_sys_debug(svinst, "script %s successfully compiled", - script_path); + sieve_sys_debug(svinst, "script `%s' from %s successfully compiled", + sieve_script_name(script), sieve_script_location(script)); } } } T_END; + + return sbin; +} + +struct sieve_binary *sieve_open +(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) +{ + struct sieve_script *script; + struct sieve_binary *sbin; + /* First open the scriptfile itself */ + script = sieve_script_create + (svinst, script_location, script_name, ehandler, error_r); + + if ( script == NULL ) { + /* Failed */ + return NULL; + } + + sbin = sieve_open_script(script, ehandler, flags, error_r); + /* Drop script reference, if sbin != NULL it holds a reference of its own. * Otherwise the script object is freed here. */ @@ -385,11 +402,23 @@ bool sieve_is_loaded(struct sieve_binary *sbin) return sieve_binary_loaded(sbin); } -int sieve_save +int sieve_save_as (struct sieve_binary *sbin, const char *bin_path, bool update, - enum sieve_error *error_r) + mode_t save_mode, enum sieve_error *error_r) { - return sieve_binary_save(sbin, bin_path, update, error_r); + return sieve_binary_save(sbin, bin_path, update, save_mode, error_r); +} + +int sieve_save +(struct sieve_binary *sbin, bool update, enum sieve_error *error_r) +{ + struct sieve_script *script = sieve_binary_script(sbin); + + if ( script == NULL ) { + return sieve_binary_save(sbin, NULL, update, 0600, error_r); + } + + return sieve_script_binary_save(script, sbin, update, error_r); } void sieve_close(struct sieve_binary **sbin) diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index b95c776b455b58e2c6d00d27ac3f776420ac4a60..20e5d8299072a17b26081180c84acb2225871d35 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -20,7 +20,8 @@ struct sieve_binary; * is used. */ struct sieve_instance *sieve_init - (const struct sieve_environment *env, void *context, bool debug); + (const struct sieve_environment *env, const struct sieve_callbacks *callbacks, + void *context, bool debug); /* sieve_deinit(): * Frees all memory allocated by the sieve engine. @@ -54,7 +55,7 @@ struct sieve_binary *sieve_compile_script * Compiles the script into a binary. */ struct sieve_binary *sieve_compile - (struct sieve_instance *svinst, const char *script_path, + (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); @@ -70,6 +71,16 @@ struct sieve_binary *sieve_load (struct sieve_instance *svinst, const char *bin_path, enum sieve_error *error_r); +/* sieve_open_script: + * + * First tries to open the binary version of the specified script and if it + * does not exist or if it contains errors, the script is (re-)compiled. Note + * that errors in the bytecode are caught only at runtime. + */ +struct sieve_binary *sieve_open_script + (struct sieve_script *script, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r); + /* sieve_open: * * First tries to open the binary version of the specified script and if it @@ -77,20 +88,28 @@ struct sieve_binary *sieve_load * that errors in the bytecode are caught only at runtime. */ struct sieve_binary *sieve_open - (struct sieve_instance *svinst, const char *script_path, + (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); +/* sieve_save_as: + * + * Saves the binary as the file indicated by the path parameter. This function + * will not write the binary to disk when it was loaded from the indicated + * bin_path, unless update is TRUE. + */ +int sieve_save_as + (struct sieve_binary *sbin, const char *bin_path, bool update, + mode_t save_mode, enum sieve_error *error_r); + /* sieve_save: * - * Saves the binary as the file indicated by the path parameter. If - * path is NULL, it chooses the default path relative to the original - * script. This function will not write the binary to disk when it was - * loaded from the indicated bin_path, unless update is TRUE. + * Saves the binary to the default location. This function will not overwrite + * the binary it was loaded earlier from the default location, unless update + * is TRUE. */ int sieve_save - (struct sieve_binary *sbin, const char *bin_path, bool update, - enum sieve_error *error_r); + (struct sieve_binary *sbin, bool update, enum sieve_error *error_r); /* sieve_close: * diff --git a/src/lib-sievestorage/sieve-storage-list.c b/src/lib-sievestorage/sieve-storage-list.c index 0e36d1ea8d18d41521d61a074c4f4758d860eff4..3941f41b5a8ae1b862c0a141e1c8d4a7c21346cf 100644 --- a/src/lib-sievestorage/sieve-storage-list.c +++ b/src/lib-sievestorage/sieve-storage-list.c @@ -3,6 +3,11 @@ #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" diff --git a/src/lib-sievestorage/sieve-storage-quota.c b/src/lib-sievestorage/sieve-storage-quota.c index 8317b716d1085cc8205e60363b8a261edc5698e4..f081ed51b4acad5b493566c2c900290badce46be 100644 --- a/src/lib-sievestorage/sieve-storage-quota.c +++ b/src/lib-sievestorage/sieve-storage-quota.c @@ -3,6 +3,11 @@ #include "lib.h" #include "str.h" + +#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" diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c index 0db76c2478326bc4f7569f92c438394119da997d..ddc1afefd5dad4aca3debe4bad01e623198d8515 100644 --- a/src/lib-sievestorage/sieve-storage-save.c +++ b/src/lib-sievestorage/sieve-storage-save.c @@ -11,6 +11,7 @@ #include "eacces-error.h" #include "sieve-script.h" +#include "sieve-script-file.h" #include "sieve-storage-private.h" #include "sieve-storage-script.h" diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c index 08c6cd9aa02ecff70bb8737a7e86e637f8f0151c..d240697212f074115ca398a32e21db0812e2007c 100644 --- a/src/lib-sievestorage/sieve-storage-script.c +++ b/src/lib-sievestorage/sieve-storage-script.c @@ -10,6 +10,7 @@ #include "eacces-error.h" #include "sieve-script-private.h" +#include "sieve-script-file.h" #include "sieve-storage.h" #include "sieve-storage-private.h" @@ -24,7 +25,7 @@ #include <fcntl.h> struct sieve_storage_script { - struct sieve_script script; + struct sieve_file_script file; struct sieve_storage *storage; }; @@ -58,12 +59,14 @@ struct sieve_script *sieve_storage_script_init_from_path pool = pool_alloconly_create("sieve_storage_script", 4096); st_script = p_new(pool, struct sieve_storage_script, 1); - st_script->script.pool = pool; + st_script->file.script = sieve_file_script; + st_script->file.script.pool = pool; st_script->storage = storage; - if ( sieve_script_init(&st_script->script, storage->svinst, path, scriptname, - sieve_storage_get_error_handler(storage), &error) != NULL ) { - return &st_script->script; + if ( sieve_script_init + (&st_script->file.script, storage->svinst, &sieve_file_script, path, + scriptname, sieve_storage_get_error_handler(storage), &error) != NULL ) { + return &st_script->file.script; } pool_unref(&pool); @@ -298,7 +301,7 @@ int sieve_storage_script_is_active(struct sieve_script *script) if ( ret > 0 ) { /* Is the requested script active? */ - ret = ( strcmp(script->filename, afile) == 0 ? 1 : 0 ); + ret = ( strcmp(st_script->file.filename, afile) == 0 ? 1 : 0 ); } } T_END; @@ -318,7 +321,7 @@ int sieve_storage_script_delete(struct sieve_script **script) "Cannot delete the active sieve script."); ret = -1; } else { - ret = unlink((*script)->path); + ret = unlink(st_script->file.path); if ( ret < 0 ) { if ( errno == ENOENT ) @@ -327,7 +330,7 @@ int sieve_storage_script_delete(struct sieve_script **script) else sieve_storage_set_critical( storage, "Performing unlink() failed on sieve file '%s': %m", - (*script)->path); + st_script->file.path); } } @@ -484,14 +487,14 @@ static int _sieve_storage_script_activate(struct sieve_script *script) ret = sieve_storage_get_active_scriptfile(storage, &afile); /* Is the requested script already active? */ - if ( ret <= 0 || strcmp(script->filename, afile) != 0 ) + if ( ret <= 0 || strcmp(st_script->file.filename, afile) != 0 ) activated = 1; /* Check the scriptfile we are trying to activate */ - if ( lstat(script->path, &st) != 0 ) { + 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.", - script->path); + st_script->file.path); return -1; } @@ -505,7 +508,7 @@ static int _sieve_storage_script_activate(struct sieve_script *script) /* Just try to create the symlink first */ link_path = t_strconcat - ( storage->link_path, script->filename, NULL ); + ( storage->link_path, st_script->file.filename, NULL ); ret = symlink(link_path, storage->active_path); @@ -566,7 +569,7 @@ int sieve_storage_script_rename */ /* Link to the new path */ - ret = link(script->path, newpath); + ret = link(st_script->file.path, newpath); if ( ret >= 0 ) { /* Is the requested script active? */ if ( sieve_storage_script_is_active(script) ) { @@ -579,16 +582,15 @@ int sieve_storage_script_rename if ( ret >= 0 ) { /* If all is good, remove the old link */ - if ( unlink(script->path) < 0 ) { + if ( unlink(st_script->file.path) < 0 ) { i_error("Failed to clean up old file link '%s' after rename: %m", - script->path); + st_script->file.path); } if ( script->name != NULL && *script->name != '\0' ) script->name = p_strdup(script->pool, newname); - script->path = p_strdup(script->pool, newpath); - script->filename = p_strdup(script->pool, newfile); - script->basename = 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 @@ -612,7 +614,7 @@ int sieve_storage_script_rename default: sieve_storage_set_critical( storage, "Performing link(%s, %s) failed: %m", - script->path, newpath); + st_script->file.path, newpath); } } } T_END; diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c index 568834ed7d82c913c280e4b9e86ac0c5d2f83870..5984f15b393cab7d9a98b7ee2f9e5869dd6bfe48 100644 --- a/src/lib-sievestorage/sieve-storage.c +++ b/src/lib-sievestorage/sieve-storage.c @@ -224,12 +224,30 @@ static struct sieve_storage *_sieve_storage_create /* 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; " @@ -315,6 +333,23 @@ static struct sieve_storage *_sieve_storage_create } } } 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; } diff --git a/src/managesieve-login/client-authenticate.c b/src/managesieve-login/client-authenticate.c index 3b4be6f157747cb2167fea31285e1627199ebe39..c2c7e3ad60d34a9300385e64cb11a9bf8d9faf6a 100644 --- a/src/managesieve-login/client-authenticate.c +++ b/src/managesieve-login/client-authenticate.c @@ -135,6 +135,7 @@ static int managesieve_client_auth_read_response bool fatal; const unsigned char *data; size_t size; + uoff_t resp_size; int ret; if ( i_stream_read(client->input) == -1 ) { @@ -186,7 +187,7 @@ static int managesieve_client_auth_read_response } if ( !managesieve_arg_get_string_stream - (&args[0], &msieve_client->auth_response_input) + (&args[0], &msieve_client->auth_response_input) || !MANAGESIEVE_ARG_IS_EOL(&args[1]) ) { if ( !initial ) *error_r = "Invalid AUTHENTICATE client response."; @@ -197,11 +198,11 @@ static int managesieve_client_auth_read_response } if ( i_stream_get_size - (msieve_client->auth_response_input, FALSE, &size) <= 0 ) - size = 0; + (msieve_client->auth_response_input, FALSE, &resp_size) <= 0 ) + resp_size = 0; if (client->auth_response == NULL) - client->auth_response = str_new(default_pool, I_MAX(size+1, 256)); + client->auth_response = str_new(default_pool, I_MAX(resp_size+1, 256)); } while ( (ret=i_stream_read_data diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c index 08ae77c882899a3e38e7f25ff19ea8a2284fc30a..59e24230a344347561910272856906919d4a2f6f 100644 --- a/src/managesieve/cmd-getscript.c +++ b/src/managesieve/cmd-getscript.c @@ -18,7 +18,7 @@ struct cmd_getscript_context { struct sieve_script *script; struct istream *script_stream; - + unsigned int failed:1; }; @@ -54,8 +54,8 @@ static bool cmd_getscript_continue(struct client_command_context *cmd) if ( ret < 0 ) { sieve_storage_set_critical(ctx->storage, - "o_stream_send_istream(%s) failed: %m", - sieve_script_filename(ctx->script)); + "o_stream_send_istream() failed for script `%s' from %s: %m", + sieve_script_name(ctx->script), sieve_script_location(ctx->script)); ctx->failed = TRUE; return cmd_getscript_finish(ctx); } @@ -67,9 +67,9 @@ static bool cmd_getscript_continue(struct client_command_context *cmd) if ( !i_stream_have_bytes_left(ctx->script_stream) ) { /* Input stream gave less data than expected */ sieve_storage_set_critical(ctx->storage, - "GETSCRIPT for SCRIPT %s got too little data: " + "GETSCRIPT for script `%s' from %s got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, sieve_script_name(ctx->script), - ctx->script_offset, ctx->script_size); + sieve_script_location(ctx->script), ctx->script_offset, ctx->script_size); client_disconnect(ctx->client, "GETSCRIPT failed"); ctx->failed = TRUE; @@ -108,13 +108,20 @@ bool cmd_getscript(struct client_command_context *cmd) ctx->script_stream = sieve_script_open(ctx->script, &error); if ( ctx->script_stream == NULL ) { - ctx->failed = TRUE; if ( error == SIEVE_ERROR_NOT_FOUND ) - sieve_storage_set_error(client->storage, error, "Script does not exist."); + sieve_storage_set_error(client->storage, error, "Script does not exist."); + ctx->failed = TRUE; + return cmd_getscript_finish(ctx); + } + + if ( sieve_script_get_size(ctx->script, &ctx->script_size) <= 0 ) { + sieve_storage_set_critical(ctx->storage, + "failed to obtain script size for script `%s' from %s", + sieve_script_name(ctx->script), sieve_script_location(ctx->script)); + ctx->failed = TRUE; return cmd_getscript_finish(ctx); } - ctx->script_size = sieve_script_get_size(ctx->script); ctx->script_offset = 0; client_send_line diff --git a/src/managesieve/main.c b/src/managesieve/main.c index 64985567df784e6b4e050ae959d0a8e2d7057100..a50b4378ae381baaaa0216b9805d1dac0fee5aa7 100644 --- a/src/managesieve/main.c +++ b/src/managesieve/main.c @@ -139,11 +139,11 @@ client_create_from_input(const struct mail_storage_service_input *input, if (set->verbose_proctitle) verbose_proctitle = TRUE; - client = client_create(fd_in, fd_out, mail_user, user, set); + client = client_create + (fd_in, fd_out, input->session_id, mail_user, user, set); T_BEGIN { client_add_input(client, input_buf); } T_END; - return 0; } @@ -178,6 +178,7 @@ static void login_client_connected(const struct master_login_client *client, const char *username, const char *const *extra_fields) { +#define MSG_BYE_INTERNAL_ERROR "BYE \""CRITICAL_MSG"\"\r\n" struct mail_storage_service_input input; const char *error; buffer_t input_buf; @@ -188,11 +189,17 @@ login_client_connected(const struct master_login_client *client, input.remote_ip = client->auth_req.remote_ip; input.username = username; input.userdb_fields = extra_fields; + input.session_id = client->session_id; buffer_create_const_data(&input_buf, client->data, client->auth_req.data_size); if (client_create_from_input(&input, client->fd, client->fd, &input_buf, &error) < 0) { + if (write(client->fd, MSG_BYE_INTERNAL_ERROR, + strlen(MSG_BYE_INTERNAL_ERROR)) < 0) { + if (errno != EAGAIN && errno != EPIPE) + i_error("write(client) failed: %m"); + } i_error("%s", error); (void)close(client->fd); master_service_client_connection_destroyed(master_service); diff --git a/src/managesieve/managesieve-capabilities.c b/src/managesieve/managesieve-capabilities.c index 3edf3b530a8463a9c1d9c68a8725ad27d7664f52..e2f573da0e34939a2a19e34c2caa99aec596c2d3 100644 --- a/src/managesieve/managesieve-capabilities.c +++ b/src/managesieve/managesieve-capabilities.c @@ -88,11 +88,6 @@ static const char *plugin_settings_get * Sieve environment */ -static const char *sieve_get_homedir(void *context ATTR_UNUSED) -{ - return "/tmp"; -} - static const char *sieve_get_setting (void *context, const char *identifier) { @@ -101,8 +96,8 @@ static const char *sieve_get_setting return plugin_settings_get(set, identifier); } -static const struct sieve_environment sieve_env = { - sieve_get_homedir, +static const struct sieve_callbacks sieve_callbacks = { + NULL, sieve_get_setting }; @@ -113,6 +108,7 @@ static const struct sieve_environment sieve_env = { void managesieve_capabilities_dump(void) { const struct plugin_settings *global_plugin_settings; + struct sieve_environment svenv; struct sieve_instance *svinst; const char *notify_cap; @@ -122,7 +118,11 @@ void managesieve_capabilities_dump(void) /* Initialize Sieve engine */ - svinst = sieve_init(&sieve_env, (void *) global_plugin_settings, FALSE); + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.home_dir = "/tmp"; + + svinst = sieve_init + (&svenv, &sieve_callbacks, (void *) global_plugin_settings, FALSE); /* Dump capabilities */ diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c index bde2b957664ce26d8b06574d52e8b04b56e26502..a1a4342689759f819a374a4ebe79708cde3824e9 100644 --- a/src/managesieve/managesieve-client.c +++ b/src/managesieve/managesieve-client.c @@ -24,44 +24,25 @@ #include <stdlib.h> #include <unistd.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]" - extern struct mail_storage_callbacks mail_storage_callbacks; struct managesieve_module_register managesieve_module_register = { 0 }; struct client *managesieve_clients = NULL; unsigned int managesieve_client_count = 0; -static const char *managesieve_sieve_get_homedir -(void *context) -{ - struct mail_user *mail_user = (struct mail_user *) context; - const char *home = NULL; - - if ( mail_user == NULL ) - return NULL; - - if ( mail_user_get_home(mail_user, &home) <= 0 ) - return NULL; - - return home; -} - static const char *managesieve_sieve_get_setting (void *context, const char *identifier) { - struct mail_user *mail_user = (struct mail_user *) context; + struct mail_user *mail_user = (struct mail_user *) context; - if ( mail_user == NULL ) - return NULL; + if ( mail_user == NULL ) + return NULL; - return mail_user_plugin_getenv(mail_user, identifier); + return mail_user_plugin_getenv(mail_user, identifier); } -static const struct sieve_environment managesieve_sieve_env = { - managesieve_sieve_get_homedir, +static const struct sieve_callbacks managesieve_sieve_callbacks = { + NULL, managesieve_sieve_get_setting }; @@ -106,14 +87,16 @@ static struct sieve_storage *client_get_storage } struct client *client_create -(int fd_in, int fd_out, struct mail_user *user, +(int fd_in, int fd_out, const char *session_id, struct mail_user *user, struct mail_storage_service_user *service_user, const struct managesieve_settings *set) { struct client *client; const char *ident; + struct sieve_environment svenv; struct sieve_instance *svinst; struct sieve_storage *storage; + pool_t pool; /* Always use nonblocking I/O */ @@ -122,7 +105,14 @@ struct client *client_create /* Initialize Sieve instance */ - svinst = sieve_init(&managesieve_sieve_env, (void *) user, set->mail_debug); + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.base_dir = user->set->base_dir; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + + svinst = sieve_init + (&svenv, &managesieve_sieve_callbacks, (void *) user, set->mail_debug); /* Get Sieve storage */ @@ -132,9 +122,12 @@ struct client *client_create net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); - client = i_new(struct client, 1); + pool = pool_alloconly_create("managesieve client", 1024); + client = p_new(pool, struct client, 1); + client->pool = pool; client->set = set; client->service_user = service_user; + client->session_id = p_strdup(pool, session_id); client->fd_in = fd_in; client->fd_out = fd_out; client->input = i_stream_create_fd @@ -179,6 +172,7 @@ static const char *client_stats(struct client *client) static struct var_expand_table static_tab[] = { { 'i', NULL, "input" }, { 'o', NULL, "output" }, + { '\0', NULL, "session" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; @@ -189,6 +183,7 @@ static const char *client_stats(struct client *client) tab[0].value = dec2str(client->input->v_offset); tab[1].value = dec2str(client->output->offset); + tab[2].value = client->session_id; str = t_str_new(128); var_expand(str, client->set->managesieve_logout_format, tab); @@ -264,7 +259,7 @@ void client_destroy(struct client *client, const char *reason) sieve_deinit(&client->svinst); pool_unref(&client->cmd.pool); - i_free(client); + pool_unref(&client->pool); master_service_client_connection_destroyed(master_service); managesieve_refresh_proctitle(); diff --git a/src/managesieve/managesieve-client.h b/src/managesieve/managesieve-client.h index 50f9290e2e85677d2afd315a71908b75c146e675..c5b24c0681f15175cc2aba8940c05cff4cb834a0 100644 --- a/src/managesieve/managesieve-client.h +++ b/src/managesieve/managesieve-client.h @@ -35,12 +35,14 @@ extern struct managesieve_module_register managesieve_module_register; struct client { struct client *prev, *next; + const char *session_id; int fd_in, fd_out; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_idle, *to_idle_output; + pool_t pool; struct mail_storage_service_user *service_user; const struct managesieve_settings *set; @@ -71,9 +73,10 @@ extern unsigned int managesieve_client_count; /* Create new client with specified input/output handles. socket specifies if the handle is a socket. */ -struct client *client_create(int fd_in, int fd_out, struct mail_user *user, - struct mail_storage_service_user *service_user, - const struct managesieve_settings *set); +struct client *client_create + (int fd_in, int fd_out, const char *session_id, struct mail_user *user, + struct mail_storage_service_user *service_user, + const struct managesieve_settings *set); void client_destroy(struct client *client, const char *reason); void client_dump_capability(struct client *client); diff --git a/src/managesieve/managesieve-common.h b/src/managesieve/managesieve-common.h index c9eebdc9f354a0461a6964085f28fbc3df1790d9..6f3591e323c728a2099718d466ef9b668e0307e3 100644 --- a/src/managesieve/managesieve-common.h +++ b/src/managesieve/managesieve-common.h @@ -18,6 +18,10 @@ /* Disconnect client when it sends too many bad commands in a row */ #define CLIENT_MAX_BAD_COMMANDS 20 +#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]" + #include "lib.h" #include "managesieve-client.h" #include "managesieve-settings.h" diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index 3a4c02df88b7c1952c761d84a802751e1fd34039..dd41984a145fc19578f624a3b3222508761acd85 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -14,6 +14,8 @@ #include "lda-settings.h" #include "sieve.h" +#include "sieve-script.h" +#include "sieve-script-file.h" #include "lda-sieve-plugin.h" @@ -25,7 +27,7 @@ * Configuration */ -#define SIEVE_DEFAULT_PATH "~/.dovecot.sieve" +#define LDA_SIEVE_DEFAULT_LOCATION "~/.dovecot.sieve" #define LDA_SIEVE_MAX_USER_ERRORS 30 @@ -39,21 +41,6 @@ static deliver_mail_func_t *next_deliver_mail; * Settings handling */ -static const char *lda_sieve_get_homedir -(void *context) -{ - struct mail_deliver_context *mdctx = (struct mail_deliver_context *)context; - const char *home = NULL; - - if ( mdctx == NULL || mdctx->dest_user == NULL ) - return NULL; - - if ( mail_user_get_home(mdctx->dest_user, &home) <= 0 ) - return NULL; - - return home; -} - static const char *lda_sieve_get_setting (void *context, const char *identifier) { @@ -72,9 +59,9 @@ static const char *lda_sieve_get_setting return value; } -static const struct sieve_environment lda_sieve_env = { - lda_sieve_get_homedir, - lda_sieve_get_setting +static const struct sieve_callbacks lda_sieve_callbacks = { + NULL, + lda_sieve_get_setting }; /* @@ -82,29 +69,30 @@ static const struct sieve_environment lda_sieve_env = { */ static void *lda_sieve_smtp_open -(void *script_ctx, const char *destination, +(const struct sieve_script_env *senv, const char *destination, const char *return_path, struct ostream **output_r) { struct mail_deliver_context *dctx = - (struct mail_deliver_context *) script_ctx; + (struct mail_deliver_context *) senv->script_context; return (void *)smtp_client_open (dctx->set, destination, return_path, output_r); } static bool lda_sieve_smtp_close -(void *script_ctx ATTR_UNUSED, void *handle) +(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) { struct smtp_client *smtp_client = (struct smtp_client *) handle; return ( smtp_client_close(smtp_client) == 0 ); } -static int lda_sieve_reject_mail(void *script_ctx, const char *recipient, +static int lda_sieve_reject_mail +(const struct sieve_script_env *senv, const char *recipient, const char *reason) { struct mail_deliver_context *dctx = - (struct mail_deliver_context *) script_ctx; + (struct mail_deliver_context *) senv->script_context; return mail_send_rejection(dctx, recipient, reason); } @@ -114,20 +102,22 @@ static int lda_sieve_reject_mail(void *script_ctx, const char *recipient, */ static int lda_sieve_duplicate_check -(void *script_ctx, const void *id, size_t id_size, const char *user) +(const struct sieve_script_env *senv, const void *id, size_t id_size) { - struct mail_deliver_context *dctx = (struct mail_deliver_context *) script_ctx; + struct mail_deliver_context *dctx = + (struct mail_deliver_context *) senv->script_context; - return duplicate_check(dctx->dup_ctx, id, id_size, user); + return duplicate_check(dctx->dup_ctx, id, id_size, senv->user->username); } static void lda_sieve_duplicate_mark -(void *script_ctx, const void *id, size_t id_size, const char *user, +(const struct sieve_script_env *senv, const void *id, size_t id_size, time_t time) { - struct mail_deliver_context *dctx = (struct mail_deliver_context *) script_ctx; + struct mail_deliver_context *dctx = + (struct mail_deliver_context *) senv->script_context; - duplicate_mark(dctx->dup_ctx, id, id_size, user, time); + duplicate_mark(dctx->dup_ctx, id, id_size, senv->user->username, time); } /* @@ -139,11 +129,11 @@ struct lda_sieve_run_context { struct mail_deliver_context *mdctx; - const char *const *script_files; + struct sieve_script **scripts; unsigned int script_count; - const char *user_script; - const char *main_script; + struct sieve_script *user_script; + struct sieve_script *main_script; const struct sieve_message_data *msgdata; const struct sieve_script_env *scriptenv; @@ -153,172 +143,173 @@ struct lda_sieve_run_context { const char *userlog; }; -static const char *lda_sieve_get_personal_path +static const char *lda_sieve_get_personal_location (struct sieve_instance *svinst, struct mail_user *user) { - const char *script_path, *home; + const char *script_location; - if ( mail_user_get_home(user, &home) <= 0 ) - home = NULL; + script_location = mail_user_plugin_getenv(user, "sieve"); - script_path = mail_user_plugin_getenv(user, "sieve"); + /* userdb may specify Sieve location */ + if (script_location != NULL) { - /* userdb may specify Sieve path */ - if (script_path != NULL) { - if (*script_path == '\0') { + if (*script_location == '\0') { /* disabled */ if ( user->mail_debug ) - sieve_sys_debug(svinst, "empty script path, disabled"); + sieve_sys_debug(svinst, "empty script location, disabled"); return NULL; } - - script_path = mail_user_home_expand(user, script_path); - - if (*script_path != '/' && *script_path != '\0') { - /* relative path. change to absolute. */ - - if ( home == NULL || *home == '\0' ) { - sieve_sys_error(svinst, "relative script path, but empty home dir: %s", script_path); - return NULL; - } - - script_path = t_strconcat(home, "/", script_path, NULL); - } } else { - if ( home == NULL || *home == '\0' ) { - sieve_sys_error(svinst, - "path to user's main active personal script is unknown. " - "See http://wiki2.dovecot.org/Pigeonhole/Sieve/Configuration"); - return NULL; - } - - script_path = mail_user_home_expand(user, SIEVE_DEFAULT_PATH); + script_location = LDA_SIEVE_DEFAULT_LOCATION; } - return script_path; + return script_location; } -static const char *lda_sieve_get_default_path +static const char *lda_sieve_get_default_location (struct mail_user *user) { - const char *script_path; + const char *script_location; - /* Use default script path, if one exists */ - script_path = mail_user_plugin_getenv(user, "sieve_default"); - if ( script_path == NULL ) { + /* Use default script location, if one exists */ + script_location = mail_user_plugin_getenv(user, "sieve_default"); + if ( script_location == NULL ) { /* For backwards compatibility */ - script_path = mail_user_plugin_getenv(user, "sieve_global_path"); + script_location = mail_user_plugin_getenv(user, "sieve_global_path"); } - return script_path; + return script_location; } -static int lda_sieve_multiscript_get_scriptfiles -(struct sieve_instance *svinst, const char *script_path, - ARRAY_TYPE(const_string) *scriptfiles) +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) { struct sieve_directory *sdir; enum sieve_error error; + ARRAY_TYPE(const_string) script_files; + const char *const *files; + unsigned int count, i; const char *file; - if ( (sdir=sieve_directory_open(svinst, script_path, &error)) == NULL ) + // FIXME: make this a generic iteration API + if ( (sdir=sieve_directory_open(svinst, location, &error)) == NULL ) return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); - while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) { - const char *const *scripts; - unsigned int count, i; + t_array_init(&script_files, 16); + while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) { /* Insert into sorted array */ - scripts = array_get(scriptfiles, &count); + files = array_get(&script_files, &count); for ( i = 0; i < count; i++ ) { - if ( strcmp(file, scripts[i]) < 0 ) + if ( strcmp(file, files[i]) < 0 ) break; } if ( i == count ) - array_append(scriptfiles, &file, 1); + array_append(&script_files, &file, 1); else - array_insert(scriptfiles, i, &file, 1); + array_insert(&script_files, i, &file, 1); } sieve_directory_close(&sdir); + + files = array_get(&script_files, &count); + for ( i = 0; i < count; i++ ) { + struct sieve_script *script = sieve_script_create + (svinst, files[i], NULL, ehandler, &error); + + if ( script == NULL ) { + switch ( errno ) { + 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]); + break; + + default: + sieve_sys_error + (svinst, "failed to access %s script %s", label, files[i]); + break; + } + + continue; + } + + array_append(scripts, &script, 1); + } + return 1; } static void lda_sieve_binary_save (struct lda_sieve_run_context *srctx, struct sieve_binary *sbin, - unsigned int script_index) + struct sieve_script *script) { - const char *script_path = srctx->script_files[script_index]; enum sieve_error error; /* Save binary when compiled */ - if ( sieve_save(sbin, NULL, FALSE, &error) < 0 && - error == SIEVE_ERROR_NO_PERM && script_path != srctx->user_script ) { + if ( sieve_save(sbin, FALSE, &error) < 0 && + error == SIEVE_ERROR_NO_PERM && script != srctx->user_script ) { + /* 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", script_path); + "pre-compiled using the sievec tool", sieve_script_location(script)); } } static struct sieve_binary *lda_sieve_open -(struct lda_sieve_run_context *srctx, unsigned int script_index, +(struct lda_sieve_run_context *srctx, struct sieve_script *script, enum sieve_compile_flags cpflags, enum sieve_error *error_r) { struct sieve_instance *svinst = srctx->svinst; - const char *script_path = srctx->script_files[script_index]; - const char *script_name = - ( script_path == srctx->main_script ? "main_script" : NULL ); struct sieve_error_handler *ehandler; struct sieve_binary *sbin; bool debug = srctx->mdctx->dest_user->mail_debug; - if ( script_path == srctx->user_script ) + if ( script == srctx->user_script ) ehandler = srctx->user_ehandler; else ehandler = srctx->master_ehandler; if ( debug ) - sieve_sys_debug(svinst, "opening script %s", script_path); + sieve_sys_debug(svinst, "opening script %s", sieve_script_location(script)); sieve_error_handler_reset(ehandler); /* Open the sieve script */ - if ( (sbin=sieve_open - (svinst, script_path, script_name, ehandler, cpflags, error_r)) == NULL ) { + if ( (sbin=sieve_open_script(script, ehandler, cpflags, error_r)) == NULL ) { if ( *error_r == SIEVE_ERROR_NOT_FOUND ) { - if ( debug ) - sieve_sys_debug(svinst, "script file %s is missing", script_path); + if ( debug ) { + sieve_sys_debug(svinst, "script file %s is missing", + sieve_script_location(script)); + } } else if ( *error_r == SIEVE_ERROR_NOT_VALID && - script_path == srctx->user_script && srctx->userlog != NULL ) { - sieve_sys_error(svinst, - "failed to open script %s " + script == srctx->user_script && srctx->userlog != NULL ) { + sieve_sys_error(svinst, "failed to open script %s " "(view user logfile %s for more information)", - script_path, srctx->userlog); + sieve_script_location(script), srctx->userlog); } else { - sieve_sys_error(svinst, - "failed to open script %s", script_path); + sieve_sys_error(svinst, "failed to open script %s", + sieve_script_location(script)); } return NULL; } - lda_sieve_binary_save(srctx, sbin, script_index); + lda_sieve_binary_save(srctx, sbin, script); return sbin; } static struct sieve_binary *lda_sieve_recompile -(struct lda_sieve_run_context *srctx, unsigned int script_index, +(struct lda_sieve_run_context *srctx, struct sieve_script *script, enum sieve_compile_flags cpflags, enum sieve_error *error_r) { struct sieve_instance *svinst = srctx->svinst; - const char *script_path = srctx->script_files[script_index]; - const char *script_name = - ( script_path == srctx->main_script ? "main_script" : NULL ); struct sieve_error_handler *ehandler; struct sieve_binary *sbin; bool debug = srctx->mdctx->dest_user->mail_debug; @@ -326,31 +317,32 @@ static struct sieve_binary *lda_sieve_recompile /* Warn */ sieve_sys_warning(svinst, - "encountered corrupt binary: re-compiling script %s", script_path); + "encountered corrupt binary: re-compiling script %s", + sieve_script_location(script)); /* Recompile */ - if ( script_path == srctx->user_script ) + if ( script == srctx->user_script ) ehandler = srctx->user_ehandler; else ehandler = srctx->master_ehandler; - if ( (sbin=sieve_compile - (svinst, script_path, script_name, ehandler, cpflags, error_r)) == NULL ) { + if ( (sbin=sieve_compile_script(script, ehandler, cpflags, error_r)) + == NULL ) { if ( *error_r == SIEVE_ERROR_NOT_FOUND ) { if ( debug ) sieve_sys_debug(svinst, "script file %s is missing for re-compile", - script_path); + sieve_script_location(script)); } else if ( *error_r == SIEVE_ERROR_NOT_VALID && - script_path == srctx->user_script && srctx->userlog != NULL ) { + script == srctx->user_script && srctx->userlog != NULL ) { sieve_sys_error(svinst, "failed to re-compile script %s " "(view user logfile %s for more information)", - script_path, srctx->userlog); + sieve_script_location(script), srctx->userlog); } else { sieve_sys_error(svinst, - "failed to re-compile script %s", script_path); + "failed to re-compile script %s", sieve_script_location(script)); } return NULL; @@ -360,13 +352,13 @@ static struct sieve_binary *lda_sieve_recompile } static int lda_sieve_handle_exec_status -(struct lda_sieve_run_context *srctx, const char *script_path, int status) +(struct lda_sieve_run_context *srctx, struct sieve_script *script, int status) { struct sieve_instance *svinst = srctx->svinst; const char *userlog_notice = ""; int ret; - if ( script_path == srctx->user_script && srctx->userlog != NULL ) { + if ( script == srctx->user_script && srctx->userlog != NULL ) { userlog_notice = t_strdup_printf (" (user logfile %s may reveal additional details)", srctx->userlog); } @@ -375,20 +367,20 @@ static int lda_sieve_handle_exec_status case SIEVE_EXEC_FAILURE: sieve_sys_error(svinst, "execution of script %s failed, but implicit keep was successful%s", - script_path, userlog_notice); + sieve_script_location(script), userlog_notice); ret = 1; break; case SIEVE_EXEC_BIN_CORRUPT: sieve_sys_error(svinst, "!!BUG!!: binary compiled from %s is still corrupt; " "bailing out and reverting to default delivery", - script_path); + sieve_script_location(script)); ret = -1; break; case SIEVE_EXEC_KEEP_FAILED: sieve_sys_error(svinst, "script %s failed with unsuccessful implicit keep%s", - script_path, userlog_notice); + sieve_script_location(script), userlog_notice); ret = -1; break; default: @@ -403,8 +395,8 @@ static int lda_sieve_singlescript_execute (struct lda_sieve_run_context *srctx) { struct sieve_instance *svinst = srctx->svinst; - const char *script_file = srctx->script_files[0]; - bool user_script = ( script_file == srctx->user_script ); + struct sieve_script *script = srctx->scripts[0]; + bool user_script = ( script == srctx->user_script ); struct sieve_error_handler *ehandler; struct sieve_binary *sbin; bool debug = srctx->mdctx->dest_user->mail_debug; @@ -423,7 +415,7 @@ static int lda_sieve_singlescript_execute /* Open the script */ - if ( (sbin=lda_sieve_open(srctx, 0, cpflags, &error)) == NULL ) + if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); /* Execute */ @@ -443,7 +435,7 @@ static int lda_sieve_singlescript_execute /* Recompile */ - if ( (sbin=lda_sieve_recompile(srctx, 0, cpflags, &error)) == NULL ) + if ( (sbin=lda_sieve_recompile(srctx, script, cpflags, &error)) == NULL ) return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); /* Execute again */ @@ -458,25 +450,25 @@ static int lda_sieve_singlescript_execute /* Save new version */ if ( ret != SIEVE_EXEC_BIN_CORRUPT ) - lda_sieve_binary_save(srctx, sbin, 0); + lda_sieve_binary_save(srctx, sbin, script); } sieve_close(&sbin); /* Report status */ - return lda_sieve_handle_exec_status(srctx, script_file, ret); + return lda_sieve_handle_exec_status(srctx, script, ret); } static int lda_sieve_multiscript_execute (struct lda_sieve_run_context *srctx) { struct sieve_instance *svinst = srctx->svinst; - const char *const *scripts = srctx->script_files; + struct sieve_script *const *scripts = srctx->scripts; unsigned int count = srctx->script_count; struct sieve_multiscript *mscript; struct sieve_error_handler *ehandler = srctx->master_ehandler; bool debug = srctx->mdctx->dest_user->mail_debug; - const char *last_script = NULL; + struct sieve_script *last_script = NULL; bool user_script = FALSE; unsigned int i; int ret = 1; @@ -488,17 +480,17 @@ static int lda_sieve_multiscript_execute mscript = sieve_multiscript_start_execute (svinst, srctx->msgdata, srctx->scriptenv); - /* Execute scripts before main script */ + /* Execute scripts */ for ( i = 0; i < count && more; i++ ) { struct sieve_binary *sbin = NULL; - const char *script_file = scripts[i]; + struct sieve_script *script = scripts[i]; enum sieve_compile_flags cpflags = 0; enum sieve_runtime_flags rtflags = 0; bool final = ( i == count - 1 ); - user_script = ( script_file == srctx->user_script ); - last_script = script_file; + user_script = ( script == srctx->user_script ); + last_script = script; if ( user_script ) { cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL; @@ -510,7 +502,7 @@ static int lda_sieve_multiscript_execute /* Open */ - if ( (sbin=lda_sieve_open(srctx, i, cpflags, &error)) == NULL ) { + if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) { ret = ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); break; } @@ -532,7 +524,8 @@ static int lda_sieve_multiscript_execute /* Recompile */ - if ( (sbin=lda_sieve_recompile(srctx, i, cpflags, &error)) == NULL ) { + sbin=lda_sieve_recompile(srctx, script, cpflags, &error); + if ( sbin == NULL ) { ret = ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); break; } @@ -544,8 +537,9 @@ static int lda_sieve_multiscript_execute /* Save new version */ if ( more && - sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT ) - lda_sieve_binary_save(srctx, sbin, i); + sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT ) { + lda_sieve_binary_save(srctx, sbin, script); + } } } @@ -559,133 +553,32 @@ static int lda_sieve_multiscript_execute return lda_sieve_handle_exec_status(srctx, last_script, ret); } -static int lda_sieve_run -(struct mail_deliver_context *mdctx, struct sieve_instance *svinst, - const char *user_script, const char *default_script, - const ARRAY_TYPE (const_string) *scripts_before, - const ARRAY_TYPE (const_string) *scripts_after, - struct mail_storage **storage_r) +static int lda_sieve_deliver_mail +(struct mail_deliver_context *mdctx, struct mail_storage **storage_r) { - ARRAY_TYPE (const_string) scripts; struct lda_sieve_run_context srctx; + struct sieve_environment svenv; + struct sieve_instance *svinst; struct sieve_message_data msgdata; struct sieve_script_env scriptenv; struct sieve_exec_status estatus; - int ret = 0; - - *storage_r = NULL; - - /* Initialize */ - - memset(&srctx, 0, sizeof(srctx)); - srctx.svinst = svinst; - srctx.mdctx = mdctx; - - /* Compose execution sequence */ - - t_array_init(&scripts, 32); - - array_append_array(&scripts, scripts_before); - - if ( user_script != NULL ) { - array_append(&scripts, &user_script, 1); - srctx.user_script = user_script; - srctx.main_script = user_script; - } else if ( default_script != NULL ) { - array_append(&scripts, &default_script, 1); - srctx.user_script = NULL; - srctx.main_script = default_script; - } else { - srctx.user_script = NULL; - srctx.main_script = NULL; - } - - array_append_array(&scripts, scripts_after); - - /* Initialize error handlers */ - - srctx.master_ehandler = sieve_system_ehandler_get(svinst); - - if ( user_script != NULL ) { - srctx.userlog = t_strconcat(user_script, ".log", NULL); - srctx.user_ehandler = sieve_logfile_ehandler_create - (svinst, srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS); - } - - /* Collect necessary message data */ - - memset(&msgdata, 0, sizeof(msgdata)); - - msgdata.mail = mdctx->src_mail; - msgdata.return_path = mail_deliver_get_return_address(mdctx); - msgdata.orig_envelope_to = mdctx->dest_addr; - msgdata.final_envelope_to = mdctx->final_dest_addr; - msgdata.auth_user = mdctx->dest_user->username; - (void)mail_get_first_header(msgdata.mail, "Message-ID", &msgdata.id); - - srctx.msgdata = &msgdata; - - /* Compose script execution environment */ - - memset(&scriptenv, 0, sizeof(scriptenv)); - memset(&estatus, 0, sizeof(estatus)); - - scriptenv.action_log_format = mdctx->set->deliver_log_format; - scriptenv.default_mailbox = mdctx->dest_mailbox_name; - scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate; - scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe; - scriptenv.user = mdctx->dest_user; - scriptenv.username = mdctx->dest_user->username; - scriptenv.hostname = mdctx->set->hostname; - scriptenv.postmaster_address = mdctx->set->postmaster_address; - scriptenv.smtp_open = lda_sieve_smtp_open; - scriptenv.smtp_close = lda_sieve_smtp_close; - scriptenv.duplicate_mark = lda_sieve_duplicate_mark; - scriptenv.duplicate_check = lda_sieve_duplicate_check; - scriptenv.reject_mail = lda_sieve_reject_mail; - scriptenv.script_context = (void *) mdctx; - scriptenv.exec_status = &estatus; - - srctx.scriptenv = &scriptenv; - - /* Assign script sequence */ - - srctx.script_files = array_get(&scripts, &srctx.script_count); - - /* Execute script(s) */ - - if ( srctx.script_count == 1 ) - ret = lda_sieve_singlescript_execute(&srctx); - else - ret = lda_sieve_multiscript_execute(&srctx); - - /* Record status */ - - mdctx->tried_default_save = estatus.tried_default_save; - *storage_r = estatus.last_storage; - - /* Clean up */ - - if ( srctx.user_ehandler != NULL ) - sieve_error_handler_unref(&srctx.user_ehandler); - - return ret; -} - -static int lda_sieve_deliver_mail -(struct mail_deliver_context *mdctx, struct mail_storage **storage_r) -{ - struct sieve_instance *svinst; - struct sieve_error_handler *master_ehandler; - const char *user_script, *default_script, *sieve_before, *sieve_after; - ARRAY_TYPE (const_string) scripts_before; - ARRAY_TYPE (const_string) scripts_after; + struct sieve_error_handler *master_ehandler; + const char *user_location, *default_location, *sieve_before, *sieve_after; + ARRAY_TYPE(sieve_scripts) script_sequence; bool debug = mdctx->dest_user->mail_debug; + enum sieve_error error; int ret = 0; /* Initialize Sieve engine */ - svinst = sieve_init(&lda_sieve_env, mdctx, debug); + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = mdctx->dest_user->username; + (void)mail_user_get_home(mdctx->dest_user, &svenv.home_dir); + svenv.hostname = mdctx->set->hostname; + svenv.base_dir = mdctx->dest_user->set->base_dir; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + + svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug); /* Initialize master error handler */ @@ -700,105 +593,224 @@ static int lda_sieve_deliver_mail /* Find scripts and run them */ T_BEGIN { - struct stat st; + struct sieve_script *const *scripts; + unsigned int count, i; - /* Find the personal script to execute */ + /* Initialize run context */ - user_script = lda_sieve_get_personal_path(svinst, mdctx->dest_user); - default_script = lda_sieve_get_default_path(mdctx->dest_user); + memset(&srctx, 0, sizeof(srctx)); + srctx.svinst = svinst; + srctx.mdctx = mdctx; + srctx.master_ehandler = master_ehandler; - if ( user_script != NULL && stat(user_script, &st) < 0 ) { + /* Find the personal script to execute */ - switch ( errno ) { - case ENOENT: - if ( debug ) - sieve_sys_debug(svinst, "user's script path %s doesn't exist " - "(using global script path in stead)", user_script); - break; - case EACCES: - sieve_sys_error(svinst, "failed to stat user's sieve script: %s " - "(using global script path in stead)", - eacces_error_get("stat", user_script)); - break; - default: - sieve_sys_error(svinst, "failed to stat user's sieve script: " - "stat(%s) failed: %m (using global script path in stead)", - user_script); - break; + user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user); + if ( user_location != NULL ) { + srctx.user_script = sieve_script_create_as + (svinst, user_location, "main script", master_ehandler, &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 " + "(using default script location instead)", user_location); + break; + default: + sieve_sys_error(svinst, "failed to access user's sieve script %s " + "(using default script location instead)", + user_location); + break; + } + } else { + srctx.main_script = srctx.user_script; } - - user_script = NULL; } - if ( debug ) { - const char *script = user_script == NULL ? default_script : user_script; + if ( srctx.user_script == NULL ) { + default_location = lda_sieve_get_default_location(mdctx->dest_user); + if ( default_location != NULL ) { + srctx.main_script = sieve_script_create_as + (svinst, default_location, "main script", master_ehandler, &error); - if ( script == NULL ) - sieve_sys_debug(svinst, "user has no valid personal script"); - else - sieve_sys_debug(svinst, "using sieve path for user's script: %s", script); + if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND && + debug ) { + sieve_sys_debug(svinst, "default user script %s doesn't exist", + default_location); + } + } else { + sieve_sys_debug(svinst, "no default script configured for user"); + } } - /* Check for multiscript */ + if ( debug && srctx.main_script == NULL ) { + sieve_sys_debug(svinst, + "user has no valid location for a personal script"); + } - t_array_init(&scripts_before, 16); - t_array_init(&scripts_after, 16); + /* Compose script array */ - sieve_before = mail_user_plugin_getenv(mdctx->dest_user, "sieve_before"); - sieve_after = mail_user_plugin_getenv(mdctx->dest_user, "sieve_after"); + t_array_init(&script_sequence, 16); + sieve_before = mail_user_plugin_getenv(mdctx->dest_user, "sieve_before"); if ( sieve_before != NULL && *sieve_before != '\0' ) { - if ( lda_sieve_multiscript_get_scriptfiles - (svinst, sieve_before, &scripts_before) == 0 && debug ) { - sieve_sys_debug(svinst, "sieve_before path not found: %s", sieve_before); + if ( lda_sieve_multiscript_get_scripts(svinst, "sieve_before", + sieve_before, master_ehandler, &script_sequence) == 0 && debug ) { + sieve_sys_debug(svinst, "sieve_before location not found: %s", + sieve_before); + } + + if ( debug ) { + scripts = array_get(&script_sequence, &count); + for ( i = 0; i < count; i ++ ) { + sieve_sys_debug(svinst, "executed before user's Sieve script(%d): %s", + i+1, sieve_script_location(scripts[i])); + } } } - if ( sieve_after != NULL && *sieve_after != '\0' ) { - if ( lda_sieve_multiscript_get_scriptfiles - (svinst, sieve_after, &scripts_after) == 0 && debug ) { - sieve_sys_debug(svinst, "sieve_after path not found: %s", sieve_after); + if ( srctx.main_script != NULL ) { + array_append(&script_sequence, &srctx.main_script, 1); + + if ( debug ) { + sieve_sys_debug(svinst, + "using the following location for user's Sieve script: %s", + sieve_script_location(srctx.main_script)); } } - if ( debug ) { - const char *const *scriptfiles; - unsigned int count, i; + sieve_after = mail_user_plugin_getenv(mdctx->dest_user, "sieve_after"); + if ( sieve_after != NULL && *sieve_after != '\0' ) { + unsigned int after_index = array_count(&script_sequence); - scriptfiles = array_get(&scripts_before, &count); - for ( i = 0; i < count; i ++ ) { - sieve_sys_debug(svinst, "executed before user's script(%d): %s", - i+1, scriptfiles[i]); + if ( lda_sieve_multiscript_get_scripts(svinst, "sieve_after", + sieve_after, master_ehandler, &script_sequence) == 0 && debug ) { + sieve_sys_debug(svinst, "sieve_after location not found: %s", + sieve_after); } - scriptfiles = array_get(&scripts_after, &count); - for ( i = 0; i < count; i ++ ) { - sieve_sys_debug(svinst, "executed after user's script(%d): %s", - i+1, scriptfiles[i]); + if ( debug ) { + scripts = array_get(&script_sequence, &count); + for ( i = after_index; i < count; i ++ ) { + sieve_sys_debug(svinst, "executed after user's Sieve script(%d): %s", + i+1, sieve_script_location(scripts[i])); + } } } - /* Check whether there are any scripts to execute */ + srctx.scripts = + array_get_modifiable(&script_sequence, &srctx.script_count); - if ( array_count(&scripts_before) == 0 && array_count(&scripts_after) == 0 && - user_script == NULL && default_script == NULL ) { - if ( debug ) - sieve_sys_debug(svinst, "no scripts to execute: reverting to default delivery."); + /* Check whether there are any scripts to execute at all */ + + if ( srctx.script_count == 0 ) { + if ( debug ) { + sieve_sys_debug(svinst, + "no scripts to execute: reverting to default delivery."); + } - /* No error, but no delivery by this plugin either. A return value of <= 0 for a - * deliver plugin is is considered a failure. In deliver itself, saved_mail and - * tried_default_save remain unset, meaning that deliver will then attempt the - * default delivery. We return 0 to signify the lack of a real error. + /* No error, but no delivery by this plugin either. A return value of <= 0 + * for a deliver plugin is is considered a failure. In deliver itself, + * saved_mail and tried_default_save remain unset, meaning that deliver + * will then attempt the default delivery. We return 0 to signify the lack + * of a real error. */ ret = 0; } else { - /* Run the script(s) */ + /* Initialize user error handler */ + + if ( srctx.user_script != NULL ) { + const char *log_path = NULL; + + /* Determine user log file path */ + if ( (log_path=mail_user_plugin_getenv + (mdctx->dest_user, "sieve_user_log")) == NULL ) { + const char *path; + + if ( (path=sieve_file_script_get_path(srctx.user_script)) == NULL ) { + /* Default */ + if ( svenv.home_dir != NULL ) { + log_path = t_strconcat + (svenv.home_dir, "/.dovecot.sieve.log", NULL); + } + } else { + /* Use script file as a basic (legacy behavior) */ + log_path = t_strconcat(path, ".log", NULL); + } + } else if ( svenv.home_dir != NULL ) { + /* Expand home dir if necessary */ + if ( log_path[0] == '~' ) { + log_path = home_expand_tilde(log_path, svenv.home_dir); + } else if ( log_path[0] != '/' ) { + log_path = t_strconcat(svenv.home_dir, "/", log_path, NULL); + } + } - ret = lda_sieve_run - (mdctx, svinst, user_script, default_script, &scripts_before, - &scripts_after, storage_r); + if ( log_path != NULL ) { + srctx.userlog = log_path; + srctx.user_ehandler = sieve_logfile_ehandler_create + (svinst, srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS); + } + } + + /* Collect necessary message data */ + + memset(&msgdata, 0, sizeof(msgdata)); + + msgdata.mail = mdctx->src_mail; + msgdata.return_path = mail_deliver_get_return_address(mdctx); + msgdata.orig_envelope_to = mdctx->dest_addr; + msgdata.final_envelope_to = mdctx->final_dest_addr; + msgdata.auth_user = mdctx->dest_user->username; + (void)mail_get_first_header(msgdata.mail, "Message-ID", &msgdata.id); + + srctx.msgdata = &msgdata; + + /* Compose script execution environment */ + + memset(&scriptenv, 0, sizeof(scriptenv)); + memset(&estatus, 0, sizeof(estatus)); + + scriptenv.action_log_format = mdctx->set->deliver_log_format; + scriptenv.default_mailbox = mdctx->dest_mailbox_name; + scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate; + scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe; + scriptenv.user = mdctx->dest_user; + scriptenv.postmaster_address = mdctx->set->postmaster_address; + scriptenv.smtp_open = lda_sieve_smtp_open; + scriptenv.smtp_close = lda_sieve_smtp_close; + scriptenv.duplicate_mark = lda_sieve_duplicate_mark; + scriptenv.duplicate_check = lda_sieve_duplicate_check; + scriptenv.reject_mail = lda_sieve_reject_mail; + scriptenv.script_context = (void *) mdctx; + scriptenv.exec_status = &estatus; + + srctx.scriptenv = &scriptenv; + + /* Execute script(s) */ + + if ( srctx.script_count == 1 ) + ret = lda_sieve_singlescript_execute(&srctx); + else + ret = lda_sieve_multiscript_execute(&srctx); + + /* Record status */ + + mdctx->tried_default_save = estatus.tried_default_save; + *storage_r = estatus.last_storage; + + /* Clean up user error handlers */ + + if ( srctx.user_ehandler != NULL ) + sieve_error_handler_unref(&srctx.user_ehandler); } + /* Cleanup scripts */ + for ( i = 0; i < srctx.script_count; i++ ) { + sieve_script_unref(&srctx.scripts[i]); + } + } T_END; sieve_deinit(&svinst); diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c index db39e4b8e12a12ab5110e7713daaca4e81321b46..32c14bd92d30ae502b8ccbbbd01b3a14af002785 100644 --- a/src/sieve-tools/sieve-filter.c +++ b/src/sieve-tools/sieve-filter.c @@ -95,7 +95,7 @@ static int filter_message msgdata.return_path = sender; msgdata.orig_envelope_to = recipient; msgdata.final_envelope_to = recipient; - msgdata.auth_user = senv->username; + msgdata.auth_user = senv->user->username; (void)mail_get_first_header(mail, "Message-ID", &msgdata.id); if ( mail_get_virtual_size(mail, &size) < 0 ) { @@ -488,7 +488,7 @@ int main(int argc, char **argv) if ( force_compile ) { main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL); if ( main_sbin != NULL ) - (void) sieve_save(main_sbin, NULL, TRUE, NULL); + (void) sieve_save(main_sbin, TRUE, NULL); } else { main_sbin = sieve_tool_script_open(svinst, scriptfile); } @@ -537,8 +537,6 @@ int main(int argc, char **argv) scriptenv.mailbox_autocreate = FALSE; scriptenv.default_mailbox = dst_mailbox; scriptenv.user = mail_user; - scriptenv.username = sieve_tool_get_username(sieve_tool); - scriptenv.hostname = "host.example.com"; scriptenv.postmaster_address = "postmaster@example.com"; scriptenv.smtp_open = NULL; scriptenv.smtp_close = NULL; diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c index 85937821500e7b143301c12d473121431c8cc9c9..b69ccd4018cd6716a899be222c5544bbd32421dc 100644 --- a/src/sieve-tools/sieve-test.c +++ b/src/sieve-tools/sieve-test.c @@ -57,7 +57,7 @@ static void print_help(void) */ static void *sieve_smtp_open -(void *script_ctx ATTR_UNUSED, const char *destination, +(const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, const char *return_path, struct ostream **output_r) { i_info("sending message from <%s> to <%s>:", @@ -70,7 +70,7 @@ static void *sieve_smtp_open } static bool sieve_smtp_close -(void *script_ctx ATTR_UNUSED, void *handle ATTR_UNUSED) +(const struct sieve_script_env *senv ATTR_UNUSED, void *handle ATTR_UNUSED) { struct ostream *output = (struct ostream *)handle; @@ -84,19 +84,18 @@ static bool sieve_smtp_close */ static int duplicate_check -(void *script_context ATTR_UNUSED, const void *id ATTR_UNUSED, - size_t id_size ATTR_UNUSED, const char *user) +(const struct sieve_script_env *senv, const void *id ATTR_UNUSED, + size_t id_size ATTR_UNUSED) { - i_info("checked duplicate for user %s.\n", user); + i_info("checked duplicate for user %s.\n", senv->user->username); return 0; } static void duplicate_mark -(void *script_context ATTR_UNUSED, const void *id ATTR_UNUSED, - size_t id_size ATTR_UNUSED, const char *user, - time_t time ATTR_UNUSED) +(const struct sieve_script_env *senv, const void *id ATTR_UNUSED, + size_t id_size ATTR_UNUSED, time_t time ATTR_UNUSED) { - i_info("marked duplicate for user %s.\n", user); + i_info("marked duplicate for user %s.\n", senv->user->username); } /* @@ -220,12 +219,13 @@ int main(int argc, char **argv) ehandler = sieve_stderr_ehandler_create(svinst, 0); sieve_system_ehandler_set(ehandler); sieve_error_handler_accept_infolog(ehandler, TRUE); + sieve_error_handler_accept_debuglog(ehandler, svinst->debug); /* Compile main sieve script */ if ( force_compile ) { main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL); if ( main_sbin != NULL ) - (void) sieve_save(main_sbin, NULL, TRUE, NULL); + (void) sieve_save(main_sbin, TRUE, NULL); } else { main_sbin = sieve_tool_script_open(svinst, scriptfile); } @@ -271,8 +271,6 @@ int main(int argc, char **argv) memset(&scriptenv, 0, sizeof(scriptenv)); scriptenv.default_mailbox = "INBOX"; scriptenv.user = sieve_tool_get_mail_user(sieve_tool); - scriptenv.username = sieve_tool_get_username(sieve_tool); - scriptenv.hostname = "host.example.com"; scriptenv.postmaster_address = "postmaster@example.com"; scriptenv.smtp_open = sieve_smtp_open; scriptenv.smtp_close = sieve_smtp_close; @@ -326,7 +324,7 @@ int main(int argc, char **argv) if ( force_compile ) { sbin = sieve_tool_script_compile(svinst, sfiles[i], sfiles[i]); if ( sbin != NULL ) - (void) sieve_save(sbin, NULL, FALSE, NULL); + (void) sieve_save(sbin, FALSE, NULL); } else { sbin = sieve_tool_script_open(svinst, sfiles[i]); } diff --git a/src/sieve-tools/sievec.c b/src/sieve-tools/sievec.c index b1e1a3cb0691a1b91038f98294e8c7bc5c5f601f..8f0e38cd4a2c802be169a93a1f09bd076002a5fb 100644 --- a/src/sieve-tools/sievec.c +++ b/src/sieve-tools/sievec.c @@ -11,6 +11,7 @@ #include "sieve.h" #include "sieve-extensions.h" #include "sieve-script.h" +#include "sieve-script-file.h" #include "sieve-tool.h" #include <stdio.h> @@ -123,7 +124,7 @@ int main(int argc, char **argv) sbin = sieve_tool_script_compile(svinst, file, dp->d_name); if ( sbin != NULL ) { - sieve_save(sbin, NULL, TRUE, NULL); + sieve_save(sbin, TRUE, NULL); sieve_close(&sbin); } } @@ -143,7 +144,7 @@ int main(int argc, char **argv) if ( dump ) sieve_tool_dump_binary_to(sbin, outfile, FALSE); else { - sieve_save(sbin, outfile, TRUE, NULL); + sieve_save_as(sbin, outfile, TRUE, 0600, NULL); } sieve_close(&sbin); diff --git a/src/testsuite/testsuite-binary.c b/src/testsuite/testsuite-binary.c index 72f3212f40305a037bc03353f55b74569bea74fe..d8232ef97276285eaf6c421e6f802aa4c93c8065 100644 --- a/src/testsuite/testsuite-binary.c +++ b/src/testsuite/testsuite-binary.c @@ -9,10 +9,12 @@ #include "unlink-directory.h" #include "sieve.h" -#include "sieve-common.h" +#include "sieve-common.h" #include "sieve-script.h" +#include "sieve-script-file.h" +#include "sieve-binary.h" #include "sieve-error.h" - + #include "testsuite-common.h" #include "testsuite-binary.h" @@ -62,8 +64,9 @@ void testsuite_binary_reset(void) bool testsuite_binary_save(struct sieve_binary *sbin, const char *name) { - return sieve_save(sbin, t_strdup_printf - ("%s/%s", testsuite_binary_tmp, sieve_binfile_from_name(name)), TRUE, NULL); + return sieve_save_as(sbin, t_strdup_printf + ("%s/%s", testsuite_binary_tmp, sieve_binfile_from_name(name)), TRUE, + 0600, NULL); } struct sieve_binary *testsuite_binary_load(const char *name) diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c index f35bdb63760c42889ac42088dd76602862df4050..386dcf1919a9cba5e197abb5434f091d491c146e 100644 --- a/src/testsuite/testsuite-script.c +++ b/src/testsuite/testsuite-script.c @@ -6,6 +6,7 @@ #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" @@ -45,8 +46,8 @@ static struct sieve_binary *_testsuite_script_compile sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "compile script `%s'", script); - script_path = sieve_script_dirpath(renv->script); - if ( script_path == NULL ) + script_path = sieve_file_script_get_dirpath(renv->script); + if ( script_path == NULL ) return SIEVE_EXEC_FAILURE; script_path = t_strconcat(script_path, "/", script, NULL); @@ -95,8 +96,6 @@ bool testsuite_script_run(const struct sieve_runtime_env *renv) /* Compose script execution environment */ memset(&scriptenv, 0, sizeof(scriptenv)); scriptenv.default_mailbox = "INBOX"; - scriptenv.username = "user"; - scriptenv.hostname = "host.example.com"; scriptenv.postmaster_address = "postmaster@example.com"; scriptenv.smtp_open = NULL; scriptenv.smtp_close = NULL; @@ -157,8 +156,6 @@ bool testsuite_script_multiscript /* Compose script execution environment */ memset(&scriptenv, 0, sizeof(scriptenv)); scriptenv.default_mailbox = "INBOX"; - scriptenv.username = "user"; - scriptenv.hostname = "host.example.com"; scriptenv.postmaster_address = "postmaster@example.com"; scriptenv.smtp_open = NULL; scriptenv.smtp_close = NULL; diff --git a/src/testsuite/testsuite-smtp.c b/src/testsuite/testsuite-smtp.c index 643bf62617c039df4c3f657623871beab4d09c5c..9d8b5d85d801ad1084bb1783f7b96e846984d10b 100644 --- a/src/testsuite/testsuite-smtp.c +++ b/src/testsuite/testsuite-smtp.c @@ -73,7 +73,7 @@ struct testsuite_smtp { }; void *testsuite_smtp_open -(void *script_ctx ATTR_UNUSED, const char *destination, +(const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, const char *return_path, struct ostream **output_r) { struct testsuite_smtp_message smtp_msg; @@ -104,7 +104,7 @@ void *testsuite_smtp_open } bool testsuite_smtp_close -(void *script_ctx ATTR_UNUSED, void *handle) +(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) { struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; diff --git a/src/testsuite/testsuite-smtp.h b/src/testsuite/testsuite-smtp.h index 67145fdd6e0863bd3aa5e6dff79ac2f351ed6923..58d4cfad918bace3f80554fae42d2da660f1d49a 100644 --- a/src/testsuite/testsuite-smtp.h +++ b/src/testsuite/testsuite-smtp.h @@ -13,10 +13,10 @@ void testsuite_smtp_reset(void); */ void *testsuite_smtp_open - (void *script_ctx, const char *destination, const char *return_path, - struct ostream **output_r); + (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, + const char *return_path, struct ostream **output_r); bool testsuite_smtp_close - (void *script_ctx, void *handle); + (const struct sieve_script_env *senv, void *handle); /* * Access diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c index 8a54124a6c5f671496840083df1284fb73b515cd..9590116612cb63fc218477b3b33207b477c300e0 100644 --- a/src/testsuite/testsuite.c +++ b/src/testsuite/testsuite.c @@ -178,9 +178,7 @@ int main(int argc, char **argv) memset(&scriptenv, 0, sizeof(scriptenv)); scriptenv.user = sieve_tool_get_mail_user(sieve_tool); scriptenv.default_mailbox = "INBOX"; - scriptenv.hostname = "testsuite.example.com"; scriptenv.postmaster_address = "postmaster@example.com"; - scriptenv.username = sieve_tool_get_username(sieve_tool); scriptenv.smtp_open = testsuite_smtp_open; scriptenv.smtp_close = testsuite_smtp_close; scriptenv.trace_stream = tracestream; diff --git a/stamp.h.in b/stamp.h.in index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4bd8276f349276147c911ae2c08b37e7b8811544 100644 --- a/stamp.h.in +++ b/stamp.h.in @@ -0,0 +1 @@ +// empty file diff --git a/tests/extensions/environment/basic.svtest b/tests/extensions/environment/basic.svtest index 7eeaa248b3446b711683017485bc2eb271030b18..82f8981639980e7ed85e6cf0874b00ab89fe3cc3 100644 --- a/tests/extensions/environment/basic.svtest +++ b/tests/extensions/environment/basic.svtest @@ -25,7 +25,7 @@ test "Name" { } test "Host" { - if not environment "host" "testsuite.example.com" { + if not environment "host" "host.example.com" { test_fail "wrong testsuite hostname"; } }