# --- SDE-COPYRIGHT-NOTE-BEGIN --- # This copyright note is auto-generated by ./scripts/Create-CopyPatch. # # Filename: package/.../dovecot-pigeonhole/dovecot-2.2-pigeonhole-0.4.0-0100-upstream-fixes.patch # Copyright (C) 2013 The OpenSDE Project # # More information can be found in the files COPYING and README. # # This patch file is dual-licensed. It is available under the license the # patched project is licensed under, as long as it is an OpenSource license # as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms # of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # --- SDE-COPYRIGHT-NOTE-END --- From 72a02fd7203a5d3422e7eaa3ff3a517b619402ae Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Thu, 9 May 2013 16:39:41 +0200 Subject: [PATCH] Forgot to update Dovecot version in README and man pages. diff --git a/README b/README index 19263a1..d4e68b4 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Pigeonhole for Dovecot v2.1 +Pigeonhole for Dovecot v2.2 Introduction ============ @@ -100,8 +100,7 @@ following list outlines the implementation status of each supported extension: copy (RFC 3894): fully supported. body (RFC 5173): almost fully supported, but the text body-transform - implementation is simple and some issues make it still not completely - RFC compliant. + implementation is simple. environment (RFC 5183): fully supported (v0.4.0+). variables (RFC 5229): fully supported. vacation (RFC 5230): fully supported. diff --git a/doc/man/pigeonhole.7.in b/doc/man/pigeonhole.7.in index f86d5e6..75db9ca 100644 --- a/doc/man/pigeonhole.7.in +++ b/doc/man/pigeonhole.7.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "PIGEONHOLE" 7 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "PIGEONHOLE" 7 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME pigeonhole \- Overview of the Pigeonhole project\(aqs Sieve support for the diff --git a/doc/man/sieve-dump.1.in b/doc/man/sieve-dump.1.in index faf886f..3ea65f7 100644 --- a/doc/man/sieve-dump.1.in +++ b/doc/man/sieve-dump.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-DUMP" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-DUMP" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME sieve\-dump \- Pigeonhole\(aqs Sieve script binary dump tool diff --git a/doc/man/sieve-filter.1.in b/doc/man/sieve-filter.1.in index 1566dfa..df3fc36 100644 --- a/doc/man/sieve-filter.1.in +++ b/doc/man/sieve-filter.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-FILTER" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-FILTER" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .SH NAME sieve\-filter \- Pigeonhole\(aqs Sieve mailbox filter tool diff --git a/doc/man/sieve-test.1.in b/doc/man/sieve-test.1.in index 946c5b7..4331250 100644 --- a/doc/man/sieve-test.1.in +++ b/doc/man/sieve-test.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-TEST" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-TEST" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .SH NAME sieve\-test \- Pigeonhole\(aqs Sieve script tester .\"------------------------------------------------------------------------ diff --git a/doc/man/sievec.1.in b/doc/man/sievec.1.in index 28ef9e3..9307800 100644 --- a/doc/man/sievec.1.in +++ b/doc/man/sievec.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVEC" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVEC" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME sievec \- Pigeonhole\(aqs Sieve script compiler -- 1.7.10.2 From 32ee54152c032b9e6f90bce9a6787de29a42ac4b Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Wed, 15 May 2013 21:59:32 +0200 Subject: [PATCH] lib-sieve: Fixed datastack problem in message header composition. diff --git a/src/lib-sieve/rfc2822.c b/src/lib-sieve/rfc2822.c index fc2bb36..27f94d4 100644 --- a/src/lib-sieve/rfc2822.c +++ b/src/lib-sieve/rfc2822.c @@ -211,32 +211,26 @@ unsigned int rfc2822_header_append void rfc2822_header_printf (string_t *header, const char *name, const char *fmt, ...) { + const char *body; va_list args; - T_BEGIN { - const char *body; + va_start(args, fmt); + body = t_strdup_vprintf(fmt, args); + va_end(args); - va_start(args, fmt); - body = t_strdup_vprintf(fmt, args); - va_end(args); - - rfc2822_header_write(header, name, body); - } T_END; + rfc2822_header_write(header, name, body); } void rfc2822_header_utf8_printf (string_t *header, const char *name, const char *fmt, ...) { + string_t *body = t_str_new(256); va_list args; - T_BEGIN { - string_t *body = t_str_new(256); - - va_start(args, fmt); - message_header_encode(t_strdup_vprintf(fmt, args), body); - va_end(args); + va_start(args, fmt); + message_header_encode(t_strdup_vprintf(fmt, args), body); + va_end(args); - rfc2822_header_write(header, name, str_c(body)); - } T_END; + rfc2822_header_write(header, name, str_c(body)); } -- 1.7.10.2 From 5989f3dbb6dc5ad54d4d6e8fa5367e34b6cded34 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Mon, 20 May 2013 21:20:04 +0200 Subject: [PATCH] Sieve extprograms plugin: Fixed interaction between pipe command and remote script service. The output from the script service was never read, causing a broken pipe error at the script service. Apparently, this was broken since the I/O handling for extprograms was last revised. diff --git a/src/plugins/sieve-extprograms/script-client-remote.c b/src/plugins/sieve-extprograms/script-client-remote.c index 54b8356..f437e04 100644 --- a/src/plugins/sieve-extprograms/script-client-remote.c +++ b/src/plugins/sieve-extprograms/script-client-remote.c @@ -183,7 +183,7 @@ static void script_client_remote_connected(struct script_client *sclient) io_remove(&sclient->io); script_client_init_streams(sclient); - if ( sclient->output != NULL && !slclient->noreply ) { + if ( !slclient->noreply ) { sclient->script_input = script_client_istream_create (sclient, sclient->script_input); } diff --git a/src/plugins/sieve-extprograms/script-client.c b/src/plugins/sieve-extprograms/script-client.c index 52832e8..06a5d5d 100644 --- a/src/plugins/sieve-extprograms/script-client.c +++ b/src/plugins/sieve-extprograms/script-client.c @@ -145,7 +145,7 @@ static int script_client_script_output(struct script_client *sclient) if ( input == NULL ) { o_stream_unref(&sclient->script_output); - if ( sclient->output == NULL ) { + if ( sclient->script_input == NULL ) { script_client_disconnect(sclient, FALSE); } else { sclient->close_output(sclient); @@ -164,17 +164,19 @@ static void script_client_script_input(struct script_client *sclient) size_t size; int ret = 0; - if ( input != NULL && output != NULL ) { - + if ( input != NULL ) { while ( (ret=i_stream_read_data(input, &data, &size, 0)) > 0 ) { - ssize_t sent; + if ( output != NULL ) { + ssize_t sent; - if ( (sent=o_stream_send(output, data, size)) < 0 ) { - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return; + if ( (sent=o_stream_send(output, data, size)) < 0 ) { + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return; + } + size = (size_t)sent; } - i_stream_skip(input, sent); + i_stream_skip(input, size); } if ( ret < 0 ) { -- 1.7.10.2 From d1951226a0c84850d3afc0335e21f0164ca16817 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Sun, 12 May 2013 19:14:18 +0200 Subject: [PATCH] Fixed setting name in example-config/conf.d/20-managesieve.conf. diff --git a/INSTALL b/INSTALL index d584340..edfed5e 100644 --- a/INSTALL +++ b/INSTALL @@ -127,8 +127,7 @@ plugin section of the config file (default values are shown if applicable): The Pigeonhole Sieve interpreter can have plugins of its own. Using this setting, the used plugins can be specified. Check the Dovecot wiki (wiki2.dovecot.org) or the pigeonhole website (http://pigeonhole.dovecot.org) - for available plugins. The sieve_extprograms plugin is included in this - release. + for available plugins. sieve_user_log = The path to the file where the user log file is written. If not configured, a @@ -368,7 +367,7 @@ Sieve Interpreter - Extension Configuration configuration options. Refer to doc/extensions/include.txt for settings specific to the include extension. -- Spamtest and virustest extensions: +- Spamtest and Virustest extensions: Using the spamtest and virustest extensions (RFC 5235), the Sieve language provides a uniform and standardized command interface for evaluating spam and @@ -383,29 +382,6 @@ Sieve Interpreter - Extension Configuration configuration and are not enabled for use by default. Refer to doc/extensions/spamtest-virustest.txt for configuration information. -- Vnd.dovecot.duplicate extension: - - The vnd.dovecot.duplicate extension augments the Sieve filtering - implementation with a test that allows detecting and handling duplicate - message deliveries, e.g. as caused by mailinglists when people reply both to - the mailinglist and the user directly. - - The vnd.dovecot.duplicate extension requires explicit configuration and is not - enabled for use by default. Refer to doc/extensions/vnd.dovecot.duplicate.txt - for configuration information. - -- Extprograms plugin; - vnd.dovovecot.pipe, vnd.dovecot.filter, vnd.dovecot.execute extensions: - - The "sieve_extprograms" plugin provides extensions to the Sieve filtering - language adding new action commands for invoking a predefined set of external - programs. Messages can be piped to or filtered through those programs and - string data can be input to and retrieved from those programs. - - This plugin and the extensions it provides require explicit configuration and - are not enabled for use by default. Refer to doc/plugins/sieve_extprograms.txt - for more information. - Sieve Interpreter - Migration from CMUSieve (Dovecot v1.0/v1.1) --------------------------------------------------------------- @@ -645,8 +621,7 @@ interpreter on your particular platform. Note that the test suite is not available when this package is compiled against the Dovecot headers only. The test suite executes a list of test cases and halts when one of them fails. If it executes all test cases successfully, the test suite finishes. You can execute -the basic test suite using `make test`, which does not include the plugins. You -can test the plugins using `make test-plugins`. +the test suite using `make test`. A failing test case is always a bug and a report is greatly appreciated. diff --git a/Makefile.am b/Makefile.am index 50d3ba8..6e91416 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,7 @@ ChangeLog: .hg/dirstate hg log --style=changelog > ChangeLog endif -# Testsuite tests (FIXME: ugly) +# Testsuite tests TESTSUITE_BIN = $(top_builddir)/src/testsuite/testsuite $(TESTSUITE_OPTIONS) @@ -76,7 +76,6 @@ test_cases = \ tests/extensions/include/variables.svtest \ tests/extensions/include/once.svtest \ tests/extensions/include/twice.svtest \ - tests/extensions/include/optional.svtest \ tests/extensions/include/rfc.svtest \ tests/extensions/include/execute.svtest \ tests/extensions/imap4flags/basic.svtest \ @@ -148,26 +147,8 @@ test_cases = \ $(test_cases): @$(TEST_BIN) $(top_srcdir)/$@ -TEST_EXTPROGRAMS_BIN = $(TEST_BIN) \ - -P src/plugins/sieve-extprograms/.libs/sieve_extprograms +.PHONY: $(test_cases) -extprograms_test_cases = \ - tests/plugins/extprograms/errors.svtest \ - tests/plugins/extprograms/pipe/command.svtest \ - tests/plugins/extprograms/pipe/errors.svtest \ - tests/plugins/extprograms/pipe/execute.svtest \ - tests/plugins/extprograms/filter/command.svtest \ - tests/plugins/extprograms/filter/errors.svtest \ - tests/plugins/extprograms/filter/execute.svtest \ - tests/plugins/extprograms/execute/command.svtest \ - tests/plugins/extprograms/execute/errors.svtest \ - tests/plugins/extprograms/execute/execute.svtest - -$(extprograms_test_cases): - @$(TEST_EXTPROGRAMS_BIN) $(top_srcdir)/$@ - -.PHONY: $(test_cases) $(extprograms_test_cases) test: $(test_cases) -test-plugins: $(extprograms_test_cases) check: check-am test all-am diff --git a/NEWS b/NEWS index 70c7929..9c0842d 100644 --- a/NEWS +++ b/NEWS @@ -1,23 +1,3 @@ -v0.4.0 09-05-2013 Stephan Bosch - - + Added doveadm-sieve plugin that provides the possibility to synch Sieve - scripts using doveadm sync along with the user's mailboxes. - + Added the Sieve extprograms plugin to the main Pigeonhole package. It is - still a plugin, but it is now included so that a separate compile is no - longer necessary and distributors are likely to include it. The extprograms - plugin provides Sieve language extensions that allows executing - (administrator-controlled) external programs for message delivery, - message filtering and string manipulation. Refer to - doc/plugins/sieve_extprograms.txt for more information. - + Added debug message showing Pigeonhole version at initialization. Makes it - very clear that the plugin is properly loaded. - + Finished implementation of the Sieve include extension. It should now - fully conform to RFC 6609. The main addition is the new :optional tag which - makes the include command ignore missing included scripts without an error. - + Finished implementation of the Sieve environment extension as much as - possible. Environment items "location", "phase" and "domain" now also - return a usable value. - v0.3.5 09-05-2013 Stephan Bosch - Sieve editheader extension: fixed interaction with the Sieve body extension. @@ -49,7 +29,7 @@ v0.3.4 06-04-2013 Stephan Bosch sieve_vacation_dont_check_recipient is active. - Sieve tools: the -D option wasn't enabled and documented for all tools. - Siev dict script storage: fixed potential segfault occuring when dict - initialization fails. + initialization fails. - ManageSieve: fixed bug in skipping of CRLF at end of AUTHENTICATE command. - ManageSieve: fixed handling of unkown commands pre-login. - Fixed compile on Mageia Linux. diff --git a/README b/README index d4e68b4..4e3c00a 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Pigeonhole for Dovecot v2.2 +Pigeonhole for Dovecot v2.1 Introduction ============ @@ -100,15 +100,17 @@ following list outlines the implementation status of each supported extension: copy (RFC 3894): fully supported. body (RFC 5173): almost fully supported, but the text body-transform - implementation is simple. - environment (RFC 5183): fully supported (v0.4.0+). + implementation is simple and some issues make it still not completely + RFC compliant. + environment (RFC 5183): basic support is provided (v0.1.5+). variables (RFC 5229): fully supported. vacation (RFC 5230): fully supported. + vacation-seconds (RFC 6131): fully supported (v0.2.3+). relational (RFC 5231): fully supported. imap4flags (RFC 5232): fully supported. subaddress (RFC 5233): fully supported, but with limited configurability. - spamtest and virustest (RFC 5235): fully supported (v0.1.16+). + spamtest and virustest (RFC 5235): fully supported (v0.1.16+), but + currently considered experimental. date (RFC 5260; Section 4): fully supported (v0.1.12+). editheader (RFC 5293): fully supported (v0.3.0+). reject (RFC 5429; Section 2.2): fully supported. @@ -119,7 +121,8 @@ following list outlines the implementation status of each supported extension: ihave (RFC 5463): fully supported (v0.2.4+). mailbox (RFC 5490; Section 3): fully supported (v0.1.10+), but ACL permissions are not verified for mailboxexists. - include (RFC 6609): fully supported (v0.4.0+) + include (draft v05; not latest version): almost fully supported, but + interaction with ManageSieve is not in accordance with specification. regex (draft v08; not latest version): almost fully supported, but UTF-8 is not supported. @@ -131,22 +134,6 @@ following list outlines the implementation status of each supported extension: The availability of these deprecated extensions is disabled by default. - The following Dovecot-specific Sieve extensions are available: - - vnd.dovecot.debug (v0.3.0+): - Allows logging debug messages - vnd.dovecot.duplicate (v0.3.1+): - Allows detecting duplicate message deliveries based on message ID and - other criteria. - vnd.dovecot.pipe (v0.4.0+; sieve_extprograms plugin): - Implements piping messages to a pre-defined set of external programs - vnd.dovecot.filter (v0.4.0+; sieve_extprograms plugin): - Implements filtering messages through a pre-defined set of external - programs - vnd.dovecot.execute (v0.4.0+; sieve_extprograms plugin): - Implements executing a pre-defined set of external programs with the - option to process string data through the external program - The following extensions are under development: ereject (RFC 5429; page 4): implemented, but currently equal to reject @@ -174,23 +161,19 @@ exist in the src/sieve-tools/ directory of this package. After installation, these are available at your $prefix/bin directory. The following commands are installed: -sievec - Compiles sieve scripts into a binary representation for later - execution. Refer to the next section on manually compiling Sieve - scripts. - -sieve-test - This is a universal Sieve test tool for testing the effect of a - Sieve script on a particular message. It allows compiling, - running and testing Sieve scripts. It can either be used to - display the actions that would be performed on the provided test - message or it can be used to test the actual delivery of the - message and show the messages that would normally be sent through - SMTP. +sievec - Compiles sieve scripts into a binary representation for later + execution. Refer to the next section on manually compiling Sieve + scripts. -sieve-dump - Dumps the content of a Sieve binary file for (development) - debugging purposes. +sieve-test - This is a universal Sieve test tool for testing the effect of a + Sieve script on a particular message. It allows compiling, running + and testing Sieve scripts. It can either be used to display the + actions that would be performed on the provided test message or it + can be used to test the actual delivery of the message and show the + messages that would normally be sent through SMTP. -sieve-filter - Allow running Sieve filters on messages already stored in a - mailbox. +sieve-dump - Dumps the content of a Sieve binary file for (development) + debugging purposes. When installed, man pages are also available for these commands. In this package the man pages are present in doc/man and can be viewed before install using diff --git a/TODO b/TODO index 7771e43..81c8766 100644 --- a/TODO +++ b/TODO @@ -1,21 +1,28 @@ -Current activities: +Active development is moved to Pigeonhole v0.4 for Dovecot v2.2. The v0.3.x +series for Dovecot v2.1 is maintained for bug fixes and small updates. Check +http://hg.rename-it.nl/dovecot-2.2-pigeonhole/raw-file/tip/TODO for the most +up-to-date TODO list. -* Make the sieve storage a base class with (possibly) various implementations, - just like mail-storage. This aims to provide support for alternate types - of script storage like LDAP or SQL database. - - Implement read/write script storages for using ManageSieve with dict - database - - Implement infrastructure for loading a sequence of global scripts from - a database (for sieve_before/sieve_after). - -Parallel plugin-based efforts (on hold at the moment): +Parallel plugin-based efforts: * Implement enotify xmpp method as a plugin. * Implement metadata and servermetadata extensions as a plugin. - - Update to native Dovecot metadata implementation once it is created. + - Compiles against dovecot metadata plugin, as currently developed by + Dennis Schridde. -Next (mostly in order of descending priority/precedence): +Open TODO issues for this revision: +* 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). +* 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). @@ -27,6 +34,7 @@ Next (mostly in order of descending priority/precedence): (posix regexes actually do support utf8, but only when locale is set accordingly) * Finish body extension: + - Implement proper :content "multipart" behavior - Build test cases for decoding MIME encodings to UTF-8 * Cleanup the test suite - Restructure test scripts @@ -43,13 +51,6 @@ Next (mostly in order of descending priority/precedence): - Verify outgoing mail addresses at runtime when necessary (e.g. after variables substitution) - Improve handling of invalid addresses in headers (requires Dovecot changes) -* Improve sieve_extprograms plugin: - - Redesign (forcible) local script termination. It should use SIGCHLD and - a ioloop-based timeout. - - Add facility to trigger a temporary failure condition when a program - fails rather than an implicit keep. - - Add a method to implicitly pass environment variables such as SENDER and - RECIPIENT through the script socket service. * Properly implement Sieve internationalization support (utf-8 handling), currently it is not complete: - Make this implementation fully conform section 2.7.2 of RFC5228 (Comparisons @@ -62,8 +63,7 @@ Next (mostly in order of descending priority/precedence): - Review the specification documents and check whether the given requirements are tested at least once. * Fix ManageSieve proxy to recognize response codes from the backend and forward - them to the user if appropriate/safe. Probably means implementing a proper - ManageSieve client library. + them to the user if appropriate/safe. * Test ManageSieve behavior thoroughly: - Test pipelined behavior - Test proxy authentication @@ -74,7 +74,7 @@ Next (mostly in order of descending priority/precedence): * Build a server with test mail accounts that processes lots and lots of mail (e.g. spam, mailing lists etc.) -Low priority items: +* ## MAKE A FOURTH MAIN RELEASE (0.4.x) ## * Implement extlists extension as a plugin * Enotify extension: detect use of variable values extracted from the message @@ -90,8 +90,8 @@ Low priority items: * Warn during compile if using non-existent folders. * Implement IMAP plugin for IMAPSieve support: - - Requires some sort of Sieve transaction support. - - Requires (IMAP) METADATA support in Dovecot. + - Requires the same Sieve transaction support as the sieve-filter tool needs. + - Requires (IMAP) metadata support in Dovecot. - This may include support for manually running a script on a set of messages through IMAP (no specification for something like this is available; we will have to provide our own) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 0c71710..0000000 --- a/configure.ac +++ /dev/null @@ -1,139 +0,0 @@ -AC_INIT([Pigeonhole], [0.4.0], [dovecot@dovecot.org], [dovecot-2.2-pigeonhole]) -AC_CONFIG_AUX_DIR([.]) -AC_CONFIG_SRCDIR([src]) -AC_CONFIG_MACRO_DIR([m4]) - -# Autoheader is not needed and does more harm than good for this package. However, it is -# tightly integrated in autoconf/automake and therefore it is difficult not to use it. As -# a workaround we give autoheader a dummy config header to chew on and we handle the -# real config header ourselves. -AC_CONFIG_HEADERS([dummy-config.h pigeonhole-config.h]) - -AC_DEFINE_UNQUOTED(PIGEONHOLE_NAME, "$PACKAGE_NAME", - [Define to the full name of Pigeonhole for Dovecot.]) -AC_DEFINE_UNQUOTED(PIGEONHOLE_VERSION, "$PACKAGE_VERSION", - [Define to the version of Pigeonhole for Dovecot.]) - -AM_INIT_AUTOMAKE([no-define foreign tar-ustar]) - -AM_MAINTAINER_MODE - -AC_PROG_CC -AC_PROG_CPP -AC_PROG_LIBTOOL - -# Couple with Dovecot -# - -DC_DOVECOT -DC_DOVECOT_MODULEDIR -LIBDOVECOT_INCLUDE="$LIBDOVECOT_INCLUDE $LIBDOVECOT_STORAGE_INCLUDE" -CFLAGS="$DOVECOT_CFLAGS" -LIBS="$DOVECOT_LIBS" -AC_SUBST(LIBDOVECOT_INCLUDE) - -# Define Sieve documentation install dir -# - -sieve_docdir='${dovecot_docdir}/sieve' -AC_SUBST(sieve_docdir) - -# Extensions under development -# - -AC_ARG_WITH(unfinished-features, -[AC_HELP_STRING([--with-unfinished-features], - [Build unfinished new features/extensions [default=no]])], - if test x$withval = xno || test x$withval = xauto; then - want_unfinished_features=$withval - else - want_unfinished_features=yes - fi, - want_unfinished_features=no) -AM_CONDITIONAL(BUILD_UNFINISHED, test "$want_unfinished_features" = "yes") - -if test "$want_unfinished_features" = "yes"; then - AC_DEFINE(HAVE_SIEVE_UNFINISHED,, - [Define to build unfinished features/extensions.]) -fi - -# -# - -AC_ARG_WITH(docs, -[ --with-docs Install documentation (default)], - if test x$withval = xno; then - want_docs=no - else - want_docs=yes - fi, - want_docs=yes) -AM_CONDITIONAL(BUILD_DOCS, test "$want_docs" = "yes") - -AC_ARG_ENABLE(valgrind, -[AC_HELP_STRING([--enable-valgrind], [Enable Valgrind memory leak checks in testsuite [default=no]])], - if test x$enableval = xno || test x$enableval = xauto; then - want_valgrind=$enableval - else - want_valgrind=yes - fi, - want_valgrind=no) -AM_CONDITIONAL(TESTSUITE_VALGRIND, test "$want_valgrind" = "yes") - -AC_ARG_WITH(managesieve, -[AC_HELP_STRING([--with-managesieve], - [Build ManageSieve service [default=yes]])], - if test x$withval = xno || test x$withval = xauto; then - want_managesieve=$withval - else - want_managesieve=yes - fi, - want_managesieve=yes) -AM_CONDITIONAL(BUILD_MANAGESIEVE, test "$want_managesieve" = "yes") - -AC_CONFIG_FILES([ -Makefile -doc/Makefile -doc/man/Makefile -doc/example-config/Makefile -doc/example-config/conf.d/Makefile -doc/rfc/Makefile -src/Makefile -src/lib-sieve/Makefile -src/lib-sieve/plugins/Makefile -src/lib-sieve/plugins/vacation/Makefile -src/lib-sieve/plugins/subaddress/Makefile -src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile -src/lib-sieve/plugins/relational/Makefile -src/lib-sieve/plugins/regex/Makefile -src/lib-sieve/plugins/imap4flags/Makefile -src/lib-sieve/plugins/copy/Makefile -src/lib-sieve/plugins/include/Makefile -src/lib-sieve/plugins/body/Makefile -src/lib-sieve/plugins/variables/Makefile -src/lib-sieve/plugins/enotify/Makefile -src/lib-sieve/plugins/enotify/mailto/Makefile -src/lib-sieve/plugins/notify/Makefile -src/lib-sieve/plugins/environment/Makefile -src/lib-sieve/plugins/mailbox/Makefile -src/lib-sieve/plugins/date/Makefile -src/lib-sieve/plugins/spamvirustest/Makefile -src/lib-sieve/plugins/ihave/Makefile -src/lib-sieve/plugins/editheader/Makefile -src/lib-sieve/plugins/vnd.dovecot/Makefile -src/lib-sieve/plugins/vnd.dovecot/debug/Makefile -src/lib-sieve/plugins/vnd.dovecot/duplicate/Makefile -src/lib-sieve-tool/Makefile -src/lib-sievestorage/Makefile -src/lib-managesieve/Makefile -src/plugins/Makefile -src/plugins/doveadm-sieve/Makefile -src/plugins/lda-sieve/Makefile -src/plugins/sieve-extprograms/Makefile -src/sieve-tools/Makefile -src/managesieve/Makefile -src/managesieve-login/Makefile -src/testsuite/Makefile -stamp.h]) - -AC_OUTPUT diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..834aa41 --- /dev/null +++ b/configure.in @@ -0,0 +1,137 @@ +AC_INIT([Pigeonhole], [0.3.5], [dovecot@dovecot.org], [dovecot-2.1-pigeonhole]) +AC_CONFIG_AUX_DIR([.]) +AC_CONFIG_SRCDIR([src]) +AC_CONFIG_MACRO_DIR([m4]) + +# Autoheader is not needed and does more harm than good for this package. However, it is +# tightly integrated in autoconf/automake and therefore it is difficult not to use it. As +# a workaround we give autoheader a dummy config header to chew on and we handle the +# real config header ourselves. +AC_CONFIG_HEADERS([dummy-config.h pigeonhole-config.h]) + +AC_DEFINE_UNQUOTED(PIGEONHOLE_NAME, "$PACKAGE_NAME", + [Define to the full name of Pigeonhole for Dovecot.]) +AC_DEFINE_UNQUOTED(PIGEONHOLE_VERSION, "$PACKAGE_VERSION", + [Define to the version of Pigeonhole for Dovecot.]) + +AM_INIT_AUTOMAKE([no-define foreign tar-ustar]) + +AM_MAINTAINER_MODE + +AC_PROG_CC +AC_PROG_CPP +AC_PROG_LIBTOOL + +# Couple with Dovecot +# + +DC_DOVECOT +DC_DOVECOT_MODULEDIR +LIBDOVECOT_INCLUDE="$LIBDOVECOT_INCLUDE $LIBDOVECOT_STORAGE_INCLUDE" +CFLAGS="$DOVECOT_CFLAGS" +LIBS="$DOVECOT_LIBS" +AC_SUBST(LIBDOVECOT_INCLUDE) + +# Define Sieve documentation install dir +# + +sieve_docdir='${dovecot_docdir}/sieve' +AC_SUBST(sieve_docdir) + +# Extensions under development +# + +AC_ARG_WITH(unfinished-features, +[AC_HELP_STRING([--with-unfinished-features], + [Build unfinished new features/extensions [default=no]])], + if test x$withval = xno || test x$withval = xauto; then + want_unfinished_features=$withval + else + want_unfinished_features=yes + fi, + want_unfinished_features=no) +AM_CONDITIONAL(BUILD_UNFINISHED, test "$want_unfinished_features" = "yes") + +if test "$want_unfinished_features" = "yes"; then + AC_DEFINE(HAVE_SIEVE_UNFINISHED,, + [Define to build unfinished features/extensions.]) +fi + +# +# + +AC_ARG_WITH(docs, +[ --with-docs Install documentation (default)], + if test x$withval = xno; then + want_docs=no + else + want_docs=yes + fi, + want_docs=yes) +AM_CONDITIONAL(BUILD_DOCS, test "$want_docs" = "yes") + +AC_ARG_ENABLE(valgrind, +[AC_HELP_STRING([--enable-valgrind], [Enable Valgrind memory leak checks in testsuite [default=no]])], + if test x$enableval = xno || test x$enableval = xauto; then + want_valgrind=$enableval + else + want_valgrind=yes + fi, + want_valgrind=no) +AM_CONDITIONAL(TESTSUITE_VALGRIND, test "$want_valgrind" = "yes") + +AC_ARG_WITH(managesieve, +[AC_HELP_STRING([--with-managesieve], + [Build ManageSieve service [default=yes]])], + if test x$withval = xno || test x$withval = xauto; then + want_managesieve=$withval + else + want_managesieve=yes + fi, + want_managesieve=yes) +AM_CONDITIONAL(BUILD_MANAGESIEVE, test "$want_managesieve" = "yes") + +AC_CONFIG_FILES([ +Makefile +doc/Makefile +doc/man/Makefile +doc/example-config/Makefile +doc/example-config/conf.d/Makefile +doc/rfc/Makefile +src/Makefile +src/lib-sieve/Makefile +src/lib-sieve/plugins/Makefile +src/lib-sieve/plugins/vacation/Makefile +src/lib-sieve/plugins/subaddress/Makefile +src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile +src/lib-sieve/plugins/relational/Makefile +src/lib-sieve/plugins/regex/Makefile +src/lib-sieve/plugins/imap4flags/Makefile +src/lib-sieve/plugins/copy/Makefile +src/lib-sieve/plugins/include/Makefile +src/lib-sieve/plugins/body/Makefile +src/lib-sieve/plugins/variables/Makefile +src/lib-sieve/plugins/enotify/Makefile +src/lib-sieve/plugins/enotify/mailto/Makefile +src/lib-sieve/plugins/notify/Makefile +src/lib-sieve/plugins/environment/Makefile +src/lib-sieve/plugins/mailbox/Makefile +src/lib-sieve/plugins/date/Makefile +src/lib-sieve/plugins/spamvirustest/Makefile +src/lib-sieve/plugins/ihave/Makefile +src/lib-sieve/plugins/editheader/Makefile +src/lib-sieve/plugins/vnd.dovecot/Makefile +src/lib-sieve/plugins/vnd.dovecot/debug/Makefile +src/lib-sieve/plugins/vnd.dovecot/duplicate/Makefile +src/lib-sieve-tool/Makefile +src/lib-sievestorage/Makefile +src/lib-managesieve/Makefile +src/plugins/Makefile +src/plugins/lda-sieve/Makefile +src/sieve-tools/Makefile +src/managesieve/Makefile +src/managesieve-login/Makefile +src/testsuite/Makefile +stamp.h]) + +AC_OUTPUT diff --git a/doc/Makefile.am b/doc/Makefile.am index b5bb82a..58b1b6e 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -10,6 +10,5 @@ endif EXTRA_DIST = \ devel \ extensions \ - plugins \ $(docfiles) diff --git a/doc/example-config/conf.d/20-managesieve.conf b/doc/example-config/conf.d/20-managesieve.conf index 4d51555..784b660 100644 --- a/doc/example-config/conf.d/20-managesieve.conf +++ b/doc/example-config/conf.d/20-managesieve.conf @@ -30,7 +30,7 @@ #service managesieve { # Max. number of ManageSieve processes (connections) - #process_count = 1024 + #process_limit = 1024 #} # Service configuration diff --git a/doc/example-config/conf.d/90-sieve-extprograms.conf b/doc/example-config/conf.d/90-sieve-extprograms.conf deleted file mode 100644 index 17dcb77..0000000 --- a/doc/example-config/conf.d/90-sieve-extprograms.conf +++ /dev/null @@ -1,44 +0,0 @@ -# Sieve Extprograms plugin configuration - -# Don't forget to add the sieve_extprograms plugin to the sieve_plugins setting. -# Also enable the extensions you need (one or more of vnd.dovecot.pipe, -# vnd.dovecot.filter and vnd.dovecot.execute) by adding these to the -# sieve_extensions or sieve_global_extensions settings. Restricting these -# extensions to a global context using sieve_global_extensions is recommended. - -plugin { - - # The directory where the program sockets are located for the - # vnd.dovecot.pipe, vnd.dovecot.filter and vnd.dovecot.execute extension - # respectively. The name of each unix socket contained in that directory - # directly maps to a program-name referenced from the Sieve script. - #sieve_pipe_socket_dir = sieve-pipe - #sieve_filter_socket_dir = sieve-filter - #sieve_execute_socket_dir = sieve-execute - - # The directory where the scripts are located for direct execution by the - # vnd.dovecot.pipe, vnd.dovecot.filter and vnd.dovecot.execute extension - # respectively. The name of each script contained in that directory - # directly maps to a program-name referenced from the Sieve script. - #sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe - #sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter - #sieve_execute_bin_dir = /usr/lib/dovecot/sieve-execute -} - -# An example program service called 'do-something' to pipe messages to -#service do-something { - # Define the executed script as parameter to the sieve service - #executable = script /usr/lib/dovecot/sieve-pipe/do-something.sh - - # Use some unprivileged user for executing the program - #user = dovenull - - # The unix socket located in the sieve_pipe_socket_dir (as defined in the - # plugin {} section above) - #unix_listener sieve-pipe/do-something { - # LDA/LMTP must have access - # user = vmail - # mode = 0600 - #} -#} - diff --git a/doc/example-config/conf.d/90-sieve.conf b/doc/example-config/conf.d/90-sieve.conf index 1ebf9f3..128d9c3 100644 --- a/doc/example-config/conf.d/90-sieve.conf +++ b/doc/example-config/conf.d/90-sieve.conf @@ -68,7 +68,6 @@ plugin { # setting, the used plugins can be specified. Check the Dovecot wiki # (wiki2.dovecot.org) or the pigeonhole website # (http://pigeonhole.dovecot.org) for available plugins. - # The sieve_extprograms plugin is included in this release. #sieve_plugins = # The separator that is expected between the :user and :detail diff --git a/doc/example-config/conf.d/Makefile.am b/doc/example-config/conf.d/Makefile.am index 8cea566..12a606c 100644 --- a/doc/example-config/conf.d/Makefile.am +++ b/doc/example-config/conf.d/Makefile.am @@ -3,8 +3,7 @@ pkgsysconfdir = $(sysconfdir)/dovecot exampledir = $(dovecot_docdir)/example-config/conf.d example_DATA = \ 20-managesieve.conf \ - 90-sieve.conf \ - 90-sieve-extprograms.conf + 90-sieve.conf EXTRA_DIST = \ $(example_DATA) diff --git a/doc/man/pigeonhole.7.in b/doc/man/pigeonhole.7.in index 75db9ca..f86d5e6 100644 --- a/doc/man/pigeonhole.7.in +++ b/doc/man/pigeonhole.7.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file -.TH "PIGEONHOLE" 7 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" +.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file +.TH "PIGEONHOLE" 7 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME pigeonhole \- Overview of the Pigeonhole project\(aqs Sieve support for the diff --git a/doc/man/sieve-dump.1.in b/doc/man/sieve-dump.1.in index 3ea65f7..faf886f 100644 --- a/doc/man/sieve-dump.1.in +++ b/doc/man/sieve-dump.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-DUMP" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" +.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-DUMP" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME sieve\-dump \- Pigeonhole\(aqs Sieve script binary dump tool diff --git a/doc/man/sieve-filter.1.in b/doc/man/sieve-filter.1.in index df3fc36..1566dfa 100644 --- a/doc/man/sieve-filter.1.in +++ b/doc/man/sieve-filter.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-FILTER" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" +.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-FILTER" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" .SH NAME sieve\-filter \- Pigeonhole\(aqs Sieve mailbox filter tool diff --git a/doc/man/sieve-test.1.in b/doc/man/sieve-test.1.in index 4331250..946c5b7 100644 --- a/doc/man/sieve-test.1.in +++ b/doc/man/sieve-test.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-TEST" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" +.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-TEST" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" .SH NAME sieve\-test \- Pigeonhole\(aqs Sieve script tester .\"------------------------------------------------------------------------ diff --git a/doc/man/sievec.1.in b/doc/man/sievec.1.in index 9307800..28ef9e3 100644 --- a/doc/man/sievec.1.in +++ b/doc/man/sievec.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file -.TH "SIEVEC" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" +.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file +.TH "SIEVEC" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME sievec \- Pigeonhole\(aqs Sieve script compiler diff --git a/doc/plugins/sieve_extprograms.txt b/doc/plugins/sieve_extprograms.txt deleted file mode 100644 index d2b59a6..0000000 --- a/doc/plugins/sieve_extprograms.txt +++ /dev/null @@ -1,175 +0,0 @@ -Sieve Extprograms plugin for Pigeonhole - -Relevant specifications -======================= - - doc/rfc/spec-bosch-sieve-extprograms.txt - -Introduction -============ - -Sieve (RFC 5228) is a highly extensible machine language specifically tailored -for internet message filtering. For the Dovecot Secure IMAP server, Sieve -support is provided by the Pigeonhole Sieve plugin. This package includes a -plugin for Pigeonhole called "sieve_extprograms", which extends the Sieve -filtering implementation with action commands for invoking a predefined set of -external programs. Messages can be piped to or filtered through those programs -and string data can be input to and retrieved from those programs. - -The Sieve language is explicitly designed to be powerful enough to be useful yet -limited in order to allow for a safe server-side filtering system. Therefore, -the base specification of the language makes it impossible for users to do -anything more complex (and dangerous) than write simple mail filters. One of the -consequences of this security-minded design is that users cannot execute -external programs from their mail filter. Particularly for server-side filtering -setups in which mail accounts have no corresponding system account, allowing the -execution of arbitrary programs from the mail filter can be a significant -security risk. However, such functionality can also be very useful, for instance -to easily implement a custom action or external effect that Sieve normally -cannot provide. - -The "sieve_extprograms" plugin provides an extension to the Sieve filtering -language adding new action commands for invoking a predefined set of external -programs. To mitigate the security concerns, the external programs cannot be -chosen arbitrarily; the available programs are restricted through administrator -configuration. - -This extension is specific to the Pigeonhole Sieve implementation for the -Dovecot Secure IMAP server. It will therefore most likely not be supported by -web interfaces or GUI-based Sieve editors. This extension is primarily meant for -use in small setups or global scripts that are managed by the systems -administrator. - -Implementation Status ---------------------- - -The "vnd.dovecot.pipe", "vnd.dovecot.filter" and "vnd.dovecot.execute" Sieve -language extensions introduced by this plugin are vendor-specific with draft -status and their implementation for Pigeonhole is experimental, which means that -the language extensions are still subject to change and that the current -implementation is not thoroughly tested. - -Configuration -============= - -The plugin is activated by adding it to the sieve_plugins setting: - -sieve_plugins = sieve_extprograms - -This plugin registers the "vnd.dovecot.pipe", "vnd.dovecot.filter" and -"vnd.dovecot.execute" extensions with the Sieve interpreter. However, these -extensions are not enabled by default and thus need to be enabled explicitly. It -is recommended to restrict the use of these extensions to global context by -adding these to the "sieve_global_extensions" setting. If personal user scripts -also need to directly access external programs, the extensions need to be added -to the "sieve_extensions" setting. - -The commands introduced by the Sieve language extensions in this plugin can -directly pipe a message or string data to an external program (typically a shell -script) by forking a new process. Alternatively, these can connect to a unix -socket behind which a Dovecot script service is listening to start the external -program, e.g. to execute as a different user or for added security. - -The program name specified for the new Sieve "pipe", "filter" and "execute" -commands is used to find the program or socket in a configured directory. -Separate directories are specified for the sockets and the directly executed -binaries. The socket directory is searched first. Since the use of "/" in -program names is prohibited, it is not possible to build a hierarchical -structure. - -Directly forked programs are executed with a limited set of environment -variables: HOME, USER, HOST, SENDER, RECIPIENT and ORIG_RECIPIENT. Programs -executed through the script-pipe socket service currently have no environment -set at all. - -If a shell script is expected to read a message or string data, it must fully -read the provided input until the data ends with EOF, otherwise the Sieve action -invoking the program will fail. The action will also fail when the shell script -returns a nonzero exit code. Standard output is available for returning a -message (for the filter command) or string data (for the execute command) to the -Sieve interpreter. Standard error is written to the LDA log file. - -The three extensions introduced by this plugin - "vnd.dovecot.pipe", -"vnd.dovecot.filter" and "vnd.dovecot.pipe" - each have separate but similar -configuration. The settings that specify a time period are specified in -s(econds), unless followed by a d(ay), h(our) or m(inute) specifier character. -The following configuration settings are used, for which "" in the -setting name is replaced by either "pipe", "filter" or "execute" depending on -which extension is being configured. - -sieve__socket_dir = - Points to a directory relative to the Dovecot base_dir where the plugin looks - for script service sockets. - -sieve__bin_dir = - Points to a directory where the plugin looks for programs (shell scripts) to - execute directly and pipe messages to. - -sieve__exec_timeout = 10s - Configures the maximum execution time after which the program is forcefully - terminated. - -Examples --------- - -Example 1: socket service for "pipe" and "execute" - -plugin { - sieve = ~/.dovecot.sieve - - sieve_plugins = sieve_extprograms - sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute - - # pipe sockets in /var/run/dovecot/sieve-pipe - sieve_pipe_socket_dir = sieve-pipe - - # execute sockets in /var/run/dovecot/sieve-execute - sieve_execute_socket_dir = sieve-execute -} - -service sieve-pipe-script { - # This script is executed for each service connection - executable = script /usr/lib/dovecot/sieve-extprograms/sieve-pipe-action.sh - - # use some unprivileged user for execution - user = dovenull - - # socket name is program-name in Sieve (without sieve-pipe/ prefix) - unix_listener sieve-pipe/sieve-pipe-script { - } -} - -service sieve-execute-action { - # This script is executed for each service connection - executable = script /usr/lib/dovecot/sieve-extprograms/sieve-execute-action.sh - - # use some unprivileged user for execution - user = dovenull - - # socket name is program-name in Sieve (without sieve-execute/ prefix) - unix_listener sieve-execute/sieve-execute-action { - } -} - -Example 2: direct execution for "pipe" and "filter" - -plugin { - sieve = ~/.dovecot.sieve - - sieve_plugins = sieve_extprograms - sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.filter - - # This directory contains the scripts that are available for the pipe command. - sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe - - # This directory contains the scripts that are available for the filter - # command. - sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter -} - -Using -===== - -Refer to doc/rfc/spec-bosch-sieve-extprograms.txt for a specification of the -Sieve language extensions. - diff --git a/m4/dovecot.m4 b/m4/dovecot.m4 index df0520b..2095875 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 11 +# serial 6 AC_DEFUN([DC_DOVECOT_MODULEDIR],[ AC_ARG_WITH(moduledir, @@ -76,8 +76,6 @@ AC_DEFUN([DC_DOVECOT],[ eval `grep '^LIBDOVECOT[[A-Z_]]*=' "$dovecotdir"/dovecot-config` if test "$use_install_dirs" = "no"; then - # the main purpose of these is to fix make distcheck for plugins - # other than that, they don't really make much sense dovecot_pkgincludedir='$(pkgincludedir)' dovecot_pkglibdir='$(pkglibdir)' dovecot_pkglibexecdir='$(libexecdir)/dovecot' @@ -86,10 +84,10 @@ AC_DEFUN([DC_DOVECOT],[ fi 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], [DOVECOT_COMPRESS_LIBS]) - AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_SSL], [LIBDOVECOT_COMPRESS], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE]) - AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_SSL_DEPS], [LIBDOVECOT_COMPRESS_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS]) - AX_SUBST_L([LIBDOVECOT_INCLUDE], [LIBDOVECOT_LDA_INCLUDE], [LIBDOVECOT_DOVEADM_INCLUDE], [LIBDOVECOT_SERVICE_INCLUDE], [LIBDOVECOT_STORAGE_INCLUDE], [LIBDOVECOT_LOGIN_INCLUDE], [LIBDOVECOT_CONFIG_INCLUDE], [LIBDOVECOT_IMAP_INCLUDE]) + 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]) DC_PLUGIN_DEPS ]) diff --git a/src/Makefile.am b/src/Makefile.am index 6f0d3ef..a04a0ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,6 @@ - sieve_subdirs = \ lib-sieve \ - lib-sievestorage \ plugins \ lib-sieve-tool \ sieve-tools \ @@ -11,6 +9,7 @@ sieve_subdirs = \ if BUILD_MANAGESIEVE managesieve_subdirs = \ lib-managesieve \ + lib-sievestorage \ managesieve \ managesieve-login else diff --git a/src/lib-managesieve/managesieve-parser.c b/src/lib-managesieve/managesieve-parser.c index b231375..d084a71 100644 --- a/src/lib-managesieve/managesieve-parser.c +++ b/src/lib-managesieve/managesieve-parser.c @@ -620,7 +620,7 @@ static ssize_t quoted_string_istream_read(struct istream_private *stream) struct quoted_string_istream *qsstream = (struct quoted_string_istream *)stream; const unsigned char *data; - size_t i, dest, size, avail; + size_t i, dest, size; ssize_t ret = 0; bool slash; @@ -648,7 +648,7 @@ static ssize_t quoted_string_istream_read(struct istream_private *stream) } /* Allocate buffer space */ - if (!i_stream_try_alloc(stream, size, &avail)) + if (!i_stream_get_buffer_space(stream, size, NULL)) return -2; /* Parse quoted string content */ @@ -712,16 +712,10 @@ static ssize_t quoted_string_istream_read(struct istream_private *stream) return ret; } -static int quoted_string_istream_stat +static const struct stat *quoted_string_istream_stat (struct istream_private *stream, bool exact) { - const struct stat *st; - - if (i_stream_stat(stream->parent, exact, &st) < 0) - return -1; - - stream->statbuf = *st; - return 0; + return i_stream_stat(stream->parent, exact); } static struct istream *quoted_string_istream_create diff --git a/src/lib-sieve-tool/mail-raw.c b/src/lib-sieve-tool/mail-raw.c index 7e9f2f8..74074ec 100644 --- a/src/lib-sieve-tool/mail-raw.c +++ b/src/lib-sieve-tool/mail-raw.c @@ -9,6 +9,7 @@ #include "str-sanitize.h" #include "strescape.h" #include "safe-mkstemp.h" +#include "close-keep-errno.h" #include "mkdir-parents.h" #include "abspath.h" #include "message-address.h" @@ -87,7 +88,7 @@ static int seekable_fd_callback if (unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_error("unlink(%s) failed: %m", str_c(path)); - i_close_fd(&fd); + close_keep_errno(fd); return -1; } @@ -188,13 +189,8 @@ static struct mail_raw *mail_raw_create } if ( ret < 0 ) { - if ( mailfile == NULL ) { - i_fatal("Can't open delivery mail as raw: %s", - mailbox_get_last_error(mailr->box, NULL)); - } else { - i_fatal("Can't open delivery mail as raw (file=%s): %s", - mailfile, mailbox_get_last_error(mailr->box, NULL)); - } + i_fatal("Can't open delivery mail as raw: %s", + mailbox_get_last_error(mailr->box, NULL)); } mailr->trans = mailbox_transaction_begin(mailr->box, 0); diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c index 19f78e6..4293542 100644 --- a/src/lib-sieve-tool/sieve-tool.c +++ b/src/lib-sieve-tool/sieve-tool.c @@ -271,10 +271,8 @@ struct sieve_instance *sieve_tool_init_finish memset((void *)&svenv, 0, sizeof(svenv)); svenv.username = username; (void)mail_user_get_home(tool->mail_user_dovecot, &svenv.home_dir); - svenv.hostname = my_hostdomain(); + svenv.hostname = "host.example.com"; svenv.base_dir = tool->mail_user_dovecot->set->base_dir; - svenv.location = SIEVE_ENV_LOCATION_MS; - svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; /* Initialize Sieve Engine */ if ( (tool->svinst=sieve_init diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index f1785b9..f3c0a97 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -5,8 +5,8 @@ #include "ioloop.h" #include "str-sanitize.h" #include "istream.h" +#include "istream-crlf.h" #include "istream-header-filter.h" -#include "ostream.h" #include "rfc2822.h" @@ -318,9 +318,11 @@ static bool act_redirect_send const struct sieve_script_env *senv = aenv->scriptenv; const char *sender = sieve_message_get_sender(msgctx); const char *recipient = sieve_message_get_final_recipient(msgctx); - struct istream *input; - struct ostream *output; + struct istream *input, *crlf_input; void *smtp_handle; + FILE *f; + const unsigned char *data; + size_t size; /* Just to be sure */ if ( !sieve_smtp_available(senv) ) { @@ -333,25 +335,32 @@ static bool act_redirect_send return FALSE; /* Open SMTP transport */ - smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &output); + smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &f); /* Remove unwanted headers */ input = i_stream_create_header_filter - (input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, - N_ELEMENTS(hide_headers), *null_header_filter_callback, (void *)NULL); - - T_BEGIN { - string_t *hdr = t_str_new(256); + (input, HEADER_FILTER_EXCLUDE, hide_headers, + N_ELEMENTS(hide_headers), null_header_filter_callback, NULL); + + /* Make sure the message contains CRLF consistently */ + crlf_input = i_stream_create_crlf(input); + + /* Prepend sieve headers (should not affect signatures) */ + rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); + if ( recipient != NULL ) + rfc2822_header_field_write(f, "X-Sieve-Redirected-From", recipient); + + /* Pipe the message to the outgoing SMTP transport */ + while (i_stream_read_data(crlf_input, &data, &size, 0) > 0) { + if (fwrite(data, size, 1, f) == 0) + break; + i_stream_skip(crlf_input, size); + } - /* Prepend sieve headers (should not affect signatures) */ - rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); - if ( recipient != NULL ) - rfc2822_header_write(hdr, "X-Sieve-Redirected-From", recipient); - o_stream_send(output, str_data(hdr), str_len(hdr)); - } T_END; + // FIXME: handle stream error. Currently, we have no means to abort here. - o_stream_send_istream(output, input); - i_stream_unref(&input); + i_stream_unref(&crlf_input); + i_stream_unref(&input); /* Close SMTP transport */ if ( !sieve_smtp_close(senv, smtp_handle) ) { diff --git a/src/lib-sieve/edit-mail.c b/src/lib-sieve/edit-mail.c index fda48a8..a5871f3 100644 --- a/src/lib-sieve/edit-mail.c +++ b/src/lib-sieve/edit-mail.c @@ -14,7 +14,6 @@ #include "message-header-decode.h" #include "mail-user.h" #include "mail-storage-private.h" -#include "index-mail.h" #include "raw-storage.h" #include "rfc2822.h" @@ -536,9 +535,11 @@ static inline char *_header_decode /* hdr_data is already unfolded */ /* Decode MIME encoded-words. */ - message_header_decode_utf8 - ((const unsigned char *)hdr_data, hdr_data_len, str, FALSE); - return i_strdup(str_c(str)); + if ( message_header_decode_utf8 + ((const unsigned char *)hdr_data, hdr_data_len, str, FALSE)) + return i_strdup(str_c(str)); + + return i_strndup(hdr_data, hdr_data_len); } static int edit_mail_headers_parse @@ -770,7 +771,7 @@ void edit_mail_header_add message_header_encode(value, enc_value); - lines = rfc2822_header_append + lines = rfc2822_header_field_append (data, field_name, str_c(enc_value), edmail->crlf, &field->body_offset); /* Copy to new field */ @@ -1085,13 +1086,6 @@ static uint64_t edit_mail_get_modseq(struct mail *mail) return edmail->wrapped->v.get_modseq(&edmail->wrapped->mail); } -static uint64_t edit_mail_get_pvt_modseq(struct mail *mail) -{ - struct edit_mail *edmail = (struct edit_mail *)mail; - - return edmail->wrapped->v.get_pvt_modseq(&edmail->wrapped->mail); -} - static int edit_mail_get_parts (struct mail *mail, struct message_part **parts_r) { @@ -1238,7 +1232,7 @@ static int edit_mail_get_headers struct _header_index *header_idx; struct _header_field_index *field_idx; const char *const *headers; - ARRAY(const char *) header_values; + ARRAY_DEFINE(header_values, const char *); if ( !edmail->modified || edmail->headers_head == NULL ) { /* Unmodified */ @@ -1411,13 +1405,6 @@ static void edit_mail_update_modseq(struct mail *mail, uint64_t min_modseq) edmail->wrapped->v.update_modseq(&edmail->wrapped->mail, min_modseq); } -static void edit_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq) -{ - struct edit_mail *edmail = (struct edit_mail *)mail; - - edmail->wrapped->v.update_pvt_modseq(&edmail->wrapped->mail, min_pvt_modseq); -} - static void edit_mail_update_pop3_uidl(struct mail *mail, const char *uidl) { struct edit_mail *edmail = (struct edit_mail *)mail; @@ -1452,7 +1439,6 @@ static struct mail_vfuncs edit_mail_vfuncs = { edit_mail_get_keywords, edit_mail_get_keyword_indexes, edit_mail_get_modseq, - edit_mail_get_pvt_modseq, edit_mail_get_parts, edit_mail_get_date, edit_mail_get_received_date, @@ -1463,13 +1449,11 @@ static struct mail_vfuncs edit_mail_vfuncs = { edit_mail_get_headers, edit_mail_get_header_stream, edit_mail_get_stream, - index_mail_get_binary_stream, edit_mail_get_special, edit_mail_get_real_mail, edit_mail_update_flags, edit_mail_update_keywords, edit_mail_update_modseq, - edit_mail_update_pvt_modseq, edit_mail_update_pop3_uidl, edit_mail_expunge, edit_mail_set_cache_corrupted, @@ -1489,7 +1473,7 @@ struct edit_mail_istream { struct _header_field_index *cur_header; - unsigned int header_read:1; + unsigned int read_header; }; static void edit_mail_istream_destroy(struct iostream_private *stream) @@ -1501,146 +1485,17 @@ static void edit_mail_istream_destroy(struct iostream_private *stream) pool_unref(&edstream->pool); } -static ssize_t merge_from_parent -(struct edit_mail_istream *edstream, uoff_t parent_v_offset, - uoff_t parent_end_v_offset, uoff_t copy_v_offset) -{ - struct istream_private *stream = &edstream->istream; - uoff_t v_offset = stream->istream.v_offset; - buffer_t *buffer = edstream->buffer; - const unsigned char *data; - size_t pos, cur_pos; - ssize_t ret; - - if (v_offset < copy_v_offset) { - i_assert(stream->skip == 0); - if (buffer->used < copy_v_offset - v_offset) { - copy_v_offset = v_offset + buffer->used; - } - } - - /* If we are still merging it with our local buffer, we need to update the - * parent seek offset to point to where we left of. - */ - if (v_offset < copy_v_offset) { - parent_v_offset += buffer->used - (copy_v_offset - v_offset) + stream->pos; - if (parent_v_offset >= parent_end_v_offset) - return 0; - cur_pos = 0; - } else { - buffer_set_used_size(buffer, 0); - stream->pos -= stream->skip; - stream->skip = 0; - cur_pos = stream->pos; - } - - i_stream_seek(stream->parent, parent_v_offset); - - /* Read from parent */ - data = i_stream_get_data(stream->parent, &pos); - if (pos > cur_pos) - ret = 0; - else do { - if ((ret = i_stream_read(stream->parent)) == -2) - return -2; - - stream->istream.stream_errno = stream->parent->stream_errno; - stream->istream.eof = stream->parent->eof; - data = i_stream_get_data(stream->parent, &pos); - /* check again, in case the parent stream had been seeked - backwards and the previous read() didn't get us far - enough. */ - } while (pos <= cur_pos && ret > 0); - - /* Don't read beyond parent end offset */ - if (pos > (parent_end_v_offset - parent_v_offset)) - pos = parent_end_v_offset - parent_v_offset; - - if (v_offset < copy_v_offset) { - /* Merging with our local buffer; copying data from parent */ - if (pos > 0) { - ret = (ssize_t)(pos); - buffer_append(buffer, data, pos); - stream->buffer = buffer_get_data(buffer, &pos); - i_assert(ret > 0); - stream->pos = pos; - } else { - ret = (ret == 0 ? 0 : -1); - } - } else { - /* Just passing buffers from parent; no copying */ - ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : - (ret == 0 ? 0 : -1); - stream->buffer = data; - stream->pos = pos; - } - - i_assert(ret != -1 || stream->istream.eof || - stream->istream.stream_errno != 0); - return ret; -} - -static ssize_t merge_modified_headers(struct edit_mail_istream *edstream) -{ - struct istream_private *stream = &edstream->istream; - struct edit_mail *edmail = edstream->mail; - size_t pos; - ssize_t ret = 0; - - if (edstream->cur_header != NULL) { - /* Merge remaining parent buffer, if any */ - if (edstream->buffer->used == 0 && stream->skip < stream->pos ) { - buffer_append(edstream->buffer, - stream->buffer + stream->skip, stream->pos - stream->skip); - } - - /* Add modified headers to buffer */ - while ( edstream->cur_header != NULL && edstream->buffer->used < 1024 ) { - buffer_append(edstream->buffer, edstream->cur_header->field->data, - edstream->cur_header->field->size); - - edstream->cur_header = edstream->cur_header->next; - - /* Stop at end of prepended headers if original header is left unparsed */ - if ( !edmail->headers_parsed - && edstream->cur_header == edmail->header_fields_appended ) - edstream->cur_header = NULL; - } - - if ( edstream->buffer->used > 0 ) { - /* Output current buffer */ - stream->buffer = buffer_get_data(edstream->buffer, &pos); - ret = (ssize_t)pos + stream->skip - stream->pos; - i_assert( ret >= 0 ); - stream->pos = pos; - stream->skip = 0; - - if ( ret != 0 ) - return ret; - - if ( edstream->buffer->used >= 1024 ) - return -2; - } - } - return 0; -} - static ssize_t edit_mail_istream_read(struct istream_private *stream) { struct edit_mail_istream *edstream = (struct edit_mail_istream *)stream; struct edit_mail *edmail = edstream->mail; - uoff_t parent_v_offset, parent_end_v_offset, copy_v_offset; - uoff_t v_offset = stream->istream.v_offset; - uoff_t prep_hdr_size, hdr_size; + uoff_t parent_v_offset, hdr_size, v_offset = stream->istream.v_offset; + size_t pos; ssize_t ret = 0; - if (stream->istream.eof) - return -1; - if ( edstream->buffer->used > 0 ) { if ( stream->skip > 0 ) { - /* Remove skipped data from buffer */ buffer_copy (edstream->buffer, 0, edstream->buffer, stream->skip, (size_t)-1); stream->pos -= stream->skip; @@ -1649,88 +1504,93 @@ static ssize_t edit_mail_istream_read(struct istream_private *stream) } } - /* Merge prepended headers */ - if (edstream->cur_header != NULL) { - if ( (ret=merge_modified_headers(edstream)) != 0 ) - return ret; + if ( edstream->buffer->used > 0 || stream->pos - stream->skip == 0 ) { + if ( edstream->cur_header != NULL ) { + while ( edstream->cur_header != NULL && edstream->buffer->used < 1024 ) { + buffer_append(edstream->buffer, edstream->cur_header->field->data, + edstream->cur_header->field->size); + + edstream->cur_header = edstream->cur_header->next; + + if ( !edmail->headers_parsed + && edstream->cur_header == edmail->header_fields_appended ) + edstream->cur_header = NULL; + } + } } - if ( !edmail->headers_parsed && !edstream->header_read && - edmail->header_fields_appended != NULL ) { - /* Output headers from original stream */ + if ( edstream->buffer->used > 0 ) { + stream->buffer = buffer_get_data(edstream->buffer, &pos); + ret = (ssize_t)pos + stream->skip - stream->pos; + i_assert( ret >= 0 ); + stream->pos = pos; + stream->skip = 0; - /* Size of the prepended header */ - prep_hdr_size = edmail->hdr_size.physical_size - - edmail->appended_hdr_size.physical_size; + if ( ret == 0 ) + return -2; - /* Offset of header end or appended header + return ret; + } + + if ( !edmail->headers_parsed && edmail->header_fields_appended != NULL ) { + /* Output headers from original stream */ + + /* At what offset does the header end (not including LF of final empty line) * Any final CR is dealt with later */ - hdr_size = prep_hdr_size + edmail->wrapped_hdr_size.physical_size; + hdr_size = edmail->wrapped_hdr_size.physical_size + + edmail->hdr_size.physical_size - + edmail->appended_hdr_size.physical_size - 1; - if ( v_offset < hdr_size - 1 ) { + if ( v_offset < hdr_size ) { parent_v_offset = stream->parent_start_offset + - (v_offset - prep_hdr_size); - parent_end_v_offset = stream->parent_start_offset + - edmail->wrapped_hdr_size.physical_size - 1; - copy_v_offset = prep_hdr_size; + (v_offset + edmail->appended_hdr_size.physical_size - + edmail->hdr_size.physical_size); + + i_stream_seek(stream->parent, parent_v_offset); - if ( (ret=merge_from_parent(edstream, parent_v_offset, - parent_end_v_offset, copy_v_offset)) < 0 ) { + if ( (ret=i_stream_read_copy_from_parent(&stream->istream)) < 0 ) return ret; - } if ( stream->pos >= hdr_size - 1 - v_offset ) { + /* Truncate buffer from original mail strictly to header */ + ret -= stream->pos - (hdr_size - v_offset); + stream->pos = hdr_size - v_offset; + /* Strip final CR too when it is present */ if ( stream->buffer[stream->pos-1] == '\r' ) { stream->pos--; ret--; - if (edstream->buffer->used > 0) - buffer_set_used_size(edstream->buffer, edstream->buffer->used-1); } i_assert(ret >= 0); - edstream->header_read = TRUE; edstream->cur_header = edmail->header_fields_appended; + if ( ret == 0 ) + return -2; } - if (ret != 0) - return ret; - } - - /* Merge Appended headers */ - if (edstream->cur_header != NULL) { - if ( (ret=merge_modified_headers(edstream)) != 0 ) - return ret; + return ret; } } - /* Header does not come from original mail at all */ - if ( edmail->headers_parsed ) { - parent_v_offset = stream->parent_start_offset + - (v_offset - edmail->hdr_size.physical_size) + - edmail->wrapped_hdr_size.physical_size - ( edmail->eoh_crlf ? 2 : 1); - copy_v_offset = edmail->hdr_size.physical_size; - - /* Header comes partially from original mail and headers are added between - header and body. - */ - } else if (edmail->header_fields_appended != NULL) { - parent_v_offset = stream->parent_start_offset + - (v_offset - edmail->hdr_size.physical_size); - copy_v_offset = edmail->hdr_size.physical_size + - edmail->wrapped_hdr_size.physical_size; + if ( !edmail->headers_parsed ) { + if ( v_offset < edmail->hdr_size.physical_size ) + return -2; - /* Header comes partially from original mail, but headers are only prepended. - */ - } else { parent_v_offset = stream->parent_start_offset + (v_offset - edmail->hdr_size.physical_size); - copy_v_offset = edmail->hdr_size.physical_size; + } else { + if ( v_offset < edmail->hdr_size.physical_size ) + return -2; + + parent_v_offset = stream->parent_start_offset + + edmail->wrapped_hdr_size.physical_size + + (v_offset - edmail->hdr_size.physical_size) + - ( edmail->eoh_crlf ? 2 : 1); } - return merge_from_parent - (edstream, parent_v_offset, (uoff_t)-1, copy_v_offset); + i_stream_seek(stream->parent, parent_v_offset); + return i_stream_read_copy_from_parent(&stream->istream); } static void @@ -1775,8 +1635,6 @@ static void edit_mail_istream_seek struct edit_mail *edmail = edstream->mail; uoff_t offset; - edstream->header_read = FALSE; - /* The beginning */ if ( v_offset == 0 ) { stream_reset_to(edstream, 0); @@ -1825,8 +1683,6 @@ static void edit_mail_istream_seek return; } - edstream->header_read = TRUE; - /* Inside appended header */ offset = edmail->hdr_size.physical_size + edmail->wrapped_hdr_size.physical_size; @@ -1861,7 +1717,7 @@ edit_mail_istream_sync(struct istream_private *stream ATTR_UNUSED) i_panic("edit-mail istream sync() not implemented"); } -static int +static const struct stat * edit_mail_istream_stat(struct istream_private *stream, bool exact) { struct edit_mail_istream *edstream = @@ -1870,16 +1726,16 @@ edit_mail_istream_stat(struct istream_private *stream, bool exact) const struct stat *st; /* Stat the original stream */ - if (i_stream_stat(stream->parent, exact, &st) < 0) - return -1; + st = i_stream_stat(stream->parent, exact); + if (st == NULL || st->st_size == -1 || !exact) + return st; + /* Adjust stat data */ stream->statbuf = *st; - if (st->st_size == -1 || !exact) - return 0; if ( !edmail->headers_parsed ) { if ( !edmail->modified ) - return 0; + return &stream->statbuf; } else { stream->statbuf.st_size = edmail->wrapped_body_size.physical_size + ( edmail->eoh_crlf ? 2 : 1 ); @@ -1887,7 +1743,8 @@ edit_mail_istream_stat(struct istream_private *stream, bool exact) stream->statbuf.st_size += edmail->hdr_size.physical_size + edmail->body_size.physical_size; - return 0; + + return &stream->statbuf; } struct istream *edit_mail_istream_create diff --git a/src/lib-sieve/ext-encoded-character.c b/src/lib-sieve/ext-encoded-character.c index 6055310..3e42069 100644 --- a/src/lib-sieve/ext-encoded-character.c +++ b/src/lib-sieve/ext-encoded-character.c @@ -28,8 +28,12 @@ static bool ext_encoded_character_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); struct sieve_extension_def encoded_character_extension = { - .name = "encoded-character", - .validator_load = ext_encoded_character_validator_load, + "encoded-character", + NULL, NULL, + ext_encoded_character_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS }; /* diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c index f39bd10..5481d9a 100644 --- a/src/lib-sieve/ext-envelope.c +++ b/src/lib-sieve/ext-envelope.c @@ -48,9 +48,12 @@ static bool ext_envelope_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def envelope_extension = { - .name = "envelope", - .validator_load = ext_envelope_validator_load, - SIEVE_EXT_DEFINE_OPERATION(envelope_operation) + "envelope", + NULL, NULL, + ext_envelope_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(envelope_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_envelope_validator_load @@ -180,7 +183,7 @@ static const struct sieve_envelope_part *_envelope_part_find static const struct sieve_address *const *_from_part_get_addresses (const struct sieve_runtime_env *renv) { - ARRAY(const struct sieve_address *) envelope_values; + ARRAY_DEFINE(envelope_values, const struct sieve_address *); const struct sieve_address *address = sieve_message_get_sender_address(renv->msgctx); @@ -199,7 +202,7 @@ static const struct sieve_address *const *_from_part_get_addresses static const char *const *_from_part_get_values (const struct sieve_runtime_env *renv) { - ARRAY(const char *) envelope_values; + ARRAY_DEFINE(envelope_values, const char *); t_array_init(&envelope_values, 2); @@ -215,7 +218,7 @@ static const char *const *_from_part_get_values static const struct sieve_address *const *_to_part_get_addresses (const struct sieve_runtime_env *renv) { - ARRAY(const struct sieve_address *) envelope_values; + ARRAY_DEFINE(envelope_values, const struct sieve_address *); const struct sieve_address *address = sieve_message_get_orig_recipient_address(renv->msgctx); @@ -234,7 +237,7 @@ static const struct sieve_address *const *_to_part_get_addresses static const char *const *_to_part_get_values (const struct sieve_runtime_env *renv) { - ARRAY(const char *) envelope_values; + ARRAY_DEFINE(envelope_values, const char *); t_array_init(&envelope_values, 2); @@ -250,7 +253,7 @@ static const char *const *_to_part_get_values static const char *const *_auth_part_get_values (const struct sieve_runtime_env *renv) { - ARRAY(const char *) envelope_values; + ARRAY_DEFINE(envelope_values, const char *); t_array_init(&envelope_values, 2); diff --git a/src/lib-sieve/ext-fileinto.c b/src/lib-sieve/ext-fileinto.c index e4648f9..e40ba39 100644 --- a/src/lib-sieve/ext-fileinto.c +++ b/src/lib-sieve/ext-fileinto.c @@ -44,9 +44,12 @@ static bool ext_fileinto_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def fileinto_extension = { - .name = "fileinto", - .validator_load = ext_fileinto_validator_load, - SIEVE_EXT_DEFINE_OPERATION(fileinto_operation) + "fileinto", + NULL, NULL, + ext_fileinto_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(fileinto_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_fileinto_validator_load diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c index 4532677..5398d6e 100644 --- a/src/lib-sieve/ext-reject.c +++ b/src/lib-sieve/ext-reject.c @@ -56,9 +56,12 @@ static bool ext_reject_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def reject_extension = { - .name = "reject", - .validator_load = ext_reject_validator_load, - SIEVE_EXT_DEFINE_OPERATION(reject_operation) + "reject", + NULL, NULL, + ext_reject_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(reject_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_reject_validator_load @@ -76,9 +79,12 @@ static bool ext_ereject_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def ereject_extension = { - .name = "ereject", - .validator_load = ext_ereject_validator_load, - SIEVE_EXT_DEFINE_OPERATION(ereject_operation) + "ereject", + NULL, NULL, + ext_ereject_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(ereject_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_ereject_validator_load diff --git a/src/lib-sieve/plugins/body/ext-body-common.c b/src/lib-sieve/plugins/body/ext-body-common.c index 9addbe3..39cf96f 100644 --- a/src/lib-sieve/plugins/body/ext-body-common.c +++ b/src/lib-sieve/plugins/body/ext-body-common.c @@ -43,8 +43,8 @@ struct ext_body_part_cached { struct ext_body_message_context { pool_t pool; - ARRAY(struct ext_body_part_cached) cached_body_parts; - ARRAY(struct ext_body_part) return_body_parts; + ARRAY_DEFINE(cached_body_parts, struct ext_body_part_cached); + ARRAY_DEFINE(return_body_parts, struct ext_body_part); buffer_t *tmp_buffer; buffer_t *raw_body; }; @@ -83,25 +83,6 @@ static bool _is_wanted_content_type return FALSE; } -static bool _want_multipart_content_type -(const char * const *wanted_types) -{ - for (; *wanted_types != NULL; wanted_types++) { - if (**wanted_types == '\0') { - /* empty string matches everything */ - return TRUE; - } - - /* match only main type */ - if ( strncasecmp(*wanted_types, "multipart", 9) == 0 && - ( strlen(*wanted_types) == 9 || *(*wanted_types+9) == '/' ) ) - return TRUE; - } - - return FALSE; -} - - static bool ext_body_get_return_parts (struct ext_body_message_context *ctx, const char * const *wanted_types, bool decode_to_plain) @@ -220,11 +201,9 @@ static bool ext_body_parts_add_missing struct message_decoder_context *decoder; struct message_block block, decoded; struct message_part *parts, *prev_part = NULL; - ARRAY(struct message_part *) part_index; struct istream *input; unsigned int idx = 0; - bool save_body = FALSE, want_multipart, have_all; - int ret; + bool save_body = FALSE, have_all; /* First check whether any are missing */ if (ext_body_get_return_parts(ctx, content_types, decode_to_plain)) { @@ -235,24 +214,18 @@ static bool ext_body_parts_add_missing /* Get the message stream */ if ( mail_get_stream(mail, NULL, NULL, &input) < 0 ) return FALSE; - - if (mail_get_parts(mail, &parts) < 0) - return FALSE; - - if ( (want_multipart=_want_multipart_content_type(content_types)) ) { - t_array_init(&part_index, 8); - } + //if (mail_get_parts(mail, &parts) < 0) + // return FALSE; buffer_set_used_size(ctx->tmp_buffer, 0); /* Initialize body decoder */ - decoder = decode_to_plain ? message_decoder_init(NULL, 0) : NULL; + decoder = decode_to_plain ? message_decoder_init(FALSE) : NULL; + + //parser = message_parser_init_from_parts(parts, input, 0, 0); + parser = message_parser_init(ctx->pool, input, 0, 0); - //parser = message_parser_init_from_parts(parts, input, 0, - //MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS); - parser = message_parser_init(ctx->pool, input, 0, - MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS); - while ( (ret = message_parser_parse_next_block(parser, &block)) > 0 ) { + while ( message_parser_parse_next_block(parser, &block) > 0 ) { if ( block.part != prev_part ) { bool message_rfc822 = FALSE; @@ -264,9 +237,8 @@ static bool ext_body_parts_add_missing strcmp(body_part->content_type, "message/rfc822") == 0 ) { message_rfc822 = TRUE; } else { - if ( save_body ) { + if ( save_body ) ext_body_part_save(ctx, body_part, decoder != NULL); - } } } @@ -274,30 +246,6 @@ static bool ext_body_parts_add_missing body_part = array_idx_modifiable(&ctx->cached_body_parts, idx); body_part->content_type = "text/plain"; - /* Check whether this is the epilogue block of a wanted multipart part */ - if ( want_multipart ) { - array_idx_set(&part_index, idx, &block.part); - - if ( prev_part != NULL && prev_part->next != block.part && - block.part->parent != prev_part ) { - struct message_part *const *iparts; - unsigned int count, i; - - iparts = array_get(&part_index, &count); - for ( i = 0; i < count; i++ ) { - if ( iparts[i] == block.part ) { - const struct ext_body_part_cached *parent = - array_idx(&ctx->cached_body_parts, i); - body_part->content_type = parent->content_type; - body_part->have_body = TRUE; - save_body = _is_wanted_content_type - (content_types, body_part->content_type); - break; - } - } - } - } - /* If this is message/rfc822 content retain the enveloping part for * storing headers as content. */ diff --git a/src/lib-sieve/plugins/body/ext-body.c b/src/lib-sieve/plugins/body/ext-body.c index 6241780..ee35598 100644 --- a/src/lib-sieve/plugins/body/ext-body.c +++ b/src/lib-sieve/plugins/body/ext-body.c @@ -53,9 +53,12 @@ static bool ext_body_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def body_extension = { - .name = "body", - .validator_load = ext_body_validator_load, - SIEVE_EXT_DEFINE_OPERATION(body_operation) + "body", + NULL, NULL, + ext_body_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(body_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_body_validator_load diff --git a/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c b/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c index 0d00b17..453ede8 100644 --- a/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c +++ b/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c @@ -38,8 +38,11 @@ static bool ext_cmp_i_ascii_numeric_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def comparator_i_ascii_numeric_extension = { - .name = "comparator-i;ascii-numeric", - .validator_load = ext_cmp_i_ascii_numeric_validator_load, + "comparator-i;ascii-numeric", + NULL, NULL, + ext_cmp_i_ascii_numeric_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, SIEVE_EXT_DEFINE_OPERAND(my_comparator_operand) }; diff --git a/src/lib-sieve/plugins/copy/ext-copy.c b/src/lib-sieve/plugins/copy/ext-copy.c index 77faa4f..4086b9d 100644 --- a/src/lib-sieve/plugins/copy/ext-copy.c +++ b/src/lib-sieve/plugins/copy/ext-copy.c @@ -39,8 +39,11 @@ static bool ext_copy_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def copy_extension = { - .name = "copy", - .validator_load = ext_copy_validator_load, + "copy", + NULL, NULL, + ext_copy_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, SIEVE_EXT_DEFINE_OPERAND(copy_side_effect_operand) }; diff --git a/src/lib-sieve/plugins/date/ext-date-common.c b/src/lib-sieve/plugins/date/ext-date-common.c index 399eb80..1c889d6 100644 --- a/src/lib-sieve/plugins/date/ext-date-common.c +++ b/src/lib-sieve/plugins/date/ext-date-common.c @@ -4,7 +4,6 @@ #include "lib.h" #include "utc-offset.h" #include "str.h" -#include "iso8601-date.h" #include "message-date.h" #include "sieve-common.h" @@ -351,7 +350,7 @@ static const char *ext_date_julian_part_get int day = tm->tm_mday; int c, ya, jd; - /* Modified from RFC 5260 Appendix A (refer to Errata) */ + /* Modified from RFC 5260 Appendix A */ if ( month > 2 ) month -= 3; @@ -395,17 +394,49 @@ static const char *ext_date_time_part_get static const char *ext_date_iso8601_part_get (struct tm *tm, int zone_offset) { - /* From RFC: `The restricted ISO 8601 format is specified by the date-time - * ABNF production given in [RFC3339], Section 5.6, with the added - * restrictions that the letters "T" and "Z" MUST be in upper case, and - * a time zone offset of zero MUST be represented by "Z" and not "+00:00". + const char *time_offset; + + /* + * RFC 3339: 5.6. Internet Date/Time Format + * + * The following profile of ISO 8601 [ISO8601] dates SHOULD be used in + * new protocols on the Internet. This is specified using the syntax + * description notation defined in [ABNF]. + * + * date-fullyear = 4DIGIT + * date-month = 2DIGIT ; 01-12 + * date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + * ; month/year + * time-hour = 2DIGIT ; 00-23 + * time-minute = 2DIGIT ; 00-59 + * time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second + * ; rules + * time-secfrac = "." 1*DIGIT + * time-numoffset = ("+" / "-") time-hour ":" time-minute + * time-offset = "Z" / time-numoffset + * + * partial-time = time-hour ":" time-minute ":" time-second + * [time-secfrac] + * full-date = date-fullyear "-" date-month "-" date-mday + * full-time = partial-time time-offset + * + * date-time = full-date "T" full-time + * */ + if ( zone_offset == 0 ) - zone_offset = INT_MAX; + time_offset = "Z"; + else { + int offset = zone_offset > 0 ? zone_offset : -zone_offset; - return iso8601_date_create_tm(tm, zone_offset); -} + time_offset = t_strdup_printf + ("%c%02d:%02d", (zone_offset > 0 ? '+' : '-'), offset / 60, offset % 60); + } + return t_strdup_printf("%04d-%02d-%02dT%02d:%02d:%02d%s", + tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, time_offset); +} static const char *ext_date_std11_part_get (struct tm *tm, int zone_offset) diff --git a/src/lib-sieve/plugins/date/ext-date.c b/src/lib-sieve/plugins/date/ext-date.c index c1b0d48..6477908 100644 --- a/src/lib-sieve/plugins/date/ext-date.c +++ b/src/lib-sieve/plugins/date/ext-date.c @@ -43,10 +43,14 @@ const struct sieve_operation_def *ext_date_operations[] = { }; const struct sieve_extension_def date_extension = { - .name = "date", - .validator_load = ext_date_validator_load, - .interpreter_load = ext_date_interpreter_load, - SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations) + "date", + NULL, NULL, + ext_date_validator_load, + NULL, + ext_date_interpreter_load, + NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_date_validator_load diff --git a/src/lib-sieve/plugins/editheader/ext-editheader-common.c b/src/lib-sieve/plugins/editheader/ext-editheader-common.c index b8bf653..a6c2926 100644 --- a/src/lib-sieve/plugins/editheader/ext-editheader-common.c +++ b/src/lib-sieve/plugins/editheader/ext-editheader-common.c @@ -29,7 +29,7 @@ struct ext_editheader_header { struct ext_editheader_config { pool_t pool; - ARRAY(struct ext_editheader_header) headers; + ARRAY_DEFINE(headers, struct ext_editheader_header); size_t max_header_size; }; diff --git a/src/lib-sieve/plugins/editheader/ext-editheader.c b/src/lib-sieve/plugins/editheader/ext-editheader.c index 17139c6..b7a49c5 100644 --- a/src/lib-sieve/plugins/editheader/ext-editheader.c +++ b/src/lib-sieve/plugins/editheader/ext-editheader.c @@ -45,11 +45,13 @@ static bool ext_editheader_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def editheader_extension = { - .name = "editheader", - .load = ext_editheader_load, - .unload = ext_editheader_unload, - .validator_load = ext_editheader_validator_load, - SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations) + "editheader", + ext_editheader_load, + ext_editheader_unload, + ext_editheader_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_editheader_validator_load diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h index d1ccf84..cf80361 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h +++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h @@ -22,7 +22,7 @@ extern const struct sieve_extension_capabilities notify_capabilities; struct ext_enotify_context { const struct sieve_extension *var_ext; - ARRAY(struct sieve_enotify_method) notify_methods; + ARRAY_DEFINE(notify_methods, struct sieve_enotify_method); }; diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c index 5310f58..7603fab 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify.c +++ b/src/lib-sieve/plugins/enotify/ext-enotify.c @@ -46,10 +46,11 @@ static bool ext_enotify_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def enotify_extension = { - .name = "enotify", - .load = ext_enotify_load, - .unload = ext_enotify_unload, - .validator_load = ext_enotify_validator_load, + "enotify", + ext_enotify_load, + ext_enotify_unload, + ext_enotify_validator_load, + NULL, NULL, NULL, NULL, NULL, SIEVE_EXT_DEFINE_OPERATIONS(ext_enotify_operations), SIEVE_EXT_DEFINE_OPERAND(encodeurl_operand) }; diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c index a358011..6b662da 100644 --- a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c +++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c @@ -23,7 +23,6 @@ #include "str.h" #include "ioloop.h" #include "str-sanitize.h" -#include "ostream.h" #include "message-date.h" #include "mail-storage.h" @@ -387,11 +386,9 @@ static bool ntfy_mailto_send const char *body = mtctx->uri->body; string_t *to, *cc; const struct uri_mailto_recipient *recipients; - const struct uri_mailto_header_field *headers; void *smtp_handle; - struct ostream *output; - string_t *msg; - unsigned int count, i, hcount, h; + unsigned int count, i; + FILE *f; const char *outmsgid; /* Get recipients */ @@ -464,81 +461,74 @@ static bool ntfy_mailto_send } } - msg = t_str_new(512); - outmsgid = sieve_message_get_new_id(nenv->svinst); - - rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_write(msg, "Message-ID", outmsgid); - rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); - rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); - - rfc2822_header_utf8_printf(msg, "From", "%s", from); - - if ( to != NULL ) - rfc2822_header_utf8_printf(msg, "To", "%s", str_c(to)); - - if ( cc != NULL ) - rfc2822_header_utf8_printf(msg, "Cc", "%s", str_c(cc)); - - rfc2822_header_printf(msg, "Auto-Submitted", - "auto-notified; owner-email=\"%s\"", recipient); - rfc2822_header_write(msg, "Precedence", "bulk"); - - /* Set importance */ - switch ( nact->importance ) { - case 1: - rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); - rfc2822_header_write(msg, "Importance", "High"); - break; - case 3: - rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); - rfc2822_header_write(msg, "Importance", "Low"); - break; - case 2: - default: - rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); - rfc2822_header_write(msg, "Importance", "Normal"); - break; - } - - /* Add custom headers */ + /* Send message to all recipients */ + for ( i = 0; i < count; i++ ) { + const struct uri_mailto_header_field *headers; + unsigned int h, hcount; - headers = array_get(&mtctx->uri->headers, &hcount); - for ( h = 0; h < hcount; h++ ) { - const char *name = rfc2822_header_field_name_sanitize(headers[h].name); + smtp_handle = sieve_smtp_open + (senv, recipients[i].normalized, from_smtp, &f); + outmsgid = sieve_message_get_new_id(nenv->svinst); + + rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_field_write(f, "Message-ID", outmsgid); + rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); + rfc2822_header_field_utf8_printf(f, "Subject", "%s", subject); + + rfc2822_header_field_utf8_printf(f, "From", "%s", from); + + if ( to != NULL ) + rfc2822_header_field_utf8_printf(f, "To", "%s", str_c(to)); + + if ( cc != NULL ) + rfc2822_header_field_utf8_printf(f, "Cc", "%s", str_c(cc)); + + rfc2822_header_field_printf(f, "Auto-Submitted", + "auto-notified; owner-email=\"%s\"", recipient); + rfc2822_header_field_write(f, "Precedence", "bulk"); + + /* Set importance */ + switch ( nact->importance ) { + case 1: + rfc2822_header_field_write(f, "X-Priority", "1 (Highest)"); + rfc2822_header_field_write(f, "Importance", "High"); + break; + case 3: + rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)"); + rfc2822_header_field_write(f, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_field_write(f, "X-Priority", "3 (Normal)"); + rfc2822_header_field_write(f, "Importance", "Normal"); + break; + } - rfc2822_header_write(msg, name, headers[h].body); - } + /* Add custom headers */ - /* Generate message body */ + headers = array_get(&mtctx->uri->headers, &hcount); + for ( h = 0; h < hcount; h++ ) { + const char *name = rfc2822_header_field_name_sanitize(headers[h].name); - rfc2822_header_write(msg, "MIME-Version", "1.0"); - if ( body != NULL ) { - if (_contains_8bit(body)) { - rfc2822_header_write - (msg, "Content-Type", "text/plain; charset=utf-8"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); - } else { - rfc2822_header_write - (msg, "Content-Type", "text/plain; charset=us-ascii"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); + rfc2822_header_field_write(f, name, headers[h].body); } - str_printfa(msg, "\r\n%s\r\n", body); - - } else { - rfc2822_header_write - (msg, "Content-Type", "text/plain; charset=US-ASCII"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); - str_append(msg, "\r\nNotification of new message.\r\n"); - } + /* Generate message body */ + if ( body != NULL ) { + if (_contains_8bit(body)) { + rfc2822_header_field_write(f, "MIME-Version", "1.0"); + rfc2822_header_field_write + (f, "Content-Type", "text/plain; charset=UTF-8"); + rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); + } - /* Send message to all recipients */ - for ( i = 0; i < count; i++ ) { - smtp_handle = sieve_smtp_open - (senv, recipients[i].normalized, from_smtp, &output); + fprintf(f, "\r\n"); + fprintf(f, "%s\r\n", body); - o_stream_send(output, str_data(msg), str_len(msg)); + } else { + fprintf(f, "\r\n"); + fprintf(f, "Notification of new message.\r\n"); + } if ( sieve_smtp_close(senv, smtp_handle) ) { sieve_enotify_global_info(nenv, @@ -562,7 +552,6 @@ static bool ntfy_mailto_action_execute const char *const *headers; const char *sender = sieve_message_get_sender(nenv->msgctx); const char *recipient = sieve_message_get_final_recipient(nenv->msgctx); - bool result; /* Is the recipient unset? */ @@ -589,11 +578,7 @@ static bool ntfy_mailto_action_execute } } - T_BEGIN { - result = ntfy_mailto_send(nenv, nact, recipient); - } T_END; - - return result; + return ntfy_mailto_send(nenv, nact, recipient); } diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c index e3a946e..35308f6 100644 --- a/src/lib-sieve/plugins/environment/ext-environment-common.c +++ b/src/lib-sieve/plugins/environment/ext-environment-common.c @@ -10,8 +10,7 @@ #include "ext-environment-common.h" struct ext_environment_context { - HASH_TABLE(const char *, - const struct sieve_environment_item *) environment_items; + struct hash_table *environment_items; }; /* @@ -37,7 +36,8 @@ static void ext_environment_item_register (struct ext_environment_context *ectx, const struct sieve_environment_item *item) { - hash_table_insert(ectx->environment_items, item->name, item); + hash_table_insert + (ectx->environment_items, (void *) item->name, (void *) item); } void sieve_ext_environment_item_register @@ -61,8 +61,8 @@ bool ext_environment_init unsigned int i; - hash_table_create - (&ectx->environment_items, default_pool, 0, str_hash, strcmp); + ectx->environment_items = hash_table_create + (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); for ( i = 0; i < core_env_items_count; i++ ) { ext_environment_item_register(ectx, core_env_items[i]); @@ -94,7 +94,8 @@ const char *ext_environment_item_get_value struct ext_environment_context *ectx = (struct ext_environment_context *) ext->context; const struct sieve_environment_item *item = - hash_table_lookup(ectx->environment_items, name); + (const struct sieve_environment_item *) + hash_table_lookup(ectx->environment_items, name); if ( item == NULL ) return NULL; @@ -102,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(ext->svinst, senv); + if ( item->get_value != NULL ) { + const char *value = item->get_value(ext->svinst, senv); + + return ( value == NULL ? "" : value ); + } return NULL; } @@ -117,17 +121,10 @@ const char *ext_environment_item_get_value * The primary DNS domain associated with the Sieve execution context, usually * but not always a proper suffix of the host name. */ - -static const char *envit_domain_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) -{ - return svinst->domainname; -} - const struct sieve_environment_item domain_env_item = { - .name = "domain", - .get_value = envit_domain_get_value, + "domain", + NULL, + NULL, }; /* "host": @@ -137,15 +134,15 @@ const struct sieve_environment_item domain_env_item = { */ static const char *envit_host_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) +(struct sieve_instance *svinst, const struct sieve_script_env *senv ATTR_UNUSED) { return svinst->hostname; } const struct sieve_environment_item host_env_item = { - .name = "host", - .get_value = envit_host_get_value, + "host", + NULL, + envit_host_get_value, }; /* "location": @@ -155,30 +152,13 @@ const struct sieve_environment_item host_env_item = { * service that is evaluating the script. Possible values are: * "MTA" - the Sieve script is being evaluated by a Message Transfer Agent * "MDA" - evaluation is being performed by a Mail Delivery Agent - * "MUA" - evaluation is being performed by a Mail User Agent (right...) + * "MUA" - evaluation is being performed by a Mail User Agent * "MS" - evaluation is being performed by a Message Store */ - -static const char *envit_location_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) -{ - switch ( svinst->env_location ) { - case SIEVE_ENV_LOCATION_MDA: - return "MDA"; - case SIEVE_ENV_LOCATION_MTA: - return "MTA"; - case SIEVE_ENV_LOCATION_MS: - return "MS"; - default: - break; - } - return NULL; -} - const struct sieve_environment_item location_env_item = { - .name = "location", - .get_value = envit_location_get_value + "location", + NULL, + NULL, }; /* "phase": @@ -189,36 +169,20 @@ const struct sieve_environment_item location_env_item = { * taken place. */ -static const char *envit_phase_get_value -(struct sieve_instance *svinst, - const struct sieve_script_env *senv ATTR_UNUSED) -{ - switch ( svinst->delivery_phase ) { - case SIEVE_DELIVERY_PHASE_PRE: - return "pre"; - case SIEVE_DELIVERY_PHASE_DURING: - return "during"; - case SIEVE_DELIVERY_PHASE_POST: - return "post"; - default: - break; - } - return NULL; -} - const struct sieve_environment_item phase_env_item = { - .name = "phase", - .get_value = envit_phase_get_value + "phase", + NULL, + NULL, }; /* "name": * * The product name associated with the Sieve interpreter. */ - const struct sieve_environment_item name_env_item = { - .name = "name", - .value = PIGEONHOLE_NAME" Sieve" + "name", + PIGEONHOLE_NAME" Sieve", + NULL, }; /* "version": @@ -229,8 +193,9 @@ const struct sieve_environment_item name_env_item = { */ const struct sieve_environment_item version_env_item = { - .name = "version", - .value = PIGEONHOLE_VERSION, + "version", + PIGEONHOLE_VERSION, + NULL, }; diff --git a/src/lib-sieve/plugins/environment/ext-environment.c b/src/lib-sieve/plugins/environment/ext-environment.c index 402e0a5..d90b597 100644 --- a/src/lib-sieve/plugins/environment/ext-environment.c +++ b/src/lib-sieve/plugins/environment/ext-environment.c @@ -32,11 +32,13 @@ static bool ext_environment_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def environment_extension = { - .name = "environment", - .load = ext_environment_init, - .unload = ext_environment_deinit, - .validator_load = ext_environment_validator_load, - SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation) + "environment", + ext_environment_init, + ext_environment_deinit, + ext_environment_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_environment_validator_load diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-binary.c b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c index f348675..ee86035 100644 --- a/src/lib-sieve/plugins/ihave/ext-ihave-binary.c +++ b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c @@ -48,7 +48,7 @@ struct ext_ihave_binary_context { struct sieve_binary *binary; struct sieve_binary_block *block; - ARRAY(const char *) missing_extensions; + ARRAY_DEFINE(missing_extensions, const char *); }; static struct ext_ihave_binary_context *ext_ihave_binary_create_context diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-common.h b/src/lib-sieve/plugins/ihave/ext-ihave-common.h index a85d5f0..29f2246 100644 --- a/src/lib-sieve/plugins/ihave/ext-ihave-common.h +++ b/src/lib-sieve/plugins/ihave/ext-ihave-common.h @@ -35,7 +35,7 @@ extern const struct sieve_operation_def error_operation; */ struct ext_ihave_ast_context { - ARRAY(const char *) missing_extensions; + ARRAY_DEFINE(missing_extensions, const char *); }; struct ext_ihave_ast_context *ext_ihave_get_ast_context diff --git a/src/lib-sieve/plugins/ihave/ext-ihave.c b/src/lib-sieve/plugins/ihave/ext-ihave.c index 125eb9f..8def774 100644 --- a/src/lib-sieve/plugins/ihave/ext-ihave.c +++ b/src/lib-sieve/plugins/ihave/ext-ihave.c @@ -34,11 +34,15 @@ static bool ext_ihave_generator_load const struct sieve_extension_def ihave_extension = { "ihave", - .validator_load = ext_ihave_validator_load, - .generator_load = ext_ihave_generator_load, - .binary_load = ext_ihave_binary_load, - .binary_dump = ext_ihave_binary_dump, - SIEVE_EXT_DEFINE_OPERATION(error_operation) + NULL, NULL, + ext_ihave_validator_load, + ext_ihave_generator_load, + NULL, + ext_ihave_binary_load, + ext_ihave_binary_dump, + NULL, + SIEVE_EXT_DEFINE_OPERATION(error_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_ihave_validator_load diff --git a/src/lib-sieve/plugins/ihave/tst-ihave.c b/src/lib-sieve/plugins/ihave/tst-ihave.c index a73fdfb..853474a 100644 --- a/src/lib-sieve/plugins/ihave/tst-ihave.c +++ b/src/lib-sieve/plugins/ihave/tst-ihave.c @@ -51,7 +51,7 @@ static bool tst_ihave_validate struct sieve_ast_argument *stritem; enum sieve_compile_flags cpflags = sieve_validator_compile_flags(valdtr); bool no_global = ( (cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0 ); - ARRAY(struct _capability) capabilities; + ARRAY_DEFINE(capabilities, struct _capability); struct _capability capability; const struct _capability *caps; unsigned int i, count; diff --git a/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c b/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c index fb477ed..f5b3910 100644 --- a/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c +++ b/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c @@ -49,9 +49,12 @@ static bool ext_imap4flags_interpreter_load sieve_size_t *address); const struct sieve_extension_def imap4flags_extension = { - .name = "imap4flags", - .validator_load = ext_imap4flags_validator_load, - .interpreter_load = ext_imap4flags_interpreter_load, + "imap4flags", + NULL, NULL, + ext_imap4flags_validator_load, + NULL, + ext_imap4flags_interpreter_load, + NULL, NULL, NULL, SIEVE_EXT_DEFINE_OPERATIONS(imap4flags_operations), SIEVE_EXT_DEFINE_OPERAND(flags_side_effect_operand) }; diff --git a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c index 64a893d..558987a 100644 --- a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c +++ b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c @@ -77,10 +77,15 @@ static bool ext_imapflags_interpreter_load sieve_size_t *address); const struct sieve_extension_def imapflags_extension = { - .name = "imapflags", - .load = ext_imapflags_load, - .validator_load = ext_imapflags_validator_load, - .interpreter_load = ext_imapflags_interpreter_load + "imapflags", + ext_imapflags_load, + NULL, + ext_imapflags_validator_load, + NULL, + ext_imapflags_interpreter_load, + NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_imapflags_load diff --git a/src/lib-sieve/plugins/imap4flags/tag-flags.c b/src/lib-sieve/plugins/imap4flags/tag-flags.c index 001f69c..068fa2c 100644 --- a/src/lib-sieve/plugins/imap4flags/tag-flags.c +++ b/src/lib-sieve/plugins/imap4flags/tag-flags.c @@ -187,7 +187,7 @@ static bool tag_flags_generate /* Context data */ struct seff_flags_context { - ARRAY(const char *) keywords; + ARRAY_DEFINE(keywords, const char *); enum mail_flags flags; }; diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c index 6247a67..7264115 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -23,7 +23,7 @@ * Include command * * Syntax: - * include [LOCATION] [":once"] [":optional"] + * include [LOCATION] * * [LOCATION]: * ":personal" / ":global" @@ -74,11 +74,11 @@ const struct sieve_operation_def include_operation = { struct cmd_include_context_data { enum ext_include_script_location location; + bool location_assigned; - struct sieve_script *script; - enum ext_include_flags flags; + bool include_once; - unsigned int location_assigned:1; + struct sieve_script *script; }; /* @@ -103,25 +103,17 @@ static const struct sieve_argument_def include_global_tag = { NULL, NULL, NULL }; -static bool cmd_include_validate_boolean_tag +static bool cmd_include_validate_once_tag (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd); static const struct sieve_argument_def include_once_tag = { "once", NULL, - cmd_include_validate_boolean_tag, - NULL, NULL, NULL -}; - -static const struct sieve_argument_def include_optional_tag = { - "optional", - NULL, - cmd_include_validate_boolean_tag, + cmd_include_validate_once_tag, NULL, NULL, NULL }; - /* * Tag validation */ @@ -155,17 +147,14 @@ static bool cmd_include_validate_location_tag return TRUE; } -static bool cmd_include_validate_boolean_tag +static bool cmd_include_validate_once_tag (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, struct sieve_command *cmd) { struct cmd_include_context_data *ctx_data = (struct cmd_include_context_data *) cmd->data; - if ( sieve_argument_is(*arg, include_once_tag) ) - ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE; - else - ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL; + ctx_data->include_once = TRUE; /* Delete this tag (for now) */ *arg = sieve_ast_arguments_detach(*arg, 1); @@ -184,7 +173,6 @@ static bool cmd_include_registered sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0); sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0); sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0); - sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0); return TRUE; } @@ -216,7 +204,7 @@ static bool cmd_include_validate struct sieve_script *script; const char *script_location, *script_name; enum sieve_error error = SIEVE_ERROR_NONE; - int ret; + bool include = TRUE; /* Check argument */ if ( !sieve_validate_positional_argument @@ -263,47 +251,33 @@ static bool cmd_include_validate (this_ext->svinst, script_location, script_name, sieve_validator_error_handler(valdtr), &error); - ret = 0; - if ( script != NULL ) - ret = sieve_script_open(script, &error); - - if ( script == NULL || ret < 0 ) { + if ( script == NULL ) { if ( error != SIEVE_ERROR_NOT_FOUND ) { - if ( script != NULL ) - sieve_script_unref(&script); return FALSE; - - /* Not found */ } else { enum sieve_compile_flags cpflags = sieve_validator_compile_flags(valdtr); - if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) { - /* :optional */ - - } else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) { - /* Script is being uploaded */ + if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) { sieve_argument_validate_warning(valdtr, arg, "included %s script '%s' does not exist (ignored during upload)", ext_include_script_location_name(ctx_data->location), str_sanitize(script_name, 80)); - ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD; - + include = FALSE; } else { - /* Should have existed */ sieve_argument_validate_error(valdtr, arg, "included %s script '%s' does not exist", ext_include_script_location_name(ctx_data->location), str_sanitize(script_name, 80)); - if ( script != NULL ) - sieve_script_unref(&script); return FALSE; } } } - ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); - ctx_data->script = script; + if ( include ) { + ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); + ctx_data->script = script; + } (void)sieve_ast_arguments_detach(arg, 1); return TRUE; @@ -319,20 +293,26 @@ static bool cmd_include_generate struct cmd_include_context_data *ctx_data = (struct cmd_include_context_data *) cmd->data; const struct ext_include_script_info *included; + unsigned int flags = ctx_data->include_once; int ret; - /* Compile (if necessary) and include the script into the binary. - * This yields the id of the binary block containing the compiled byte code. + /* Upon upload ctx_data->script may be NULL if the script was not found. We + * don't emit any code for this include command in that case. */ - if ( (ret=ext_include_generate_include - (cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script, - &included)) < 0 ) - return FALSE; - - if ( ret > 0 ) { - (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); - (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); - (void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags); + if ( ctx_data->script != NULL ) { + /* Compile (if necessary) and include the script into the binary. + * This yields the id of the binary block containing the compiled byte code. + */ + if ( (ret=ext_include_generate_include + (cgenv, cmd, ctx_data->location, ctx_data->script, &included, + ctx_data->include_once)) < 0 ) + return FALSE; + + if ( ret > 0 ) { + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); + (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); + (void)sieve_binary_emit_byte(cgenv->sblock, flags); + } } return TRUE; @@ -391,8 +371,7 @@ static int opc_include_execute return SIEVE_EXEC_BIN_CORRUPT; } - return ext_include_execute_include - (renv, include_id, (enum ext_include_flags)flags); + return ext_include_execute_include(renv, include_id, flags & 0x01); } diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index 2483be7..a06d340 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -53,9 +53,8 @@ struct ext_include_binary_context { struct sieve_binary *binary; struct sieve_binary_block *dependency_block; - HASH_TABLE(struct sieve_script *, - struct ext_include_script_info *) included_scripts; - ARRAY(struct ext_include_script_info *) include_index; + struct hash_table *included_scripts; + ARRAY_DEFINE(include_index, struct ext_include_script_info *); struct sieve_variable_scope_binary *global_vars; @@ -71,8 +70,9 @@ static struct ext_include_binary_context *ext_include_binary_create_context p_new(pool, struct ext_include_binary_context, 1); ctx->binary = sbin; - hash_table_create(&ctx->included_scripts, pool, 0, - sieve_script_hash, sieve_script_cmp); + ctx->included_scripts = hash_table_create(default_pool, pool, 0, + (hash_callback_t *) sieve_script_hash, + (hash_cmp_callback_t *) sieve_script_cmp); p_array_init(&ctx->include_index, pool, 128); sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx); @@ -122,24 +122,23 @@ struct ext_include_binary_context *ext_include_binary_init */ const struct ext_include_script_info *ext_include_binary_script_include -(struct ext_include_binary_context *binctx, - enum ext_include_script_location location, enum ext_include_flags flags, - struct sieve_script *script, struct sieve_binary_block *inc_block) +(struct ext_include_binary_context *binctx, struct sieve_script *script, + enum ext_include_script_location location, struct sieve_binary_block *inc_block) { pool_t pool = sieve_binary_pool(binctx->binary); struct ext_include_script_info *incscript; incscript = p_new(pool, struct ext_include_script_info, 1); incscript->id = array_count(&binctx->include_index)+1; - incscript->location = location; - incscript->flags = flags; incscript->script = script; + incscript->location = location; incscript->block = inc_block; /* Unreferenced on binary_free */ sieve_script_ref(script); - hash_table_insert(binctx->included_scripts, script, incscript); + hash_table_insert + (binctx->included_scripts, (void *) script, (void *) incscript); array_append(&binctx->include_index, &incscript, 1); return incscript; @@ -149,7 +148,7 @@ bool ext_include_binary_script_is_included (struct ext_include_binary_context *binctx, struct sieve_script *script, const struct ext_include_script_info **script_info_r) { - struct ext_include_script_info *incscript = + struct ext_include_script_info *incscript = (struct ext_include_script_info *) hash_table_lookup(binctx->included_scripts, script); if ( incscript == NULL ) @@ -176,7 +175,8 @@ const struct ext_include_script_info *ext_include_binary_script_get_included const struct ext_include_script_info *ext_include_binary_script_get (struct ext_include_binary_context *binctx, struct sieve_script *script) { - return hash_table_lookup(binctx->included_scripts, script); + return (struct ext_include_script_info *) + hash_table_lookup(binctx->included_scripts, script); } unsigned int ext_include_binary_script_get_count @@ -222,15 +222,9 @@ static bool ext_include_binary_save for ( i = 0; i < script_count; i++ ) { struct ext_include_script_info *incscript = scripts[i]; - if ( incscript->block != NULL ) { - sieve_binary_emit_unsigned - (sblock, sieve_binary_block_get_id(incscript->block)); - } else { - sieve_binary_emit_unsigned(sblock, 0); - } + 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_binary_emit_byte(sblock, incscript->flags); sieve_script_binary_write_metadata(incscript->script, sblock); } @@ -275,19 +269,17 @@ static bool ext_include_binary_open /* Read dependencies */ for ( i = 0; i < depcount; i++ ) { unsigned int inc_block_id; - struct sieve_binary_block *inc_block = NULL; - unsigned int location, flags; + struct sieve_binary_block *inc_block; + unsigned int location; string_t *script_name; const char *script_location; struct sieve_script *script; - enum sieve_error error; int ret; if ( !sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) || !sieve_binary_read_byte(sblock, &offset, &location) || - !sieve_binary_read_string(sblock, &offset, &script_name) || - !sieve_binary_read_byte(sblock, &offset, &flags) ) { + !sieve_binary_read_string(sblock, &offset, &script_name) ) { /* Binary is corrupt, recompile */ sieve_sys_error(svinst, "include: failed to read included script " @@ -296,8 +288,7 @@ static bool ext_include_binary_open return FALSE; } - if ( inc_block_id != 0 && - (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) { + if ( (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) { sieve_sys_error(svinst, "include: failed to find block %d for included script " "from dependency block %d of binary %s", inc_block_id, block_id, @@ -314,55 +305,18 @@ static bool ext_include_binary_open return FALSE; } - /* Can we find the script dependency ? */ + /* Can we find/open the script dependency ? */ script_location = ext_include_get_script_location (ext, location, str_c(script_name)); - if ( script_location == NULL ) { + if ( script_location == NULL || (script=sieve_script_create + (ext->svinst, script_location, str_c(script_name), NULL, NULL)) == NULL ) + { /* No, recompile */ return FALSE; } - /* Can we open the script dependency ? */ - script = sieve_script_create - (ext->svinst, script_location, str_c(script_name), NULL, &error); - if ( script == NULL ) { - /* No, recompile */ - return FALSE; - } - if ( sieve_script_open(script, &error) < 0 ) { - if ( error != SIEVE_ERROR_NOT_FOUND ) { - /* No, recompile */ - sieve_script_unref(&script); - return FALSE; - } - - if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) { - /* Not supposed to be missing, recompile */ - if ( svinst->debug ) { - sieve_sys_debug(svinst, - "include: script '%s' included in binary %s is missing, " - "so recompile", str_c(script_name), sieve_binary_path(sbin)); - } - sieve_script_unref(&script); - return FALSE; - } - - } else if (inc_block == NULL) { - /* Script exists, but it is missing from the binary, recompile no matter - * what. - */ - if ( svinst->debug ) { - sieve_sys_debug(svinst, - "include: script '%s' is missing in binary %s, but is now available, " - "so recompile", str_c(script_name), sieve_binary_path(sbin)); - } - sieve_script_unref(&script); - return FALSE; - } - - /* Can we read script metadata ? */ - if ( (ret=sieve_script_binary_read_metadata - (script, sblock, &offset)) < 0 ) { + 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 " @@ -376,7 +330,7 @@ static bool ext_include_binary_open binctx->outdated = TRUE; (void)ext_include_binary_script_include - (binctx, location, flags, script, inc_block); + (binctx, script, location, inc_block); sieve_script_unref(&script); } @@ -406,14 +360,16 @@ static void ext_include_binary_free struct ext_include_binary_context *binctx = (struct ext_include_binary_context *) context; struct hash_iterate_context *hctx; - struct sieve_script *script; - struct ext_include_script_info *incscript; + void *key, *value; /* Release references to all included script objects */ hctx = hash_table_iterate_init(binctx->included_scripts); - while ( hash_table_iterate - (hctx, binctx->included_scripts, &script, &incscript) ) + while ( hash_table_iterate(hctx, &key, &value) ) { + struct ext_include_script_info *incscript = + (struct ext_include_script_info *) value; + sieve_script_unref(&incscript->script); + } hash_table_iterate_deinit(&hctx); hash_table_destroy(&binctx->included_scripts); @@ -433,38 +389,31 @@ bool ext_include_binary_dump struct ext_include_binary_context *binctx = ext_include_binary_get_context(ext, sbin); struct hash_iterate_context *hctx; - struct sieve_script *script; - struct ext_include_script_info *incscript; + void *key, *value; if ( !ext_include_variables_dump(denv, binctx->global_vars) ) return FALSE; hctx = hash_table_iterate_init(binctx->included_scripts); - while ( hash_table_iterate - (hctx, binctx->included_scripts, &script, &incscript) ) { + while ( hash_table_iterate(hctx, &key, &value) ) { + struct ext_include_script_info *incscript = + (struct ext_include_script_info *) value; + unsigned int block_id = sieve_binary_block_get_id(incscript->block); - if ( incscript->block == NULL ) { - sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)", - ext_include_script_location_name(incscript->location), - sieve_script_name(incscript->script)); + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script), block_id); - } else { - unsigned int block_id = sieve_binary_block_get_id(incscript->block); + denv->sblock = incscript->block; + denv->cdumper = sieve_code_dumper_create(denv); - sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)", - ext_include_script_location_name(incscript->location), - sieve_script_name(incscript->script), block_id); - - denv->sblock = incscript->block; - denv->cdumper = sieve_code_dumper_create(denv); - - if ( denv->cdumper == NULL ) - return FALSE; + if ( denv->cdumper == NULL ) + return FALSE; - sieve_code_dumper_run(denv->cdumper); - sieve_code_dumper_free(&(denv->cdumper)); - } + sieve_code_dumper_run(denv->cdumper); + sieve_code_dumper_free(&(denv->cdumper)); } + hash_table_iterate_deinit(&hctx); return TRUE; diff --git a/src/lib-sieve/plugins/include/ext-include-binary.h b/src/lib-sieve/plugins/include/ext-include-binary.h index 0910f92..8cd304d 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.h +++ b/src/lib-sieve/plugins/include/ext-include-binary.h @@ -30,19 +30,18 @@ struct sieve_variable_scope_binary *ext_include_binary_get_global_scope */ struct ext_include_script_info { - unsigned int id; + unsigned int id; - struct sieve_script *script; - enum ext_include_flags flags; - enum ext_include_script_location location; + struct sieve_script *script; + enum ext_include_script_location location; - struct sieve_binary_block *block; + struct sieve_binary_block *block; }; const struct ext_include_script_info *ext_include_binary_script_include - (struct ext_include_binary_context *binctx, - enum ext_include_script_location location, enum ext_include_flags flags, - struct sieve_script *script, struct sieve_binary_block *inc_block); + (struct ext_include_binary_context *binctx, struct sieve_script *script, + enum ext_include_script_location location, + struct sieve_binary_block *block); bool ext_include_binary_script_is_included (struct ext_include_binary_context *binctx, struct sieve_script *script, const struct ext_include_script_info **script_info_r); diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index 833c355..0324f49 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -43,7 +43,7 @@ static inline struct ext_include_generator_context * /* Interpreter context */ struct ext_include_interpreter_global { - ARRAY(struct sieve_script *) included_scripts; + ARRAY_DEFINE(included_scripts, struct sieve_script *); struct sieve_variable_scope_binary *var_scope; struct sieve_variable_storage *var_storage; @@ -454,9 +454,8 @@ struct sieve_variable_storage *ext_include_interpreter_get_global_variables int ext_include_generate_include (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, - enum ext_include_script_location location, enum ext_include_flags flags, - struct sieve_script *script, - const struct ext_include_script_info **included_r) + enum ext_include_script_location location, struct sieve_script *script, + const struct ext_include_script_info **included_r, bool once) { const struct sieve_extension *this_ext = cmd->ext; struct ext_include_context *ext_ctx = @@ -490,7 +489,7 @@ int ext_include_generate_include } /* Check for circular include */ - if ( (flags & EXT_INCLUDE_FLAG_ONCE) == 0 ) { + if ( !once ) { pctx = ctx; while ( pctx != NULL ) { if ( sieve_script_equals(pctx->script, script) ) { @@ -516,19 +515,12 @@ int ext_include_generate_include binctx = ext_include_binary_init(this_ext, sbin, cgenv->ast); /* Is the script already compiled into the current binary? */ - if ( ext_include_binary_script_is_included(binctx, script, &included) ) { - /* Yes, only update flags */ - if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) - flags &= ~EXT_INCLUDE_FLAG_OPTIONAL; - if ( (flags & EXT_INCLUDE_FLAG_ONCE) == 0 ) // for consistency - flags &= ~EXT_INCLUDE_FLAG_ONCE; - - } else { + if ( !ext_include_binary_script_is_included(binctx, script, &included) ) + { + struct sieve_binary_block *inc_block; const char *script_name = sieve_script_name(script); enum sieve_compile_flags cpflags = cgenv->flags; - /* No, include new script */ - /* Check whether include limit is exceeded */ if ( ext_include_binary_script_get_count(binctx) >= ext_ctx->max_includes ) { @@ -538,69 +530,57 @@ int ext_include_generate_include return -1; } - /* Allocate a new block in the binary and mark the script as included. + /* No, allocate a new block in the binary and mark the script as included. */ - if ( !sieve_script_is_open(script) ) { - /* Just making an empty entry to mark a missing script */ - i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 || - (flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0); - included = ext_include_binary_script_include - (binctx, location, flags, script, NULL); - result = 0; - - } else { - struct sieve_binary_block *inc_block = sieve_binary_block_create(sbin); - - /* Real include */ - included = ext_include_binary_script_include - (binctx, location, flags, script, inc_block); - - /* Parse */ - if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) { - sieve_command_generate_error(gentr, cmd, - "failed to parse included script '%s'", str_sanitize(script_name, 80)); - return -1; - } + inc_block = sieve_binary_block_create(sbin); + included = ext_include_binary_script_include + (binctx, script, location, inc_block); + + /* Parse */ + if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) { + sieve_command_generate_error(gentr, cmd, + "failed to parse included script '%s'", str_sanitize(script_name, 80)); + return -1; + } - /* Included scripts inherit global variable scope */ - (void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast); + /* Included scripts inherit global variable scope */ + (void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast); - if ( location == EXT_INCLUDE_LOCATION_GLOBAL ) + if ( location == EXT_INCLUDE_LOCATION_GLOBAL ) cpflags &= ~SIEVE_RUNTIME_FLAG_NOGLOBAL; - else + else cpflags |= SIEVE_RUNTIME_FLAG_NOGLOBAL; - /* Validate */ - if ( !sieve_validate(ast, ehandler, cpflags, NULL) ) { - sieve_command_generate_error(gentr, cmd, - "failed to validate included script '%s'", - str_sanitize(script_name, 80)); - sieve_ast_unref(&ast); - return -1; - } - - /* Generate - * - * FIXME: It might not be a good idea to recurse code generation for - * included scripts. - */ - subgentr = sieve_generator_create(ast, ehandler, cpflags); - ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script); - - if ( sieve_generator_run(subgentr, &inc_block) == NULL ) { - sieve_command_generate_error(gentr, cmd, - "failed to generate code for included script '%s'", - str_sanitize(script_name, 80)); - result = -1; - } + /* Validate */ + if ( !sieve_validate(ast, ehandler, cpflags, NULL) ) { + sieve_command_generate_error(gentr, cmd, + "failed to validate included script '%s'", + str_sanitize(script_name, 80)); + sieve_ast_unref(&ast); + return -1; + } - sieve_generator_free(&subgentr); + /* Generate + * + * FIXME: It might not be a good idea to recurse code generation for + * included scripts. + */ + subgentr = sieve_generator_create(ast, ehandler, cpflags); + ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script); - /* Cleanup */ - sieve_ast_unref(&ast); + if ( sieve_generator_run(subgentr, &inc_block) == NULL ) { + sieve_command_generate_error(gentr, cmd, + "failed to generate code for included script '%s'", + str_sanitize(script_name, 80)); + result = -1; } + + sieve_generator_free(&subgentr); + + /* Cleanup */ + sieve_ast_unref(&ast); } - + if ( result > 0 ) *included_r = included; return result; diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index f2857da..217ef27 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -21,12 +21,6 @@ struct ext_include_binary_context; * Types */ -enum ext_include_flags { // stored in one byte - EXT_INCLUDE_FLAG_ONCE = 0x01, - EXT_INCLUDE_FLAG_OPTIONAL = 0x02, - EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD = 0x04 -}; - enum ext_include_script_location { EXT_INCLUDE_LOCATION_PERSONAL, EXT_INCLUDE_LOCATION_GLOBAL, @@ -126,7 +120,7 @@ static inline struct ext_include_context *ext_include_get_context struct ext_include_ast_context { struct sieve_variable_scope *global_vars; - ARRAY(struct sieve_script *) included_scripts; + ARRAY_DEFINE(included_scripts, struct sieve_script *); }; struct ext_include_ast_context *ext_include_create_ast_context @@ -150,9 +144,8 @@ void ext_include_register_generator_context int ext_include_generate_include (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, - enum ext_include_script_location location, - enum ext_include_flags flags, struct sieve_script *script, - const struct ext_include_script_info **included_r); + enum ext_include_script_location location, struct sieve_script *script, + const struct ext_include_script_info **included_r, bool once); /* Interpreter context */ diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c index f765df1..41fb71e 100644 --- a/src/lib-sieve/plugins/include/ext-include.c +++ b/src/lib-sieve/plugins/include/ext-include.c @@ -63,19 +63,17 @@ static bool ext_include_binary_load /* Extension objects */ const struct sieve_extension_def include_extension = { - .name = "include", - .version = 1, - - .load = ext_include_load, - .unload = ext_include_unload, - .validator_load = ext_include_validator_load, - .generator_load = ext_include_generator_load, - .interpreter_load = ext_include_interpreter_load, - .binary_load = ext_include_binary_load, - .binary_dump = ext_include_binary_dump, - .code_dump = ext_include_code_dump, - - SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations) + "include", + ext_include_load, + ext_include_unload, + ext_include_validator_load, + ext_include_generator_load, + ext_include_interpreter_load, + ext_include_binary_load, + ext_include_binary_dump, + ext_include_code_dump, + SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_include_validator_load diff --git a/src/lib-sieve/plugins/mailbox/ext-mailbox.c b/src/lib-sieve/plugins/mailbox/ext-mailbox.c index 9a8879e..c565627 100644 --- a/src/lib-sieve/plugins/mailbox/ext-mailbox.c +++ b/src/lib-sieve/plugins/mailbox/ext-mailbox.c @@ -32,8 +32,10 @@ static bool ext_mailbox_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def mailbox_extension = { - .name = "mailbox", - .validator_load = ext_mailbox_validator_load, + "mailbox", + NULL, NULL, + ext_mailbox_validator_load, + NULL, NULL, NULL, NULL, NULL, SIEVE_EXT_DEFINE_OPERATION(mailboxexists_operation), SIEVE_EXT_DEFINE_OPERAND(mailbox_create_operand) }; diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c index 8496623..84d8ea3 100644 --- a/src/lib-sieve/plugins/notify/cmd-notify.c +++ b/src/lib-sieve/plugins/notify/cmd-notify.c @@ -6,7 +6,6 @@ #include "str.h" #include "ioloop.h" #include "str-sanitize.h" -#include "ostream.h" #include "message-date.h" #include "mail-storage.h" @@ -691,10 +690,8 @@ static bool act_notify_send const struct ext_notify_recipient *recipients; void *smtp_handle; unsigned int count, i; - struct ostream *output; - string_t *msg; + FILE *f; const char *outmsgid; - size_t hdr_size; /* Get recipients */ recipients = array_get(&act->recipients, &count); @@ -711,68 +708,59 @@ static bool act_notify_send return TRUE; } - /* Compose common headers */ - msg = t_str_new(512); - rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); - - /* Set importance */ - switch ( act->importance ) { - case 1: - rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); - rfc2822_header_write(msg, "Importance", "High"); - break; - case 3: - rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); - rfc2822_header_write(msg, "Importance", "Low"); - break; - case 2: - default: - rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); - rfc2822_header_write(msg, "Importance", "Normal"); - break; - } - - rfc2822_header_printf(msg, "From", - "Postmaster <%s>", senv->postmaster_address); - - rfc2822_header_write(msg, "Subject", "[SIEVE] New mail notification"); - - rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (notify)"); - rfc2822_header_write(msg, "Precedence", "bulk"); - - rfc2822_header_write(msg, "MIME-Version", "1.0"); - if (contains_8bit(act->message)) { - rfc2822_header_write(msg, - "Content-Type", "text/plain; charset=utf-8"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); - } else { - rfc2822_header_write(msg, - "Content-Type", "text/plain; charset=us-ascii"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); - } - - hdr_size = str_len(msg); - /* Send message to all recipients */ for ( i = 0; i < count; i++ ) { + if ( sieve_message_get_sender(aenv->msgctx) != NULL ) smtp_handle = sieve_smtp_open - (senv, recipients[i].normalized, senv->postmaster_address, &output); + (senv, recipients[i].normalized, senv->postmaster_address, &f); else smtp_handle = sieve_smtp_open - (senv, recipients[i].normalized, NULL, &output); - - str_truncate(msg, hdr_size); + (senv, recipients[i].normalized, NULL, &f); outmsgid = sieve_message_get_new_id(aenv->svinst); - rfc2822_header_write(msg, "Message-ID", outmsgid); - rfc2822_header_write(msg, "To", recipients[i].full); - /* Generate message body */ - str_printfa(msg, "\r\n%s\r\n", act->message); + rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_field_write(f, "Message-ID", outmsgid); + rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); + + /* Set importance */ + switch ( act->importance ) { + case 1: + rfc2822_header_field_write(f, "X-Priority", "1 (Highest)"); + rfc2822_header_field_write(f, "Importance", "High"); + break; + case 3: + rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)"); + rfc2822_header_field_write(f, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_field_write(f, "X-Priority", "3 (Normal)"); + rfc2822_header_field_write(f, "Importance", "Normal"); + break; + } + + rfc2822_header_field_printf(f, "From", "%s", + t_strdup_printf("Postmaster <%s>", senv->postmaster_address)); + + rfc2822_header_field_printf(f, "To", "%s", recipients[i].full); - o_stream_send(output, str_data(msg), str_len(msg)); + rfc2822_header_field_write(f, "Subject", "[SIEVE] New mail notification"); + + rfc2822_header_field_write(f, "Auto-Submitted", "auto-generated (notify)"); + rfc2822_header_field_write(f, "Precedence", "bulk"); + + if (contains_8bit(act->message)) { + rfc2822_header_field_write(f, "MIME-Version", "1.0"); + rfc2822_header_field_write(f, + "Content-Type", "text/plain; charset=UTF-8"); + rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); + } + + /* Generate message body */ + fprintf(f, "\r\n"); + fprintf(f, "%s\r\n", act->message); if ( sieve_smtp_close(senv, smtp_handle) ) { sieve_result_global_log(aenv, @@ -797,7 +785,6 @@ static bool act_notify_commit (const struct ext_notify_action *) action->context; const struct sieve_message_data *msgdata = aenv->msgdata; const char *const *headers; - bool result; /* Is the message an automatic reply ? */ if ( mail_get_headers @@ -816,11 +803,7 @@ static bool act_notify_commit } } - T_BEGIN { - result = act_notify_send(aenv, act); - } T_END; - - return result; + return act_notify_send(aenv, act); } diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c index 04ee634..bd06dd4 100644 --- a/src/lib-sieve/plugins/notify/ext-notify-common.c +++ b/src/lib-sieve/plugins/notify/ext-notify-common.c @@ -175,7 +175,7 @@ static buffer_t *cmd_notify_extract_body_text return NULL; /* Initialize body decoder */ - decoder = message_decoder_init(NULL, 0); + decoder = message_decoder_init(FALSE); parser = message_parser_init(mctx->pool, input, 0, 0); is_text = TRUE; diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c index e119bc0..c4088cb 100644 --- a/src/lib-sieve/plugins/notify/ext-notify.c +++ b/src/lib-sieve/plugins/notify/ext-notify.c @@ -41,9 +41,13 @@ static bool ext_notify_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def notify_extension = { - .name = "notify", - .validator_load = ext_notify_validator_load, - SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations) + "notify", + NULL, + NULL, + ext_notify_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations), + SIEVE_EXT_DEFINE_NO_OPERANDS, }; /* diff --git a/src/lib-sieve/plugins/regex/ext-regex.c b/src/lib-sieve/plugins/regex/ext-regex.c index dfa14bc..c7a50f7 100644 --- a/src/lib-sieve/plugins/regex/ext-regex.c +++ b/src/lib-sieve/plugins/regex/ext-regex.c @@ -49,8 +49,11 @@ static bool ext_regex_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def regex_extension = { - .name = "regex", - .validator_load = ext_regex_validator_load, + "regex", + NULL, NULL, + ext_regex_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, SIEVE_EXT_DEFINE_OPERAND(regex_match_type_operand) }; diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c index 4710285..aee20f4 100644 --- a/src/lib-sieve/plugins/regex/mcht-regex.c +++ b/src/lib-sieve/plugins/regex/mcht-regex.c @@ -178,7 +178,7 @@ struct mcht_regex_key { }; struct mcht_regex_context { - ARRAY(struct mcht_regex_key) reg_expressions; + ARRAY_DEFINE(reg_expressions, struct mcht_regex_key); regmatch_t *pmatch; size_t nmatch; unsigned int all_compiled:1; diff --git a/src/lib-sieve/plugins/relational/ext-relational.c b/src/lib-sieve/plugins/relational/ext-relational.c index cd229dc..c7a433c 100644 --- a/src/lib-sieve/plugins/relational/ext-relational.c +++ b/src/lib-sieve/plugins/relational/ext-relational.c @@ -36,8 +36,11 @@ static bool ext_relational_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def relational_extension = { - .name = "relational", - .validator_load = ext_relational_validator_load, + "relational", + NULL, NULL, + ext_relational_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, SIEVE_EXT_DEFINE_OPERAND(rel_match_type_operand) }; diff --git a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c index 42dda49..0ec424e 100644 --- a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c +++ b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c @@ -68,27 +68,33 @@ static bool ext_spamvirustest_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def spamtest_extension = { - .name = "spamtest", - .load = ext_spamvirustest_load, - .unload = ext_spamvirustest_unload, - .validator_load = ext_spamvirustest_validator_load, - SIEVE_EXT_DEFINE_OPERATION(spamtest_operation) + "spamtest", + ext_spamvirustest_load, + ext_spamvirustest_unload, + ext_spamvirustest_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(spamtest_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; const struct sieve_extension_def spamtestplus_extension = { - .name = "spamtestplus", - .load = ext_spamvirustest_load, - .unload = ext_spamvirustest_unload, - .validator_load = ext_spamvirustest_validator_load, - SIEVE_EXT_DEFINE_OPERATION(spamtest_operation) + "spamtestplus", + ext_spamvirustest_load, + ext_spamvirustest_unload, + ext_spamvirustest_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(spamtest_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; const struct sieve_extension_def virustest_extension = { - .name = "virustest", - .load = ext_spamvirustest_load, - .unload = ext_spamvirustest_unload, - .validator_load = ext_spamvirustest_validator_load, - SIEVE_EXT_DEFINE_OPERATION(virustest_operation) + "virustest", + ext_spamvirustest_load, + ext_spamvirustest_unload, + ext_spamvirustest_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(virustest_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; /* diff --git a/src/lib-sieve/plugins/subaddress/ext-subaddress.c b/src/lib-sieve/plugins/subaddress/ext-subaddress.c index 248c499..2a33494 100644 --- a/src/lib-sieve/plugins/subaddress/ext-subaddress.c +++ b/src/lib-sieve/plugins/subaddress/ext-subaddress.c @@ -57,10 +57,12 @@ static bool ext_subaddress_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def subaddress_extension = { - .name = "subaddress", - .load = ext_subaddress_load, - .unload = ext_subaddress_unload, - .validator_load = ext_subaddress_validator_load, + "subaddress", + ext_subaddress_load, + ext_subaddress_unload, + ext_subaddress_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, SIEVE_EXT_DEFINE_OPERAND(subaddress_operand) }; diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c index e7196d1..348d8d9 100644 --- a/src/lib-sieve/plugins/vacation/cmd-vacation.c +++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c @@ -7,7 +7,6 @@ #include "md5.h" #include "hostpid.h" #include "str-sanitize.h" -#include "ostream.h" #include "message-address.h" #include "message-date.h" #include "ioloop.h" @@ -694,7 +693,7 @@ static int ext_vacation_operation_execute /* Normalize all addresses */ if ( addresses != NULL ) { - ARRAY(const char *) norm_addresses; + ARRAY_DEFINE(norm_addresses, const char *); string_t *raw_address; int ret; @@ -902,8 +901,7 @@ static bool act_vacation_send const struct sieve_message_data *msgdata = aenv->msgdata; const struct sieve_script_env *senv = aenv->scriptenv; void *smtp_handle; - struct ostream *output; - string_t *msg; + FILE *f; const char *outmsgid; const char *const *headers; const char *subject; @@ -934,32 +932,31 @@ static bool act_vacation_send /* Open smtp session */ - smtp_handle = sieve_smtp_open(senv, reply_to, NULL, &output); + smtp_handle = sieve_smtp_open(senv, reply_to, NULL, &f); outmsgid = sieve_message_get_new_id(aenv->svinst); /* Produce a proper reply */ - msg = t_str_new(512); - rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_write(msg, "Message-ID", outmsgid); - rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_field_write(f, "Message-ID", outmsgid); + rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); if ( ctx->from != NULL && *(ctx->from) != '\0' ) - rfc2822_header_utf8_printf(msg, "From", "%s", ctx->from); + rfc2822_header_field_utf8_printf(f, "From", "%s", ctx->from); else if ( reply_from != NULL ) - rfc2822_header_printf(msg, "From", "<%s>", reply_from); + rfc2822_header_field_printf(f, "From", "<%s>", reply_from); else - rfc2822_header_printf(msg, "From", "Postmaster <%s>", senv->postmaster_address); + rfc2822_header_field_printf(f, "From", "Postmaster <%s>", senv->postmaster_address); - /* FIXME: If From header of message has same address, we should use that - * instead to properly include the phrase part. + /* FIXME: If From header of message has same address, we should use that in + * stead properly include the phrase part. */ - rfc2822_header_printf(msg, "To", "<%s>", reply_to); + rfc2822_header_field_printf(f, "To", "<%s>", reply_to); if ( _contains_8bit(subject) ) - rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); + rfc2822_header_field_utf8_printf(f, "Subject", "%s", subject); else - rfc2822_header_printf(msg, "Subject", "%s", subject); + rfc2822_header_field_printf(f, "Subject", "%s", subject); /* Compose proper in-reply-to and references headers */ @@ -967,30 +964,29 @@ static bool act_vacation_send (aenv->msgdata->mail, "references", &headers); if ( msgdata->id != NULL ) { - rfc2822_header_write(msg, "In-Reply-To", msgdata->id); + rfc2822_header_field_write(f, "In-Reply-To", msgdata->id); if ( ret >= 0 && headers[0] != NULL ) - rfc2822_header_write - (msg, "References", t_strconcat(headers[0], " ", msgdata->id, NULL)); + rfc2822_header_field_write + (f, "References", t_strconcat(headers[0], " ", msgdata->id, NULL)); else - rfc2822_header_write(msg, "References", msgdata->id); + rfc2822_header_field_write(f, "References", msgdata->id); } else if ( ret >= 0 && headers[0] != NULL ) { - rfc2822_header_write(msg, "References", headers[0]); + rfc2822_header_field_write(f, "References", headers[0]); } - rfc2822_header_write(msg, "Auto-Submitted", "auto-replied (vacation)"); - rfc2822_header_write(msg, "Precedence", "bulk"); + rfc2822_header_field_write(f, "Auto-Submitted", "auto-replied (vacation)"); + rfc2822_header_field_write(f, "Precedence", "bulk"); - rfc2822_header_write(msg, "MIME-Version", "1.0"); + rfc2822_header_field_write(f, "MIME-Version", "1.0"); if ( !ctx->mime ) { - rfc2822_header_write(msg, "Content-Type", "text/plain; charset=utf-8"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); - str_append(msg, "\r\n"); + rfc2822_header_field_write(f, "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); + fprintf(f, "\r\n"); } - str_printfa(msg, "%s\r\n", ctx->reason); - o_stream_send(output, str_data(msg), str_len(msg)); + fprintf(f, "%s\r\n", ctx->reason); /* Close smtp session */ if ( !sieve_smtp_close(senv, smtp_handle) ) { @@ -1036,7 +1032,6 @@ static bool act_vacation_commit const char *const *hdsp; const char *const *headers; const char *reply_from = NULL, *orig_recipient = NULL; - bool result; /* Is the recipient unset? */ @@ -1219,11 +1214,7 @@ static bool act_vacation_commit /* Send the message */ - T_BEGIN { - result = act_vacation_send(aenv, ctx, sender, reply_from); - } T_END; - - if ( result ) { + if ( act_vacation_send(aenv, ctx, sender, reply_from) ) { sieve_number_t seconds; sieve_result_global_log(aenv, "sent vacation response to <%s>", @@ -1241,6 +1232,8 @@ static bool act_vacation_commit sieve_action_duplicate_mark (senv, dupl_hash, sizeof(dupl_hash), ioloop_time + seconds); } + + return TRUE; } return TRUE; diff --git a/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c b/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c index 0d99241..1693fe0 100644 --- a/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c +++ b/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c @@ -30,9 +30,13 @@ static bool ext_vacation_seconds_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def vacation_seconds_extension = { - .name = "vacation-seconds", - .load = ext_vacation_seconds_load, - .validator_load = ext_vacation_seconds_validator_load, + "vacation-seconds", + ext_vacation_seconds_load, + NULL, + ext_vacation_seconds_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS }; bool ext_vacation_seconds_load diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c index 9dc212d..58f4a0c 100644 --- a/src/lib-sieve/plugins/vacation/ext-vacation.c +++ b/src/lib-sieve/plugins/vacation/ext-vacation.c @@ -32,11 +32,13 @@ static bool ext_vacation_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def vacation_extension = { - .name = "vacation", - .load = ext_vacation_load, - .unload = ext_vacation_unload, - .validator_load = ext_vacation_validator_load, - SIEVE_EXT_DEFINE_OPERATION(vacation_operation) + "vacation", + ext_vacation_load, + ext_vacation_unload, + ext_vacation_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(vacation_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_vacation_validator_load diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c index a1d807c..328e82f 100644 --- a/src/lib-sieve/plugins/variables/cmd-set.c +++ b/src/lib-sieve/plugins/variables/cmd-set.c @@ -73,7 +73,7 @@ const struct sieve_operation_def cmd_set_operation = { */ struct cmd_set_context { - ARRAY(const struct sieve_variables_modifier *) modifiers; + ARRAY_DEFINE(modifiers, const struct sieve_variables_modifier *); }; /* diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c index f4bd3bd..4ba886f 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-common.c +++ b/src/lib-sieve/plugins/variables/ext-variables-common.c @@ -47,8 +47,8 @@ struct sieve_variable_scope { struct sieve_variable *error_var; - HASH_TABLE(const char *, struct sieve_variable *) variables; - ARRAY(struct sieve_variable *) variable_index; + struct hash_table *variables; + ARRAY_DEFINE(variable_index, struct sieve_variable *); }; struct sieve_variable_scope_binary { @@ -78,7 +78,8 @@ struct sieve_variable_scope *sieve_variable_scope_create scope->svinst = svinst; scope->ext = ext; - hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp); + scope->variables = hash_table_create + (default_pool, pool, 0, strcase_hash, (hash_cmp_callback_t *)strcasecmp); p_array_init(&scope->variable_index, pool, 128); return scope; @@ -132,7 +133,8 @@ struct sieve_variable *sieve_variable_scope_declare new_var->identifier = p_strdup(scope->pool, identifier); new_var->index = array_count(&scope->variable_index); - hash_table_insert(scope->variables, new_var->identifier, new_var); + hash_table_insert + (scope->variables, (void *) new_var->identifier, (void *) new_var); array_append(&scope->variable_index, &new_var, 1); return new_var; @@ -143,7 +145,8 @@ struct sieve_variable *sieve_variable_scope_get_variable { struct sieve_variable *var; - var = hash_table_lookup(scope->variables, identifier); + var = (struct sieve_variable *) + hash_table_lookup(scope->variables, identifier); if ( var == NULL && declare ) { var = sieve_variable_scope_declare(scope, identifier); @@ -160,7 +163,8 @@ struct sieve_variable *sieve_variable_scope_import new_var = p_new(scope->pool, struct sieve_variable, 1); memcpy(new_var, var, sizeof(struct sieve_variable)); - hash_table_insert(scope->variables, new_var->identifier, new_var); + hash_table_insert + (scope->variables, (void *) new_var->identifier, (void *) new_var); /* Not entered into the index because it is an external variable * (This can be done unlimited; only limited by the size of the external scope) @@ -184,10 +188,13 @@ struct sieve_variable_scope_iter *sieve_variable_scope_iterate_init bool sieve_variable_scope_iterate (struct sieve_variable_scope_iter *iter, struct sieve_variable **var_r) { - const char *key; + void *key, *value; - return hash_table_iterate - (iter->hctx, iter->scope->variables, &key, var_r); + if ( !hash_table_iterate(iter->hctx, &key, &value) ) + return FALSE; + + *var_r = (struct sieve_variable *) value; + return TRUE; } void sieve_variable_scope_iterate_deinit @@ -398,7 +405,7 @@ struct sieve_variable_storage { struct sieve_variable_scope *scope; struct sieve_variable_scope_binary *scope_bin; unsigned int max_size; - ARRAY(string_t *) var_values; + ARRAY_DEFINE(var_values, string_t *); }; struct sieve_variable_storage *sieve_variable_storage_create @@ -683,7 +690,7 @@ struct ext_variables_interpreter_context { struct sieve_variable_scope_binary *local_scope_bin; struct sieve_variable_storage *local_storage; - ARRAY(struct sieve_variable_storage *) ext_storages; + ARRAY_DEFINE(ext_storages, struct sieve_variable_storage *); }; static void ext_variables_interpreter_free diff --git a/src/lib-sieve/plugins/variables/ext-variables-dump.c b/src/lib-sieve/plugins/variables/ext-variables-dump.c index 1228991..05baaec 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-dump.c +++ b/src/lib-sieve/plugins/variables/ext-variables-dump.c @@ -30,7 +30,7 @@ const struct sieve_code_dumper_extension variables_dump_extension = { struct ext_variables_dump_context { struct sieve_variable_scope *local_scope; - ARRAY(struct sieve_variable_scope *) ext_scopes; + ARRAY_DEFINE(ext_scopes, struct sieve_variable_scope *); }; static void ext_variables_code_dumper_free diff --git a/src/lib-sieve/plugins/variables/ext-variables.c b/src/lib-sieve/plugins/variables/ext-variables.c index 56c2c57..61d12fe 100644 --- a/src/lib-sieve/plugins/variables/ext-variables.c +++ b/src/lib-sieve/plugins/variables/ext-variables.c @@ -57,11 +57,13 @@ static bool ext_variables_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def variables_extension = { - .name = "variables", - .validator_load = ext_variables_validator_load, - .generator_load = ext_variables_generator_load, - .interpreter_load = ext_variables_interpreter_load, - .code_dump = ext_variables_code_dump, + "variables", + NULL, NULL, + ext_variables_validator_load, + ext_variables_generator_load, + ext_variables_interpreter_load, + NULL, NULL, + ext_variables_code_dump, SIEVE_EXT_DEFINE_OPERATIONS(ext_variables_operations), SIEVE_EXT_DEFINE_OPERANDS(ext_variables_operands) }; diff --git a/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c b/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c index 1e15b93..0be9f56 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c +++ b/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c @@ -40,10 +40,14 @@ static bool ext_debug_interpreter_load const struct sieve_extension_def debug_extension = { - .name = "vnd.dovecot.debug", - .validator_load = ext_debug_validator_load, - .interpreter_load = ext_debug_interpreter_load, + "vnd.dovecot.debug", + NULL, NULL, + ext_debug_validator_load, + NULL, + ext_debug_interpreter_load, + NULL, NULL, NULL, SIEVE_EXT_DEFINE_OPERATION(debug_log_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_debug_validator_load diff --git a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c index 836baaf..ccf947e 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c +++ b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c @@ -135,7 +135,7 @@ struct ext_duplicate_handle { }; struct ext_duplicate_context { - ARRAY(struct ext_duplicate_handle) handles; + ARRAY_DEFINE(handles, struct ext_duplicate_handle); unsigned int nohandle_duplicate:1; unsigned int nohandle_checked:1; @@ -154,7 +154,7 @@ int ext_duplicate_check struct act_duplicate_mark_data *act; struct md5_context ctx; - if ( !sieve_action_duplicate_check_available(senv) || value == NULL ) + if ( !sieve_action_duplicate_check_available(senv) || value == NULL ) return 0; /* Get context; find out whether duplicate was checked earlier */ @@ -179,7 +179,7 @@ int ext_duplicate_check return ( record->duplicate ? 1 : 0 ); } } - } + } result_pool = sieve_result_pool(renv->result); act = p_new(result_pool, struct act_duplicate_mark_data, 1); @@ -216,7 +216,7 @@ int ext_duplicate_check rctx->nohandle_checked = TRUE; } else { struct ext_duplicate_handle *record; - + if ( msg_pool == NULL ) msg_pool = sieve_message_context_pool(renv->msgctx); if ( !array_is_created(&rctx->handles) ) diff --git a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c index b91c78e..b08a7e8 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c +++ b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c @@ -29,11 +29,13 @@ static bool ext_duplicate_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def duplicate_extension = { - .name = "vnd.dovecot.duplicate", - .load = ext_duplicate_load, - .unload = ext_duplicate_unload, - .validator_load = ext_duplicate_validator_load, - SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation) + "vnd.dovecot.duplicate", + ext_duplicate_load, + ext_duplicate_unload, + ext_duplicate_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation), + SIEVE_EXT_DEFINE_NO_OPERANDS }; /* diff --git a/src/lib-sieve/rfc2822.c b/src/lib-sieve/rfc2822.c index 27f94d4..8611416 100644 --- a/src/lib-sieve/rfc2822.c +++ b/src/lib-sieve/rfc2822.c @@ -116,10 +116,11 @@ const char *rfc2822_header_field_name_sanitize(const char *name) */ /* FIXME: This should be collected into a Dovecot API for composing internet - * mail messages. + * mail messages. These functions now use FILE * output streams, but this should + * be changed to proper dovecot streams. */ -unsigned int rfc2822_header_append +unsigned int rfc2822_header_field_append (string_t *header, const char *name, const char *body, bool crlf, uoff_t *body_offset_r) { @@ -143,8 +144,7 @@ unsigned int rfc2822_header_append /* Add field body; fold it if necessary and account for existing folding */ while ( *bp != '\0' ) { - while ( *bp != '\0' && nlp == NULL && - (wp == NULL || line_len < max_line) ) { + while ( *bp != '\0' && nlp == NULL && (wp == NULL || line_len < max_line) ) { if ( *bp == ' ' || *bp == '\t' ) { wp = bp; } else if ( *bp == '\r' || *bp == '\n' ) { @@ -208,29 +208,70 @@ unsigned int rfc2822_header_append return lines; } -void rfc2822_header_printf -(string_t *header, const char *name, const char *fmt, ...) +static int rfc2822_header_field_write_real +(FILE *f, const char *name, const char *body, size_t size) { - const char *body; + string_t *header = t_str_new(strlen(name) + size + 256); + + (void)rfc2822_header_field_append(header, name, body, TRUE, NULL); + + if ( !fwrite(str_data(header), str_len(header), 1, f) != 1 ) + return -1; + + return str_len(header); +} + +int rfc2822_header_field_write +(FILE *f, const char *name, const char *body) +{ + int ret; + + T_BEGIN { + ret = rfc2822_header_field_write_real(f, name, body, strlen(body)); + } T_END; + + return ret; +} + +int rfc2822_header_field_printf +(FILE *f, const char *name, const char *body_fmt, ...) +{ + string_t *body; va_list args; + int ret; + + T_BEGIN { + body = t_str_new(256); - va_start(args, fmt); - body = t_strdup_vprintf(fmt, args); - va_end(args); + va_start(args, body_fmt); + str_vprintfa(body, body_fmt, args); + va_end(args); - rfc2822_header_write(header, name, body); + ret = rfc2822_header_field_write_real + (f, name, (const char *) str_data(body), str_len(body)); + } T_END; + + return ret; } -void rfc2822_header_utf8_printf -(string_t *header, const char *name, const char *fmt, ...) +int rfc2822_header_field_utf8_printf +(FILE *f, const char *name, const char *body_fmt, ...) { - string_t *body = t_str_new(256); + string_t *body; va_list args; + int ret; + + T_BEGIN { + body = t_str_new(256); + + va_start(args, body_fmt); + message_header_encode(t_strdup_vprintf(body_fmt, args), body); + va_end(args); - va_start(args, fmt); - message_header_encode(t_strdup_vprintf(fmt, args), body); - va_end(args); + ret = rfc2822_header_field_write_real + (f, name, (const char *) str_data(body), str_len(body)); + } T_END; - rfc2822_header_write(header, name, str_c(body)); + return ret; } diff --git a/src/lib-sieve/rfc2822.h b/src/lib-sieve/rfc2822.h index 2a943cf..49b5c72 100644 --- a/src/lib-sieve/rfc2822.h +++ b/src/lib-sieve/rfc2822.h @@ -27,19 +27,17 @@ const char *rfc2822_header_field_name_sanitize(const char *name); * Message composition */ -unsigned int rfc2822_header_append +unsigned int rfc2822_header_field_append (string_t *header, const char *name, const char *body, bool crlf, uoff_t *body_offset_r); -static inline void rfc2822_header_write -(string_t *header, const char *name, const char *body) -{ - (void)rfc2822_header_append(header, name, body, TRUE, NULL); -} +int rfc2822_header_field_write + (FILE *f, const char *name, const char *body); -void rfc2822_header_printf - (string_t *header, const char *name, const char *fmt, ...) ATTR_FORMAT(3, 4); -void rfc2822_header_utf8_printf - (string_t *header, const char *name, const char *fmt, ...) ATTR_FORMAT(3, 4); +int rfc2822_header_field_printf + (FILE *f, const char *name, const char *body_fmt, ...) ATTR_FORMAT(3, 4); + +int rfc2822_header_field_utf8_printf + (FILE *f, const char *name, const char *body_fmt, ...) ATTR_FORMAT(3, 4); #endif /* __RFC2822_H */ diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index e39db49..1f6c091 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -10,7 +10,6 @@ #include "unichar.h" #include "istream.h" #include "istream-header-filter.h" -#include "ostream.h" #include "mail-deliver.h" #include "mail-storage.h" #include "message-date.h" @@ -749,11 +748,13 @@ static bool sieve_action_do_reject_mail const struct sieve_script_env *senv = aenv->scriptenv; const struct sieve_message_data *msgdata = aenv->msgdata; struct istream *input; - struct ostream *output; void *smtp_handle; + struct message_size hdr_size; + FILE *f; const char *new_msgid, *boundary; + const unsigned char *data; const char *header; - string_t *hdr; + size_t size; int ret; /* Just to be sure */ @@ -763,84 +764,81 @@ static bool sieve_action_do_reject_mail return TRUE; } - smtp_handle = sieve_smtp_open(senv, sender, NULL, &output); + smtp_handle = sieve_smtp_open(senv, sender, NULL, &f); 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); - rfc2822_header_write(hdr, "Message-ID", new_msgid); - rfc2822_header_write(hdr, "Date", message_date_create(ioloop_time)); - rfc2822_header_printf(hdr, "From", "Mail Delivery Subsystem <%s>", + rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_field_write(f, "Message-ID", new_msgid); + rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); + rfc2822_header_field_printf(f, "From", "Mail Delivery Subsystem <%s>", senv->postmaster_address); - rfc2822_header_printf(hdr, "To", "<%s>", sender); - rfc2822_header_write(hdr, "Subject", "Automatically rejected mail"); - rfc2822_header_write(hdr, "Auto-Submitted", "auto-replied (rejected)"); - rfc2822_header_write(hdr, "Precedence", "bulk"); - - rfc2822_header_write(hdr, "MIME-Version", "1.0"); - rfc2822_header_printf(hdr, "Content-Type", - "multipart/report; report-type=disposition-notification;\r\n" + rfc2822_header_field_printf(f, "To", "<%s>", sender); + rfc2822_header_field_write(f, "Subject", "Automatically rejected mail"); + rfc2822_header_field_write(f, "Auto-Submitted", "auto-replied (rejected)"); + rfc2822_header_field_write(f, "Precedence", "bulk"); + + rfc2822_header_field_write(f, "MIME-Version", "1.0"); + rfc2822_header_field_printf(f, "Content-Type", + "multipart/report; report-type=disposition-notification;\n" "boundary=\"%s\"", boundary); - str_append(hdr, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); /* Human readable status report */ - str_printfa(hdr, "--%s\r\n", boundary); - rfc2822_header_write(hdr, "Content-Type", "text/plain; charset=utf-8"); - rfc2822_header_write(hdr, "Content-Disposition", "inline"); - rfc2822_header_write(hdr, "Content-Transfer-Encoding", "8bit"); + fprintf(f, "--%s\r\n", boundary); + fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n"); + fprintf(f, "Content-Disposition: inline\r\n"); + fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n"); - str_printfa(hdr, "\r\nYour message to <%s> was automatically rejected:\r\n" + fprintf(f, "Your message to <%s> was automatically rejected:\r\n" "%s\r\n", recipient, reason); /* MDN status report */ - str_printfa(hdr, "--%s\r\n", boundary); - 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", + fprintf(f, "--%s\r\n" + "Content-Type: message/disposition-notification\r\n\r\n", boundary); + fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", 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); + fprintf(f, "Original-Recipient: rfc822; %s\r\n", header); + fprintf(f, "Final-Recipient: rfc822; %s\r\n", recipient); if ( msgdata->id != NULL ) - rfc2822_header_write(hdr, "Original-Message-ID", msgdata->id); - rfc2822_header_write(hdr, "Disposition", - "automatic-action/MDN-sent-automatically; deleted"); - str_append(hdr, "\r\n"); + fprintf(f, "Original-Message-ID: %s\r\n", msgdata->id); + fprintf(f, "Disposition: " + "automatic-action/MDN-sent-automatically; deleted\r\n"); + fprintf(f, "\r\n"); /* original message's headers */ - str_printfa(hdr, "--%s\r\n", boundary); - rfc2822_header_write(hdr, "Content-Type", "message/rfc822"); - str_append(hdr, "\r\n"); - o_stream_send(output, str_data(hdr), str_len(hdr)); - - if (mail_get_hdr_stream(msgdata->mail, NULL, &input) == 0) { - /* Note: If you add more headers, they need to be sorted. - We'll drop Content-Type because we're not including the message - body, and having a multipart Content-Type may confuse some - MIME parsers when they don't see the message boundaries. */ - static const char *const exclude_headers[] = { - "Content-Type" - }; - - input = i_stream_create_header_filter(input, - HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | - HEADER_FILTER_HIDE_BODY, exclude_headers, - N_ELEMENTS(exclude_headers), - *null_header_filter_callback, (void *)NULL); - - ret = o_stream_send_istream(output, input); - i_stream_unref(&input); - - i_assert(ret != 0); - } - - str_truncate(hdr, 0); - str_printfa(hdr, "\r\n\r\n--%s--\r\n", boundary); - o_stream_send(output, str_data(hdr), str_len(hdr)); + fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); + + if (mail_get_stream(msgdata->mail, &hdr_size, NULL, &input) == 0) { + /* Note: If you add more headers, they need to be sorted. + * We'll drop Content-Type because we're not including the message + * body, and having a multipart Content-Type may confuse some + * MIME parsers when they don't see the message boundaries. + */ + static const char *const exclude_headers[] = { + "Content-Type" + }; + + input = i_stream_create_header_filter(input, + HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_HIDE_BODY, + exclude_headers, N_ELEMENTS(exclude_headers), + null_header_filter_callback, NULL); + + while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { + if (fwrite(data, size, 1, f) == 0) + break; + i_stream_skip(input, size); + } + i_stream_unref(&input); + + i_assert(ret != 0); + } + + fprintf(f, "\r\n\r\n--%s--\r\n", boundary); if ( !sieve_smtp_close(senv, smtp_handle) ) { sieve_result_global_error(aenv, @@ -858,18 +856,12 @@ bool sieve_action_reject_mail const char *sender, const char *recipient, const char *reason) { const struct sieve_script_env *senv = aenv->scriptenv; - bool result; - T_BEGIN { - if ( senv->reject_mail != NULL ) { - result = - ( senv->reject_mail(senv, recipient, reason) >= 0 ); - } else { - result = sieve_action_do_reject_mail(aenv, sender, recipient, reason); - } - } T_END; + if ( senv->reject_mail != NULL ) { + return ( senv->reject_mail(senv, recipient, reason) >= 0 ); + } - return result; + return sieve_action_do_reject_mail(aenv, sender, recipient, reason); } diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c index c962395..2f154b9 100644 --- a/src/lib-sieve/sieve-address-parts.c +++ b/src/lib-sieve/sieve-address-parts.c @@ -44,8 +44,12 @@ static bool addrp_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def address_part_extension = { - .name = "@address-parts", - .validator_load = addrp_validator_load + "@address-parts", + NULL, NULL, + addrp_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS /* Defined as core operand */ }; /* diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index c5fdd0e..fcea092 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -48,8 +48,8 @@ struct sieve_ast { struct sieve_ast_node *root; - ARRAY(const struct sieve_extension *) linked_extensions; - ARRAY(struct sieve_ast_extension_reg) extensions; + ARRAY_DEFINE(linked_extensions, const struct sieve_extension *); + ARRAY_DEFINE(extensions, struct sieve_ast_extension_reg); }; struct sieve_ast *sieve_ast_create diff --git a/src/lib-sieve/sieve-binary-file.c b/src/lib-sieve/sieve-binary-file.c index 0f68b37..cb611a7 100644 --- a/src/lib-sieve/sieve-binary-file.c +++ b/src/lib-sieve/sieve-binary-file.c @@ -233,11 +233,11 @@ static bool _sieve_binary_save sizeof(struct sieve_binary_block_index) * blk_count, &block_index) ) return FALSE; - /* Create block containing all used extensions */ - + /* Create block containing all used extensions + * FIXME: Per-extension this should also store binary version numbers. + */ ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); i_assert( ext_block != NULL ); - sieve_binary_block_clear(ext_block); ext_count = array_count(&sbin->linked_extensions); sieve_binary_emit_unsigned(ext_block, ext_count); @@ -248,8 +248,6 @@ static bool _sieve_binary_save sieve_binary_emit_cstring (ext_block, sieve_extension_name((*ext)->extension)); - sieve_binary_emit_unsigned - (ext_block, sieve_extension_version((*ext)->extension)); sieve_binary_emit_unsigned(ext_block, (*ext)->block_id); } @@ -721,48 +719,38 @@ static bool _read_block_index_record return TRUE; } -static int _read_extensions(struct sieve_binary_block *sblock) +static bool _read_extensions(struct sieve_binary_block *sblock) { struct sieve_binary *sbin = sblock->sbin; sieve_size_t offset = 0; unsigned int i, count; - bool result = 1; + bool result = TRUE; if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) - return -1; + return FALSE; - for ( i = 0; result > 0 && i < count; i++ ) { + for ( i = 0; result && i < count; i++ ) { T_BEGIN { string_t *extension; const struct sieve_extension *ext; - unsigned int version; if ( sieve_binary_read_string(sblock, &offset, &extension) ) { ext = sieve_extension_get_by_name(sbin->svinst, str_c(extension)); if ( ext == NULL ) { sieve_sys_error(sbin->svinst, - "binary open: binary %s requires unknown extension `%s'", + "binary open: binary %s requires unknown extension '%s'", sbin->path, str_sanitize(str_c(extension), 128)); - result = 0; + result = FALSE; } else { struct sieve_binary_extension_reg *ereg = NULL; (void) sieve_binary_extension_register(sbin, ext, &ereg); - if ( !sieve_binary_read_unsigned(sblock, &offset, &version) || - !sieve_binary_read_unsigned(sblock, &offset, &ereg->block_id) ) { - result = -1; - } else if ( !sieve_extension_version_is(ext, version) ) { - sieve_sys_debug(sbin->svinst, - "binary open: binary %s was compiled with different version " - "of the `%s' extension (compiled v%d, expected v%d;" - "automatically fixed when re-compiled)", sbin->path, - sieve_extension_name(ext), version, sieve_extension_version(ext)); - result = 0; - } + if ( !sieve_binary_read_unsigned(sblock, &offset, &ereg->block_id) ) + result = FALSE; } } else - result = -1; + result = FALSE; } T_END; } @@ -776,7 +764,6 @@ static bool _sieve_binary_open(struct sieve_binary *sbin) const struct sieve_binary_header *header; struct sieve_binary_block *ext_block; unsigned int i, blk_count; - int ret; /* Verify header */ @@ -851,12 +838,10 @@ 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 ( (ret=_read_extensions(ext_block)) <= 0 ) { - if ( ret < 0 ) { - sieve_sys_error(sbin->svinst, - "binary open: binary %s is corrupt: failed to load extension block", - sbin->path); - } + } 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-private.h b/src/lib-sieve/sieve-binary-private.h index c87f541..969bbe0 100644 --- a/src/lib-sieve/sieve-binary-private.h +++ b/src/lib-sieve/sieve-binary-private.h @@ -99,15 +99,15 @@ struct sieve_binary { * context data to the binary object in memory. This is stored in these * registration objects as well. */ - ARRAY(struct sieve_binary_extension_reg *) extensions; - ARRAY(struct sieve_binary_extension_reg *) extension_index; - ARRAY(struct sieve_binary_extension_reg *) linked_extensions; + ARRAY_DEFINE(extensions, struct sieve_binary_extension_reg *); + ARRAY_DEFINE(extension_index, struct sieve_binary_extension_reg *); + ARRAY_DEFINE(linked_extensions, struct sieve_binary_extension_reg *); /* Attributes of a loaded binary */ const char *path; /* Blocks */ - ARRAY(struct sieve_binary_block *) blocks; + ARRAY_DEFINE(blocks, struct sieve_binary_block *); }; struct sieve_binary *sieve_binary_create diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h index b1e9afc..2b5a432 100644 --- a/src/lib-sieve/sieve-binary.h +++ b/src/lib-sieve/sieve-binary.h @@ -12,8 +12,8 @@ * Config */ -#define SIEVE_BINARY_VERSION_MAJOR 1 -#define SIEVE_BINARY_VERSION_MINOR 0 +#define SIEVE_BINARY_VERSION_MAJOR 0 +#define SIEVE_BINARY_VERSION_MINOR 4 /* * Binary object diff --git a/src/lib-sieve/sieve-code-dumper.c b/src/lib-sieve/sieve-code-dumper.c index 77e25b3..7410c65 100644 --- a/src/lib-sieve/sieve-code-dumper.c +++ b/src/lib-sieve/sieve-code-dumper.c @@ -46,7 +46,7 @@ struct sieve_code_dumper { struct sieve_binary_debug_reader *dreader; - ARRAY(struct sieve_code_dumper_extension_reg) extensions; + ARRAY_DEFINE(extensions, struct sieve_code_dumper_extension_reg); }; struct sieve_code_dumper *sieve_code_dumper_create diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index ee0f785..b9671e8 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -157,7 +157,6 @@ struct sieve_instance { /* System environment */ const char *hostname; - const char *domainname; const char *base_dir; /* User environment */ @@ -182,8 +181,6 @@ struct sieve_instance { /* Plugin modules */ struct sieve_plugin *plugins; - enum sieve_env_location env_location; - enum sieve_delivery_phase delivery_phase; /* Limits */ size_t max_script_size; diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c index 5f83631..bd1b416 100644 --- a/src/lib-sieve/sieve-comparators.c +++ b/src/lib-sieve/sieve-comparators.c @@ -39,8 +39,12 @@ static bool cmp_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def comparator_extension = { - .name = "@comparators", - .validator_load = cmp_validator_load + "@comparators", + NULL, NULL, + cmp_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS /* Defined as core operand */ }; /* diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c index 0a5e2ce..ad11171 100644 --- a/src/lib-sieve/sieve-error.c +++ b/src/lib-sieve/sieve-error.c @@ -1247,7 +1247,7 @@ struct sieve_varexpand_ehandler { struct sieve_error_handler handler; const char *format; - ARRAY(struct var_expand_table) table; + ARRAY_DEFINE(table, struct var_expand_table); }; static const char *ATTR_FORMAT(3, 0) _expand_message diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index bc3c4a2..0c1ae2d 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -31,9 +31,9 @@ static struct sieve_extension *_sieve_extension_register */ struct sieve_extension_registry { - ARRAY(struct sieve_extension *) extensions; - HASH_TABLE(const char *, struct sieve_extension *) extension_index; - HASH_TABLE(const char *, struct sieve_capability_registration *) capabilities_index; + ARRAY_DEFINE(extensions, struct sieve_extension *); + struct hash_table *extension_index; + struct hash_table *capabilities_index; /* Core language 'extensions' */ const struct sieve_extension *comparator_extension; @@ -41,7 +41,7 @@ struct sieve_extension_registry { const struct sieve_extension *address_part_extension; /* Preloaded extensions */ - ARRAY(const struct sieve_extension *) preloaded_extensions; + ARRAY_DEFINE(preloaded_extensions, const struct sieve_extension *); }; /* @@ -59,11 +59,17 @@ extern const struct sieve_extension_def address_part_extension; /* FIXME: This is stupid. Define a comparator-* extension and be done with it */ static const struct sieve_extension_def comparator_i_octet_extension = { - .name = "comparator-i;octet", + "comparator-i;octet", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS }; static const struct sieve_extension_def comparator_i_ascii_casemap_extension = { - .name = "comparator-i;ascii-casemap", + "comparator-i;ascii-casemap", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS }; /* @@ -321,8 +327,8 @@ static void sieve_extension_registry_init(struct sieve_instance *svinst) struct sieve_extension_registry *ext_reg = svinst->ext_reg; p_array_init(&ext_reg->extensions, svinst->pool, 50); - hash_table_create - (&ext_reg->extension_index, default_pool, 0, str_hash, strcmp); + ext_reg->extension_index = hash_table_create + (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); } static void sieve_extension_registry_deinit(struct sieve_instance *svinst) @@ -331,7 +337,7 @@ static void sieve_extension_registry_deinit(struct sieve_instance *svinst) struct sieve_extension * const *exts; unsigned int i, ext_count; - if ( !hash_table_is_created(ext_reg->extension_index) ) return; + if ( ext_reg->extension_index == NULL ) return; exts = array_get_modifiable(&ext_reg->extensions, &ext_count); for ( i = 0; i < ext_count; i++ ) { @@ -362,7 +368,7 @@ static struct sieve_extension *_sieve_extension_register bool load, bool required) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - struct sieve_extension *ext = + struct sieve_extension *ext = (struct sieve_extension *) hash_table_lookup(ext_reg->extension_index, extdef->name); /* Register extension if it is not registered already */ @@ -379,7 +385,8 @@ static struct sieve_extension *_sieve_extension_register ext->def = extdef; ext->svinst = svinst; - hash_table_insert(ext_reg->extension_index, extdef->name, ext); + hash_table_insert + (ext_reg->extension_index, (void *) extdef->name, (void *) ext); /* Re-register it if it were previously unregistered * (not going to happen) @@ -472,7 +479,8 @@ const struct sieve_extension *sieve_extension_get_by_name if ( strlen(name) > 128 ) return NULL; - ext = hash_table_lookup(ext_reg->extension_index, name); + ext = (const struct sieve_extension *) + hash_table_lookup(ext_reg->extension_index, name); if ( ext == NULL || ext->def == NULL || (!ext->enabled && !ext->required)) return NULL; @@ -558,8 +566,8 @@ void sieve_extensions_set_string (struct sieve_instance *svinst, const char *ext_string, bool global) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - ARRAY(const struct sieve_extension *) enabled_extensions; - ARRAY(const struct sieve_extension *) disabled_extensions; + ARRAY_DEFINE(enabled_extensions, const struct sieve_extension *); + ARRAY_DEFINE(disabled_extensions, const struct sieve_extension *); const struct sieve_extension *const *ext_enabled; const struct sieve_extension *const *ext_disabled; struct sieve_extension **exts; @@ -603,7 +611,8 @@ void sieve_extensions_set_string if ( *name == '@' ) ext = NULL; else - ext = hash_table_lookup(ext_reg->extension_index, name); + ext = (const struct sieve_extension *) + hash_table_lookup(ext_reg->extension_index, name); if ( ext == NULL || ext->def == NULL ) { sieve_sys_warning(svinst, @@ -710,15 +719,15 @@ void sieve_capability_registry_init(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - hash_table_create - (&ext_reg->capabilities_index, default_pool, 0, str_hash, strcmp); + ext_reg->capabilities_index = hash_table_create + (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); } void sieve_capability_registry_deinit(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - if ( !hash_table_is_created(ext_reg->capabilities_index) ) return; + if ( ext_reg->capabilities_index == NULL ) return; hash_table_destroy(&svinst->ext_reg->capabilities_index); } @@ -735,7 +744,8 @@ void sieve_extension_capabilities_register reg->ext = ext; reg->capabilities = cap; - hash_table_insert(ext_reg->capabilities_index, cap->name, reg); + hash_table_insert + (ext_reg->capabilities_index, (void *) cap->name, (void *) reg); } void sieve_extension_capabilities_unregister @@ -743,13 +753,15 @@ void sieve_extension_capabilities_unregister { struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; struct hash_iterate_context *hictx; - const char *name; - struct sieve_capability_registration *reg; + void *key = NULL, *value = NULL; hictx = hash_table_iterate_init(ext_reg->capabilities_index); - while ( hash_table_iterate(hictx, ext_reg->capabilities_index, &name, ®) ) { + while ( hash_table_iterate(hictx, &key, &value) ) { + struct sieve_capability_registration *reg = + (struct sieve_capability_registration *) value; + if ( reg->ext == ext ) - hash_table_remove(ext_reg->capabilities_index, name); + hash_table_remove(ext_reg->capabilities_index, key); } hash_table_iterate_deinit(&hictx); } @@ -759,7 +771,8 @@ const char *sieve_extension_capabilities_get_string { struct sieve_extension_registry *ext_reg = svinst->ext_reg; const struct sieve_capability_registration *cap_reg = - hash_table_lookup(ext_reg->capabilities_index, cap_name); + (const struct sieve_capability_registration *) + hash_table_lookup(ext_reg->capabilities_index, cap_name); const struct sieve_extension_capabilities *cap; if ( cap_reg == NULL || cap_reg->capabilities == NULL ) diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h index d1bcf5a..e1607c8 100644 --- a/src/lib-sieve/sieve-extensions.h +++ b/src/lib-sieve/sieve-extensions.h @@ -23,9 +23,6 @@ struct sieve_extension_objects { struct sieve_extension_def { const char *name; - /* Version */ - unsigned int version; - /* Registration */ bool (*load)(const struct sieve_extension *ext, void **context); void (*unload)(const struct sieve_extension *ext); @@ -53,8 +50,6 @@ struct sieve_extension_def { struct sieve_extension_objects operands; }; -/* Defining opcodes and operands */ - #define SIEVE_EXT_DEFINE_NO_OBJECTS \ { NULL, 0 } #define SIEVE_EXT_DEFINE_OBJECT(OBJ) \ @@ -65,20 +60,6 @@ struct sieve_extension_def { #define SIEVE_EXT_GET_OBJECTS_COUNT(ext, field) \ ext->field->count; -#define SIEVE_EXT_DEFINE_NO_OPERATIONS \ - .operations = SIEVE_EXT_DEFINE_NO_OBJECTS -#define SIEVE_EXT_DEFINE_OPERATION(OP) \ - .operations = SIEVE_EXT_DEFINE_OBJECT(OP) -#define SIEVE_EXT_DEFINE_OPERATIONS(OPS) \ - .operations = SIEVE_EXT_DEFINE_OBJECTS(OPS) - -#define SIEVE_EXT_DEFINE_NO_OPERANDS \ - .operands = SIEVE_EXT_DEFINE_NO_OBJECTS -#define SIEVE_EXT_DEFINE_OPERAND(OP) \ - .operands = SIEVE_EXT_DEFINE_OBJECT(OP) -#define SIEVE_EXT_DEFINE_OPERANDS(OPS) \ - .operands = SIEVE_EXT_DEFINE_OBJECTS(OPS) - /* * Extension instance */ @@ -100,13 +81,21 @@ struct sieve_extension { #define sieve_extension_is(ext, definition) \ ( (ext)->def == &(definition) ) #define sieve_extension_name(ext) \ - ((ext)->def->name) + (ext)->def->name #define sieve_extension_name_is(ext, _name) \ ( strcmp((ext)->def->name, (_name)) == 0 ) -#define sieve_extension_version(ext) \ - ((ext)->def->version) -#define sieve_extension_version_is(ext, _version) \ - ((ext)->def->version == (_version)) + +/* + * Defining opcodes and operands + */ + +#define SIEVE_EXT_DEFINE_NO_OPERATIONS SIEVE_EXT_DEFINE_NO_OBJECTS +#define SIEVE_EXT_DEFINE_OPERATION(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_OPERATIONS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) + +#define SIEVE_EXT_DEFINE_NO_OPERANDS SIEVE_EXT_DEFINE_NO_OBJECTS +#define SIEVE_EXT_DEFINE_OPERAND(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_OPERANDS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) /* * Extensions init/deinit diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c index fdbf7e5..5faa656 100644 --- a/src/lib-sieve/sieve-generator.c +++ b/src/lib-sieve/sieve-generator.c @@ -72,7 +72,7 @@ struct sieve_generator { struct sieve_codegen_env genenv; struct sieve_binary_debug_writer *dwriter; - ARRAY(void *) ext_contexts; + ARRAY_DEFINE(ext_contexts, void *); }; struct sieve_generator *sieve_generator_create diff --git a/src/lib-sieve/sieve-generator.h b/src/lib-sieve/sieve-generator.h index 30a7426..86f4875 100644 --- a/src/lib-sieve/sieve-generator.h +++ b/src/lib-sieve/sieve-generator.h @@ -75,7 +75,7 @@ const void *sieve_generator_extension_get_context struct sieve_jumplist { pool_t pool; struct sieve_binary_block *block; - ARRAY(sieve_size_t) jumps; + ARRAY_DEFINE(jumps, sieve_size_t); }; struct sieve_jumplist *sieve_jumplist_create diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c index 42409c1..6bb1988 100644 --- a/src/lib-sieve/sieve-interpreter.c +++ b/src/lib-sieve/sieve-interpreter.c @@ -45,7 +45,7 @@ struct sieve_interpreter { pool_t pool; /* Runtime data for extensions */ - ARRAY(struct sieve_interpreter_extension_reg) extensions; + ARRAY_DEFINE(extensions, struct sieve_interpreter_extension_reg); sieve_size_t reset_vector; diff --git a/src/lib-sieve/sieve-lexer.c b/src/lib-sieve/sieve-lexer.c index 7218b64..51db5b6 100644 --- a/src/lib-sieve/sieve-lexer.c +++ b/src/lib-sieve/sieve-lexer.c @@ -71,12 +71,13 @@ const struct sieve_lexer *sieve_lexer_create const struct stat *st; /* Open script as stream */ - if ( sieve_script_get_stream(script, &stream, error_r) < 0 ) + stream = sieve_script_open(script, error_r); + if ( stream == NULL ) return NULL; /* Check script size */ - if ( i_stream_stat(stream, TRUE, &st) >= 0 && st->st_size > 0 && - svinst->max_script_size > 0 && + st = i_stream_stat(stream, TRUE); + if ( st != NULL && st->st_size > 0 && svinst->max_script_size > 0 && (uoff_t)st->st_size > svinst->max_script_size ) { sieve_error(ehandler, sieve_script_name(script), "sieve script is too large (max %"PRIuSIZE_T" bytes)", @@ -120,6 +121,7 @@ void sieve_lexer_free(const struct sieve_lexer **lexer) i_stream_unref(&scanner->input); + sieve_script_close(scanner->script); sieve_script_unref(&scanner->script); sieve_error_handler_unref(&scanner->ehandler); diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c index 150a82f..0a7ccba 100644 --- a/src/lib-sieve/sieve-match-types.c +++ b/src/lib-sieve/sieve-match-types.c @@ -29,7 +29,7 @@ struct sieve_match_values { pool_t pool; - ARRAY(string_t *) values; + ARRAY_DEFINE(values, string_t *); unsigned count; }; @@ -52,8 +52,12 @@ static bool mtch_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def match_type_extension = { - .name = "@match-types", - .validator_load = mtch_validator_load + "@match-types", + NULL, NULL, + mtch_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_NO_OPERANDS }; /* diff --git a/src/lib-sieve/sieve-message.c b/src/lib-sieve/sieve-message.c index df90d6e..53c1eba 100644 --- a/src/lib-sieve/sieve-message.c +++ b/src/lib-sieve/sieve-message.c @@ -71,10 +71,10 @@ struct sieve_message_context { /* Message versioning */ struct mail_user *raw_mail_user; - ARRAY(struct sieve_message_version) versions; + ARRAY_DEFINE(versions, struct sieve_message_version); /* Context data for extensions */ - ARRAY(void *) ext_contexts; + ARRAY_DEFINE(ext_contexts, void *); unsigned int edit_snapshot:1; unsigned int substitute_snapshot:1; diff --git a/src/lib-sieve/sieve-plugins.c b/src/lib-sieve/sieve-plugins.c index 2fdcf42..520f12d 100644 --- a/src/lib-sieve/sieve-plugins.c +++ b/src/lib-sieve/sieve-plugins.c @@ -77,7 +77,7 @@ void sieve_plugins_load path = MODULEDIR"/sieve"; memset(&mod_set, 0, sizeof(mod_set)); - mod_set.abi_version = PIGEONHOLE_VERSION; + mod_set.version = PIGEONHOLE_VERSION; mod_set.require_init_funcs = TRUE; mod_set.debug = FALSE; diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c index a67dc4e..90d5cde 100644 --- a/src/lib-sieve/sieve-result.c +++ b/src/lib-sieve/sieve-result.c @@ -76,7 +76,7 @@ struct sieve_result { struct sieve_instance *svinst; /* Context data for extensions */ - ARRAY(void *) ext_contexts; + ARRAY_DEFINE(ext_contexts, void *); struct sieve_error_handler *ehandler; @@ -91,8 +91,7 @@ struct sieve_result { struct sieve_result_action *last_attempted_action; - HASH_TABLE(const struct sieve_action_def *, - struct sieve_result_action_context *) action_contexts; + struct hash_table *action_contexts; }; struct sieve_result *sieve_result_create @@ -130,6 +129,7 @@ struct sieve_result *sieve_result_create result->first_action = NULL; result->last_action = NULL; + result->action_contexts = NULL; return result; } @@ -147,7 +147,7 @@ void sieve_result_unref(struct sieve_result **result) sieve_message_context_unref(&(*result)->action_env.msgctx); - if ( hash_table_is_created((*result)->action_contexts) ) + if ( (*result)->action_contexts != NULL ) hash_table_destroy(&(*result)->action_contexts); if ( (*result)->ehandler != NULL ) @@ -326,10 +326,12 @@ void sieve_result_add_implicit_side_effect to_action = to_keep ? &act_store : to_action; - if ( !hash_table_is_created(result->action_contexts) ) { - hash_table_create_direct(&result->action_contexts, result->pool, 0); + if ( result->action_contexts == NULL ) { + result->action_contexts = hash_table_create + (default_pool, result->pool, 0, NULL, NULL); } else { - actctx = hash_table_lookup(result->action_contexts, to_action); + actctx = (struct sieve_result_action_context *) + hash_table_lookup(result->action_contexts, to_action); } if ( actctx == NULL ) { @@ -338,7 +340,8 @@ void sieve_result_add_implicit_side_effect actctx->action = to_action; actctx->seffects = sieve_side_effects_list_create(result); - hash_table_insert(result->action_contexts, to_action, actctx); + hash_table_insert(result->action_contexts, (void *) to_action, + (void *) actctx); } seffect.object.def = &seff_def->obj_def; @@ -620,11 +623,12 @@ static int _sieve_result_add_action result->action_count++; /* Apply any implicit side effects */ - if ( hash_table_is_created(result->action_contexts) ) { + if ( result->action_contexts != NULL ) { struct sieve_result_action_context *actctx; /* Check for implicit side effects to this particular action */ - actctx = hash_table_lookup(result->action_contexts, + actctx = (struct sieve_result_action_context *) + hash_table_lookup(result->action_contexts, ( keep ? &act_store : act_def )); if ( actctx != NULL ) { @@ -789,11 +793,12 @@ static void sieve_result_print_implicit_side_effects bool dummy = TRUE; /* Print any implicit side effects if applicable */ - if ( hash_table_is_created(result->action_contexts) ) { + if ( result->action_contexts != NULL ) { struct sieve_result_action_context *actctx; /* Check for implicit side effects to keep action */ - actctx = hash_table_lookup(rpenv->result->action_contexts, &act_store); + actctx = (struct sieve_result_action_context *) + hash_table_lookup(rpenv->result->action_contexts, &act_store); if ( actctx != NULL && actctx->seffects != NULL ) sieve_result_print_side_effects @@ -958,11 +963,12 @@ static bool _sieve_result_implicit_keep } /* Apply any implicit side effects if applicable */ - if ( !rollback && hash_table_is_created(result->action_contexts) ) { + if ( !rollback && result->action_contexts != NULL ) { struct sieve_result_action_context *actctx; /* Check for implicit side effects to keep action */ - actctx = hash_table_lookup(result->action_contexts, act_keep.def); + actctx = (struct sieve_result_action_context *) + hash_table_lookup(result->action_contexts, act_keep.def); if ( actctx != NULL && actctx->seffects != NULL ) rsef_first = actctx->seffects->first_effect; diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c index 6260528..7975105 100644 --- a/src/lib-sieve/sieve-script-dict.c +++ b/src/lib-sieve/sieve-script-dict.c @@ -49,18 +49,7 @@ static struct sieve_script *sieve_dict_script_alloc(void) return &script->script; } -static void sieve_dict_script_free(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 int sieve_dict_script_open +static int sieve_dict_script_create (struct sieve_script *_script, const char *data, const char *const *options, enum sieve_error *error_r) { @@ -68,7 +57,7 @@ static int sieve_dict_script_open struct sieve_instance *svinst = _script->svinst; struct sieve_error_handler *ehandler = _script->ehandler; const char *username = NULL, *name = _script->name; - const char *path, *error; + const char *path; int ret; if ( options != NULL ) { @@ -116,12 +105,12 @@ static int sieve_dict_script_open } script->dict_uri = p_strdup(_script->pool, data); - ret = dict_init(script->dict_uri, DICT_DATA_TYPE_STRING, username, - svinst->base_dir, &script->dict, &error); - if ( ret < 0 ) { + script->dict = dict_init + (script->dict_uri, 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': %s", data, username, error); + "for user `%s'", data, username); *error_r = SIEVE_ERROR_TEMP_FAIL; return -1; } @@ -164,9 +153,19 @@ static int sieve_dict_script_open return 0; } -static int sieve_dict_script_get_stream -(struct sieve_script *_script, struct istream **stream_r, - enum sieve_error *error_r) +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; @@ -194,11 +193,21 @@ static int sieve_dict_script_get_stream "not found at path %s", script->data_id, name, path); } *error_r = SIEVE_ERROR_TEMP_FAIL; - return -1; + return NULL; } - *stream_r = i_stream_create_from_data(script->data, strlen(script->data)); - return 0; + 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 @@ -260,18 +269,18 @@ static int sieve_dict_script_binary_save static bool sieve_dict_script_equals (const struct sieve_script *_script, const struct sieve_script *_other) { - struct sieve_dict_script *script = (struct sieve_dict_script *)_script; - struct sieve_dict_script *other = (struct sieve_dict_script *)_other; + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + struct sieve_dict_script *other = (struct sieve_dict_script *)_other; - if ( script == NULL || other == NULL ) - return FALSE; + if ( script == NULL || other == NULL ) + return FALSE; - if ( strcmp(script->dict_uri, other->dict_uri) != 0 ) + if ( strcmp(script->dict_uri, other->dict_uri) != 0 ) return FALSE; i_assert( _script->name != NULL && _other->name != NULL ); - return ( strcmp(_script->name, _other->name) == 0 ); + return ( strcmp(_script->name, _other->name) == 0 ); } @@ -279,11 +288,11 @@ const struct sieve_script sieve_dict_script = { .driver_name = SIEVE_DICT_SCRIPT_DRIVER_NAME, .v = { sieve_dict_script_alloc, - sieve_dict_script_free, + sieve_dict_script_create, + sieve_dict_script_destroy, sieve_dict_script_open, - - sieve_dict_script_get_stream, + sieve_dict_script_close, sieve_dict_script_binary_read_metadata, sieve_dict_script_binary_write_metadata, diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c index 18df9fb..cc9cf64 100644 --- a/src/lib-sieve/sieve-script-file.c +++ b/src/lib-sieve/sieve-script-file.c @@ -113,7 +113,7 @@ static struct sieve_script *sieve_file_script_alloc(void) return &script->script; } -static int sieve_file_script_open +static int sieve_file_script_create (struct sieve_script *_script, const char *path, const char *const *options, enum sieve_error *error_r) { @@ -248,9 +248,8 @@ static int sieve_file_script_open return ( success ? 0 : -1 ); } -static int sieve_file_script_get_stream -(struct sieve_script *_script, struct istream **stream_r, - enum sieve_error *error_r) +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; @@ -262,7 +261,7 @@ static int sieve_file_script_get_stream if ( (fd=open(script->path, O_RDONLY)) < 0 ) { sieve_file_script_handle_error(_script, script->path, name, error_r); - return -1; + return NULL; } if ( fstat(fd, &st) != 0 ) { @@ -293,8 +292,7 @@ static int sieve_file_script_get_stream } } - *stream_r = result; - return 0; + return result; } static int sieve_file_script_get_size @@ -358,11 +356,11 @@ 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, - - sieve_file_script_get_stream, + NULL, sieve_file_script_binary_read_metadata, NULL, diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h index 753d64a..9570b9c 100644 --- a/src/lib-sieve/sieve-script-private.h +++ b/src/lib-sieve/sieve-script-private.h @@ -12,16 +12,17 @@ struct sieve_script_vfuncs { struct sieve_script *(*alloc)(void); - void (*destroy)(struct sieve_script *script); + int (*create) + (struct sieve_script *script, const char *data, const char *const *options, + enum sieve_error *error_r); + void (*destroy) + (struct sieve_script *script); - int (*open) - (struct sieve_script *script, const char *data, - const char *const *options, enum sieve_error *error_r); + struct istream *(*open) + (struct sieve_script *script, enum sieve_error *error_r); + void (*close) + (struct sieve_script *script); - int (*get_stream) - (struct sieve_script *script, struct istream **stream_r, - enum sieve_error *error_r); - int (*binary_read_metadata) (struct sieve_script *_script, struct sieve_binary_block *sblock, sieve_size_t *offset); @@ -52,20 +53,18 @@ struct sieve_script { struct sieve_error_handler *ehandler; const char *name; - const char *data; const char *location; const char *bin_dir; /* Stream */ struct istream *stream; - - unsigned int open:1; }; -void sieve_script_init +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); + 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); diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index ced71c9..50c35dd 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -180,19 +180,51 @@ static bool sieve_script_location_parse return TRUE; } -void sieve_script_init +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) + const struct sieve_script *script_class, const char *data, const char *name, + struct sieve_error_handler *ehandler, enum sieve_error *error_r) { + 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; + script->script_class = script_class; script->refcount = 1; script->svinst = svinst; + script->ehandler = ehandler; - script->data = p_strdup_empty(script->pool, data); + script->name = p_strdup_empty(script->pool, name); + 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); + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_TEMP_FAIL; + return NULL; + } + + 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 { + *error_r = error; + } + return NULL; + } + + i_assert( script->location != NULL ); + sieve_error_handler_ref(ehandler); + return script; } struct sieve_script *sieve_script_create @@ -225,111 +257,36 @@ struct sieve_script *sieve_script_create else script_class = NULL; - if ( script_class == NULL ) { - sieve_sys_error(svinst, - "Unknown sieve script driver module: %s", driver); - } + if ( script_class == NULL ) + i_error("Unknown sieve script driver module: %s", driver); } T_END; } - if ( script_class == NULL ) { - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + if ( script_class == NULL ) return NULL; - } script = script_class->v.alloc(); - sieve_script_init(script, svinst, script_class, data, name, ehandler); - script->location = p_strdup(script->pool, location); - return script; -} - -int sieve_script_open -(struct sieve_script *script, enum sieve_error *error_r) -{ - struct sieve_instance *svinst = script->svinst; - struct sieve_error_handler *ehandler = script->ehandler; - enum sieve_error error; - const char *const *options = NULL; - const char *location = NULL, *parse_error = NULL; - - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NONE; - - if ( script->open ) - return 0; - - if ( !sieve_script_location_parse - (script, script->data, &location, &options, &parse_error) ) { - sieve_critical(svinst, ehandler, NULL, - "failed to access sieve script", "failed to parse script location: %s", - parse_error); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; - return -1; - } - - script->location = NULL; - if ( script->v.open(script, location, options, &error) < 0 ) { - if ( error_r == NULL ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) - sieve_error(ehandler, script->name, "sieve script does not exist"); - } else { - *error_r = error; - } - return -1; - } - - i_assert( script->location != NULL ); - i_assert( script->name != NULL ); - script->open = TRUE; - return 0; -} - -int sieve_script_open_as -(struct sieve_script *script, const char *name, enum sieve_error *error_r) -{ - if ( sieve_script_open(script, error_r) < 0 ) - return -1; - - /* override name */ - script->name = p_strdup(script->pool, name); - return 0; -} - -struct sieve_script *sieve_script_create_open -(struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r) -{ - struct sieve_script *script; - - script = sieve_script_create(svinst, location, name, ehandler, error_r); - if ( script == NULL ) - return NULL; - - if ( sieve_script_open(script, error_r) < 0 ) { - sieve_script_unref(&script); + 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_open_as +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 *script; - script = sieve_script_create(svinst, location, name, ehandler, error_r); - if ( script == NULL ) + if ( (script=sieve_script_create(svinst, location, NULL, ehandler, error_r)) + == NULL ) return NULL; - if ( sieve_script_open_as(script, name, error_r) < 0 ) { - sieve_script_unref(&script); - return NULL; - } - + /* override name */ + script->name = p_strdup(script->pool, name); return script; } @@ -381,7 +338,6 @@ struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) { - struct istream *stream; int ret; if ( script->v.get_size != NULL ) { @@ -390,42 +346,31 @@ int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) } /* Try getting size from the stream */ - if ( script->stream == NULL && - sieve_script_get_stream(script, &stream, NULL) < 0 ) + if ( script->stream == NULL && sieve_script_open(script, NULL) == NULL ) return -1; return i_stream_get_size(script->stream, TRUE, size_r); } -bool sieve_script_is_open(const struct sieve_script *script) -{ - return script->open; -} - /* * Stream management */ -int sieve_script_get_stream -(struct sieve_script *script, struct istream **stream_r, - enum sieve_error *error_r) +struct istream *sieve_script_open +(struct sieve_script *script, enum sieve_error *error_r) { enum sieve_error error; - int ret; if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; - if ( script->stream != NULL ) { - *stream_r = script->stream; - return 0; + if ( script->stream == NULL ) { + T_BEGIN { + script->stream = script->v.open(script, &error); + } T_END; } - T_BEGIN { - ret = script->v.get_stream(script, &script->stream, &error); - } T_END; - - if ( ret < 0 ) { + if ( script->stream == NULL ) { if ( error_r == NULL ) { if ( error == SIEVE_ERROR_NOT_FOUND ) { sieve_error(script->ehandler, script->name, @@ -434,11 +379,23 @@ int sieve_script_get_stream } else { *error_r = error; } - return -1; } - *stream_r = script->stream; - return 0; + return script->stream; +} + +void sieve_script_close(struct sieve_script *script) +{ + if ( script->stream != NULL ) + return; + + i_stream_unref(&script->stream); + + if ( script->v.close != NULL ) { + T_BEGIN { + script->v.close(script); + } T_END; + } } /* @@ -468,8 +425,6 @@ bool sieve_script_equals unsigned int sieve_script_hash(const struct sieve_script *script) { - i_assert( script->name != NULL ); - return str_hash(script->name); } diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h index 74c02b8..09b7d7d 100644 --- a/src/lib-sieve/sieve-script.h +++ b/src/lib-sieve/sieve-script.h @@ -26,22 +26,13 @@ ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *); struct sieve_script *sieve_script_create (struct sieve_instance *svinst, const char *location, const char *name, struct sieve_error_handler *ehandler, enum sieve_error *error_r); +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); void sieve_script_ref(struct sieve_script *script); void sieve_script_unref(struct sieve_script **script); -int sieve_script_open - (struct sieve_script *script, enum sieve_error *error_r); -int sieve_script_open_as - (struct sieve_script *script, const char *name, enum sieve_error *error_r); - -struct sieve_script *sieve_script_create_open - (struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r); -struct sieve_script *sieve_script_create_open_as - (struct sieve_instance *svinst, const char *location, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r); - /* * Accessors */ @@ -50,8 +41,6 @@ const char *sieve_script_name(const struct sieve_script *script); const char *sieve_script_location(const struct sieve_script *script); struct sieve_instance *sieve_script_svinst(const struct sieve_script *script); -bool sieve_script_is_open(const struct sieve_script *script); - /* * Saving/loading Sieve binaries */ @@ -72,9 +61,10 @@ int sieve_script_binary_save * Stream management */ -int sieve_script_get_stream - (struct sieve_script *script, struct istream **stream_r, - enum sieve_error *error_r); +struct istream *sieve_script_open + (struct sieve_script *script, enum sieve_error *error_r); +void sieve_script_close(struct sieve_script *script); + int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r); /* diff --git a/src/lib-sieve/sieve-smtp.c b/src/lib-sieve/sieve-smtp.c index b4102a4..e203cbd 100644 --- a/src/lib-sieve/sieve-smtp.c +++ b/src/lib-sieve/sieve-smtp.c @@ -14,12 +14,12 @@ bool sieve_smtp_available void *sieve_smtp_open (const struct sieve_script_env *senv, const char *destination, - const char *return_path, struct ostream **output_r) + const char *return_path, FILE **file_r) { if ( senv->smtp_open == NULL || senv->smtp_close == NULL ) return NULL; - return senv->smtp_open(senv, destination, return_path, output_r); + return senv->smtp_open(senv, destination, return_path, file_r); } bool sieve_smtp_close diff --git a/src/lib-sieve/sieve-smtp.h b/src/lib-sieve/sieve-smtp.h index 63b7cbe..5ee286d 100644 --- a/src/lib-sieve/sieve-smtp.h +++ b/src/lib-sieve/sieve-smtp.h @@ -11,7 +11,7 @@ bool sieve_smtp_available void *sieve_smtp_open (const struct sieve_script_env *senv, const char *destination, - const char *return_path, struct ostream **output_r); + const char *return_path, FILE **file_r); bool sieve_smtp_close (const struct sieve_script_env *senv, void *handle); diff --git a/src/lib-sieve/sieve-stringlist.c b/src/lib-sieve/sieve-stringlist.c index 01e9c5d..4c8a703 100644 --- a/src/lib-sieve/sieve-stringlist.c +++ b/src/lib-sieve/sieve-stringlist.c @@ -17,7 +17,7 @@ int sieve_stringlist_read_all const char * const **list_r) { if ( strlist->read_all == NULL ) { - ARRAY(const char *) items; + ARRAY_DEFINE(items, const char *); string_t *item; int ret; diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h index 375a0f7..d8380ab 100644 --- a/src/lib-sieve/sieve-types.h +++ b/src/lib-sieve/sieve-types.h @@ -31,39 +31,14 @@ enum sieve_flag { SIEVE_FLAG_HOME_RELATIVE = (1 << 0), }; -/* Sieve evaluation can be performed at various different points as messages - are processed. */ -enum sieve_env_location { - /* Unknown */ - SIEVE_ENV_LOCATION_UNKNOWN = 0, - /* "MDA" - evaluation is being performed by a Mail Delivery Agent */ - SIEVE_ENV_LOCATION_MDA, - /* "MTA" - the Sieve script is being evaluated by a Message Transfer Agent */ - SIEVE_ENV_LOCATION_MTA, - /* "MS" - evaluation is being performed by a Message Store */ - SIEVE_ENV_LOCATION_MS -}; - -/* The point relative to final delivery where the Sieve script is being - evaluated. */ -enum sieve_delivery_phase { - SIEVE_DELIVERY_PHASE_UNKNOWN = 0, - SIEVE_DELIVERY_PHASE_PRE, - SIEVE_DELIVERY_PHASE_DURING, - SIEVE_DELIVERY_PHASE_POST, -}; - struct sieve_environment { const char *hostname; - const char *domainname; - const char *base_dir; + const char *username; const char *home_dir; enum sieve_flag flags; - enum sieve_env_location location; - enum sieve_delivery_phase delivery_phase; }; /* @@ -193,7 +168,7 @@ struct sieve_script_env { /* Interface for sending mail */ void *(*smtp_open) (const struct sieve_script_env *senv, const char *destination, - const char *return_path, struct ostream **output_r); + const char *return_path, FILE **file_r); bool (*smtp_close)(const struct sieve_script_env *senv, void *handle); /* Interface for marking and checking duplicates */ diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index 9763f62..0c866d3 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -48,9 +48,9 @@ struct sieve_command_registration { const struct sieve_command_def *cmd_def; const struct sieve_extension *ext; - ARRAY(struct sieve_tag_registration *) normal_tags; - ARRAY(struct sieve_tag_registration *) instanced_tags; - ARRAY(struct sieve_tag_registration *) persistent_tags; + ARRAY_DEFINE(normal_tags, struct sieve_tag_registration *); + ARRAY_DEFINE(instanced_tags, struct sieve_tag_registration *); + ARRAY_DEFINE(persistent_tags, struct sieve_tag_registration *); }; /* Default (literal) arguments */ @@ -93,9 +93,9 @@ struct sieve_validator { /* Registries */ - HASH_TABLE(const char *, struct sieve_command_registration *) commands; + struct hash_table *commands; - ARRAY(struct sieve_validator_extension_reg) extensions; + ARRAY_DEFINE(extensions, struct sieve_validator_extension_reg); /* This is currently a wee bit ugly and needs more thought */ struct sieve_default_argument default_arguments[SAT_COUNT]; @@ -179,8 +179,8 @@ struct sieve_validator *sieve_validator_create sieve_extensions_get_count(valdtr->svinst)); /* Setup command registry */ - hash_table_create - (&valdtr->commands, pool, 0, strcase_hash, strcasecmp); + valdtr->commands = hash_table_create + (default_pool, pool, 0, strcase_hash, (hash_cmp_callback_t *)strcasecmp); sieve_validator_register_core_commands(valdtr); sieve_validator_register_core_tests(valdtr); @@ -306,7 +306,8 @@ static struct sieve_command_registration * sieve_validator_find_command_registration (struct sieve_validator *valdtr, const char *command) { - return hash_table_lookup(valdtr->commands, command); + return (struct sieve_command_registration *) + hash_table_lookup(valdtr->commands, command); } static struct sieve_command_registration *_sieve_validator_register_command @@ -319,7 +320,7 @@ static struct sieve_command_registration *_sieve_validator_register_command cmd_reg->cmd_def = cmd_def; cmd_reg->ext = ext; - hash_table_insert(valdtr->commands, identifier, cmd_reg); + hash_table_insert(valdtr->commands, (void *) identifier, (void *) cmd_reg); return cmd_reg; } @@ -1401,7 +1402,7 @@ struct sieve_validator_object_reg { struct sieve_validator_object_registry { struct sieve_validator *valdtr; - ARRAY(struct sieve_validator_object_reg) registrations; + ARRAY_DEFINE(registrations, struct sieve_validator_object_reg); }; struct sieve_validator_object_registry *sieve_validator_object_registry_get diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index c7bfd0a..e5d38f3 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -49,7 +49,6 @@ struct sieve_instance *sieve_init struct sieve_instance *svinst; unsigned long long int uint_setting; size_t size_setting; - const char *domain; pool_t pool; /* Create Sieve engine instance */ @@ -59,44 +58,14 @@ struct sieve_instance *sieve_init 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; - svinst->env_location = env->location; - svinst->delivery_phase = env->delivery_phase; - - /* Determine domain */ - if ( env->domainname != NULL && *(env->domainname) != '\0' ) { - domain = env->domainname; - } else { - /* Fall back to parsing username localpart@domain */ - domain = svinst->username == NULL ? NULL : - strchr(svinst->username, '@'); - if ( domain == NULL || *(domain+1) == '\0' ) { - /* Fall back to parsing hostname host.domain */ - domain = ( env->hostname != NULL ? strchr(env->hostname, '.') : NULL ); - if ( domain == NULL || *(domain+1) == '\0' - || strchr(domain+1, '.') == NULL ) { - /* Fall back to bare hostname */ - domain = env->hostname; - } else { - domain++; - } - } else { - domain++; - } - } - svinst->hostname = p_strdup_empty(pool, env->hostname); - svinst->domainname = p_strdup(pool, domain); sieve_errors_init(svinst); - if ( debug ) { - sieve_sys_debug(svinst, "%s version %s initializing", - PIGEONHOLE_NAME, PIGEONHOLE_VERSION); - } - /* Read limits from configuration */ svinst->max_script_size = SIEVE_DEFAULT_MAX_SCRIPT_SIZE; @@ -286,7 +255,7 @@ struct sieve_binary *sieve_compile struct sieve_script *script; struct sieve_binary *sbin; - if ( (script = sieve_script_create_open + if ( (script = sieve_script_create (svinst, script_location, script_name, ehandler, error_r)) == NULL ) return NULL; @@ -406,8 +375,10 @@ struct sieve_binary *sieve_open struct sieve_binary *sbin; /* First open the scriptfile itself */ - if ( (script=sieve_script_create_open - (svinst, script_location, script_name, ehandler, error_r)) == NULL ) { + script = sieve_script_create + (svinst, script_location, script_name, ehandler, error_r); + + if ( script == NULL ) { /* Failed */ return NULL; } diff --git a/src/lib-sievestorage/Makefile.am b/src/lib-sievestorage/Makefile.am index c9f3b82..181971d 100644 --- a/src/lib-sievestorage/Makefile.am +++ b/src/lib-sievestorage/Makefile.am @@ -1,11 +1,11 @@ -noinst_LTLIBRARIES = libsievestorage.la +noinst_LIBRARIES = libsievestorage.a INCLUDES = \ $(LIBDOVECOT_INCLUDE) \ -I$(top_srcdir) \ -I$(top_srcdir)/src/lib-sieve -libsievestorage_la_SOURCES = \ +libsievestorage_a_SOURCES = \ sieve-storage-save.c \ sieve-storage-script.c \ sieve-storage-list.c \ diff --git a/src/lib-sievestorage/sieve-storage-list.c b/src/lib-sievestorage/sieve-storage-list.c index 72b5962..214d778 100644 --- a/src/lib-sievestorage/sieve-storage-list.c +++ b/src/lib-sievestorage/sieve-storage-list.c @@ -45,7 +45,7 @@ struct sieve_list_context *sieve_storage_list_init T_BEGIN { /* Get the name of the active script */ - if ( sieve_storage_active_script_get_file(storage, &active) < 0) { + if ( sieve_storage_get_active_scriptfile(storage, &active) < 0) { ctx = NULL; } else { pool = pool_alloconly_create("sieve_list_context", 4096); diff --git a/src/lib-sievestorage/sieve-storage-private.h b/src/lib-sievestorage/sieve-storage-private.h index 5133026..4b2dc54 100644 --- a/src/lib-sievestorage/sieve-storage-private.h +++ b/src/lib-sievestorage/sieve-storage-private.h @@ -9,6 +9,14 @@ #include "sieve-storage.h" + +enum sieve_storage_flags { + /* Print debugging information while initializing the storage */ + SIEVE_STORAGE_FLAG_DEBUG = 0x01, + /* Use CRLF linefeeds when saving mails. */ + SIEVE_STORAGE_FLAG_SAVE_CRLF = 0x02, +}; + #define SIEVE_READ_BLOCK_SIZE (1024*8) /* How often to scan tmp/ directory for old files (based on dir's atime) */ @@ -30,20 +38,19 @@ struct sieve_storage { char *name; char *dir; + bool debug; /* Private */ char *active_path; char *active_fname; char *link_path; char *error; - char *username; /* name of user accessing the storage */ + char *user; /* name of user accessing the storage */ mode_t dir_create_mode; mode_t file_create_mode; gid_t file_create_gid; - struct mailbox *inbox; - uint64_t max_scripts; uint64_t max_storage; @@ -51,19 +58,10 @@ struct sieve_storage { struct sieve_error_handler *ehandler; enum sieve_storage_flags flags; - time_t prev_mtime; }; struct sieve_script *sieve_storage_script_init_from_path (struct sieve_storage *storage, const char *path, const char *scriptname); -void sieve_storage_inbox_script_attribute_set - (struct sieve_storage *storage, const char *name); -void sieve_storage_inbox_script_attribute_rename - (struct sieve_storage *storage, const char *oldname, const char *newname); -void sieve_storage_inbox_script_attribute_unset - (struct sieve_storage *storage, const char *name); - - #endif diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c index ad8039f..b4bece5 100644 --- a/src/lib-sievestorage/sieve-storage-save.c +++ b/src/lib-sievestorage/sieve-storage-save.c @@ -9,7 +9,6 @@ #include "ostream.h" #include "str.h" #include "eacces-error.h" -#include "safe-mkstemp.h" #include "sieve-script.h" #include "sieve-script-file.h" @@ -37,8 +36,6 @@ struct sieve_save_context { int fd; const char *tmp_path; - time_t mtime; - unsigned int failed:1; unsigned int moving:1; unsigned int finished:1; @@ -207,7 +204,6 @@ sieve_storage_save_init(struct sieve_storage *storage, ctx->storage = storage; ctx->scriptname = p_strdup(pool, scriptname); ctx->scriptobject = NULL; - ctx->mtime = (time_t)-1; T_BEGIN { ctx->fd = sieve_storage_create_tmp(storage, scriptname, &path); @@ -282,12 +278,6 @@ int sieve_storage_save_finish(struct sieve_save_context *ctx) return ( ctx->failed ? -1 : 0 ); } -void sieve_storage_save_set_mtime -(struct sieve_save_context *ctx, time_t mtime) -{ - ctx->mtime = mtime; -} - static void sieve_storage_save_destroy(struct sieve_save_context **ctx) { if ((*ctx)->scriptobject != NULL) @@ -334,7 +324,7 @@ bool sieve_storage_save_will_activate const char *scriptname; int ret; - ret = sieve_storage_active_script_get_name(ctx->storage, &scriptname); + ret = sieve_storage_get_active_scriptname(ctx->storage, &scriptname); if ( ret > 0 ) { /* Is the requested script active? */ result = ( strcmp(ctx->scriptname, scriptname) == 0 ); @@ -344,48 +334,23 @@ bool sieve_storage_save_will_activate return result; } -static void sieve_storage_update_mtime(const char *path, time_t mtime) -{ - struct utimbuf times = { .actime = mtime, .modtime = mtime }; - - if ( utime(path, ×) < 0 ) { - switch ( errno ) { - case ENOENT: - break; - case EACCES: - i_error("sieve-storage: %s", eacces_error_get("utime", path)); - break; - default: - i_error("sieve-storage: utime(%s) failed: %m", path); - } - } -} - -int sieve_storage_save_commit(struct sieve_save_context **_ctx) +int sieve_storage_save_commit(struct sieve_save_context **ctx) { - struct sieve_save_context *ctx = *_ctx; - struct sieve_storage *storage = ctx->storage; const char *dest_path; bool failed = FALSE; - i_assert(ctx->output == NULL); - i_assert(ctx->finished); - i_assert(ctx->scriptname != NULL); + i_assert((*ctx)->output == NULL); + i_assert((*ctx)->finished); + i_assert((*ctx)->scriptname != NULL); T_BEGIN { - dest_path = t_strconcat(storage->dir, "/", - sieve_scriptfile_from_name(ctx->scriptname), NULL); + dest_path = t_strconcat((*ctx)->storage->dir, "/", + sieve_scriptfile_from_name((*ctx)->scriptname), NULL); - failed = !sieve_storage_script_move(ctx, dest_path); - if ( ctx->mtime != (time_t)-1 ) - sieve_storage_update_mtime(dest_path, ctx->mtime); + failed = !sieve_storage_script_move((*ctx), dest_path); } T_END; - /* set INBOX mailbox attribute */ - if ( !failed ) - sieve_storage_inbox_script_attribute_set(storage, ctx->scriptname); - - sieve_storage_save_destroy(_ctx); + sieve_storage_save_destroy(ctx); return ( failed ? -1 : 0 ); } @@ -403,57 +368,3 @@ void sieve_storage_save_cancel(struct sieve_save_context **ctx) sieve_storage_save_destroy(ctx); } - -int sieve_storage_save_as_active_script(struct sieve_storage *storage, - struct istream *input, time_t mtime) -{ - int fd; - string_t *temp_path; - struct ostream *output; - - temp_path = t_str_new(256); - str_append(temp_path, storage->active_path); - str_append_c(temp_path, '.'); - fd = safe_mkstemp_hostpid - (temp_path, storage->file_create_mode, (uid_t)-1, (gid_t)-1); - if ( fd < 0 ) { - if ( errno == EACCES ) { - sieve_storage_set_critical(storage, - "failed to create temporary file: %s", - eacces_error_get_creating("open", str_c(temp_path))); - } else { - sieve_storage_set_critical(storage, - "failed to create temporary file: open(%s) failed: %m", - str_c(temp_path)); - } - return -1; - } - - output = o_stream_create_fd(fd, 0, FALSE); - if (o_stream_send_istream(output, input) < 0) { - sieve_storage_set_critical(storage, - "o_stream_send_istream(%s) failed: %m", str_c(temp_path)); - o_stream_destroy(&output); - (void)unlink(str_c(temp_path)); - return -1; - } - o_stream_destroy(&output); - - if (rename(str_c(temp_path), storage->active_path) < 0) { - if ( ENOSPACE(errno) ) { - sieve_storage_set_error(storage, - SIEVE_ERROR_NO_SPACE, "Not enough disk space"); - } else if ( errno == EACCES ) { - sieve_storage_set_critical(storage, - "%s", eacces_error_get("rename", storage->active_path)); - } else { - sieve_storage_set_critical(storage, - "rename(%s, %s) failed: %m", str_c(temp_path), storage->active_path); - } - } else { - sieve_storage_update_mtime(storage->active_path, mtime); - } - - (void)unlink(str_c(temp_path)); - return 0; -} diff --git a/src/lib-sievestorage/sieve-storage-save.h b/src/lib-sievestorage/sieve-storage-save.h index 491b5ca..d3d3232 100644 --- a/src/lib-sievestorage/sieve-storage-save.h +++ b/src/lib-sievestorage/sieve-storage-save.h @@ -22,18 +22,9 @@ struct sieve_script *sieve_storage_save_get_tempscript bool sieve_storage_save_will_activate (struct sieve_save_context *ctx); -void sieve_storage_save_set_mtime - (struct sieve_save_context *ctx, time_t mtime); - void sieve_storage_save_cancel(struct sieve_save_context **ctx); int sieve_storage_save_commit(struct sieve_save_context **ctx); -/* Saves input directly as a regular file at the active script path. - * This is needed for the doveadm-sieve plugin. - */ -int sieve_storage_save_as_active_script(struct sieve_storage *storage, - struct istream *input, time_t mtime); - #endif diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c index 55f6756..134fcf5 100644 --- a/src/lib-sievestorage/sieve-storage-script.c +++ b/src/lib-sievestorage/sieve-storage-script.c @@ -41,7 +41,7 @@ struct sieve_script *sieve_storage_script_init_from_path /* Prevent initializing the active script link as a script when it * resides in the sieve storage directory. */ - if ( scriptname != NULL && *(storage->link_path) == '\0' ) { + if ( *(storage->link_path) == '\0' ) { const char *fname; fname = strrchr(path, '/'); @@ -63,18 +63,18 @@ struct sieve_script *sieve_storage_script_init_from_path st_script->file.script.pool = pool; st_script->storage = storage; - sieve_script_init + if ( sieve_script_init (&st_script->file.script, storage->svinst, &sieve_file_script, path, - scriptname, sieve_storage_get_error_handler(storage)); - - if ( sieve_script_open(&st_script->file.script, &error) < 0 ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) - sieve_storage_set_error(storage, error, "Script does not exist."); - pool_unref(&pool); - return NULL; + scriptname, sieve_storage_get_error_handler(storage), &error) != NULL ) { + return &st_script->file.script; } - return &st_script->file.script; + pool_unref(&pool); + + if ( error == SIEVE_ERROR_NOT_FOUND ) + sieve_storage_set_error(storage, error, "Script does not exist."); + + return NULL; } struct sieve_script *sieve_storage_script_init @@ -126,20 +126,18 @@ static int sieve_storage_read_active_link ret = readlink(storage->active_path, linkbuf, sizeof(linkbuf)); if ( ret < 0 ) { - if ( errno == EINVAL ) { + if (errno == EINVAL) { /* Our symlink is no symlink. Report 'no active script'. * Activating a script will automatically resolve this, so * there is no need to panic on this one. */ - if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { - i_warning - ("sieve-storage: Active sieve script symlink %s is no symlink.", - storage->active_path); - } + i_warning + ("sieve-storage: Active sieve script symlink %s is no symlink.", + storage->active_path); return 0; } - if ( errno == ENOENT ) { + if (errno == ENOENT ) { /* Symlink not found */ return 0; } @@ -156,7 +154,7 @@ static int sieve_storage_read_active_link return 1; } -static const char *sieve_storage_parse_active_link +static const char *sieve_storage_parse_link (struct sieve_storage *storage, const char *link, const char **scriptname_r) { const char *fname, *scriptname, *scriptpath; @@ -199,7 +197,7 @@ static const char *sieve_storage_parse_active_link return fname; } -int sieve_storage_active_script_get_file +int sieve_storage_get_active_scriptfile (struct sieve_storage *storage, const char **file_r) { const char *link, *scriptfile; @@ -212,7 +210,7 @@ int sieve_storage_active_script_get_file return ret; /* Parse the link */ - scriptfile = sieve_storage_parse_active_link(storage, link, NULL); + scriptfile = sieve_storage_parse_link(storage, link, NULL); if (scriptfile == NULL) { /* Obviously someone has been playing with our symlink, @@ -226,7 +224,7 @@ int sieve_storage_active_script_get_file return 1; } -int sieve_storage_active_script_get_name +int sieve_storage_get_active_scriptname (struct sieve_storage *storage, const char **name_r) { const char *link; @@ -238,7 +236,7 @@ int sieve_storage_active_script_get_name if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) return ret; - if ( sieve_storage_parse_active_link(storage, link, name_r) == NULL ) { + if ( sieve_storage_parse_link(storage, link, name_r) == NULL ) { /* Obviously someone has been playing with our symlink, * ignore this situation and report 'no active script'. * Activation should fix this situation. @@ -249,13 +247,7 @@ int sieve_storage_active_script_get_name return 1; } -const char *sieve_storage_active_script_get_path - (struct sieve_storage *storage) -{ - return storage->active_path; -} - -struct sieve_script *sieve_storage_active_script_get +struct sieve_script *sieve_storage_get_active_script (struct sieve_storage *storage) { struct sieve_script *script; @@ -276,7 +268,7 @@ struct sieve_script *sieve_storage_active_script_get } /* Parse the link */ - scriptfile = sieve_storage_parse_active_link(storage, link, NULL); + scriptfile = sieve_storage_parse_link(storage, link, NULL); if (scriptfile == NULL) { /* Obviously someone has been playing with our symlink, @@ -297,55 +289,6 @@ struct sieve_script *sieve_storage_active_script_get return script; } -int sieve_storage_active_script_get_last_change -(struct sieve_storage *storage, time_t *last_change_r) -{ - struct stat st; - - /* Try direct lstat first */ - if (lstat(storage->active_path, &st) == 0) { - if (!S_ISLNK(st.st_mode)) { - *last_change_r = st.st_mtime; - return 0; - } - } - /* Check error */ - else if (errno != ENOENT) { - sieve_storage_set_critical(storage, "lstat(%s) failed: %m", - storage->active_path); - } - - /* Fall back to statting storage directory */ - return sieve_storage_get_last_change(storage, last_change_r); -} - -int -sieve_storage_active_script_is_no_link(struct sieve_storage *storage) -{ - struct stat st; - - /* Stat the file */ - if ( lstat(storage->active_path, &st) != 0 ) { - if ( errno != ENOENT ) { - sieve_storage_set_critical(storage, - "Failed to stat active sieve script symlink (%s): %m.", - storage->active_path); - return -1; - } - return 0; - } - - if ( S_ISLNK( st.st_mode ) ) - return 0; - if ( !S_ISREG( st.st_mode ) ) { - sieve_storage_set_critical( storage, - "Active sieve script file '%s' is no symlink nor a regular file.", - storage->active_path ); - return -1; - } - return 1; -} - int sieve_storage_script_is_active(struct sieve_script *script) { struct sieve_storage_script *st_script = @@ -354,7 +297,7 @@ int sieve_storage_script_is_active(struct sieve_script *script) int ret = 0; T_BEGIN { - ret = sieve_storage_active_script_get_file(st_script->storage, &afile); + ret = sieve_storage_get_active_scriptfile(st_script->storage, &afile); if ( ret > 0 ) { /* Is the requested script active? */ @@ -391,10 +334,6 @@ int sieve_storage_script_delete(struct sieve_script **script) } } - /* unset INBOX mailbox attribute */ - if ( ret >= 0 ) - sieve_storage_inbox_script_attribute_unset(storage, (*script)->name); - /* Always deinitialize the script object */ sieve_script_unref(script); return ret; @@ -402,7 +341,6 @@ int sieve_storage_script_delete(struct sieve_script **script) static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) { - bool debug = ( (storage->flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 ); struct stat st; /* Stat the file */ @@ -417,7 +355,7 @@ static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) } if ( S_ISLNK( st.st_mode ) ) { - if ( debug ) + if ( storage->debug ) i_debug( "sieve-storage: nothing to rescue %s.", storage->active_path); return TRUE; /* Nothing to rescue */ } @@ -453,7 +391,7 @@ static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) return FALSE; } -int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime) +int sieve_storage_deactivate(struct sieve_storage *storage) { int ret; @@ -468,12 +406,10 @@ int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime) sieve_storage_set_critical(storage, "sieve_storage_deactivate(): " "error on unlink(%s): %m", storage->active_path); return -1; - } else { + } else return 0; - } } - sieve_storage_set_modified(storage, mtime); return 1; } @@ -533,7 +469,7 @@ static int sieve_storage_replace_active_link return 1; } -static int _sieve_storage_script_activate(struct sieve_script *script, time_t mtime) +static int _sieve_storage_script_activate(struct sieve_script *script) { struct sieve_storage_script *st_script = (struct sieve_storage_script *) script; @@ -548,7 +484,7 @@ static int _sieve_storage_script_activate(struct sieve_script *script, time_t mt * resolves automatically. This step is only necessary to provide a * proper return value indicating whether the script was already active. */ - ret = sieve_storage_active_script_get_file(storage, &afile); + ret = sieve_storage_get_active_scriptfile(storage, &afile); /* Is the requested script already active? */ if ( ret <= 0 || strcmp(st_script->file.filename, afile) != 0 ) @@ -592,16 +528,15 @@ static int _sieve_storage_script_activate(struct sieve_script *script, time_t mt } } - sieve_storage_set_modified(storage, mtime); return activated; } -int sieve_storage_script_activate(struct sieve_script *script, time_t mtime) +int sieve_storage_script_activate(struct sieve_script *script) { int ret; T_BEGIN { - ret = _sieve_storage_script_activate(script, mtime); + ret = _sieve_storage_script_activate(script); } T_END; return ret; @@ -613,7 +548,7 @@ int sieve_storage_script_rename struct sieve_storage_script *st_script = (struct sieve_storage_script *) script; struct sieve_storage *storage = st_script->storage; - const char *oldname = script->name, *newpath, *newfile, *link_path; + const char *newpath, *newfile, *link_path; int ret = 0; /* Check script name */ @@ -684,10 +619,6 @@ int sieve_storage_script_rename } } T_END; - /* rename INBOX mailbox attribute */ - if ( ret >= 0 && oldname != NULL ) - sieve_storage_inbox_script_attribute_rename(storage, oldname, newname); - return ret; } diff --git a/src/lib-sievestorage/sieve-storage-script.h b/src/lib-sievestorage/sieve-storage-script.h index 6186c8e..06fdf16 100644 --- a/src/lib-sievestorage/sieve-storage-script.h +++ b/src/lib-sievestorage/sieve-storage-script.h @@ -11,24 +11,24 @@ struct sieve_script *sieve_storage_script_init (struct sieve_storage *storage, const char *scriptname); -int sieve_storage_active_script_get_file +int sieve_storage_get_active_scriptfile (struct sieve_storage *storage, const char **file_r); -int sieve_storage_active_script_get_name +int sieve_storage_get_active_scriptname (struct sieve_storage *storage, const char **name_r); -const char *sieve_storage_active_script_get_path - (struct sieve_storage *storage); -int sieve_storage_active_script_is_no_link(struct sieve_storage *storage); -struct sieve_script *sieve_storage_active_script_get + +struct sieve_script *sieve_storage_get_active_script (struct sieve_storage *storage); -int sieve_storage_active_script_get_last_change - (struct sieve_storage *storage, time_t *last_change_r); -int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime); int sieve_storage_script_is_active(struct sieve_script *script); -int sieve_storage_script_activate(struct sieve_script *script, time_t mtime); + int sieve_storage_script_delete(struct sieve_script **script); + +int sieve_storage_deactivate(struct sieve_storage *storage); + +int sieve_storage_script_activate(struct sieve_script *script); + int sieve_storage_script_rename - (struct sieve_script *script, const char *newname); +(struct sieve_script *script, const char *newname); #endif diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c index eb6a4a4..87a5a0d 100644 --- a/src/lib-sievestorage/sieve-storage.c +++ b/src/lib-sievestorage/sieve-storage.c @@ -7,7 +7,6 @@ #include "mkdir-parents.h" #include "eacces-error.h" #include "unlink-old-files.h" -#include "mail-storage-private.h" #include "sieve.h" #include "sieve-common.h" @@ -23,7 +22,6 @@ #include #include #include -#include #define SIEVE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT @@ -203,36 +201,13 @@ static int check_tmp(const char *path) return 1; } -static int _sieve_storage_open_inbox -(struct mail_user *user, struct mailbox **box_r) -{ - struct mail_namespace *ns; - struct mailbox *box; - enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS; - enum mail_error error; - - ns = mail_namespace_find_inbox(user->namespaces); - *box_r = box = mailbox_alloc(ns->list, "INBOX", flags); - if (mailbox_open(box) == 0) - return 0; - - i_warning("sieve-storage: " - "Failed to open user INBOX for attribute modifications: %s", - mailbox_get_last_error(box, &error)); - return -1; -} - static struct sieve_storage *_sieve_storage_create -(struct sieve_instance *svinst, struct mail_user *user, const char *home, - enum sieve_storage_flags flags) +(struct sieve_instance *svinst, const char *user, const char *home, bool debug) { pool_t pool; struct sieve_storage *storage; - struct mailbox *inbox = NULL; - bool debug = ( (flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 ); const char *tmp_dir, *link_path, *path; const char *sieve_data, *active_path, *active_fname, *storage_dir; - const char *username = user->username; mode_t dir_create_mode, file_create_mode; gid_t file_create_gid; const char *file_create_gid_origin; @@ -287,8 +262,7 @@ static struct sieve_storage *_sieve_storage_create path = home_expand_tilde(active_path, home); if ( path == NULL ) { i_error("sieve-storage: userdb(%s) didn't return a home directory " - "for substitition in active script path (sieve=%s)", - username, active_path); + "for substitition in active script path (sieve=%s)", user, active_path); return NULL; } @@ -391,7 +365,7 @@ static struct sieve_storage *_sieve_storage_create if ( path == NULL ) { i_error("sieve-storage: userdb(%s) didn't return a home directory " "for substitition in storage root directory (sieve_dir=%s)", - username, storage_dir); + user, storage_dir); return NULL; } @@ -404,17 +378,6 @@ static struct sieve_storage *_sieve_storage_create "using sieve script storage directory: %s", storage_dir); } - /* get the storage mtime before we modify it ourself. FIXME: do this - later, only just before modifying the sieve dir */ - struct stat st; - if (stat(storage_dir, &st) < 0) { - if (errno != ENOENT) { - i_error("stat(%s) failed: %m", storage_dir); - return NULL; - } - st.st_mtime = 0; - } - /* Get permissions */ sieve_storage_get_permissions @@ -437,10 +400,6 @@ static struct sieve_storage *_sieve_storage_create file_create_gid_origin, debug) < 0 ) return NULL; - /* Open user's INBOX for attribute updates if necessary */ - if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) - (void)_sieve_storage_open_inbox(user, &inbox); - /* * Create storage object */ @@ -448,20 +407,17 @@ static struct sieve_storage *_sieve_storage_create pool = pool_alloconly_create("sieve-storage", 512+256); storage = p_new(pool, struct sieve_storage, 1); storage->svinst = svinst; - storage->flags = flags; + storage->debug = debug; storage->pool = pool; storage->dir = p_strdup(pool, storage_dir); - storage->username = p_strdup(pool, username); + storage->user = p_strdup(pool, user); storage->active_path = p_strdup(pool, active_path); storage->active_fname = p_strdup(pool, active_fname); - storage->prev_mtime = st.st_mtime; storage->dir_create_mode = dir_create_mode; storage->file_create_mode = file_create_mode; storage->file_create_gid = file_create_gid; - storage->inbox = inbox; - /* Get the path to be prefixed to the script name in the symlink pointing * to the active script. */ @@ -499,69 +455,30 @@ static struct sieve_storage *_sieve_storage_create (unsigned long long int) storage->max_scripts); } } + return storage; } struct sieve_storage *sieve_storage_create -(struct sieve_instance *svinst, struct mail_user *user, const char *home, - enum sieve_storage_flags flags) +(struct sieve_instance *svinst, const char *user, const char *home, bool debug) { struct sieve_storage *storage; T_BEGIN { - storage = _sieve_storage_create(svinst, user, home, flags); + storage = _sieve_storage_create(svinst, user, home, debug); } T_END; return storage; } + void sieve_storage_free(struct sieve_storage *storage) { - if (storage->inbox != NULL) - mailbox_free(&storage->inbox); sieve_error_handler_unref(&storage->ehandler); pool_unref(&storage->pool); } -int sieve_storage_get_last_change -(struct sieve_storage *storage, time_t *last_change_r) -{ - *last_change_r = storage->prev_mtime; - return 0; -} - -void sieve_storage_set_modified -(struct sieve_storage *storage, time_t mtime) -{ - struct utimbuf times; - time_t cur_mtime; - - if ( mtime != (time_t)-1 ) { - if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 && - cur_mtime > mtime ) - return; - } else { - mtime = ioloop_time; - } - - times.actime = mtime; - times.modtime = mtime; - if ( utime(storage->dir, ×) < 0 ) { - switch ( errno ) { - case ENOENT: - break; - case EACCES: - i_error("sieve-storage: %s", eacces_error_get("utime", storage->dir)); - break; - default: - i_error("sieve-storage: utime(%s) failed: %m", storage->dir); - } - } else { - storage->prev_mtime = mtime; - } -} - /* Error handling */ struct sieve_error_handler *sieve_storage_get_error_handler @@ -623,6 +540,20 @@ void sieve_storage_set_error storage->error_code = error; } +void sieve_storage_set_internal_error(struct sieve_storage *storage) +{ + struct tm *tm; + char str[256]; + + tm = localtime(&ioloop_time); + + i_free(storage->error); + storage->error_code = SIEVE_ERROR_TEMP_FAIL; + storage->error = + strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + i_strdup(str) : i_strdup(CRITICAL_MSG); +} + void sieve_storage_set_critical (struct sieve_storage *storage, const char *fmt, ...) { @@ -630,31 +561,14 @@ void sieve_storage_set_critical sieve_storage_clear_error(storage); if (fmt != NULL) { - i_free(storage->error); - storage->error_code = SIEVE_ERROR_TEMP_FAIL; - - if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { - struct tm *tm; - char str[256]; - - va_start(va, fmt); - i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va)); - va_end(va); - - /* critical errors may contain sensitive data, so let user - see only "Internal error" with a timestamp to make it - easier to look from log files the actual error message. */ - tm = localtime(&ioloop_time); - storage->error = - strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? - i_strdup(str) : i_strdup(CRITICAL_MSG); - } else { - /* no user is involved while synchronizing, so do it the - normal way */ - va_start(va, fmt); - storage->error = i_strdup_vprintf(fmt, va); - va_end(va); - } + va_start(va, fmt); + i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va)); + va_end(va); + + /* critical errors may contain sensitive data, so let user + see only "Internal error" with a timestamp to make it + easier to look from log files the actual error message. */ + sieve_storage_set_internal_error(storage); } } @@ -672,73 +586,4 @@ const char *sieve_storage_get_last_error return storage->error != NULL ? storage->error : "Unknown error"; } -/* - * INBOX attributes - */ - -static void sieve_storage_inbox_transaction_finish -(struct sieve_storage *storage, struct mailbox_transaction_context **t) -{ - struct mailbox *inbox = storage->inbox; - - if (mailbox_transaction_commit(t) < 0) { - enum mail_error error; - - i_warning("sieve-storage: Failed to update INBOX attributes: %s", - mail_storage_get_last_error(mailbox_get_storage(inbox), &error)); - } -} - -void sieve_storage_inbox_script_attribute_set -(struct sieve_storage *storage, const char *name) -{ - struct mailbox_transaction_context *t; - const char *key; - - if (storage->inbox == NULL) - return; - - key = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); - t = mailbox_transaction_begin(storage->inbox, 0); - mail_index_attribute_set(t->itrans, TRUE, key, ioloop_time, 0); - sieve_storage_inbox_transaction_finish(storage, &t); -} - -void sieve_storage_inbox_script_attribute_rename -(struct sieve_storage *storage, const char *oldname, const char *newname) -{ - struct mailbox_transaction_context *t; - const char *oldkey, *newkey; - - if (storage->inbox == NULL) - return; - - oldkey = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL); - newkey = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL); - - t = mailbox_transaction_begin(storage->inbox, 0); - mail_index_attribute_unset(t->itrans, TRUE, oldkey, ioloop_time); - mail_index_attribute_set(t->itrans, TRUE, newkey, ioloop_time, 0); - sieve_storage_inbox_transaction_finish(storage, &t); -} - -void sieve_storage_inbox_script_attribute_unset -(struct sieve_storage *storage, const char *name) -{ - struct mailbox_transaction_context *t; - const char *key; - - if (storage->inbox == NULL) - return; - - key = t_strconcat - (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); - - t = mailbox_transaction_begin(storage->inbox, 0); - mail_index_attribute_unset(t->itrans, TRUE, key, ioloop_time); - sieve_storage_inbox_transaction_finish(storage, &t); -} diff --git a/src/lib-sievestorage/sieve-storage.h b/src/lib-sievestorage/sieve-storage.h index 81bfc80..d98c4a2 100644 --- a/src/lib-sievestorage/sieve-storage.h +++ b/src/lib-sievestorage/sieve-storage.h @@ -5,32 +5,12 @@ #define __SIEVE_STORAGE_H #include "lib.h" -#include "mail-storage.h" #include "mail-user.h" #include "sieve.h" -#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ - MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"sieve/" -#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ - MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" -#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ - MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" - -#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' -#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' - -enum sieve_storage_flags { - /* Print debugging information */ - SIEVE_STORAGE_FLAG_DEBUG = 0x01, - /* This storage is used for synchronization (and not normal ManageSieve) - */ - SIEVE_STORAGE_FLAG_SYNCHRONIZING = 0x02 -}; - struct sieve_storage *sieve_storage_create - (struct sieve_instance *svinst, struct mail_user *user, const char *home, - enum sieve_storage_flags flags); + (struct sieve_instance *svinst, const char *user, const char *home, bool debug); void sieve_storage_free(struct sieve_storage *storage); struct sieve_error_handler *sieve_storage_get_error_handler @@ -47,12 +27,9 @@ void sieve_storage_set_error void sieve_storage_set_critical(struct sieve_storage *storage, const char *fmt, ...) ATTR_FORMAT(2, 3); +void sieve_storage_set_internal_error(struct sieve_storage *storage); + const char *sieve_storage_get_last_error (struct sieve_storage *storage, enum sieve_error *error_r); -int sieve_storage_get_last_change - (struct sieve_storage *storage, time_t *last_change_r); -void sieve_storage_set_modified - (struct sieve_storage *storage, time_t mtime); - #endif diff --git a/src/managesieve-login/client-authenticate.c b/src/managesieve-login/client-authenticate.c index 7ebdd41..32b89fd 100644 --- a/src/managesieve-login/client-authenticate.c +++ b/src/managesieve-login/client-authenticate.c @@ -4,12 +4,14 @@ #include "login-common.h" #include "base64.h" #include "buffer.h" +#include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" +#include "time-util.h" #include "auth-client.h" #include "managesieve-parser.h" @@ -47,20 +49,17 @@ const char *client_authenticate_get_capabilities return str_c(str); } -void managesieve_client_auth_result(struct client *client, - enum client_auth_result result, - const struct client_auth_reply *reply, const char *text) +bool managesieve_client_auth_handle_reply +(struct client *client, const struct client_auth_reply *reply) { struct managesieve_client *msieve_client = - (struct managesieve_client *)client; - string_t *referral; - - switch (result) { - case CLIENT_AUTH_RESULT_SUCCESS: - /* nothing to be done for IMAP */ - break; - case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS: - case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN: + (struct managesieve_client *) client; + const char *timestamp, *msg; + + if ( reply->host != NULL ) { + string_t *resp_code; + const char *reason; + /* MANAGESIEVE referral [nologin] referral host=.. [port=..] [destuser=..] @@ -70,41 +69,56 @@ void managesieve_client_auth_result(struct client *client, OK [...] "Logged in, but you should use this server instead." .. [REFERRAL ..] Reason from auth server */ - referral = t_str_new(128); - str_printfa(referral, "REFERRAL sieve://%s;AUTH=%s@%s", + resp_code = t_str_new(128); + str_printfa(resp_code, "REFERRAL sieve://%s;AUTH=%s@%s", reply->destuser, client->auth_mech_name, reply->host); if ( reply->port != 4190 ) - str_printfa(referral, ":%u", reply->port); + str_printfa(resp_code, ":%u", reply->port); - if ( result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS ) { - client_send_okresp(client, str_c(referral), text);; + if ( reply->reason == NULL ) { + if ( reply->nologin ) + reason = "Try this server instead."; + else + reason = "Logged in, but you should use " + "this server instead."; } else { - client_send_noresp(client, str_c(referral), text); + reason = reply->reason; } - break; - case CLIENT_AUTH_RESULT_ABORTED: - client_send_no(client, text); - break; - case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: - client_send_noresp(client, "ALERT", text); - break; - case CLIENT_AUTH_RESULT_AUTHZFAILED: - client_send_no(client, text); - break; - case CLIENT_AUTH_RESULT_TEMPFAIL: - client_send_noresp(client, "TRYLATER", text); - break; - case CLIENT_AUTH_RESULT_SSL_REQUIRED: - client_send_noresp(client, "ENCRYPT-NEEDED", text); - break; - case CLIENT_AUTH_RESULT_AUTHFAILED: - default: - client_send_no(client, text); - break; + + if ( !reply->nologin ) { + client_send_okresp(client, str_c(resp_code), reason); + client_destroy_success(client, "Login with referral"); + return TRUE; + } + client_send_noresp(client, str_c(resp_code), reason); + } else if (!reply->nologin) { + /* normal login/failure */ + return FALSE; + } else if (reply->reason != NULL) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_REASON, + reply->reason); + } else if (reply->temp) { + timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time); + msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]", + my_hostname, timestamp); + client_send_line(client, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, msg); + } else if (reply->authz_failure) { + client_send_line(client, CLIENT_CMD_REPLY_AUTHZ_FAILED, + "Authorization failed"); + } else { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); } + i_assert(reply->nologin); + msieve_client->auth_response_input = NULL; managesieve_parser_reset(msieve_client->parser); + + if ( !client->destroyed ) + client_auth_failed(client); + return TRUE; } void managesieve_client_auth_send_challenge @@ -248,7 +262,7 @@ static int managesieve_client_auth_read_response return 1; } -void managesieve_client_auth_parse_response(struct client *client) +int managesieve_client_auth_parse_response(struct client *client) { struct managesieve_client *msieve_client = (struct managesieve_client *) client; @@ -258,21 +272,18 @@ void managesieve_client_auth_parse_response(struct client *client) if ( (ret=managesieve_client_auth_read_response(msieve_client, FALSE, &error)) < 0 ) { if ( error != NULL ) - client_auth_fail(client, error); - return; + sasl_server_auth_failed(client, error); + return -1; } - if ( ret == 0 ) return; + if ( ret == 0 ) return 0; if ( strcmp(str_c(client->auth_response), "*") == 0 ) { - client_auth_abort(client); - return; + sasl_server_auth_abort(client); + return -1; } - client_auth_respond(client, str_c(client->auth_response)); - - memset(str_c_modifiable(client->auth_response), 0, - str_len(client->auth_response)); + return 1; } int cmd_authenticate diff --git a/src/managesieve-login/client-authenticate.h b/src/managesieve-login/client-authenticate.h index 18e8841..f8ca147 100644 --- a/src/managesieve-login/client-authenticate.h +++ b/src/managesieve-login/client-authenticate.h @@ -9,13 +9,12 @@ struct managesieve_arg; const char *client_authenticate_get_capabilities (struct client *client); -void managesieve_client_auth_result - (struct client *client, enum client_auth_result result, - const struct client_auth_reply *reply, const char *text); +bool managesieve_client_auth_handle_reply(struct client *client, + const struct client_auth_reply *reply); void managesieve_client_auth_send_challenge (struct client *client, const char *data); -void managesieve_client_auth_parse_response +int managesieve_client_auth_parse_response (struct client *client); int cmd_authenticate diff --git a/src/managesieve-login/client.c b/src/managesieve-login/client.c index 9ce677a..ec14114 100644 --- a/src/managesieve-login/client.c +++ b/src/managesieve-login/client.c @@ -105,15 +105,6 @@ static int cmd_starttls return 1; } -static void managesieve_client_notify_starttls -(struct client *client, bool success, const char *text) -{ - if ( success ) - client_send_ok(client, text); - else - client_send_no(client, text); -} - static int cmd_noop (struct managesieve_client *client, const struct managesieve_arg *args) @@ -334,7 +325,7 @@ static void managesieve_client_destroy(struct client *client) managesieve_parser_destroy(&managesieve_client->parser); } -static void managesieve_client_notify_auth_ready(struct client *client) +static void managesieve_client_send_greeting(struct client *client) { /* Cork the stream to send the capability data as a single tcp frame * Some naive clients break if we don't. @@ -346,6 +337,7 @@ static void managesieve_client_notify_auth_ready(struct client *client) client_send_ok(client, client->set->login_greeting); o_stream_uncork(client->output); + client->greeting_sent = TRUE; } static void managesieve_client_starttls(struct client *client) @@ -371,15 +363,13 @@ static void managesieve_client_starttls(struct client *client) o_stream_uncork(client->output); } -static void -client_send_reply_raw(struct client *client, - const char *prefix, const char *resp_code, - const char *text) +void _client_send_response(struct client *client, + const char *oknobye, const char *resp_code, const char *msg) { T_BEGIN { string_t *line = t_str_new(256); - str_append(line, prefix); + str_append(line, oknobye); if (resp_code != NULL) { str_append(line, " ["); @@ -387,9 +377,10 @@ client_send_reply_raw(struct client *client, str_append_c(line, ']'); } - if ( text != NULL ) { + if ( msg != NULL ) + { str_append_c(line, ' '); - managesieve_quote_append_string(line, text, TRUE); + managesieve_quote_append_string(line, msg, TRUE); } str_append(line, "\r\n"); @@ -398,43 +389,42 @@ client_send_reply_raw(struct client *client, } T_END; } -void client_send_reply_code -(struct client *client, enum managesieve_cmd_reply reply, const char *resp_code, - const char *text) +static void managesieve_client_send_line +(struct client *client, enum client_cmd_reply reply, const char *text) { + const char *resp_code = NULL; const char *prefix = "NO"; switch (reply) { - case MANAGESIEVE_CMD_REPLY_OK: + case CLIENT_CMD_REPLY_OK: prefix = "OK"; break; - case MANAGESIEVE_CMD_REPLY_NO: + case CLIENT_CMD_REPLY_AUTH_FAILED: + break; + case CLIENT_CMD_REPLY_AUTHZ_FAILED: + break; + case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP: + resp_code = "TRYLATER"; break; - case MANAGESIEVE_CMD_REPLY_BYE: + case CLIENT_CMD_REPLY_AUTH_FAIL_REASON: + break; + case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL: + resp_code = "ENCRYPT-NEEDED"; + break; + case CLIENT_CMD_REPLY_BAD: + prefix = "NO"; + break; + case CLIENT_CMD_REPLY_BYE: prefix = "BYE"; break; + case CLIENT_CMD_REPLY_STATUS: + return; + case CLIENT_CMD_REPLY_STATUS_BAD: + prefix = "NO"; + break; } - client_send_reply_raw(client, prefix, resp_code, text); -} - -void client_send_reply -(struct client *client, enum managesieve_cmd_reply reply, const char *text) -{ - client_send_reply_code(client, reply, NULL, text); -} - -static void -managesieve_client_notify_disconnect -(struct client *client, enum client_disconnect_reason reason, const char *text) -{ - if ( reason == CLIENT_DISCONNECT_SYSTEM_SHUTDOWN ) { - client_send_reply_code - (client, MANAGESIEVE_CMD_REPLY_BYE, "TRYLATER", text); - } else { - client_send_reply_code - (client, MANAGESIEVE_CMD_REPLY_BYE, NULL, text); - } + _client_send_response(client, prefix, resp_code, text); } static void managesieve_login_preinit(void) @@ -455,18 +445,15 @@ static struct client_vfuncs managesieve_client_vfuncs = { managesieve_client_alloc, managesieve_client_create, managesieve_client_destroy, - managesieve_client_notify_auth_ready, - managesieve_client_notify_disconnect, - NULL, - managesieve_client_notify_starttls, + managesieve_client_send_greeting, managesieve_client_starttls, managesieve_client_input, + managesieve_client_send_line, + managesieve_client_auth_handle_reply, managesieve_client_auth_send_challenge, managesieve_client_auth_parse_response, - managesieve_client_auth_result, managesieve_proxy_reset, - managesieve_proxy_parse_line, - managesieve_proxy_error + managesieve_proxy_parse_line }; static const struct login_binary managesieve_login_binary = { diff --git a/src/managesieve-login/client.h b/src/managesieve-login/client.h index b3449c5..fd5a526 100644 --- a/src/managesieve-login/client.h +++ b/src/managesieve-login/client.h @@ -4,7 +4,7 @@ #ifndef __CLIENT_H #define __CLIENT_H -#include "net.h" +#include "network.h" #include "client-common.h" /* maximum length for managesieve command line. */ @@ -36,32 +36,21 @@ struct managesieve_client { bool client_skip_line(struct managesieve_client *client); -enum managesieve_cmd_reply { - MANAGESIEVE_CMD_REPLY_OK, - MANAGESIEVE_CMD_REPLY_NO, - MANAGESIEVE_CMD_REPLY_BYE -}; - -void client_send_reply(struct client *client, - enum managesieve_cmd_reply reply, const char *text); - -void client_send_reply_code(struct client *client, - enum managesieve_cmd_reply reply, const char *resp_code, - const char *text); - -#define client_send_ok(client, text) \ - client_send_reply(client, MANAGESIEVE_CMD_REPLY_OK, text) -#define client_send_no(client, text) \ - client_send_reply(client, MANAGESIEVE_CMD_REPLY_NO, text) -#define client_send_bye(client, text) \ - client_send_reply(client, MANAGESIEVE_CMD_REPLY_BYE, text) - -#define client_send_okresp(client, resp_code, text) \ - client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_OK, resp_code, text) -#define client_send_noresp(client, resp_code, text) \ - client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, resp_code, text) -#define client_send_byeresp(client, resp_code, text) \ - client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_BYE, resp_code, text) - +void _client_send_response(struct client *client, + const char *oknobye, const char *resp_code, const char *msg); + +#define client_send_ok(client, msg) \ + _client_send_response(client, "OK", NULL, msg) +#define client_send_no(client, msg) \ + _client_send_response(client, "NO", NULL, msg) +#define client_send_bye(client, msg) \ + _client_send_response(client, "BYE", NULL, msg) + +#define client_send_okresp(client, resp_code, msg) \ + _client_send_response(client, "OK", resp_code, msg) +#define client_send_noresp(client, resp_code, msg) \ + _client_send_response(client, "NO", resp_code, msg) +#define client_send_byeresp(client, resp_code, msg) \ + _client_send_response(client, "BYE", resp_code, msg) #endif /* __CLIENT_H */ diff --git a/src/managesieve-login/managesieve-login-settings-plugin.c b/src/managesieve-login/managesieve-login-settings-plugin.c index 64b0acb..78296d0 100644 --- a/src/managesieve-login/managesieve-login-settings-plugin.c +++ b/src/managesieve-login/managesieve-login-settings-plugin.c @@ -28,7 +28,7 @@ static void (*next_hook_config_parser_begin)(struct config_parser_context *ctx) static void managesieve_login_config_parser_begin(struct config_parser_context *ctx); -const char *managesieve_login_settings_version = DOVECOT_ABI_VERSION; +const char *managesieve_login_settings_version = DOVECOT_VERSION; void managesieve_login_settings_init(struct module *module ATTR_UNUSED) { @@ -199,18 +199,9 @@ static void managesieve_login_config_set } static void managesieve_login_config_parser_begin(struct config_parser_context *ctx) -{ - const char *const *module = ctx->modules; - - if ( module != NULL && *module != NULL ) { - while ( *module != NULL ) { - if ( strcmp(*module, "managesieve-login") == 0 ) - break; - module++; - } - if ( *module == NULL ) - return; - } +{ + if (*ctx->module != '\0' && strcmp(ctx->module, "managesieve-login") != 0) + return; if ( !capability_dumped ) { (void)capability_dump(); diff --git a/src/managesieve-login/managesieve-proxy.c b/src/managesieve-login/managesieve-proxy.c index 5c8e547..676f932 100644 --- a/src/managesieve-login/managesieve-proxy.c +++ b/src/managesieve-login/managesieve-proxy.c @@ -366,8 +366,3 @@ void managesieve_proxy_reset(struct client *client ATTR_UNUSED) /* struct managesieve_client *msieve_client = (struct managesieve_client *) client; */ } - -void managesieve_proxy_error(struct client *client, const char *text) -{ - client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, "TRYLATER", text); -} diff --git a/src/managesieve-login/managesieve-proxy.h b/src/managesieve-login/managesieve-proxy.h index 0048f54..d2a8aac 100644 --- a/src/managesieve-login/managesieve-proxy.h +++ b/src/managesieve-login/managesieve-proxy.h @@ -7,6 +7,4 @@ void managesieve_proxy_reset(struct client *client); int managesieve_proxy_parse_line(struct client *client, const char *line); -void managesieve_proxy_error(struct client *client, const char *text); - #endif diff --git a/src/managesieve/Makefile.am b/src/managesieve/Makefile.am index a523ffe..43bab10 100644 --- a/src/managesieve/Makefile.am +++ b/src/managesieve/Makefile.am @@ -24,7 +24,7 @@ managesieve_LDFLAGS = -export-dynamic libs = \ managesieve-settings.lo \ $(top_builddir)/src/lib-managesieve/libmanagesieve.a \ - $(top_builddir)/src/lib-sievestorage/libsievestorage.la \ + $(top_builddir)/src/lib-sievestorage/libsievestorage.a \ $(top_builddir)/src/lib-sieve/libdovecot-sieve.la managesieve_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c index 08bd8f3..c194559 100644 --- a/src/managesieve/cmd-getscript.c +++ b/src/managesieve/cmd-getscript.c @@ -105,8 +105,9 @@ bool cmd_getscript(struct client_command_context *cmd) return cmd_getscript_finish(ctx); } - if ( sieve_script_get_stream - (ctx->script, &ctx->script_stream, &error) < 0 ) { + ctx->script_stream = sieve_script_open(ctx->script, &error); + + if ( ctx->script_stream == NULL ) { if ( error == SIEVE_ERROR_NOT_FOUND ) sieve_storage_set_error(client->storage, error, "Script does not exist."); ctx->failed = TRUE; diff --git a/src/managesieve/cmd-putscript.c b/src/managesieve/cmd-putscript.c index 8ab8a4d..c26fa03 100644 --- a/src/managesieve/cmd-putscript.c +++ b/src/managesieve/cmd-putscript.c @@ -43,8 +43,9 @@ struct cmd_putscript_context { static void cmd_putscript_finish(struct cmd_putscript_context *ctx); static bool cmd_putscript_continue_script(struct client_command_context *cmd); -static void client_input_putscript(struct client *client) +static void client_input_putscript(void *context) { + struct client *client = context; struct client_command_context *cmd = &client->cmd; i_assert(!client->destroyed); diff --git a/src/managesieve/cmd-setactive.c b/src/managesieve/cmd-setactive.c index 7558767..06fbf8d 100644 --- a/src/managesieve/cmd-setactive.c +++ b/src/managesieve/cmd-setactive.c @@ -68,7 +68,7 @@ bool cmd_setactive(struct client_command_context *cmd) /* Refresh activation no matter what; this can also resolve some erroneous * situations. */ - ret = sieve_storage_script_activate(script, (time_t)-1); + ret = sieve_storage_script_activate(script); if ( ret < 0 ) { client_send_storage_error(client, storage); } else { @@ -90,7 +90,7 @@ bool cmd_setactive(struct client_command_context *cmd) /* ... deactivate */ } else { - ret = sieve_storage_deactivate(storage, (time_t)-1); + ret = sieve_storage_deactivate(storage); if ( ret < 0 ) client_send_storage_error(client, storage); diff --git a/src/managesieve/main.c b/src/managesieve/main.c index 5f18f94..80d47f3 100644 --- a/src/managesieve/main.c +++ b/src/managesieve/main.c @@ -191,7 +191,7 @@ login_client_connected(const struct master_login_client *client, input.userdb_fields = extra_fields; input.session_id = client->session_id; - buffer_create_from_const_data(&input_buf, client->data, + 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) { diff --git a/src/managesieve/managesieve-capabilities.c b/src/managesieve/managesieve-capabilities.c index fd8ffd6..2e447d5 100644 --- a/src/managesieve/managesieve-capabilities.c +++ b/src/managesieve/managesieve-capabilities.c @@ -22,7 +22,7 @@ */ struct plugin_settings { - ARRAY(const char *) plugin_envs; + ARRAY_DEFINE(plugin_envs, const char *); }; static const struct setting_parser_info **plugin_set_roots; diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c index de97723..1f8e9c8 100644 --- a/src/managesieve/managesieve-client.c +++ b/src/managesieve/managesieve-client.c @@ -6,7 +6,7 @@ #include "llist.h" #include "str.h" #include "hostpid.h" -#include "net.h" +#include "network.h" #include "istream.h" #include "ostream.h" #include "var-expand.h" @@ -62,17 +62,13 @@ static struct sieve_storage *client_get_storage const struct managesieve_settings *set) { struct sieve_storage *storage; - enum sieve_storage_flags flags = 0; const char *home; if ( mail_user_get_home(user, &home) <= 0 ) home = NULL; - if ( set->mail_debug ) - flags |= SIEVE_STORAGE_FLAG_DEBUG; - storage = sieve_storage_create - (svinst, user, home, flags); + (svinst, user->username, home, set->mail_debug); if (storage == NULL) { struct tm *tm; diff --git a/src/managesieve/managesieve-commands.c b/src/managesieve/managesieve-commands.c index 34531c5..b100643 100644 --- a/src/managesieve/managesieve-commands.c +++ b/src/managesieve/managesieve-commands.c @@ -29,7 +29,7 @@ static const struct command managesieve_base_commands[] = { #define MANAGESIEVE_COMMANDS_COUNT N_ELEMENTS(managesieve_base_commands) -static ARRAY(struct command) managesieve_commands; +static ARRAY_DEFINE(managesieve_commands, struct command); static bool commands_unsorted; void command_register(const char *name, command_func_t *func) diff --git a/src/managesieve/managesieve-settings.c b/src/managesieve/managesieve-settings.c index f22b404..f9c825e 100644 --- a/src/managesieve/managesieve-settings.c +++ b/src/managesieve/managesieve-settings.c @@ -167,4 +167,4 @@ static bool managesieve_settings_verify /* */ -const char *managesieve_settings_version = DOVECOT_ABI_VERSION; +const char *managesieve_settings_version = DOVECOT_VERSION; diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index c8f8d97..4623241 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1 +1 @@ -SUBDIRS = doveadm-sieve lda-sieve sieve-extprograms +SUBDIRS = lda-sieve diff --git a/src/plugins/doveadm-sieve/Makefile.am b/src/plugins/doveadm-sieve/Makefile.am deleted file mode 100644 index ea1d992..0000000 --- a/src/plugins/doveadm-sieve/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib-sieve \ - -I$(top_srcdir)/src/lib-sievestorage \ - $(LIBDOVECOT_INCLUDE) - -doveadm_moduledir = $(dovecot_moduledir)/doveadm -lib10_doveadm_sieve_plugin_la_LDFLAGS = -module -avoid-version - -doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la - -lib10_doveadm_sieve_plugin_la_SOURCES = \ - doveadm-sieve-plugin.c -lib10_doveadm_sieve_plugin_la_LIBADD = \ - $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ - $(top_builddir)/src/lib-sievestorage/libsievestorage.la diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c deleted file mode 100644 index bfe7a3b..0000000 --- a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c +++ /dev/null @@ -1,706 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "ioloop.h" -#include "istream.h" -#include "istream-concat.h" -#include "sieve-script.h" -#include "sieve-script-file.h" -#include "sieve-storage.h" -#include "sieve-storage-list.h" -#include "sieve-storage-save.h" -#include "sieve-storage-script.h" -#include "mail-storage-private.h" - -#define SIEVE_MAIL_CONTEXT(obj) \ - MODULE_CONTEXT(obj, sieve_storage_module) -#define SIEVE_USER_CONTEXT(obj) \ - MODULE_CONTEXT(obj, sieve_user_module) - -struct sieve_mail_user { - union mail_user_module_context module_ctx; - - struct sieve_instance *svinst; - struct sieve_storage *sieve_storage; -}; - -struct sieve_mailbox_attribute_iter { - struct mailbox_attribute_iter iter; - struct mailbox_attribute_iter *super; - - struct sieve_list_context *sieve_list; - string_t *name; - - bool failed; - bool have_active; -}; - -void doveadm_sieve_plugin_init(struct module *module); -void doveadm_sieve_plugin_deinit(void); - -static MODULE_CONTEXT_DEFINE_INIT(sieve_storage_module, - &mail_storage_module_register); -static MODULE_CONTEXT_DEFINE_INIT(sieve_user_module, - &mail_user_module_register); - -const char *doveadm_sieve_plugin_version = DOVECOT_ABI_VERSION; - -static const char * -mail_sieve_get_setting(void *context, const char *identifier) -{ - struct mail_user *mail_user = context; - - return mail_user_plugin_getenv(mail_user, identifier); -} - -static const struct sieve_callbacks mail_sieve_callbacks = { - NULL, - mail_sieve_get_setting -}; - -static void mail_sieve_user_deinit(struct mail_user *user) -{ - struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); - - sieve_storage_free(suser->sieve_storage); - sieve_deinit(&suser->svinst); - - suser->module_ctx.super.deinit(user); -} - -static int -mail_sieve_user_init -(struct mail_user *user, struct sieve_storage **svstorage_r) -{ - struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); - enum sieve_storage_flags storage_flags = SIEVE_STORAGE_FLAG_SYNCHRONIZING; - struct mail_user_vfuncs *v = user->vlast; - struct sieve_environment svenv; - - if (suser != NULL) { - *svstorage_r = suser->sieve_storage; - return 0; - } - - /* Delayed initialization of sieve storage until it's actually needed */ - memset(&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; - - suser = p_new(user->pool, struct sieve_mail_user, 1); - suser->module_ctx.super = *v; - user->vlast = &suser->module_ctx.super; - v->deinit = mail_sieve_user_deinit; - - if (user->mail_debug) - storage_flags |= SIEVE_STORAGE_FLAG_DEBUG; - - suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks, - user, user->mail_debug); - suser->sieve_storage = sieve_storage_create(suser->svinst, user, - svenv.home_dir, storage_flags); - - MODULE_CONTEXT_SET(user, sieve_user_module, suser); - *svstorage_r = suser->sieve_storage; - return 0; -} - -static int sieve_attribute_unset_script(struct mail_storage *storage, - struct sieve_storage *svstorage, - const char *scriptname) -{ - struct sieve_script *script; - const char *errstr; - enum sieve_error error; - int ret = 0; - - script = sieve_storage_script_init(svstorage, scriptname); - ret = script == NULL ? -1 : - sieve_storage_script_delete(&script); - if (ret < 0) { - errstr = sieve_storage_get_last_error(svstorage, &error); - if (error == SIEVE_ERROR_NOT_FOUND) { - /* already deleted, ignore */ - return 0; - } - mail_storage_set_critical(storage, - "Failed to delete Sieve script '%s': %s", scriptname, - errstr); - return -1; - } - return 0; -} - -static int -sieve_attribute_set_active(struct mail_storage *storage, - struct sieve_storage *svstorage, - const struct mail_attribute_value *value) -{ - const char *scriptname; - struct sieve_script *script; - int ret; - - if (mailbox_attribute_value_to_string(storage, value, &scriptname) < 0) - return -1; - i_assert(scriptname[0] == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK); - scriptname++; - - if (scriptname == NULL) { - /* don't affect non-link active script */ - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) != 0) { - if (ret < 0) { - mail_storage_set_internal_error(storage); - return -1; - } - return 0; - } - - /* deactivate current script */ - if (sieve_storage_deactivate(svstorage, value->last_change) < 0) { - mail_storage_set_critical(storage, - "Failed to deactivate Sieve: %s", - sieve_storage_get_last_error(svstorage, NULL)); - return -1; - } - return 0; - } - - /* activate specified script */ - script = sieve_storage_script_init(svstorage, scriptname); - ret = script == NULL ? -1 : - sieve_storage_script_activate(script, value->last_change); - if (ret < 0) { - mail_storage_set_critical(storage, - "Failed to activate Sieve script '%s': %s", scriptname, - sieve_storage_get_last_error(svstorage, NULL)); - } - if (script != NULL) - sieve_script_unref(&script); - sieve_storage_set_modified(svstorage, value->last_change); - return ret; -} - -static int -sieve_attribute_unset_active_script(struct mail_storage *storage, - struct sieve_storage *svstorage, time_t last_change) -{ - int ret; - - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) { - if (ret < 0) - mail_storage_set_internal_error(storage); - return ret; - } - - if (sieve_storage_deactivate(svstorage, last_change) < 0) { - mail_storage_set_critical(storage, - "Failed to deactivate sieve: %s", - sieve_storage_get_last_error(svstorage, NULL)); - return -1; - } - return 0; -} - -static int -sieve_attribute_set_active_script(struct mail_storage *storage, - struct sieve_storage *svstorage, - const struct mail_attribute_value *value) -{ - struct istream *input; - - if (value->value != NULL) { - input = i_stream_create_from_data(value->value, strlen(value->value)); - } else if (value->value_stream != NULL) { - input = value->value_stream; - i_stream_ref(input); - } else { - return sieve_attribute_unset_active_script(storage, svstorage, value->last_change); - } - /* skip over the 'S' type */ - i_stream_skip(input, 1); - - if (sieve_storage_save_as_active_script - (svstorage, input, value->last_change) < 0) { - mail_storage_set_critical(storage, - "Failed to save active sieve script: %s", - sieve_storage_get_last_error(svstorage, NULL)); - i_stream_unref(&input); - return -1; - } - - sieve_storage_set_modified(svstorage, value->last_change); - i_stream_unref(&input); - return 0; -} - -static int -sieve_attribute_set_default(struct mail_storage *storage, - struct sieve_storage *svstorage, - const struct mail_attribute_value *value) -{ - const unsigned char *data; - size_t size; - ssize_t ret; - char type; - - if (value->value != NULL) { - type = value->value[0]; - } else { - ret = i_stream_read_data(value->value_stream, &data, &size, 0); - if (ret == -1) { - mail_storage_set_critical(storage, "read(%s) failed: %m", - i_stream_get_name(value->value_stream)); - return -1; - } - i_assert(ret > 0); - type = data[0]; - } - if (type == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK) - return sieve_attribute_set_active(storage, svstorage, value); - if (type == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT) - return sieve_attribute_set_active_script(storage, svstorage, value); - mail_storage_set_error(storage, MAIL_ERROR_PARAMS, - "Invalid value for default sieve attribute"); - return -1; -} - -static int -sieve_attribute_set_sieve(struct mail_storage *storage, - const char *key, - const struct mail_attribute_value *value) -{ - struct sieve_storage *svstorage; - struct sieve_save_context *save_ctx; - struct istream *input; - const char *scriptname; - int ret = 0; - - if (mail_sieve_user_init(storage->user, &svstorage) < 0) - return -1; - - if (strcmp(key, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT) == 0) - return sieve_attribute_set_default(storage, svstorage, value); - if (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, - strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)) != 0) { - mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, - "Nonexistent sieve attribute"); - return -1; - } - scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); - - if (value->value != NULL) { - input = i_stream_create_from_data(value->value, - strlen(value->value)); - save_ctx = sieve_storage_save_init(svstorage, scriptname, input); - i_stream_unref(&input); - } else if (value->value_stream != NULL) { - input = value->value_stream; - save_ctx = sieve_storage_save_init(svstorage, scriptname, input); - } else { - return sieve_attribute_unset_script(storage, svstorage, scriptname); - } - - sieve_storage_save_set_mtime(save_ctx, value->last_change); - - if (save_ctx == NULL) { - /* save initialization failed */ - mail_storage_set_critical(storage, - "Failed to save sieve script '%s': %s", scriptname, - sieve_storage_get_last_error(svstorage, NULL)); - return -1; - } - while (i_stream_read(input) > 0) { - if (sieve_storage_save_continue(save_ctx) < 0) { - mail_storage_set_critical(storage, - "Failed to save sieve script '%s': %s", scriptname, - sieve_storage_get_last_error(svstorage, NULL)); - ret = -1; - break; - } - } - i_assert(input->eof); - if (input->stream_errno != 0) { - errno = input->stream_errno; - mail_storage_set_critical(storage, - "Saving sieve script: read(%s) failed: %m", - i_stream_get_name(input)); - ret = -1; - } - if (ret == 0 && sieve_storage_save_finish(save_ctx) < 0) { - mail_storage_set_critical(storage, - "Failed to save sieve script '%s': %s", scriptname, - sieve_storage_get_last_error(svstorage, NULL)); - ret = -1; - } - if (ret < 0) - sieve_storage_save_cancel(&save_ctx); - else if (sieve_storage_save_commit(&save_ctx) < 0) { - mail_storage_set_critical(storage, - "Failed to save sieve script '%s': %s", scriptname, - sieve_storage_get_last_error(svstorage, NULL)); - ret = -1; - } - return ret; -} - -static int -sieve_attribute_set(struct mailbox_transaction_context *t, - enum mail_attribute_type type, const char *key, - const struct mail_attribute_value *value) -{ - struct mail_user *user = t->box->storage->user; - union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(t->box); - time_t ts = value->last_change != 0 ? value->last_change : ioloop_time; - - if (t->box->storage->user->dsyncing && - type == MAIL_ATTRIBUTE_TYPE_PRIVATE && - strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, - strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) == 0) { - if (sieve_attribute_set_sieve(t->box->storage, key, value) < 0) - return -1; - if (user->mail_debug) - i_debug("doveadm-sieve: Assigned value for key `%s'", key); - /* FIXME: set value len to sieve script size / active name - length */ - if (value->value != NULL || value->value_stream != NULL) - mail_index_attribute_set(t->itrans, TRUE, key, ts, 0); - else - mail_index_attribute_unset(t->itrans, TRUE, key, ts); - return 0; - } - return sbox->super.attribute_set(t, type, key, value); -} - -static int -sieve_attribute_retrieve_script(struct mail_storage *storage, - struct sieve_storage *svstorage, struct sieve_script *script, - bool add_type_prefix, - struct mail_attribute_value *value_r, const char **errorstr_r) -{ - char type = MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT; - struct istream *input, *inputs[3]; - const struct stat *st; - enum sieve_error error; - - if (script == NULL) - *errorstr_r = sieve_storage_get_last_error(svstorage, &error); - else if (sieve_script_get_stream(script, &input, &error) < 0) - sieve_script_unref(&script); - - if (script == NULL) { - if (error == SIEVE_ERROR_NOT_FOUND) { - /* already deleted, but return the last_change */ - (void)sieve_storage_get_last_change(svstorage, - &value_r->last_change); - return 0; - } - *errorstr_r = sieve_storage_get_last_error(svstorage, &error); - return -1; - } - if (i_stream_stat(input, FALSE, &st) < 0) { - mail_storage_set_critical(storage, - "stat(%s) failed: %m", i_stream_get_name(input)); - } else { - value_r->last_change = st->st_mtime; - } - if (!add_type_prefix) { - i_stream_ref(input); - value_r->value_stream = input; - } else { - inputs[0] = i_stream_create_from_data(&type, 1); - inputs[1] = input; - inputs[2] = NULL; - value_r->value_stream = i_stream_create_concat(inputs); - } - sieve_script_unref(&script); - return 1; -} - -static int -sieve_attribute_get_active_script(struct mail_storage *storage, - struct sieve_storage *svstorage, - struct mail_attribute_value *value_r) -{ - struct sieve_script *script; - const char *errstr; - int ret; - - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) { - if (ret == 0 && sieve_storage_active_script_get_last_change - (svstorage, &value_r->last_change) < 0) { - ret = -1; - } - if (ret < 0) - mail_storage_set_internal_error(storage); - return ret; - } - - if ((script=sieve_storage_active_script_get(svstorage)) == NULL) - return 0; - if ((ret=sieve_attribute_retrieve_script - (storage, svstorage, script, TRUE, value_r, &errstr)) < 0) { - mail_storage_set_critical(storage, - "Failed to access active sieve script: %s", errstr); - } - return ret; -} - -static int -sieve_attribute_get_default(struct mail_storage *storage, - struct sieve_storage *svstorage, - struct mail_attribute_value *value_r) -{ - const char *scriptname; - int ret; - - ret = sieve_storage_active_script_get_name(svstorage, &scriptname); - if (ret == 0) - return sieve_attribute_get_active_script(storage, svstorage, value_r); - - if (ret > 0) { - value_r->value = t_strdup_printf("%c%s", - MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK, scriptname); - if (sieve_storage_active_script_get_last_change - (svstorage, &value_r->last_change) < 0) - ret = -1; - } - if (ret < 0) - mail_storage_set_internal_error(storage); - return ret; -} - -static int -sieve_attribute_get_sieve(struct mail_storage *storage, const char *key, - struct mail_attribute_value *value_r) -{ - struct sieve_storage *svstorage; - struct sieve_script *script; - const char *scriptname, *errstr; - int ret; - - if (mail_sieve_user_init(storage->user, &svstorage) < 0) - return -1; - - if (strcmp(key, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT) == 0) - return sieve_attribute_get_default(storage, svstorage, value_r); - if (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, - strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)) != 0) - return 0; - if ((value_r->flags & MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS) == 0) { - mail_storage_set_error(storage, MAIL_ERROR_PARAMS, - "Sieve attributes are available only as streams"); - return -1; - } - scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); - script = sieve_storage_script_init(svstorage, scriptname); - if ((ret=sieve_attribute_retrieve_script - (storage, svstorage, script, FALSE, value_r, &errstr)) < 0) { - mail_storage_set_critical(storage, - "Failed to access sieve script '%s': %s", - scriptname, errstr); - } - return ret; -} - -static int -sieve_attribute_get(struct mailbox_transaction_context *t, - enum mail_attribute_type type, const char *key, - struct mail_attribute_value *value_r) -{ - union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(t->box); - struct mail_user *user = t->box->storage->user; - int ret; - - if (t->box->storage->user->dsyncing && - type == MAIL_ATTRIBUTE_TYPE_PRIVATE && - strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, - strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) == 0) { - - ret = sieve_attribute_get_sieve(t->box->storage, key, value_r); - if (ret >= 0 && user->mail_debug) { - struct tm *tm = localtime(&value_r->last_change); - char str[256]; - const char *timestamp = ""; - - if (strftime(str, sizeof(str), - " (last change: %Y-%m-%d %H:%M:%S)", tm) > 0) - timestamp = str; - - if (ret > 0) { - i_debug("doveadm-sieve: Retrieved value for key `%s'%s", - key, timestamp); - } else { - i_debug("doveadm-sieve: Value missing for key `%s'%s", - key, timestamp); - } - } - return ret; - } - return sbox->super.attribute_get(t, type, key, value_r); -} - -static int -sieve_attribute_iter_script_init(struct sieve_mailbox_attribute_iter *siter) -{ - struct mail_user *user = siter->iter.box->storage->user; - struct sieve_storage *svstorage; - - if (user->mail_debug) - i_debug("doveadm-sieve: Iterating Sieve mailbox attributes"); - - if (mail_sieve_user_init(user, &svstorage) < 0) - return -1; - - siter->sieve_list = sieve_storage_list_init(svstorage); - if (siter->sieve_list == NULL) { - mail_storage_set_critical(siter->iter.box->storage, - "Failed to iterate sieve scripts: %s", - sieve_storage_get_last_error(svstorage, NULL)); - return -1; - } - siter->name = str_new(default_pool, 128); - str_append(siter->name, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); - return 0; -} - -static struct mailbox_attribute_iter * -sieve_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, - const char *prefix) -{ - union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(box); - struct sieve_mailbox_attribute_iter *siter; - - siter = i_new(struct sieve_mailbox_attribute_iter, 1); - siter->iter.box = box; - siter->super = sbox->super.attribute_iter_init(box, type, prefix); - - if (box->storage->user->dsyncing && - type == MAIL_ATTRIBUTE_TYPE_PRIVATE && - strncmp(prefix, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, - strlen(prefix)) == 0) { - if (sieve_attribute_iter_script_init(siter) < 0) - siter->failed = TRUE; - } - return &siter->iter; -} - -static const char * -sieve_attribute_iter_next_script(struct sieve_mailbox_attribute_iter *siter) -{ - struct mail_user *user = siter->iter.box->storage->user; - struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); - struct sieve_storage *svstorage = suser->sieve_storage; - const char *scriptname; - bool active; - int ret; - - /* Iterate through all scripts in sieve_dir */ - while ((scriptname = sieve_storage_list_next(siter->sieve_list, &active)) - != NULL) { - if (active) - siter->have_active = TRUE; - str_truncate(siter->name, strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)); - str_append(siter->name, scriptname); - return str_c(siter->name); - } - if (sieve_storage_list_deinit(&siter->sieve_list) < 0) { - mail_storage_set_critical(siter->iter.box->storage, - "Failed to iterate sieve scripts: %s", - sieve_storage_get_last_error(svstorage, NULL)); - siter->failed = TRUE; - return NULL; - } - - /* Check whether active script is a proper symlink or a regular file */ - if ((ret=sieve_storage_active_script_is_no_link(svstorage)) < 0) { - mail_storage_set_critical(siter->iter.box->storage, - "Failed to iterate sieve scripts: %s", - sieve_storage_get_last_error(svstorage, NULL)); - return NULL; - } - - /* Regular file */ - if (ret > 0) - return MAILBOX_ATTRIBUTE_SIEVE_DEFAULT; - - /* Symlink or none active */ - return siter->have_active ? MAILBOX_ATTRIBUTE_SIEVE_DEFAULT : NULL; -} - -static const char * -sieve_attribute_iter_next(struct mailbox_attribute_iter *iter) -{ - struct sieve_mailbox_attribute_iter *siter = - (struct sieve_mailbox_attribute_iter *)iter; - union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(iter->box); - struct mail_user *user = iter->box->storage->user; - const char *key; - - if (siter->sieve_list != NULL) { - if ((key = sieve_attribute_iter_next_script(siter)) != NULL) { - if (user->mail_debug) { - i_debug("doveadm-sieve: Iterating Sieve mailbox attribute: %s", key); - } - return key; - } - } - return sbox->super.attribute_iter_next(siter->super); -} - -static int -sieve_attribute_iter_deinit(struct mailbox_attribute_iter *iter) -{ - struct sieve_mailbox_attribute_iter *siter = - (struct sieve_mailbox_attribute_iter *)iter; - union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(iter->box); - int ret = siter->failed ? -1 : 0; - - if (siter->super != NULL) { - if (sbox->super.attribute_iter_deinit(siter->super) < 0) - ret = -1; - } - if (siter->sieve_list != NULL) - (void)sieve_storage_list_deinit(&siter->sieve_list); - if (siter->name != NULL) - str_free(&siter->name); - i_free(siter); - return ret; -} - -static void sieve_mailbox_allocated(struct mailbox *box) -{ - struct mailbox_vfuncs *v = box->vlast; - union mailbox_module_context *sbox; - - /* attribute syncing is done via INBOX */ - if (!box->inbox_user) - return; - - sbox = p_new(box->pool, union mailbox_module_context, 1); - sbox->super = *v; - box->vlast = &sbox->super; - v->attribute_set = sieve_attribute_set; - v->attribute_get = sieve_attribute_get; - v->attribute_iter_init = sieve_attribute_iter_init; - v->attribute_iter_next = sieve_attribute_iter_next; - v->attribute_iter_deinit = sieve_attribute_iter_deinit; - MODULE_CONTEXT_SET_SELF(box, sieve_storage_module, sbox); -} - -static struct mail_storage_hooks doveadm_sieve_mail_storage_hooks = { - .mailbox_allocated = sieve_mailbox_allocated -}; - -void doveadm_sieve_plugin_init(struct module *module) -{ - mail_storage_hooks_add_forced(module, &doveadm_sieve_mail_storage_hooks); -} - -void doveadm_sieve_plugin_deinit(void) -{ - /* the hooks array is freed already */ - /*mail_storage_hooks_remove(&doveadm_sieve_mail_storage_hooks);*/ -} diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index 77a1850..c5d4174 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -70,13 +70,12 @@ static const struct sieve_callbacks lda_sieve_callbacks = { static void *lda_sieve_smtp_open (const struct sieve_script_env *senv, const char *destination, - const char *return_path, struct ostream **output_r) + const char *return_path, FILE **file_r) { struct mail_deliver_context *dctx = (struct mail_deliver_context *) senv->script_context; - return (void *)smtp_client_open - (dctx->set, destination, return_path, output_r); + return (void *) smtp_client_open(dctx->set, destination, return_path, file_r); } static bool lda_sieve_smtp_close @@ -88,8 +87,7 @@ static bool lda_sieve_smtp_close } static int lda_sieve_reject_mail -(const struct sieve_script_env *senv, const char *recipient, - const char *reason) +(const struct sieve_script_env *senv, const char *recipient, const char *reason) { struct mail_deliver_context *dctx = (struct mail_deliver_context *) senv->script_context; @@ -217,7 +215,7 @@ static int lda_sieve_multiscript_get_scripts files = array_get(&script_files, &count); for ( i = 0; i < count; i++ ) { - struct sieve_script *script = sieve_script_create_open + struct sieve_script *script = sieve_script_create (svinst, files[i], NULL, ehandler, &error); if ( script == NULL ) { @@ -533,12 +531,6 @@ static int lda_sieve_multiscript_execute /* Open */ - if ( debug ) { - sieve_sys_debug - (svinst, "opening script %d of %d from %s", i+1, count, - sieve_script_location(script)); - } - if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) { compile_error = TRUE; break; @@ -546,10 +538,9 @@ static int lda_sieve_multiscript_execute /* Execute */ - if ( debug ) { + if ( debug ) sieve_sys_debug (svinst, "executing script from %s", sieve_get_source(sbin)); - } more = sieve_multiscript_run(mscript, sbin, ehandler, rtflags, final); @@ -622,8 +613,6 @@ static int lda_sieve_deliver_mail svenv.hostname = mdctx->set->hostname; svenv.base_dir = mdctx->dest_user->set->base_dir; svenv.flags = SIEVE_FLAG_HOME_RELATIVE; - svenv.location = SIEVE_ENV_LOCATION_MDA; - svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING; svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug); @@ -654,7 +643,7 @@ static int lda_sieve_deliver_mail user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user); if ( user_location != NULL ) { - srctx.user_script = sieve_script_create_open_as + srctx.user_script = sieve_script_create_as (svinst, user_location, "main script", master_ehandler, &error); if ( srctx.user_script == NULL ) { @@ -678,7 +667,7 @@ static int lda_sieve_deliver_mail 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_open_as + srctx.main_script = sieve_script_create_as (svinst, default_location, "main script", master_ehandler, &error); if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND && @@ -879,7 +868,7 @@ static int lda_sieve_deliver_mail * Plugin interface */ -const char *sieve_plugin_version = DOVECOT_ABI_VERSION; +const char *sieve_plugin_version = DOVECOT_VERSION; const char sieve_plugin_binary_dependency[] = "lda lmtp"; void sieve_plugin_init(void) diff --git a/src/plugins/sieve-extprograms/Makefile.am b/src/plugins/sieve-extprograms/Makefile.am deleted file mode 100644 index 945ad8b..0000000 --- a/src/plugins/sieve-extprograms/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -sieve_plugindir = $(moduledir)/sieve - -sieve_plugin_LTLIBRARIES = lib90_sieve_extprograms_plugin.la - -lib90_sieve_extprograms_plugin_la_LDFLAGS = -module -avoid-version - -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib-sieve \ - -I$(top_srcdir)/src/lib-sieve/plugins/copy \ - -I$(top_srcdir)/src/lib-sieve/plugins/variables \ - $(LIBDOVECOT_INCLUDE) \ - -DPKG_RUNDIR=\""$(rundir)"\" - -commands = \ - cmd-pipe.c \ - cmd-filter.c \ - cmd-execute.c - -extensions = \ - ext-pipe.c \ - ext-filter.c \ - ext-execute.c - -lib90_sieve_extprograms_plugin_la_SOURCES = \ - script-client-local.c \ - script-client-remote.c \ - script-client.c \ - $(commands) \ - $(extensions) \ - sieve-extprograms-common.c \ - sieve-extprograms-plugin.c - -noinst_HEADERS = \ - script-client.h \ - script-client-private.h \ - sieve-extprograms-common.h \ - sieve-extprograms-plugin.h - diff --git a/src/plugins/sieve-extprograms/cmd-execute.c b/src/plugins/sieve-extprograms/cmd-execute.c deleted file mode 100644 index 0a71f36..0000000 --- a/src/plugins/sieve-extprograms/cmd-execute.c +++ /dev/null @@ -1,452 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "array.h" -#include "buffer.h" -#include "str.h" -#include "str-sanitize.h" -#include "istream.h" -#include "ostream.h" - -#include "sieve-common.h" -#include "sieve-stringlist.h" -#include "sieve-binary.h" -#include "sieve-code.h" -#include "sieve-message.h" -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-actions.h" -#include "sieve-validator.h" -#include "sieve-generator.h" -#include "sieve-interpreter.h" -#include "sieve-dump.h" -#include "sieve-result.h" - -#include "sieve-ext-variables.h" - -#include "sieve-extprograms-common.h" - -/* Execute command - * - * Syntax: - * "execute" [":input" / ":pipe"] - * [":output" ] - * [] - * - */ - -static bool cmd_execute_registered - (struct sieve_validator *valdtr, const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg); -static bool cmd_execute_generate - (const struct sieve_codegen_env *cgenv, - struct sieve_command *ctx); - -const struct sieve_command_def cmd_execute = { - "execute", - SCT_HYBRID, - -1, /* We check positional arguments ourselves */ - 0, FALSE, FALSE, - cmd_execute_registered, - NULL, - sieve_extprogram_command_validate, - NULL, - cmd_execute_generate, - NULL, -}; - -/* - * Tagged arguments - */ - -static bool cmd_execute_validate_input_tag - (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, - struct sieve_command *cmd); -static bool cmd_execute_generate_input_tag -(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, - struct sieve_command *cmd); - -static bool cmd_execute_validate_output_tag - (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, - struct sieve_command *cmd); - -static const struct sieve_argument_def execute_input_tag = { - "input", - NULL, - cmd_execute_validate_input_tag, - NULL, NULL, - cmd_execute_generate_input_tag -}; - -static const struct sieve_argument_def execute_pipe_tag = { - "pipe", - NULL, - cmd_execute_validate_input_tag, - NULL, NULL, - cmd_execute_generate_input_tag -}; - -static const struct sieve_argument_def execute_output_tag = { - "output", - NULL, - cmd_execute_validate_output_tag, - NULL, NULL, NULL -}; - - -/* - * Execute operation - */ - -static bool cmd_execute_operation_dump - (const struct sieve_dumptime_env *denv, sieve_size_t *address); -static int cmd_execute_operation_execute - (const struct sieve_runtime_env *renv, sieve_size_t *address); - -const struct sieve_operation_def cmd_execute_operation = { - "EXECUTE", &execute_extension, - 0, - cmd_execute_operation_dump, - cmd_execute_operation_execute -}; - -/* Codes for optional operands */ - -enum cmd_execute_optional { - OPT_END, - OPT_INPUT, - OPT_OUTPUT -}; - -/* - * Tag validation - */ - -static bool cmd_execute_validate_input_tag -(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, - struct sieve_command *cmd) -{ - struct sieve_ast_argument *tag = *arg; - - if ( (bool) cmd->data ) { - sieve_argument_validate_error(valdtr, *arg, - "multiple :input or :pipe arguments specified for the %s %s", - sieve_command_identifier(cmd), sieve_command_type_name(cmd)); - return FALSE; - } - - cmd->data = (void *) TRUE; - - /* Skip tag */ - *arg = sieve_ast_argument_next(*arg); - - if ( sieve_argument_is(tag, execute_input_tag) ) { - /* Check syntax: - * :input - */ - if ( !sieve_validate_tag_parameter - (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, FALSE) ) { - return FALSE; - } - - /* Assign tag parameters */ - tag->parameters = *arg; - *arg = sieve_ast_arguments_detach(*arg,1); - } - - return TRUE; -} - -static bool cmd_execute_validate_output_tag -(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, - struct sieve_command *cmd) -{ - struct sieve_ast_argument *tag = *arg; - struct sieve_extprograms_config *ext_config = - (struct sieve_extprograms_config *) cmd->ext->context; - - if ( ext_config == NULL || ext_config->var_ext == NULL || - !sieve_ext_variables_is_active(ext_config->var_ext, valdtr) ) { - sieve_argument_validate_error(valdtr,*arg, - "the %s %s only allows for the specification of an " - ":output argument when the variables extension is active", - sieve_command_identifier(cmd), sieve_command_type_name(cmd)); - return FALSE; - } - - /* Detach the tag itself */ - *arg = sieve_ast_arguments_detach(*arg, 1); - - if ( !sieve_variable_argument_activate - (ext_config->var_ext, valdtr, cmd, *arg, TRUE) ) - return FALSE; - - (*arg)->argument->id_code = tag->argument->id_code; - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - - return TRUE; -} - -/* - * Command registration - */ - -static bool cmd_execute_registered -(struct sieve_validator *valdtr, const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg) -{ - sieve_validator_register_tag - (valdtr, cmd_reg, ext, &execute_input_tag, OPT_INPUT); - sieve_validator_register_tag - (valdtr, cmd_reg, ext, &execute_pipe_tag, OPT_INPUT); - sieve_validator_register_tag - (valdtr, cmd_reg, ext, &execute_output_tag, OPT_OUTPUT); - return TRUE; -} - -/* - * Code generation - */ - -static bool cmd_execute_generate_input_tag -(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, - struct sieve_command *cmd) -{ - if ( arg->parameters == NULL ) { - sieve_opr_omitted_emit(cgenv->sblock); - return TRUE; - } - - return sieve_generate_argument_parameters(cgenv, cmd, arg); -} - -static bool cmd_execute_generate -(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) -{ - sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_execute_operation); - - /* Emit is_test flag */ - sieve_binary_emit_byte(cgenv->sblock, ( cmd->ast_node->type == SAT_TEST )); - - /* Generate arguments */ - if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) - return FALSE; - - /* Emit a placeholder when the argument is missing */ - if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) - sieve_opr_omitted_emit(cgenv->sblock); - - return TRUE; -} - -/* - * Code dump - */ - -static bool cmd_execute_operation_dump -(const struct sieve_dumptime_env *denv, sieve_size_t *address) -{ - int opt_code = 0; - unsigned int is_test = 0; - - /* Read is_test flag */ - if ( !sieve_binary_read_byte(denv->sblock, address, &is_test) ) - return FALSE; - - sieve_code_dumpf(denv, "EXECUTE (%s)", (is_test ? "test" : "command")); - sieve_code_descend(denv); - - /* Dump optional operands */ - for (;;) { - int opt; - bool opok = TRUE; - - if ( (opt=sieve_action_opr_optional_dump(denv, address, &opt_code)) < 0 ) - return FALSE; - - if ( opt == 0 ) break; - - switch ( opt_code ) { - case OPT_INPUT: - opok = sieve_opr_string_dump_ex(denv, address, "input", "PIPE"); - break; - case OPT_OUTPUT: - opok = sieve_opr_string_dump(denv, address, "output"); - break; - default: - return FALSE; - } - - if ( !opok ) return FALSE; - } - - if ( !sieve_opr_string_dump(denv, address, "program-name") ) - return FALSE; - - return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); -} - -/* - * Code execution - */ - -static int cmd_execute_operation_execute -(const struct sieve_runtime_env *renv, sieve_size_t *address) -{ - const struct sieve_extension *this_ext = renv->oprtn->ext; - struct sieve_side_effects_list *slist = NULL; - int opt_code = 0; - unsigned int is_test = 0; - struct sieve_stringlist *args_list = NULL; - string_t *pname = NULL, *input = NULL; - struct sieve_variable_storage *var_storage = NULL; - unsigned int var_index; - bool have_input = FALSE; - const char *program_name = NULL; - const char *const *args = NULL; - enum sieve_error error = SIEVE_ERROR_NONE; - buffer_t *outbuf = NULL; - struct sieve_extprogram *sprog = NULL; - int ret; - - /* - * Read operands - */ - - /* The is_test flag */ - if ( !sieve_binary_read_byte(renv->sblock, address, &is_test) ) { - sieve_runtime_trace_error(renv, "invalid is_test flag"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - /* Optional operands */ - - for (;;) { - int opt; - - if ( (opt=sieve_action_opr_optional_read - (renv, address, &opt_code, &ret, &slist)) < 0 ) - return ret; - - if ( opt == 0 ) break; - - switch ( opt_code ) { - case OPT_INPUT: - if ( (ret=sieve_opr_string_read_ex - (renv, address, "input", TRUE, &input, NULL)) <= 0 ) - return ret; - have_input = TRUE; - break; - case OPT_OUTPUT: - if ( (ret=sieve_variable_operand_read - (renv, address, "output", &var_storage, &var_index)) <= 0 ) - return ret; - break; - default: - sieve_runtime_trace_error(renv, "unknown optional operand"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - if ( ret <= 0 ) return ret; - } - - /* Fixed operands */ - - if ( (ret=sieve_extprogram_command_read_operands - (renv, address, &pname, &args_list)) <= 0 ) - return ret; - - program_name = str_c(pname); - if ( args_list != NULL && - sieve_stringlist_read_all(args_list, pool_datastack_create(), &args) < 0 ) { - sieve_runtime_trace_error(renv, "failed to read args operand"); - return args_list->exec_status; - } - - /* - * Perform operation - */ - - /* Trace */ - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "execute action"); - sieve_runtime_trace_descend(renv); - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "execute program `%s'", str_sanitize(program_name, 128)); - - sprog = sieve_extprogram_create - (this_ext, renv->scriptenv, renv->msgdata, "execute", program_name, args, - &error); - if ( sprog != NULL ) { - if ( var_storage != NULL ) { - // FIXME: limit output size - struct ostream *outdata; - - outbuf = buffer_create_dynamic(pool_datastack_create(), 1024); - outdata = o_stream_create_buffer(outbuf); - sieve_extprogram_set_output(sprog, outdata); - o_stream_unref(&outdata); - } - - if ( input == NULL && have_input ) { - ret = sieve_extprogram_set_input_mail - (sprog, sieve_message_get_mail(renv->msgctx)); - } else if ( input != NULL ) { - struct istream *indata = - i_stream_create_from_data(str_data(input), str_len(input)); - sieve_extprogram_set_input(sprog, indata); - i_stream_unref(&indata); - ret = 1; - } - - if ( ret >= 0 ) - ret = sieve_extprogram_run(sprog); - sieve_extprogram_destroy(&sprog); - } else { - ret = -1; - } - - if ( ret > 0 ) { - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "executed program successfully"); - - if ( var_storage != NULL ) { - string_t *var; - - if ( sieve_variable_get_modifiable(var_storage, var_index, &var) ) { - str_truncate(var, 0); - str_append_str(var, outbuf); - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "assigned output variable"); - } // FIXME: handle failure - } - - } else if ( ret < 0 ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) { - sieve_runtime_error(renv, NULL, - "execute action: program `%s' not found", - str_sanitize(program_name, 80)); - } else { - sieve_extprogram_exec_error(renv->ehandler, - sieve_runtime_get_full_command_location(renv), - "execute action: failed to execute to program `%s'", - str_sanitize(program_name, 80)); - } - } else { - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "execute action: program indicated false result"); - } - - if ( outbuf != NULL ) - buffer_free(&outbuf); - - if ( is_test ) - sieve_interpreter_set_test_result(renv->interp, ( ret > 0 )); - - return SIEVE_EXEC_OK; -} - diff --git a/src/plugins/sieve-extprograms/cmd-filter.c b/src/plugins/sieve-extprograms/cmd-filter.c deleted file mode 100644 index c57935b..0000000 --- a/src/plugins/sieve-extprograms/cmd-filter.c +++ /dev/null @@ -1,283 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "array.h" -#include "buffer.h" -#include "str.h" -#include "str-sanitize.h" -#include "istream.h" -#include "ostream.h" -#include "safe-mkstemp.h" -#include "mail-user.h" - -#include "sieve-common.h" -#include "sieve-stringlist.h" -#include "sieve-binary.h" -#include "sieve-code.h" -#include "sieve-message.h" -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-actions.h" -#include "sieve-validator.h" -#include "sieve-generator.h" -#include "sieve-interpreter.h" -#include "sieve-dump.h" -#include "sieve-result.h" - -#include "sieve-ext-variables.h" - -#include "sieve-extprograms-common.h" - -/* Filter command - * - * Syntax: - * "filter" [] - * - */ - -static bool cmd_filter_generate - (const struct sieve_codegen_env *cgenv, - struct sieve_command *ctx); - -const struct sieve_command_def cmd_filter = { - "filter", - SCT_HYBRID, - -1, /* We check positional arguments ourselves */ - 0, FALSE, FALSE, - NULL, NULL, - sieve_extprogram_command_validate, - NULL, - cmd_filter_generate, - NULL, -}; - -/* - * Filter operation - */ - -static bool cmd_filter_operation_dump - (const struct sieve_dumptime_env *denv, sieve_size_t *address); -static int cmd_filter_operation_execute - (const struct sieve_runtime_env *renv, sieve_size_t *address); - -const struct sieve_operation_def cmd_filter_operation = { - "FILTER", &filter_extension, - 0, - cmd_filter_operation_dump, - cmd_filter_operation_execute -}; - -/* - * Code generation - */ - -static bool cmd_filter_generate -(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) -{ - sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_filter_operation); - - /* Emit is_test flag */ - sieve_binary_emit_byte(cgenv->sblock, ( cmd->ast_node->type == SAT_TEST )); - - /* Generate arguments */ - if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) - return FALSE; - - /* Emit a placeholder when the argument is missing */ - if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) - sieve_opr_omitted_emit(cgenv->sblock); - - return TRUE; -} - -/* - * Code dump - */ - -static bool cmd_filter_operation_dump -(const struct sieve_dumptime_env *denv, sieve_size_t *address) -{ - unsigned int is_test = 0; - - /* Read is_test flag */ - if ( !sieve_binary_read_byte(denv->sblock, address, &is_test) ) - return FALSE; - - sieve_code_dumpf(denv, "FILTER (%s)", (is_test ? "test" : "command")); - sieve_code_descend(denv); - - /* Dump optional operands */ - if ( sieve_action_opr_optional_dump(denv, address, NULL) != 0 ) - return FALSE; - - if ( !sieve_opr_string_dump(denv, address, "program-name") ) - return FALSE; - - return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); -} - -/* - * Code execution - */ - -static int cmd_filter_get_tempfile -(const struct sieve_runtime_env *renv) -{ - struct sieve_instance *svinst = renv->svinst; - struct mail_user *mail_user = renv->scriptenv->user; - string_t *path; - int fd; - - path = t_str_new(128); - mail_user_set_get_temp_prefix(path, mail_user->set); - fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); - if (fd == -1) { - sieve_sys_error(svinst, "filter action: " - "safe_mkstemp(%s) failed: %m", str_c(path)); - return -1; - } - - /* We just want the fd, unlink it */ - if (unlink(str_c(path)) < 0) { - /* Shouldn't happen.. */ - sieve_sys_error(svinst, "filter action: " - "unlink(%s) failed: %m", str_c(path)); - if ( close(fd) < 0 ) { - sieve_sys_error(svinst, "filter action: " - "close(%s) failed after error: %m", str_c(path)); - } - return -1; - } - - return fd; -} - -static int cmd_filter_operation_execute -(const struct sieve_runtime_env *renv, sieve_size_t *address) -{ - const struct sieve_extension *this_ext = renv->oprtn->ext; - unsigned int is_test = 0; - struct sieve_stringlist *args_list = NULL; - enum sieve_error error = SIEVE_ERROR_NONE; - string_t *pname = NULL; - const char *program_name = NULL; - const char *const *args = NULL; - int tmp_fd = -1; - int ret; - - /* - * Read operands - */ - - /* The is_test flag */ - - if ( !sieve_binary_read_byte(renv->sblock, address, &is_test) ) { - sieve_runtime_trace_error(renv, "invalid is_test flag"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - /* Optional operands */ - - if ( sieve_action_opr_optional_read(renv, address, NULL, &ret, NULL) != 0 ) - return ret; - - /* Fixed operands */ - - if ( (ret=sieve_extprogram_command_read_operands - (renv, address, &pname, &args_list)) <= 0 ) - return ret; - - program_name = str_c(pname); - if ( args_list != NULL && - sieve_stringlist_read_all(args_list, pool_datastack_create(), &args) < 0 ) { - sieve_runtime_trace_error(renv, "failed to read args operand"); - return args_list->exec_status; - } - - /* - * Perform operation - */ - - /* Trace */ - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "filter action"); - sieve_runtime_trace_descend(renv); - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "execute program `%s'", str_sanitize(program_name, 128)); - - ret = 1; - if ( (tmp_fd=cmd_filter_get_tempfile(renv)) < 0 ) { - ret = -1; - } - - if ( ret > 0 ) { - struct sieve_extprogram *sprog = sieve_extprogram_create - (this_ext, renv->scriptenv, renv->msgdata, "filter", program_name, args, - &error); - - if ( sprog != NULL && sieve_extprogram_set_input_mail - (sprog, sieve_message_get_mail(renv->msgctx)) >= 0 ) { - struct ostream *outdata = - o_stream_create_fd(tmp_fd, 0, FALSE); - sieve_extprogram_set_output(sprog, outdata); - o_stream_unref(&outdata); - - ret = sieve_extprogram_run(sprog); - } else { - ret = -1; - } - - if ( sprog != NULL ) - sieve_extprogram_destroy(&sprog); - } - - if ( ret > 0 ) { - struct istream *newmsg; - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "executed program successfully"); - - newmsg = i_stream_create_fd(tmp_fd, (size_t)-1, TRUE); - - if ( (ret=sieve_message_substitute(renv->msgctx, newmsg)) >= 0 ) { - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "changed message"); - } else { - sieve_runtime_critical(renv, NULL, "filter action", - "filter action: failed to substitute message"); - } - - i_stream_unref(&newmsg); - - } else { - if ( tmp_fd >= 0 ) { - if ( close(tmp_fd) < 0 ) { - sieve_sys_error - (renv->svinst, "filter action: close(temp_file) failed: %m"); - } - } - - if ( ret < 0 ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) { - sieve_runtime_error(renv, NULL, - "filter action: program `%s' not found", - str_sanitize(program_name, 80)); - } else { - sieve_extprogram_exec_error(renv->ehandler, - sieve_runtime_get_full_command_location(renv), - "filter action: failed to execute to program `%s'", - str_sanitize(program_name, 80)); - } - } else { - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, - "filter action: program indicated false result"); - } - } - - if ( is_test ) - sieve_interpreter_set_test_result(renv->interp, ( ret > 0 )); - - return SIEVE_EXEC_OK; -} - diff --git a/src/plugins/sieve-extprograms/cmd-pipe.c b/src/plugins/sieve-extprograms/cmd-pipe.c deleted file mode 100644 index 94f05ce..0000000 --- a/src/plugins/sieve-extprograms/cmd-pipe.c +++ /dev/null @@ -1,388 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "array.h" -#include "str.h" -#include "str-sanitize.h" - -#include "sieve-common.h" -#include "sieve-stringlist.h" -#include "sieve-code.h" -#include "sieve-message.h" -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-actions.h" -#include "sieve-validator.h" -#include "sieve-generator.h" -#include "sieve-interpreter.h" -#include "sieve-dump.h" -#include "sieve-result.h" - -#include "sieve-extprograms-common.h" - -/* Pipe command - * - * Syntax: - * pipe [":copy"] [":try"] [] - * - */ - -static bool cmd_pipe_registered - (struct sieve_validator *valdtr, const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg); -static bool cmd_pipe_generate - (const struct sieve_codegen_env *cgenv, - struct sieve_command *ctx); - -const struct sieve_command_def cmd_pipe = { - "pipe", - SCT_COMMAND, - -1, /* We check positional arguments ourselves */ - 0, FALSE, FALSE, - cmd_pipe_registered, - NULL, - sieve_extprogram_command_validate, - NULL, - cmd_pipe_generate, - NULL, -}; - -/* - * Tagged arguments - */ - -static const struct sieve_argument_def pipe_try_tag = { - "try", - NULL, NULL, NULL, NULL, NULL -}; - -/* - * Pipe operation - */ - -static bool cmd_pipe_operation_dump - (const struct sieve_dumptime_env *denv, sieve_size_t *address); -static int cmd_pipe_operation_execute - (const struct sieve_runtime_env *renv, sieve_size_t *address); - -const struct sieve_operation_def cmd_pipe_operation = { - "PIPE", &pipe_extension, 0, - cmd_pipe_operation_dump, - cmd_pipe_operation_execute -}; - -/* Codes for optional operands */ - -enum cmd_pipe_optional { - OPT_END, - OPT_TRY -}; - -/* - * Pipe action - */ - -/* Forward declarations */ - -static int act_pipe_check_duplicate - (const struct sieve_runtime_env *renv, - const struct sieve_action *act, - const struct sieve_action *act_other); -static void act_pipe_print - (const struct sieve_action *action, - const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_pipe_commit - (const struct sieve_action *action, - const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); - -/* Action object */ - -const struct sieve_action_def act_pipe = { - "pipe", - SIEVE_ACTFLAG_TRIES_DELIVER, - NULL, - act_pipe_check_duplicate, - NULL, - act_pipe_print, - NULL, NULL, - act_pipe_commit, - NULL -}; - -/* Action context information */ - -struct ext_pipe_action { - const char *program_name; - const char * const *args; - bool try; -}; - -/* - * Command registration - */ - -static bool cmd_pipe_registered -(struct sieve_validator *valdtr, const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg) -{ - sieve_validator_register_tag - (valdtr, cmd_reg, ext, &pipe_try_tag, OPT_TRY); - - return TRUE; -} - -/* - * Code generation - */ - -static bool cmd_pipe_generate -(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) -{ - sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_pipe_operation); - - /* Generate arguments */ - if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) - return FALSE; - - /* Emit a placeholder when the argument is missing */ - if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) - sieve_opr_omitted_emit(cgenv->sblock); - - return TRUE; -} - -/* - * Code dump - */ - -static bool cmd_pipe_operation_dump -(const struct sieve_dumptime_env *denv, sieve_size_t *address) -{ - int opt_code = 0; - - sieve_code_dumpf(denv, "PIPE"); - sieve_code_descend(denv); - - /* Dump optional operands */ - for (;;) { - int opt; - bool opok = TRUE; - - if ( (opt=sieve_action_opr_optional_dump(denv, address, &opt_code)) < 0 ) - return FALSE; - - if ( opt == 0 ) break; - - switch ( opt_code ) { - case OPT_TRY: - sieve_code_dumpf(denv, "try"); - break; - default: - return FALSE; - } - - if ( !opok ) return FALSE; - } - - if ( !sieve_opr_string_dump(denv, address, "program-name") ) - return FALSE; - - return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); -} - -/* - * Code execution - */ - -static int cmd_pipe_operation_execute -(const struct sieve_runtime_env *renv, sieve_size_t *address) -{ - const struct sieve_extension *this_ext = renv->oprtn->ext; - struct sieve_side_effects_list *slist = NULL; - struct ext_pipe_action *act; - pool_t pool; - int opt_code = 0; - struct sieve_stringlist *args_list = NULL; - string_t *pname = NULL; - bool try = FALSE; - int ret; - - /* - * Read operands - */ - - /* Optional operands */ - - for (;;) { - int opt; - - if ( (opt=sieve_action_opr_optional_read - (renv, address, &opt_code, &ret, &slist)) < 0 ) - return ret; - - if ( opt == 0 ) break; - - switch ( opt_code ) { - case OPT_TRY: - try = TRUE; - ret = SIEVE_EXEC_OK; - break; - default: - sieve_runtime_trace_error(renv, "unknown optional operand"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - if ( ret <= 0 ) return ret; - } - - /* Fixed operands */ - - if ( (ret=sieve_extprogram_command_read_operands - (renv, address, &pname, &args_list)) <= 0 ) - return ret; - - /* - * Perform operation - */ - - /* Trace */ - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "pipe action"); - - /* Compose action */ - - pool = sieve_result_pool(renv->result); - act = p_new(pool, struct ext_pipe_action, 1); - - if ( args_list != NULL && - sieve_stringlist_read_all(args_list, pool, &act->args) < 0 ) { - sieve_runtime_trace_error(renv, "failed to read args operand"); - return args_list->exec_status; - } - - act->program_name = p_strdup(pool, str_c(pname)); - act->try = try; - - if ( sieve_result_add_action - (renv, this_ext, &act_pipe, slist, (void *) act, 0, TRUE) < 0 ) { - return SIEVE_EXEC_FAILURE; - } - - return SIEVE_EXEC_OK; -} - -/* - * Action - */ - -/* Runtime verification */ - -static int act_pipe_check_duplicate -(const struct sieve_runtime_env *renv ATTR_UNUSED, - const struct sieve_action *act, - const struct sieve_action *act_other) -{ - struct ext_pipe_action *new_act, *old_act; - - if ( act->context == NULL || act_other->context == NULL ) - return 0; - - new_act = (struct ext_pipe_action *) act->context; - old_act = (struct ext_pipe_action *) act_other->context; - - if ( strcmp(new_act->program_name, old_act->program_name) == 0 ) { - sieve_runtime_error(renv, act->location, - "duplicate pipe \"%s\" action not allowed " - "(previously triggered one was here: %s)", - new_act->program_name, act_other->location); - return -1; - } - - return 0; -} - -/* Result printing */ - -static void act_pipe_print -(const struct sieve_action *action, const struct sieve_result_print_env *rpenv, - bool *keep ATTR_UNUSED) -{ - const struct ext_pipe_action *act = - (const struct ext_pipe_action *) action->context; - - sieve_result_action_printf - ( rpenv, "pipe message to external program '%s':", act->program_name); - - /* Print main method parameters */ - - sieve_result_printf - ( rpenv, " => try : %s\n", (act->try ? "yes" : "no") ); - - /* FIXME: print args */ - - /* Finish output with an empty line */ - - sieve_result_printf(rpenv, "\n"); -} - -/* Result execution */ - -static bool act_pipe_commit -(const struct sieve_action *action, const struct sieve_action_exec_env *aenv, - void *tr_context ATTR_UNUSED, bool *keep) -{ - const struct ext_pipe_action *act = - (const struct ext_pipe_action *) action->context; - enum sieve_error error = SIEVE_ERROR_NONE; - struct mail *mail = ( action->mail != NULL ? - action->mail : sieve_message_get_mail(aenv->msgctx) ); - struct sieve_extprogram *sprog; - int ret; - - sprog = sieve_extprogram_create - (action->ext, aenv->scriptenv, aenv->msgdata, "pipe", - act->program_name, act->args, &error); - if ( sprog != NULL && sieve_extprogram_set_input_mail(sprog, mail) >= 0 ) { - ret = sieve_extprogram_run(sprog); - } else { - ret = -1; - } - if ( sprog != NULL ) - sieve_extprogram_destroy(&sprog); - - if ( ret > 0 ) { - sieve_result_global_log(aenv, "pipe action: " - "piped message to program `%s'", str_sanitize(act->program_name, 128)); - } else { - if ( ret < 0 ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) { - sieve_result_error(aenv, "pipe action: " - "failed to pipe message to program: program `%s' not found", - str_sanitize(act->program_name, 80)); - } else { - sieve_extprogram_exec_error(aenv->ehandler, NULL, - "pipe action: failed to pipe message to program `%s'", - str_sanitize(act->program_name, 80)); - } - } else { - sieve_extprogram_exec_error(aenv->ehandler, NULL, - "pipe action: failed to execute to program `%s'", - str_sanitize(act->program_name, 80)); - } - - if ( act->try ) return TRUE; - - return FALSE; - } - - *keep = FALSE; - return TRUE; -} - - - - - - - diff --git a/src/plugins/sieve-extprograms/ext-execute.c b/src/plugins/sieve-extprograms/ext-execute.c deleted file mode 100644 index 057ac18..0000000 --- a/src/plugins/sieve-extprograms/ext-execute.c +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -/* Extension vnd.dovecot.execute - * ----------------------------- - * - * Authors: Stephan Bosch - * Specification: spec-bosch-sieve-extprograms - * Implementation: full - * Status: experimental - * - */ - -#include "lib.h" - -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-binary.h" - -#include "sieve-validator.h" -#include "sieve-interpreter.h" - -#include "sieve-ext-copy.h" - -#include "sieve-extprograms-common.h" - -/* - * Extension - */ - -static bool ext_execute_load(const struct sieve_extension *ext, void **context); -static void ext_execute_unload(const struct sieve_extension *ext); -static bool ext_execute_validator_load - (const struct sieve_extension *ext, struct sieve_validator *valdtr); - -const struct sieve_extension_def execute_extension = { - .name = "vnd.dovecot.execute", - .load = ext_execute_load, - .unload = ext_execute_unload, - .validator_load = ext_execute_validator_load, - SIEVE_EXT_DEFINE_OPERATION(cmd_execute_operation) -}; - -/* - * Context - */ - -static bool ext_execute_load(const struct sieve_extension *ext, void **context) -{ - if ( *context != NULL ) { - ext_execute_unload(ext); - *context = NULL; - } - - *context = (void *)sieve_extprograms_config_init(ext); - return TRUE; -} - -static void ext_execute_unload(const struct sieve_extension *ext) -{ - struct sieve_extprograms_config *ext_config = - (struct sieve_extprograms_config *)ext->context; - - if ( ext_config == NULL ) return; - - sieve_extprograms_config_deinit(&ext_config); -} - -/* - * Validation - */ - -static bool ext_execute_validator_load -(const struct sieve_extension *ext, struct sieve_validator *valdtr) -{ - /* Register commands */ - sieve_validator_register_command(valdtr, ext, &cmd_execute); - - return TRUE; -} diff --git a/src/plugins/sieve-extprograms/ext-filter.c b/src/plugins/sieve-extprograms/ext-filter.c deleted file mode 100644 index 572dd31..0000000 --- a/src/plugins/sieve-extprograms/ext-filter.c +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -/* Extension vnd.dovecot.filter - * ----------------------------- - * - * Authors: Stephan Bosch - * Specification: spec-bosch-sieve-extprograms - * Implementation: full - * Status: experimental - * - */ - -#include "lib.h" - -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-binary.h" - -#include "sieve-validator.h" -#include "sieve-interpreter.h" - -#include "sieve-ext-copy.h" - -#include "sieve-extprograms-common.h" - -/* - * Extension - */ - -static bool ext_filter_load(const struct sieve_extension *ext, void **context); -static void ext_filter_unload(const struct sieve_extension *ext); -static bool ext_filter_validator_load - (const struct sieve_extension *ext, struct sieve_validator *valdtr); - -const struct sieve_extension_def filter_extension = { - .name = "vnd.dovecot.filter", - .load = ext_filter_load, - .unload = ext_filter_unload, - .validator_load = ext_filter_validator_load, - SIEVE_EXT_DEFINE_OPERATION(cmd_filter_operation), -}; - -/* - * Context - */ - -static bool ext_filter_load(const struct sieve_extension *ext, void **context) -{ - if ( *context != NULL ) { - ext_filter_unload(ext); - *context = NULL; - } - - *context = (void *)sieve_extprograms_config_init(ext); - return TRUE; -} - -static void ext_filter_unload(const struct sieve_extension *ext) -{ - struct sieve_extprograms_config *ext_config = - (struct sieve_extprograms_config *)ext->context; - - if ( ext_config == NULL ) return; - - sieve_extprograms_config_deinit(&ext_config); -} - -/* - * Validation - */ - -static bool ext_filter_validator_load -(const struct sieve_extension *ext, struct sieve_validator *valdtr) -{ - /* Register commands */ - sieve_validator_register_command(valdtr, ext, &cmd_filter); - - return TRUE; -} diff --git a/src/plugins/sieve-extprograms/ext-pipe.c b/src/plugins/sieve-extprograms/ext-pipe.c deleted file mode 100644 index aa9203f..0000000 --- a/src/plugins/sieve-extprograms/ext-pipe.c +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -/* Extension vnd.dovecot.pipe - * ----------------------------- - * - * Authors: Stephan Bosch - * Specification: spec-bosch-sieve-extprograms - * Implementation: full - * Status: experimental - * - */ - -#include "lib.h" - -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-binary.h" - -#include "sieve-validator.h" -#include "sieve-interpreter.h" - -#include "sieve-ext-copy.h" - -#include "sieve-extprograms-common.h" - -/* - * Extension - */ - -static bool ext_pipe_load(const struct sieve_extension *ext, void **context); -static void ext_pipe_unload(const struct sieve_extension *ext); -static bool ext_pipe_validator_load - (const struct sieve_extension *ext, struct sieve_validator *valdtr); - -const struct sieve_extension_def pipe_extension = { - .name = "vnd.dovecot.pipe", - .load = ext_pipe_load, - .unload = ext_pipe_unload, - .validator_load = ext_pipe_validator_load, - SIEVE_EXT_DEFINE_OPERATION(cmd_pipe_operation), -}; - -/* - * Context - */ - -static bool ext_pipe_load(const struct sieve_extension *ext, void **context) -{ - if ( *context != NULL ) { - ext_pipe_unload(ext); - *context = NULL; - } - - *context = (void *)sieve_extprograms_config_init(ext); - return TRUE; -} - -static void ext_pipe_unload(const struct sieve_extension *ext) -{ - struct sieve_extprograms_config *ext_config = - (struct sieve_extprograms_config *)ext->context; - - if ( ext_config == NULL ) return; - - sieve_extprograms_config_deinit(&ext_config); -} - -/* - * Validation - */ - -static bool ext_pipe_validator_extension_validate - (const struct sieve_extension *ext, struct sieve_validator *valdtr, - void *context, struct sieve_ast_argument *require_arg); - -const struct sieve_validator_extension pipe_validator_extension = { - &pipe_extension, - ext_pipe_validator_extension_validate, - NULL -}; - -static bool ext_pipe_validator_load -(const struct sieve_extension *ext, struct sieve_validator *valdtr) -{ - /* Register commands */ - sieve_validator_register_command(valdtr, ext, &cmd_pipe); - - /* Register extension to validator */ - sieve_validator_extension_register - (valdtr, ext, &pipe_validator_extension, NULL); - - return TRUE; -} - -static bool ext_pipe_validator_extension_validate -(const struct sieve_extension *ext, struct sieve_validator *valdtr, - void *context ATTR_UNUSED, struct sieve_ast_argument *require_arg ATTR_UNUSED) -{ - struct sieve_extprograms_config *ext_config = - (struct sieve_extprograms_config *) ext->context; - - if ( ext_config != NULL && ext_config->copy_ext != NULL ) { - /* Register :copy command tag */ - sieve_ext_copy_register_tag - (valdtr, ext_config->copy_ext, cmd_pipe.identifier); - } - return TRUE; -} - - diff --git a/src/plugins/sieve-extprograms/script-client-local.c b/src/plugins/sieve-extprograms/script-client-local.c deleted file mode 100644 index 6d5bb6c..0000000 --- a/src/plugins/sieve-extprograms/script-client-local.c +++ /dev/null @@ -1,300 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "lib-signals.h" -#include "env-util.h" -#include "execv-const.h" -#include "array.h" -#include "net.h" -#include "istream.h" -#include "ostream.h" - -#include "script-client-private.h" - -#include -#include -#include -#include -#include -#include - - -struct script_client_local { - struct script_client client; - - pid_t pid; -}; - -static void exec_child -(const char *bin_path, const char *const *args, const char *const *envs, - int in_fd, int out_fd) -{ - ARRAY_TYPE(const_string) exec_args; - - if ( in_fd < 0 ) { - in_fd = open("/dev/null", O_RDONLY); - - if ( in_fd == -1 ) - i_fatal("open(/dev/null) failed: %m"); - } - - if ( out_fd < 0 ) { - out_fd = open("/dev/null", O_WRONLY); - - if ( out_fd == -1 ) - i_fatal("open(/dev/null) failed: %m"); - } - - if ( dup2(in_fd, STDIN_FILENO) < 0 ) - i_fatal("dup2(stdin) failed: %m"); - if ( dup2(out_fd, STDOUT_FILENO) < 0 ) - i_fatal("dup2(stdout) failed: %m"); - - /* Close all fds */ - if ( close(in_fd) < 0 ) - i_error("close(in_fd) failed: %m"); - if ( (out_fd != in_fd) && close(out_fd) < 0 ) - i_error("close(out_fd) failed: %m"); - - t_array_init(&exec_args, 16); - array_append(&exec_args, &bin_path, 1); - if ( args != NULL ) { - for (; *args != NULL; args++) - array_append(&exec_args, args, 1); - } - (void)array_append_space(&exec_args); - - env_clean(); - if ( envs != NULL ) { - for (; *envs != NULL; envs++) - env_put(*envs); - } - - args = array_idx(&exec_args, 0); - execvp_const(args[0], args); -} - -static int script_client_local_connect -(struct script_client *sclient) -{ - struct script_client_local *slclient = - (struct script_client_local *) sclient; - int fd[2] = { -1, -1 }; - - if ( sclient->input != NULL || sclient->output != NULL ) { - if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) { - i_error("socketpair() failed: %m"); - return -1; - } - } - - if ( (slclient->pid = fork()) == (pid_t)-1 ) { - i_error("fork() failed: %m"); - if ( fd[0] >= 0 && close(fd[0]) < 0 ) { - i_error("close(pipe_fd[0]) failed: %m"); - } - if ( fd[1] >= 0 && close(fd[1]) < 0 ) { - i_error("close(pipe_fd[1]) failed: %m"); - } - return -1; - } - - if ( slclient->pid == 0 ) { - unsigned int count; - const char *const *envs = NULL; - - /* child */ - if ( fd[1] >= 0 && close(fd[1]) < 0 ) { - i_error("close(pipe_fd[1]) failed: %m"); - } - - if ( array_is_created(&sclient->envs) ) - envs = array_get(&sclient->envs, &count); - - exec_child(sclient->path, sclient->args, envs, - ( sclient->input != NULL ? fd[0] : -1 ), - ( sclient->output != NULL ? fd[0] : -1 )); - i_unreached(); - } - - /* parent */ - if ( fd[0] >= 0 && close(fd[0]) < 0 ) { - i_error("close(pipe_fd[0]) failed: %m"); - } - - if ( fd[1] >= 0 ) { - net_set_nonblock(fd[1], TRUE); - sclient->fd_in = ( sclient->output != NULL ? fd[1] : -1 ); - sclient->fd_out = ( sclient->input != NULL ? fd[1] : -1 ); - } - script_client_init_streams(sclient); - return script_client_script_connected(sclient); -} - -static int script_client_local_close_output(struct script_client *sclient) -{ - /* Shutdown output; script stdin will get EOF */ - if ( sclient->fd_out >= 0 && shutdown(sclient->fd_out, SHUT_WR) < 0 ) { - i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path); - return -1; - } - sclient->fd_out = -1; - return 1; -} - -static int script_client_local_disconnect -(struct script_client *sclient, bool force) -{ - struct script_client_local *slclient = - (struct script_client_local *) sclient; - pid_t pid = slclient->pid; - time_t runtime, timeout = 0; - int status; - - i_assert( pid >= 0 ); - slclient->pid = -1; - - /* Calculate timeout */ - runtime = ioloop_time - sclient->start_time; - if ( !force && sclient->set->input_idle_timeout_secs > 0 && - runtime < sclient->set->input_idle_timeout_secs ) - timeout = sclient->set->input_idle_timeout_secs - runtime; - - if ( sclient->debug ) { - i_debug("waiting for program `%s' to finish after %llu seconds", - sclient->path, (unsigned long long int)runtime); - } - - /* Wait for child to exit */ - force = force || - (timeout == 0 && sclient->set->input_idle_timeout_secs > 0); - if ( !force ) - alarm(timeout); - if ( force || waitpid(pid, &status, 0) < 0 ) { - if ( !force && errno != EINTR ) { - i_error("waitpid(%s) failed: %m", sclient->path); - (void)kill(pid, SIGKILL); - return -1; - } - - /* Timed out */ - force = TRUE; - if ( sclient->error == SCRIPT_CLIENT_ERROR_NONE ) - sclient->error = SCRIPT_CLIENT_ERROR_RUN_TIMEOUT; - if ( sclient->debug ) { - i_debug("program `%s' execution timed out after %llu seconds: " - "sending TERM signal", sclient->path, - (unsigned long long int)sclient->set->input_idle_timeout_secs); - } - - /* Kill child gently first */ - if ( kill(pid, SIGTERM) < 0 ) { - i_error("failed to send SIGTERM signal to program `%s'", sclient->path); - (void)kill(pid, SIGKILL); - return -1; - } - - /* Wait for it to die (give it some more time) */ - alarm(5); - if ( waitpid(pid, &status, 0) < 0 ) { - if ( errno != EINTR ) { - i_error("waitpid(%s) failed: %m", sclient->path); - (void)kill(pid, SIGKILL); - return -1; - } - - /* Timed out again */ - if ( sclient->debug ) { - i_debug("program `%s' execution timed out: sending KILL signal", - sclient->path); - } - - /* Kill it brutally now */ - if ( kill(pid, SIGKILL) < 0 ) { - i_error("failed to send SIGKILL signal to program `%s'", - sclient->path); - return -1; - } - - /* Now it will die immediately */ - if ( waitpid(pid, &status, 0) < 0 ) { - i_error("waitpid(%s) failed: %m", sclient->path); - return -1; - } - } - } - - /* Evaluate child exit status */ - sclient->exit_code = -1; - if ( WIFEXITED(status) ) { - /* Exited */ - int exit_code = WEXITSTATUS(status); - - if ( exit_code != 0 ) { - i_info("program `%s' terminated with non-zero exit code %d", - sclient->path, exit_code); - sclient->exit_code = 0; - return 0; - } - - sclient->exit_code = 1; - return 1; - - } else if ( WIFSIGNALED(status) ) { - /* Killed with a signal */ - - if ( force ) { - i_error("program `%s' was forcibly terminated with signal %d", - sclient->path, WTERMSIG(status)); - } else { - i_error("program `%s' terminated abnormally, signal %d", - sclient->path, WTERMSIG(status)); - } - return -1; - - } else if ( WIFSTOPPED(status) ) { - /* Stopped */ - i_error("program `%s' stopped, signal %d", - sclient->path, WSTOPSIG(status)); - return -1; - } - - /* Something else */ - i_error("program `%s' terminated abnormally, return status %d", - sclient->path, status); - return -1; -} - -static void script_client_local_failure -(struct script_client *sclient, enum script_client_error error) -{ - switch ( error ) { - case SCRIPT_CLIENT_ERROR_RUN_TIMEOUT: - i_error("program `%s' execution timed out (> %d secs)", - sclient->path, sclient->set->input_idle_timeout_secs); - break; - default: - break; - } -} - -struct script_client *script_client_local_create -(const char *bin_path, const char *const *args, - const struct script_client_settings *set) -{ - struct script_client_local *sclient; - pool_t pool; - - pool = pool_alloconly_create("script client local", 1024); - sclient = i_new(struct script_client_local, 1); - script_client_init(&sclient->client, pool, bin_path, args, set); - sclient->client.connect = script_client_local_connect; - sclient->client.close_output = script_client_local_close_output; - sclient->client.disconnect = script_client_local_disconnect; - sclient->client.failure = script_client_local_failure; - sclient->pid = -1; - - return &sclient->client; -} - diff --git a/src/plugins/sieve-extprograms/script-client-private.h b/src/plugins/sieve-extprograms/script-client-private.h deleted file mode 100644 index 9396b41..0000000 --- a/src/plugins/sieve-extprograms/script-client-private.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SCRIPT_CLIENT_PRIVATE_H -#define __SCRIPT_CLIENT_PRIVATE_H - -#include "script-client.h" - -enum script_client_error { - SCRIPT_CLIENT_ERROR_NONE, - SCRIPT_CLIENT_ERROR_CONNECT_TIMEOUT, - SCRIPT_CLIENT_ERROR_RUN_TIMEOUT, - SCRIPT_CLIENT_ERROR_IO, - SCRIPT_CLIENT_ERROR_UNKNOWN -}; - -struct script_client { - pool_t pool; - const struct script_client_settings *set; - - char *path; - const char **args; - ARRAY_TYPE(const_string) envs; - - int fd_in, fd_out; - struct io *io; - struct ioloop *ioloop; - struct timeout *to; - time_t start_time; - - struct istream *input, *script_input; - struct ostream *output, *script_output; - - enum script_client_error error; - int exit_code; - - int (*connect)(struct script_client *sclient); - int (*close_output)(struct script_client *sclient); - int (*disconnect)(struct script_client *sclient, bool force); - void (*failure) - (struct script_client *sclient, enum script_client_error error); - - unsigned int debug:1; - unsigned int disconnected:1; -}; - -void script_client_init - (struct script_client *sclient, pool_t pool, const char *path, - const char *const *args, const struct script_client_settings *set); - -void script_client_init_streams(struct script_client *sclient); - -int script_client_script_connected(struct script_client *sclient); - -void script_client_fail - (struct script_client *sclient, enum script_client_error error); - -#endif - diff --git a/src/plugins/sieve-extprograms/script-client-remote.c b/src/plugins/sieve-extprograms/script-client-remote.c deleted file mode 100644 index f437e04..0000000 --- a/src/plugins/sieve-extprograms/script-client-remote.c +++ /dev/null @@ -1,319 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "ioloop.h" -#include "str.h" -#include "net.h" -#include "write-full.h" -#include "eacces-error.h" -#include "istream-private.h" -#include "ostream.h" - -#include "script-client-private.h" - -#include -#include -#include - -/* - * Script client input stream - */ - -struct script_client_istream { - struct istream_private istream; - - struct stat statbuf; - - struct script_client *client; -}; - -static void script_client_istream_destroy(struct iostream_private *stream) -{ - struct script_client_istream *scstream = - (struct script_client_istream *)stream; - - i_stream_unref(&scstream->istream.parent); -} - -static ssize_t script_client_istream_read(struct istream_private *stream) -{ - struct script_client_istream *scstream = - (struct script_client_istream *)stream; - size_t pos, reserved; - ssize_t ret; - - i_stream_skip(stream->parent, stream->skip); - stream->pos -= stream->skip; - stream->skip = 0; - - stream->buffer = i_stream_get_data(stream->parent, &pos); - - if ( stream->buffer != NULL && pos >= 1 ) { - /* retain/hide potential return code at end of buffer */ - reserved = ( stream->buffer[pos-1] == '\n' && pos > 1 ? 2 : 1 ); - pos -= reserved; - } - - if (pos > stream->pos) { - ret = 0; - } else if ( stream->parent->eof ) { - stream->istream.eof = TRUE; - ret = -1; - } else do { - if ((ret = i_stream_read(stream->parent)) == -2) - return -2; /* input buffer full */ - - stream->istream.stream_errno = stream->parent->stream_errno; - stream->buffer = i_stream_get_data(stream->parent, &pos); - - if ( stream->parent->eof ) { - /* Check return code at EOF */ - if ( stream->buffer != NULL && pos >= 2 && - stream->buffer[pos-1] == '\n' ) { - switch ( stream->buffer[pos-2] ) { - case '+': - scstream->client->exit_code = 1; - break; - case '-': - scstream->client->exit_code = 0; - default: - scstream->client->exit_code = -1; - } - } else { - scstream->client->exit_code = -1; - } - } - - if ( ret == 0 || (ret < 0 && !stream->parent->eof) ) break; - - if ( stream->buffer != NULL && pos >= 1 ) { - /* retain/hide potential return code at end of buffer */ - reserved = ( stream->buffer[pos-1] == '\n' && pos > 1 ? 2 : 1 ); - - pos -= reserved; - - if ( ret > 0 ) { - ret = ( (size_t)ret > reserved ? ret - reserved : 0 ); - } - } - - if ( ret <= 0 && stream->parent->eof ) { - /* Parent EOF and not more data to return; EOF here as well */ - stream->istream.eof = TRUE; - ret = -1; - } - } while ( ret == 0 ); - - ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : (ret == 0 ? 0 : -1); - stream->pos = pos; - - i_assert(ret != -1 || stream->istream.eof || - stream->istream.stream_errno != 0); - - return ret; -} - -static void ATTR_NORETURN script_client_istream_sync -(struct istream_private *stream ATTR_UNUSED) -{ - i_panic("script_client_istream sync() not implemented"); -} - -static int script_client_istream_stat -(struct istream_private *stream, bool exact) -{ - struct script_client_istream *scstream = - (struct script_client_istream *)stream; - const struct stat *st; - int ret; - - /* Stat the original stream */ - ret = i_stream_stat(stream->parent, exact, &st); - if (ret < 0 || st->st_size == -1 || !exact) - return ret; - - scstream->statbuf = *st; - scstream->statbuf.st_size = -1; - - return ret; -} - -static struct istream *script_client_istream_create -(struct script_client *script_client, struct istream *input) -{ - struct script_client_istream *scstream; - - scstream = i_new(struct script_client_istream, 1); - scstream->client = script_client; - - scstream->istream.max_buffer_size = input->real_stream->max_buffer_size; - - scstream->istream.iostream.destroy = script_client_istream_destroy; - scstream->istream.read = script_client_istream_read; - scstream->istream.sync = script_client_istream_sync; - scstream->istream.stat = script_client_istream_stat; - - scstream->istream.istream.readable_fd = FALSE; - scstream->istream.istream.blocking = input->blocking; - scstream->istream.istream.seekable = FALSE; - - i_stream_seek(input, 0); - - return i_stream_create(&scstream->istream, input, -1); -} - -/* - * Script client - */ - -struct script_client_remote { - struct script_client client; - - unsigned int noreply:1; -}; - -static void script_client_remote_connected(struct script_client *sclient) -{ - struct script_client_remote *slclient = - (struct script_client_remote *)sclient; - const char **args = sclient->args; - string_t *str; - - io_remove(&sclient->io); - script_client_init_streams(sclient); - - if ( !slclient->noreply ) { - sclient->script_input = script_client_istream_create - (sclient, sclient->script_input); - } - - str = t_str_new(1024); - str_append(str, "VERSION\tscript\t3\t0\n"); - if ( slclient->noreply ) - str_append(str, "noreply\n"); - else - str_append(str, "-\n"); - if ( args != NULL ) { - for (; *args != NULL; args++) { - str_append(str, *args); - str_append_c(str, '\n'); - } - } - str_append_c(str, '\n'); - - if ( o_stream_send - (sclient->script_output, str_data(str), str_len(str)) < 0 ) { - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return; - } - - (void)script_client_script_connected(sclient); -} - -static int script_client_remote_connect(struct script_client *sclient) -{ - struct script_client_remote *slclient = - (struct script_client_remote *)sclient; - int fd; - - if ((fd = net_connect_unix(sclient->path)) < 0) { - switch (errno) { - case EAGAIN: - case ECONNREFUSED: - // FIXME: retry; - return -1; - case EACCES: - i_error("%s", eacces_error_get("net_connect_unix", sclient->path)); - return -1; - default: - i_error("net_connect_unix(%s) failed: %m", sclient->path); - return -1; - } - } - - net_set_nonblock(fd, TRUE); - - sclient->fd_in = ( slclient->noreply && sclient->output == NULL ? -1 : fd ); - sclient->fd_out = fd; - sclient->io = io_add(fd, IO_WRITE, script_client_remote_connected, sclient); - return 1; -} - -static int script_client_remote_close_output(struct script_client *sclient) -{ - /* Shutdown output; script stdin will get EOF */ - if ( shutdown(sclient->fd_out, SHUT_WR) < 0 ) { - i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path); - return -1; - } - - return 1; -} - -static int script_client_remote_disconnect -(struct script_client *sclient, bool force) -{ - struct script_client_remote *slclient = - (struct script_client_remote *)sclient; - int ret = 0; - - if ( sclient->error == SCRIPT_CLIENT_ERROR_NONE && !slclient->noreply && - sclient->script_input != NULL && !force) { - const unsigned char *data; - size_t size; - - /* Skip any remaining script output and parse the exit code */ - while ((ret = i_stream_read_data - (sclient->script_input, &data, &size, 0)) > 0) { - i_stream_skip(sclient->script_input, size); - } - - /* Get exit code */ - if ( !sclient->script_input->eof ) - ret = -1; - else - ret = sclient->exit_code; - } else { - ret = 1; - } - - return ret; -} - -static void script_client_remote_failure -(struct script_client *sclient, enum script_client_error error) -{ - switch ( error ) { - case SCRIPT_CLIENT_ERROR_CONNECT_TIMEOUT: - i_error("program `%s' socket connection timed out (> %d msecs)", - sclient->path, sclient->set->client_connect_timeout_msecs); - break; - case SCRIPT_CLIENT_ERROR_RUN_TIMEOUT: - i_error("program `%s' execution timed out (> %d secs)", - sclient->path, sclient->set->input_idle_timeout_secs); - break; - default: - break; - } -} - -struct script_client *script_client_remote_create -(const char *socket_path, const char *const *args, - const struct script_client_settings *set, bool noreply) -{ - struct script_client_remote *sclient; - pool_t pool; - - pool = pool_alloconly_create("script client remote", 1024); - sclient = p_new(pool, struct script_client_remote, 1); - script_client_init(&sclient->client, pool, socket_path, args, set); - sclient->client.connect = script_client_remote_connect; - sclient->client.close_output = script_client_remote_close_output; - sclient->client.disconnect = script_client_remote_disconnect; - sclient->client.failure = script_client_remote_failure; - sclient->noreply = noreply; - - return &sclient->client; -} - diff --git a/src/plugins/sieve-extprograms/script-client.c b/src/plugins/sieve-extprograms/script-client.c deleted file mode 100644 index 06a5d5d..0000000 --- a/src/plugins/sieve-extprograms/script-client.c +++ /dev/null @@ -1,329 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "ioloop.h" -#include "array.h" -#include "istream-private.h" -#include "ostream.h" - -#include "script-client-private.h" - -#include - -#define MAX_OUTBUF_SIZE 16384 - -static void script_client_timeout(struct script_client *sclient) -{ - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_RUN_TIMEOUT); -} - -static void script_client_connect_timeout(struct script_client *sclient) -{ - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_CONNECT_TIMEOUT); -} - -static int script_client_connect(struct script_client *sclient) -{ - if (sclient->set->client_connect_timeout_msecs != 0) { - sclient->to = timeout_add - (sclient->set->client_connect_timeout_msecs, - script_client_connect_timeout, sclient); - } - - if ( sclient->connect(sclient) < 0 ) { - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return -1; - } - - return 1; -} - -static void script_client_disconnect -(struct script_client *sclient, bool force) -{ - int ret, error = FALSE; - - if ( sclient->ioloop != NULL ) - io_loop_stop(sclient->ioloop); - - if ( sclient->disconnected ) - return; - - if ( (ret=sclient->close_output(sclient)) < 0 ) - error = TRUE; - - if ( (ret=sclient->disconnect(sclient, force)) < 0 ) - error = TRUE; - - if ( sclient->script_input != NULL ) - i_stream_destroy(&sclient->script_input); - if ( sclient->script_output != NULL ) - o_stream_destroy(&sclient->script_output); - - if ( sclient->to != NULL ) - timeout_remove(&sclient->to); - if ( sclient->io != NULL ) - io_remove(&sclient->io); - - if (sclient->fd_in != -1 && close(sclient->fd_in) < 0) - i_error("close(%s) failed: %m", sclient->path); - if (sclient->fd_out != -1 && sclient->fd_out != sclient->fd_out) - i_error("close(%s/out) failed: %m", sclient->path); - sclient->fd_in = sclient->fd_out = -1; - - sclient->disconnected = TRUE; - if (error && sclient->error == SCRIPT_CLIENT_ERROR_NONE ) { - sclient->error = SCRIPT_CLIENT_ERROR_UNKNOWN; - } -} - -void script_client_fail -(struct script_client *sclient, enum script_client_error error) -{ - if ( sclient->error != SCRIPT_CLIENT_ERROR_NONE ) - return; - - sclient->error = error; - script_client_disconnect(sclient, TRUE); - - sclient->failure(sclient, error); -} - -static int script_client_script_output(struct script_client *sclient) -{ - struct istream *input = sclient->input; - struct ostream *output = sclient->script_output; - const unsigned char *data; - size_t size; - int ret = 0; - - if ((ret = o_stream_flush(output)) <= 0) { - if (ret < 0) - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return ret; - } - - if ( input != NULL && output != NULL ) { - do { - while ( (data=i_stream_get_data(input, &size)) != NULL ) { - ssize_t sent; - - if ( (sent=o_stream_send(output, data, size)) < 0 ) { - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return -1; - } - - if ( sent == 0 ) - return 0; - i_stream_skip(input, sent); - } - } while ( (ret=i_stream_read(input)) > 0 ); - - if ( ret == 0 ) { - // FIXME: not supposed to happen; returning 0 will poll the input stream - return 0; - } - - if ( ret < 0 ) { - if ( !input->eof ) { - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return -1; - } else if ( !i_stream_have_bytes_left(input) ) { - i_stream_unref(&sclient->input); - input = NULL; - - if ( (ret = o_stream_flush(output)) <= 0 ) { - if ( ret < 0 ) - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return ret; - } - } - } - } - - if ( input == NULL ) { - o_stream_unref(&sclient->script_output); - - if ( sclient->script_input == NULL ) { - script_client_disconnect(sclient, FALSE); - } else { - sclient->close_output(sclient); - } - return 0; - } - - return 1; -} - -static void script_client_script_input(struct script_client *sclient) -{ - struct istream *input = sclient->script_input; - struct ostream *output = sclient->output; - const unsigned char *data; - size_t size; - int ret = 0; - - if ( input != NULL ) { - while ( (ret=i_stream_read_data(input, &data, &size, 0)) > 0 ) { - if ( output != NULL ) { - ssize_t sent; - - if ( (sent=o_stream_send(output, data, size)) < 0 ) { - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - return; - } - size = (size_t)sent; - } - - i_stream_skip(input, size); - } - - if ( ret < 0 ) { - if ( input->eof ) { - script_client_disconnect(sclient, FALSE); - return; - } - script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); - } - } -} - -int script_client_script_connected -(struct script_client *sclient) -{ - int ret = 1; - - sclient->start_time = ioloop_time; - if (sclient->to != NULL) - timeout_remove(&sclient->to); - if ( sclient->set->input_idle_timeout_secs != 0 ) { - sclient->to = timeout_add(sclient->set->input_idle_timeout_secs*1000, - script_client_timeout, sclient); - } - - /* run output */ - if ( sclient->script_output != NULL && - (ret=script_client_script_output(sclient)) == 0 ) { - if ( sclient->script_output != NULL ) { - o_stream_set_flush_callback - (sclient->script_output, script_client_script_output, sclient); - } - } - - return ret; -} - -void script_client_init -(struct script_client *sclient, pool_t pool, const char *path, - const char *const *args, const struct script_client_settings *set) -{ - sclient->pool = pool; - sclient->path = p_strdup(pool, path); - if ( args != NULL ) - sclient->args = p_strarray_dup(pool, args); - sclient->set = set; - sclient->debug = set->debug; - sclient->fd_in = -1; - sclient->fd_out = -1; -} - -void script_client_set_input -(struct script_client *sclient, struct istream *input) -{ - if ( sclient->input ) - i_stream_unref(&sclient->input); - if ( input != NULL ) - i_stream_ref(input); - sclient->input = input; -} - -void script_client_set_output -(struct script_client *sclient, struct ostream *output) -{ - if ( sclient->output ) - o_stream_unref(&sclient->output); - if ( output != NULL ) - o_stream_ref(output); - sclient->output = output; -} - -void script_client_set_env -(struct script_client *sclient, const char *name, const char *value) -{ - const char *env; - - if ( !array_is_created(&sclient->envs) ) - p_array_init(&sclient->envs, sclient->pool, 16); - - env = p_strdup_printf(sclient->pool, "%s=%s", name, value); - array_append(&sclient->envs, &env, 1); -} - -void script_client_init_streams(struct script_client *sclient) -{ - if ( sclient->fd_out >= 0 ) { - sclient->script_output = - o_stream_create_fd(sclient->fd_out, MAX_OUTBUF_SIZE, FALSE); - } - if ( sclient->fd_in >= 0 ) { - sclient->script_input = - i_stream_create_fd(sclient->fd_in, (size_t)-1, FALSE); - sclient->io = io_add - (sclient->fd_in, IO_READ, script_client_script_input, sclient); - } -} - -void script_client_destroy(struct script_client **_sclient) -{ - struct script_client *sclient = *_sclient; - - script_client_disconnect(sclient, TRUE); - - if ( sclient->input != NULL ) - i_stream_unref(&sclient->input); - if ( sclient->output != NULL ) - o_stream_unref(&sclient->output); - if ( sclient->io != NULL ) - io_remove(&sclient->io); - if ( sclient->ioloop != NULL ) - io_loop_destroy(&sclient->ioloop); - - pool_unref(&sclient->pool); - *_sclient = NULL; -} - -int script_client_run(struct script_client *sclient) -{ - int ret; - - sclient->ioloop = io_loop_create(); - - if ( script_client_connect(sclient) >= 0 ) { - /* run output */ - ret = 1; - if ( sclient->script_output != NULL && - (ret=o_stream_flush(sclient->script_output)) == 0 ) { - o_stream_set_flush_callback - (sclient->script_output, script_client_script_output, sclient); - } - - /* run i/o event loop */ - if ( ret < 0 ) { - sclient->error = SCRIPT_CLIENT_ERROR_IO; - } else if ( sclient->io != NULL || ret == 0 ) { - io_loop_run(sclient->ioloop); - } - - /* finished */ - script_client_disconnect(sclient, FALSE); - } - - io_loop_destroy(&sclient->ioloop); - - if ( sclient->error != SCRIPT_CLIENT_ERROR_NONE ) - return -1; - - return sclient->exit_code; -} - diff --git a/src/plugins/sieve-extprograms/script-client.h b/src/plugins/sieve-extprograms/script-client.h deleted file mode 100644 index d642d4e..0000000 --- a/src/plugins/sieve-extprograms/script-client.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SCRIPT_CLIENT_H -#define __SCRIPT_CLIENT_H - -struct script_client; - -struct script_client_settings { - unsigned int client_connect_timeout_msecs; - unsigned int input_idle_timeout_secs; - bool debug; -}; - -struct script_client *script_client_local_create - (const char *bin_path, const char *const *args, - const struct script_client_settings *set); -struct script_client *script_client_remote_create - (const char *socket_path, const char *const *args, - const struct script_client_settings *set, bool noreply); - -void script_client_destroy(struct script_client **_sclient); - -void script_client_set_input - (struct script_client *sclient, struct istream *input); -void script_client_set_output - (struct script_client *sclient, struct ostream *output); - -void script_client_set_env - (struct script_client *sclient, const char *name, const char *value); - -int script_client_run(struct script_client *sclient); - -#endif - diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.c b/src/plugins/sieve-extprograms/sieve-extprograms-common.c deleted file mode 100644 index 59f39f1..0000000 --- a/src/plugins/sieve-extprograms/sieve-extprograms-common.c +++ /dev/null @@ -1,594 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "lib-signals.h" -#include "str.h" -#include "strfuncs.h" -#include "str-sanitize.h" -#include "unichar.h" -#include "array.h" -#include "eacces-error.h" -#include "istream.h" -#include "istream-crlf.h" -#include "istream-header-filter.h" -#include "ostream.h" -#include "mail-user.h" -#include "mail-storage.h" - -#include -#include -#include -#include -#include - -#include "sieve-common.h" -#include "sieve-settings.h" -#include "sieve-error.h" -#include "sieve-extensions.h" -#include "sieve-ast.h" -#include "sieve-commands.h" -#include "sieve-stringlist.h" -#include "sieve-code.h" -#include "sieve-actions.h" -#include "sieve-validator.h" -#include "sieve-runtime.h" -#include "sieve-interpreter.h" - -#include "sieve-ext-copy.h" -#include "sieve-ext-variables.h" - -#include "script-client.h" -#include "sieve-extprograms-common.h" - -/* - * Limits - */ - -#define SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN 128 -#define SIEVE_EXTPROGRAMS_MAX_PROGRAM_ARG_LEN 1024 - -#define SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS 10 -#define SIEVE_EXTPROGRAMS_CONNECT_TIMEOUT_MSECS 5 - -/* - * Pipe Extension Context - */ - -struct sieve_extprograms_config *sieve_extprograms_config_init -(const struct sieve_extension *ext) -{ - struct sieve_instance *svinst = ext->svinst; - struct sieve_extprograms_config *ext_config; - const char *extname = sieve_extension_name(ext); - const char *bin_dir, *socket_dir; - sieve_number_t execute_timeout = - SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS; - - extname = strrchr(extname, '.'); - i_assert(extname != NULL); - extname++; - - bin_dir = sieve_setting_get - (svinst, t_strdup_printf("sieve_%s_bin_dir", extname)); - socket_dir = sieve_setting_get - (svinst, t_strdup_printf("sieve_%s_socket_dir", extname)); - - ext_config = i_new(struct sieve_extprograms_config, 1); - - if ( bin_dir == NULL && socket_dir == NULL ) { - if ( svinst->debug ) { - sieve_sys_debug(svinst, "%s extension: " - "no bin or socket directory specified; extension is unconfigured " - "(both sieve_%s_bin_dir and sieve_%s_socket_dir are not set)", - sieve_extension_name(ext), extname, extname); - } - } else { - ext_config->bin_dir = i_strdup(bin_dir); - ext_config->socket_dir = i_strdup(socket_dir); - } - - if (sieve_setting_get_duration_value - (svinst, t_strdup_printf("sieve_%s_exec_timeout", extname), - &execute_timeout)) { - ext_config->execute_timeout = execute_timeout; - } - - if ( sieve_extension_is(ext, pipe_extension) ) - ext_config->copy_ext = sieve_ext_copy_get_extension(ext->svinst); - if ( sieve_extension_is(ext, execute_extension) ) - ext_config->var_ext = sieve_ext_variables_get_extension(ext->svinst); - return ext_config; -} - -void sieve_extprograms_config_deinit -(struct sieve_extprograms_config **ext_config) -{ - if ( *ext_config == NULL ) - return; - - i_free((*ext_config)->bin_dir); - i_free((*ext_config)->socket_dir); - i_free((*ext_config)); - - *ext_config = NULL; -} - -/* - * Program name and arguments - */ - -bool sieve_extprogram_name_is_valid(string_t *name) -{ - ARRAY_TYPE(unichars) uni_name; - unsigned int count, i; - const unichar_t *name_chars; - size_t namelen = str_len(name); - - /* Check minimum length */ - if ( namelen == 0 ) - return FALSE; - - /* Check worst-case maximum length */ - if ( namelen > SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN * 4 ) - return FALSE; - - /* Intialize array for unicode characters */ - t_array_init(&uni_name, namelen * 4); - - /* Convert UTF-8 to UCS4/UTF-32 */ - if ( uni_utf8_to_ucs4_n(str_data(name), namelen, &uni_name) < 0 ) - return FALSE; - name_chars = array_get(&uni_name, &count); - - /* Check true maximum length */ - if ( count > SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN ) - return FALSE; - - /* Scan name for invalid characters - * FIXME: compliance with Net-Unicode Definition (Section 2 of - * RFC 5198) is not checked fully and no normalization - * is performed. - */ - for ( i = 0; i < count; i++ ) { - - /* 0000-001F; [CONTROL CHARACTERS] */ - if ( name_chars[i] <= 0x001f ) - return FALSE; - - /* 002F; SLASH */ - if ( name_chars[i] == 0x002f ) - return FALSE; - - /* 007F; DELETE */ - if ( name_chars[i] == 0x007f ) - return FALSE; - - /* 0080-009F; [CONTROL CHARACTERS] */ - if ( name_chars[i] >= 0x0080 && name_chars[i] <= 0x009f ) - return FALSE; - - /* 00FF */ - if ( name_chars[i] == 0x00ff ) - return FALSE; - - /* 2028; LINE SEPARATOR */ - /* 2029; PARAGRAPH SEPARATOR */ - if ( name_chars[i] == 0x2028 || name_chars[i] == 0x2029 ) - return FALSE; - } - - return TRUE; -} - -bool sieve_extprogram_arg_is_valid(string_t *arg) -{ - const unsigned char *chars; - unsigned int i; - - /* Check maximum length */ - if ( str_len(arg) > SIEVE_EXTPROGRAMS_MAX_PROGRAM_ARG_LEN ) - return FALSE; - - /* Check invalid characters */ - chars = str_data(arg); - for ( i = 0; i < str_len(arg); i++ ) { - /* 0010; CR */ - if ( chars[i] == 0x0D ) - return FALSE; - - /* 0010; LF */ - if ( chars[i] == 0x0A ) - return FALSE; - } - - return TRUE; -} - -/* - * Command validation - */ - -struct _arg_validate_context { - struct sieve_validator *valdtr; - struct sieve_command *cmd; -}; - -static int _arg_validate -(void *context, struct sieve_ast_argument *item) -{ - struct _arg_validate_context *actx = (struct _arg_validate_context *) context; - - if ( sieve_argument_is_string_literal(item) ) { - string_t *arg = sieve_ast_argument_str(item); - - if ( !sieve_extprogram_arg_is_valid(arg) ) { - sieve_argument_validate_error(actx->valdtr, item, - "%s %s: specified external program argument `%s' is invalid", - sieve_command_identifier(actx->cmd), sieve_command_type_name(actx->cmd), - str_sanitize(str_c(arg), 128)); - - return FALSE; - } - } - - return TRUE; -} - -bool sieve_extprogram_command_validate -(struct sieve_validator *valdtr, struct sieve_command *cmd) -{ - struct sieve_ast_argument *arg = cmd->first_positional; - struct sieve_ast_argument *stritem; - struct _arg_validate_context actx; - string_t *program_name; - - if ( arg == NULL ) { - sieve_command_validate_error(valdtr, cmd, - "the %s %s expects at least one positional argument, but none was found", - sieve_command_identifier(cmd), sieve_command_type_name(cmd)); - return FALSE; - } - - /* argument */ - - if ( !sieve_validate_positional_argument - (valdtr, cmd, arg, "program-name", 1, SAAT_STRING) ) { - return FALSE; - } - - if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) - return FALSE; - - /* Variables are not allowed */ - if ( !sieve_argument_is_string_literal(arg) ) { - sieve_argument_validate_error(valdtr, arg, - "the %s %s requires a constant string " - "for its program-name argument", - sieve_command_identifier(cmd), sieve_command_type_name(cmd)); - return FALSE; - } - - /* Check program name */ - program_name = sieve_ast_argument_str(arg); - if ( !sieve_extprogram_name_is_valid(program_name) ) { - sieve_argument_validate_error(valdtr, arg, - "%s %s: invalid program name '%s'", - sieve_command_identifier(cmd), sieve_command_type_name(cmd), - str_sanitize(str_c(program_name), 80)); - return FALSE; - } - - /* Optional argument */ - - arg = sieve_ast_argument_next(arg); - if ( arg == NULL ) - return TRUE; - - if ( !sieve_validate_positional_argument - (valdtr, cmd, arg, "arguments", 2, SAAT_STRING_LIST) ) { - return FALSE; - } - - if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) - return FALSE; - - /* Check arguments */ - actx.valdtr = valdtr; - actx.cmd = cmd; - stritem = arg; - if ( sieve_ast_stringlist_map - (&stritem, (void *)&actx, _arg_validate) <= 0 ) { - return FALSE; - } - - if ( sieve_ast_argument_next(arg) != NULL ) { - sieve_command_validate_error(valdtr, cmd, - "the %s %s expects at most two positional arguments, but more were found", - sieve_command_identifier(cmd), sieve_command_type_name(cmd)); - return FALSE; - } - - return TRUE; -} - -/* - * Common command operands - */ - -int sieve_extprogram_command_read_operands -(const struct sieve_runtime_env *renv, sieve_size_t *address, - string_t **pname_r, struct sieve_stringlist **args_list_r) -{ - string_t *arg; - int ret; - - /* - * Read fixed operands - */ - - if ( (ret=sieve_opr_string_read - (renv, address, "program-name", pname_r)) <= 0 ) - return ret; - - if ( (ret=sieve_opr_stringlist_read_ex - (renv, address, "arguments", TRUE, args_list_r)) <= 0 ) - return ret; - - /* - * Check operands - */ - - arg = NULL; - while ( *args_list_r != NULL && - (ret=sieve_stringlist_next_item(*args_list_r, &arg)) > 0 ) { - if ( !sieve_extprogram_arg_is_valid(arg) ) { - sieve_runtime_error(renv, NULL, - "specified :args item `%s' is invalid", - str_sanitize(str_c(arg), 128)); - return SIEVE_EXEC_FAILURE; - } - } - - if ( ret < 0 ) { - sieve_runtime_trace_error(renv, "invalid args-list item"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - return SIEVE_EXEC_OK; -} - -/* - * Running external programs - */ - -struct sieve_extprogram { - struct sieve_instance *svinst; - struct script_client_settings set; - struct script_client *script_client; -}; - -void sieve_extprogram_exec_error -(struct sieve_error_handler *ehandler, const char *location, - const char *fmt, ...) -{ - char str[256]; - struct tm *tm; - const char *timestamp; - - tm = localtime(&ioloop_time); - - timestamp = - ( strftime(str, sizeof(str), " [%Y-%m-%d %H:%M:%S]", tm) > 0 ? str : "" ); - - va_list args; - va_start(args, fmt); - - T_BEGIN { - sieve_error(ehandler, location, - "%s: refer to server log for more information.%s", - t_strdup_vprintf(fmt, args), timestamp); - } T_END; - - va_end(args); -} - -/* API */ - -struct sieve_extprogram *sieve_extprogram_create -(const struct sieve_extension *ext, const struct sieve_script_env *senv, - const struct sieve_message_data *msgdata, const char *action, - const char *program_name, const char * const *args, - enum sieve_error *error_r) -{ - struct sieve_instance *svinst = ext->svinst; - struct sieve_extprograms_config *ext_config = - (struct sieve_extprograms_config *) ext->context; - struct sieve_extprogram *sprog; - const char *path = NULL; - struct stat st; - bool fork = FALSE; - int ret; - - if ( svinst->debug ) { - sieve_sys_debug(svinst, "action %s: " - "running program: %s", action, program_name); - } - - if ( ext_config == NULL ) { - sieve_sys_error(svinst, "action %s: " - "failed to execute program `%s': " - "vnd.dovecot.%s extension is unconfigured", action, program_name, action); - *error_r = SIEVE_ERROR_NOT_FOUND; - return NULL; - } - - /* Try socket first */ - if ( ext_config->socket_dir != NULL ) { - path = t_strconcat(senv->user->set->base_dir, "/", - ext_config->socket_dir, "/", program_name, NULL); - if ( (ret=stat(path, &st)) < 0 ) { - switch ( errno ) { - case ENOENT: - if ( svinst->debug ) { - sieve_sys_debug(svinst, "action %s: " - "socket path `%s' for program `%s' not found", - action, path, program_name); - } - break; - case EACCES: - sieve_sys_error(svinst, "action %s: " - "failed to stat socket: %s", action, eacces_error_get("stat", path)); - *error_r = SIEVE_ERROR_NO_PERM; - return NULL; - default: - sieve_sys_error(svinst, "action %s: " - "failed to stat socket `%s': %m", action, path); - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - return NULL; - } - path = NULL; - } else if ( !S_ISSOCK(st.st_mode) ) { - sieve_sys_error(svinst, "action %s: " - "socket path `%s' for program `%s' is not a socket", - action, path, program_name); - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - return NULL; - } - } - - /* Try executable next */ - if ( path == NULL && ext_config->bin_dir != NULL ) { - fork = TRUE; - path = t_strconcat(ext_config->bin_dir, "/", program_name, NULL); - if ( (ret=stat(path, &st)) < 0 ) { - switch ( errno ) { - case ENOENT: - if ( svinst->debug ) { - sieve_sys_debug(svinst, "action %s: " - "executable path `%s' for program `%s' not found", - action, path, program_name); - } - sieve_sys_error(svinst, "action %s: program `%s' not found", - action, program_name); - *error_r = SIEVE_ERROR_NOT_FOUND; - break; - case EACCES: - sieve_sys_error(svinst, "action %s: " - "failed to stat program: %s", action, eacces_error_get("stat", path)); - *error_r = SIEVE_ERROR_NO_PERM; - break; - default: - sieve_sys_error(svinst, "action %s: " - "failed to stat program `%s': %m", action, path); - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - break; - } - - return NULL; - } else if ( !S_ISREG(st.st_mode) ) { - sieve_sys_error(svinst, "action %s: " - "executable `%s' for program `%s' is not a regular file", - action, path, program_name); - *error_r = SIEVE_ERROR_NOT_POSSIBLE; - return NULL; - } else if ( (st.st_mode & S_IWOTH) != 0 ) { - sieve_sys_error(svinst, "action %s: " - "executable `%s' for program `%s' is world-writable", - action, path, program_name); - *error_r = SIEVE_ERROR_NO_PERM; - return NULL; - } - } - - /* None found ? */ - if ( path == NULL ) { - sieve_sys_error(svinst, "action %s: " - "program `%s' not found", action, program_name); - *error_r = SIEVE_ERROR_NOT_FOUND; - return NULL; - } - - sprog = i_new(struct sieve_extprogram, 1); - sprog->svinst = ext->svinst; - - sprog->set.client_connect_timeout_msecs = - SIEVE_EXTPROGRAMS_CONNECT_TIMEOUT_MSECS; - sprog->set.input_idle_timeout_secs = ext_config->execute_timeout; - sprog->set.debug = svinst->debug; - - if ( fork ) { - sprog->script_client = - script_client_local_create(path, args, &sprog->set); - } else { - sprog->script_client = - script_client_remote_create(path, args, &sprog->set, FALSE); - } - - if ( svinst->username != NULL ) - script_client_set_env(sprog->script_client, "USER", svinst->username); - if ( svinst->home_dir != NULL ) - script_client_set_env(sprog->script_client, "HOME", svinst->home_dir); - if ( svinst->hostname != NULL ) - script_client_set_env(sprog->script_client, "HOST", svinst->hostname); - if ( msgdata->return_path != NULL ) { - script_client_set_env - (sprog->script_client, "SENDER", msgdata->return_path); - } - if ( msgdata->final_envelope_to != NULL ) { - script_client_set_env - (sprog->script_client, "RECIPIENT", msgdata->final_envelope_to); - } - if ( msgdata->orig_envelope_to != NULL ) { - script_client_set_env - (sprog->script_client, "ORIG_RECIPIENT", msgdata->orig_envelope_to); - } - - return sprog; -} - -void sieve_extprogram_destroy(struct sieve_extprogram **_sprog) -{ - struct sieve_extprogram *sprog = *_sprog; - - script_client_destroy(&sprog->script_client); - i_free(sprog); - *_sprog = NULL; -} - -/* I/0 */ - -void sieve_extprogram_set_output -(struct sieve_extprogram *sprog, struct ostream *output) -{ - script_client_set_output(sprog->script_client, output); -} - -void sieve_extprogram_set_input -(struct sieve_extprogram *sprog, struct istream *input) -{ - script_client_set_input(sprog->script_client, input); -} - -int sieve_extprogram_set_input_mail -(struct sieve_extprogram *sprog, struct mail *mail) -{ - struct istream *input; - - if (mail_get_stream(mail, NULL, NULL, &input) < 0) - return -1; - - /* Make sure the message contains CRLF consistently */ - input = i_stream_create_crlf(input); - - script_client_set_input(sprog->script_client, input); - i_stream_unref(&input); - - return 1; -} - -int sieve_extprogram_run(struct sieve_extprogram *sprog) -{ - return script_client_run(sprog->script_client); -} - diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.h b/src/plugins/sieve-extprograms/sieve-extprograms-common.h deleted file mode 100644 index c8bfdff..0000000 --- a/src/plugins/sieve-extprograms/sieve-extprograms-common.h +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_EXTPROGRAMS_COMMON_H -#define __SIEVE_EXTPROGRAMS_COMMON_H - -#include "sieve-common.h" - -/* - * Extension configuration - */ - -struct sieve_extprograms_config { - const struct sieve_extension *copy_ext; - const struct sieve_extension *var_ext; - - char *socket_dir; - char *bin_dir; - - unsigned int execute_timeout; -}; - -struct sieve_extprograms_config *sieve_extprograms_config_init - (const struct sieve_extension *ext); -void sieve_extprograms_config_deinit - (struct sieve_extprograms_config **ext_config); - -/* - * Extensions - */ - -extern const struct sieve_extension_def pipe_extension; -extern const struct sieve_extension_def filter_extension; -extern const struct sieve_extension_def execute_extension; - -/* - * Commands - */ - -extern const struct sieve_command_def cmd_pipe; -extern const struct sieve_command_def cmd_filter; -extern const struct sieve_command_def cmd_execute; - -/* - * Operations - */ - -extern const struct sieve_operation_def cmd_pipe_operation; -extern const struct sieve_operation_def cmd_filter_operation; -extern const struct sieve_operation_def cmd_execute_operation; - -/* - * Program name and arguments - */ - -bool sieve_extprogram_arg_is_valid(string_t *arg); -bool sieve_extprogram_name_is_valid(string_t *name); - -/* - * Command validation - */ - -bool sieve_extprogram_command_validate - (struct sieve_validator *valdtr, struct sieve_command *cmd); - -/* - * Common command operands - */ - -int sieve_extprogram_command_read_operands - (const struct sieve_runtime_env *renv, sieve_size_t *address, - string_t **pname_r, struct sieve_stringlist **args_list_r); - -/* - * Running external programs - */ - -void sieve_extprogram_exec_error - (struct sieve_error_handler *ehandler, const char *location, - const char *fmt, ...) ATTR_FORMAT(3, 4); - -struct sieve_extprogram *sieve_extprogram_create - (const struct sieve_extension *ext, const struct sieve_script_env *senv, - const struct sieve_message_data *msgdata, const char *action, - const char *program_name, const char * const *args, - enum sieve_error *error_r); -void sieve_extprogram_destroy(struct sieve_extprogram **_sprog); - -void sieve_extprogram_set_output - (struct sieve_extprogram *sprog, struct ostream *output); -void sieve_extprogram_set_input - (struct sieve_extprogram *sprog, struct istream *input); -int sieve_extprogram_set_input_mail - (struct sieve_extprogram *sprog, struct mail *mail); - -int sieve_extprogram_run(struct sieve_extprogram *sprog); - -#endif /* __SIEVE_EXTPROGRAMS_COMMON_H */ diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c b/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c deleted file mode 100644 index 8031271..0000000 --- a/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#include "sieve-common.h" -#include "sieve-error.h" -#include "sieve-extensions.h" - -#include "sieve-extprograms-common.h" -#include "sieve-extprograms-plugin.h" - -/* - * Sieve plugin interface - */ - -struct _plugin_context { - const struct sieve_extension *ext_pipe; - const struct sieve_extension *ext_filter; - const struct sieve_extension *ext_execute; -}; - -void sieve_extprograms_plugin_load -(struct sieve_instance *svinst, void **context) -{ - struct _plugin_context *pctx = i_new(struct _plugin_context, 1); - - pctx->ext_pipe = sieve_extension_register - (svinst, &pipe_extension, FALSE); - pctx->ext_filter = sieve_extension_register - (svinst, &filter_extension, FALSE); - pctx->ext_execute = sieve_extension_register - (svinst, &execute_extension, FALSE); - - if ( svinst->debug ) { - sieve_sys_debug(svinst, "Sieve Extprograms plugin for %s version %s loaded", - PIGEONHOLE_NAME, PIGEONHOLE_VERSION); - } - - *context = (void *)pctx; -} - -void sieve_extprograms_plugin_unload -(struct sieve_instance *svinst ATTR_UNUSED, void *context) -{ - struct _plugin_context *pctx = (struct _plugin_context *)context; - - sieve_extension_unregister(pctx->ext_pipe); - sieve_extension_unregister(pctx->ext_filter); - sieve_extension_unregister(pctx->ext_execute); - - i_free(pctx); -} - -/* - * Module interface - */ - -void sieve_extprograms_plugin_init(void) -{ - /* Nothing */ -} - -void sieve_extprograms_plugin_deinit(void) -{ - /* Nothing */ -} diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h b/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h deleted file mode 100644 index fee16e0..0000000 --- a/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file - */ - -#ifndef __SIEVE_EXTPROGRAMS_PLUGIN_H -#define __SIEVE_EXTPROGRAMS_PLUGIN_H - -/* - * Plugin interface - */ - -void sieve_extprograms_plugin_load - (struct sieve_instance *svinst, void **context); -void sieve_extprograms_plugin_unload - (struct sieve_instance *svinst, void *context); - -/* - * Module interface - */ - -void sieve_extprograms_plugin_init(void); -void sieve_extprograms_plugin_deinit(void); - -#endif /* __SIEVE_EXTPROGRAMS_PLUGIN_H */ diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c index 3c78153..0c593bd 100644 --- a/src/sieve-tools/sieve-test.c +++ b/src/sieve-tools/sieve-test.c @@ -58,24 +58,21 @@ static void print_help(void) static void *sieve_smtp_open (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, - const char *return_path, struct ostream **output_r) + const char *return_path, FILE **file_r) { i_info("sending message from <%s> to <%s>:", ( return_path == NULL ? "" : return_path ), destination); printf("\nSTART MESSAGE:\n"); - *output_r = o_stream_create_fd(STDOUT_FILENO, (size_t)-1, FALSE); + *file_r = stdout; - return (void*)*output_r; + return NULL; } static bool sieve_smtp_close (const struct sieve_script_env *senv ATTR_UNUSED, void *handle ATTR_UNUSED) { - struct ostream *output = (struct ostream *)handle; - printf("END MESSAGE\n\n"); - o_stream_unref(&output); return TRUE; } diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c index 4754d5e..bf5e12c 100644 --- a/src/testsuite/ext-testsuite.c +++ b/src/testsuite/ext-testsuite.c @@ -106,11 +106,13 @@ static bool ext_testsuite_binary_load /* Extension object */ const struct sieve_extension_def testsuite_extension = { - .name = "vnd.dovecot.testsuite", - .validator_load = ext_testsuite_validator_load, - .generator_load = ext_testsuite_generator_load, - .interpreter_load = ext_testsuite_interpreter_load, - .binary_load = ext_testsuite_binary_load, + "vnd.dovecot.testsuite", + NULL, NULL, + ext_testsuite_validator_load, + ext_testsuite_generator_load, + ext_testsuite_interpreter_load, + ext_testsuite_binary_load, + NULL, NULL, SIEVE_EXT_DEFINE_OPERATIONS(testsuite_operations), SIEVE_EXT_DEFINE_OPERANDS(testsuite_operands) }; diff --git a/src/testsuite/testsuite-log.c b/src/testsuite/testsuite-log.c index 5a788fa..4e7e57b 100644 --- a/src/testsuite/testsuite-log.c +++ b/src/testsuite/testsuite-log.c @@ -31,9 +31,9 @@ struct _testsuite_log_message { }; static pool_t _testsuite_logmsg_pool = NULL; -ARRAY(struct _testsuite_log_message) _testsuite_log_errors; -ARRAY(struct _testsuite_log_message) _testsuite_log_warnings; -ARRAY(struct _testsuite_log_message) _testsuite_log_messages; +ARRAY_DEFINE(_testsuite_log_errors, struct _testsuite_log_message); +ARRAY_DEFINE(_testsuite_log_warnings, struct _testsuite_log_message); +ARRAY_DEFINE(_testsuite_log_messages, struct _testsuite_log_message); static inline void ATTR_FORMAT(3, 0) _testsuite_stdout_vlog (const char *prefix, const char *location, const char *fmt, diff --git a/src/testsuite/testsuite-settings.c b/src/testsuite/testsuite-settings.c index c448dcc..50e3eb7 100644 --- a/src/testsuite/testsuite-settings.c +++ b/src/testsuite/testsuite-settings.c @@ -16,14 +16,15 @@ struct testsuite_setting { char *value; }; -static HASH_TABLE(const char *, struct testsuite_setting *) settings; +static struct hash_table *settings; static const char *testsuite_setting_get (void *context, const char *identifier); void testsuite_settings_init(void) { - hash_table_create(&settings, default_pool, 0, str_hash, strcmp); + settings = hash_table_create + (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); sieve_tool_set_setting_callback(sieve_tool, testsuite_setting_get, NULL); } @@ -32,10 +33,12 @@ void testsuite_settings_deinit(void) { struct hash_iterate_context *itx = hash_table_iterate_init(settings); - const char *key; - struct testsuite_setting *setting; + void *key; + void *value; + + while ( hash_table_iterate(itx, &key, &value) ) { + struct testsuite_setting *setting = (struct testsuite_setting *) value; - while ( hash_table_iterate(itx, settings, &key, &setting) ) { i_free(setting->identifier); i_free(setting->value); i_free(setting); @@ -49,7 +52,7 @@ void testsuite_settings_deinit(void) static const char *testsuite_setting_get (void *context ATTR_UNUSED, const char *identifier) { - struct testsuite_setting *setting = + struct testsuite_setting *setting = (struct testsuite_setting *) hash_table_lookup(settings, identifier); if ( setting == NULL ) { @@ -61,7 +64,7 @@ static const char *testsuite_setting_get void testsuite_setting_set(const char *identifier, const char *value) { - struct testsuite_setting *setting = + struct testsuite_setting *setting = (struct testsuite_setting *) hash_table_lookup(settings, identifier); if ( setting != NULL ) { @@ -72,13 +75,13 @@ void testsuite_setting_set(const char *identifier, const char *value) setting->identifier = i_strdup(identifier); setting->value = i_strdup(value); - hash_table_insert(settings, identifier, setting); + hash_table_insert(settings, (void *) identifier, (void *) setting); } } void testsuite_setting_unset(const char *identifier) { - struct testsuite_setting *setting = + struct testsuite_setting *setting = (struct testsuite_setting *) hash_table_lookup(settings, identifier); if ( setting != NULL ) { diff --git a/src/testsuite/testsuite-smtp.c b/src/testsuite/testsuite-smtp.c index 01f72ca..7745dc6 100644 --- a/src/testsuite/testsuite-smtp.c +++ b/src/testsuite/testsuite-smtp.c @@ -3,7 +3,6 @@ #include "lib.h" #include "array.h" -#include "ostream.h" #include "unlink-directory.h" #include "sieve-common.h" @@ -25,7 +24,7 @@ struct testsuite_smtp_message { static pool_t testsuite_smtp_pool; static const char *testsuite_smtp_tmp; -static ARRAY(struct testsuite_smtp_message) testsuite_smtp_messages; +static ARRAY_DEFINE(testsuite_smtp_messages, struct testsuite_smtp_message); /* * Initialize @@ -69,17 +68,16 @@ void testsuite_smtp_reset(void) struct testsuite_smtp { const char *tmp_path; - struct ostream *output; + FILE *mfile; }; void *testsuite_smtp_open (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, - const char *return_path, struct ostream **output_r) + const char *return_path, FILE **file_r) { struct testsuite_smtp_message smtp_msg; struct testsuite_smtp *smtp; unsigned int smtp_count = array_count(&testsuite_smtp_messages); - int fd; smtp_msg.file = p_strdup_printf(testsuite_smtp_pool, "%s/%d.eml", testsuite_smtp_tmp, smtp_count); @@ -91,14 +89,12 @@ void *testsuite_smtp_open smtp = t_new(struct testsuite_smtp, 1); smtp->tmp_path = smtp_msg.file; + smtp->mfile = fopen(smtp->tmp_path, "w"); - if ( (fd=open(smtp->tmp_path, O_WRONLY | O_CREAT, 0600)) < 0 ) { - i_fatal("failed create tmp file for SMTP simulation: open(%s) failed: %m", - smtp->tmp_path); - } + if ( smtp->mfile == NULL ) + i_fatal("failed to open tmp file for SMTP simulation."); - smtp->output = o_stream_create_fd(fd, (size_t)-1, TRUE); - *output_r = smtp->output; + *file_r = smtp->mfile; return (void *) smtp; } @@ -108,7 +104,8 @@ bool testsuite_smtp_close { struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; - o_stream_unref(&smtp->output); + fclose(smtp->mfile); + return TRUE; } diff --git a/src/testsuite/testsuite-smtp.h b/src/testsuite/testsuite-smtp.h index 573b198..4894203 100644 --- a/src/testsuite/testsuite-smtp.h +++ b/src/testsuite/testsuite-smtp.h @@ -13,8 +13,8 @@ void testsuite_smtp_reset(void); */ void *testsuite_smtp_open - (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, - const char *return_path, struct ostream **output_r); + (const struct sieve_script_env *senv, const char *destination, + const char *return_path, FILE **file_r); bool testsuite_smtp_close (const struct sieve_script_env *senv, void *handle); diff --git a/src/testsuite/testsuite-variables.c b/src/testsuite/testsuite-variables.c index 46ac927..b80700a 100644 --- a/src/testsuite/testsuite-variables.c +++ b/src/testsuite/testsuite-variables.c @@ -77,7 +77,7 @@ bool testsuite_varnamespace_validate name_element = array_idx(var_name, 1); if ( name_element->num_variable >= 0 ) { sieve_argument_validate_error(valdtr, arg, - "testsuite: invalid variable name within testsuite namespace 'tst.%d': " + "testsuite: invalid variable name within testsuite namespace 'testsuite.%d': " "encountered numeric variable name", name_element->num_variable); return FALSE; } diff --git a/tests/extensions/environment/basic.svtest b/tests/extensions/environment/basic.svtest index bb0beb4..82f8981 100644 --- a/tests/extensions/environment/basic.svtest +++ b/tests/extensions/environment/basic.svtest @@ -24,10 +24,8 @@ test "Name" { } } -test "Location" { - if not environment "location" "MS" { - test_fail "wrong testsuite environment location"; +test "Host" { + if not environment "host" "host.example.com" { + test_fail "wrong testsuite hostname"; } } - - diff --git a/tests/extensions/environment/rfc.svtest b/tests/extensions/environment/rfc.svtest index c3177ae..70e38a6 100644 --- a/tests/extensions/environment/rfc.svtest +++ b/tests/extensions/environment/rfc.svtest @@ -22,7 +22,7 @@ test "Count" { test_fail "count should not match unknown environment item"; } - if not environment :count "eq" "location" "1" { + if not environment :count "eq" "host" "1" { test_fail "count of non-empty environment should be 1"; } } diff --git a/tests/extensions/include/execute/optional.sieve b/tests/extensions/include/execute/optional.sieve deleted file mode 100644 index a6ad479..0000000 --- a/tests/extensions/include/execute/optional.sieve +++ /dev/null @@ -1,5 +0,0 @@ -require "include"; - -include :optional "optional-1"; -include :optional "optional-2"; -include :optional "optional-3"; diff --git a/tests/extensions/include/included/optional-1.sieve b/tests/extensions/include/included/optional-1.sieve deleted file mode 100644 index 288d141..0000000 --- a/tests/extensions/include/included/optional-1.sieve +++ /dev/null @@ -1,9 +0,0 @@ -require "include"; -require "variables"; - -global "result"; - -set "result" "${result} ONE"; - -return; - diff --git a/tests/extensions/include/included/optional-2.sieve b/tests/extensions/include/included/optional-2.sieve deleted file mode 100644 index 11920f5..0000000 --- a/tests/extensions/include/included/optional-2.sieve +++ /dev/null @@ -1,9 +0,0 @@ -require "include"; -require "variables"; - -global "result"; - -set "result" "${result} TWO"; - -keep; - diff --git a/tests/extensions/include/optional.svtest b/tests/extensions/include/optional.svtest deleted file mode 100644 index 345f830..0000000 --- a/tests/extensions/include/optional.svtest +++ /dev/null @@ -1,40 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "include"; -require "variables"; - -global "result"; -set "result" ""; - -test "Included Optional" { - include :optional "optional-1"; - include :optional "optional-2"; - - if not string "${result}" " ONE TWO" { - test_fail "unexpected result value: ${result}"; - } - - # missing - include :optional "optional-3"; - - if not string "${result}" " ONE TWO" { - test_fail "unexpected result value after missing script: ${result}"; - } -} - - -test "Included Optional - Binary" { - if not test_script_compile "execute/optional.sieve" { - test_fail "failed to compile sieve script"; - } - - test_binary_save "optional"; - test_binary_load "optional"; - - if not test_script_run { - test_fail "failed to execute sieve script"; - } - - if not string "${result}" " ONE TWO" { - test_fail "unexpected result value: ${result}"; - } -} diff --git a/tests/plugins/extprograms/bin/addheader b/tests/plugins/extprograms/bin/addheader deleted file mode 100755 index 8f9805a..0000000 --- a/tests/plugins/extprograms/bin/addheader +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -echo "$1: $2" -cat - -exit 0 diff --git a/tests/plugins/extprograms/bin/cat b/tests/plugins/extprograms/bin/cat deleted file mode 100755 index 02b9858..0000000 --- a/tests/plugins/extprograms/bin/cat +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -cat diff --git a/tests/plugins/extprograms/bin/env b/tests/plugins/extprograms/bin/env deleted file mode 100755 index a7b81ac..0000000 --- a/tests/plugins/extprograms/bin/env +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -eval echo -n "\${$1}" diff --git a/tests/plugins/extprograms/bin/frame b/tests/plugins/extprograms/bin/frame deleted file mode 100755 index 225005e..0000000 --- a/tests/plugins/extprograms/bin/frame +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -echo -n "FRAMED $1{ " -cat -echo -n " }" - -exit 0 diff --git a/tests/plugins/extprograms/bin/modify b/tests/plugins/extprograms/bin/modify deleted file mode 100755 index ce87014..0000000 --- a/tests/plugins/extprograms/bin/modify +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -echo "X-Frop: Extra header" -cat -echo -echo "Extra body content!" - -exit 0 diff --git a/tests/plugins/extprograms/bin/program b/tests/plugins/extprograms/bin/program deleted file mode 100755 index 4b5edbf..0000000 --- a/tests/plugins/extprograms/bin/program +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -cat > /dev/null - -exit 0 diff --git a/tests/plugins/extprograms/bin/replace b/tests/plugins/extprograms/bin/replace deleted file mode 100755 index b010f06..0000000 --- a/tests/plugins/extprograms/bin/replace +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -cat > /dev/null - -echo "From: hatseflat@example.com" -echo "To: frutsel@example.org" -echo "Subject: replacement message" -echo -echo "Replaced!" - - -exit 0 diff --git a/tests/plugins/extprograms/bin/sleep10 b/tests/plugins/extprograms/bin/sleep10 deleted file mode 100755 index 8c1b96d..0000000 --- a/tests/plugins/extprograms/bin/sleep10 +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -sleep 10 diff --git a/tests/plugins/extprograms/bin/sleep2 b/tests/plugins/extprograms/bin/sleep2 deleted file mode 100755 index a814acd..0000000 --- a/tests/plugins/extprograms/bin/sleep2 +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -sleep 2 diff --git a/tests/plugins/extprograms/bin/stderr b/tests/plugins/extprograms/bin/stderr deleted file mode 100755 index 1dfd9bc..0000000 --- a/tests/plugins/extprograms/bin/stderr +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -echo "========================================" 1>&2 -echo "Test shell script successfully executed!" 1>&2 -echo 1>&2 -echo "Arguments: $1 $2" 1>&2 -echo 1>&2 -echo "Environment:" 1>&2 -env 1>&2 -echo 1>&2 -echo "Message:" 1>&2 -cat 1>&2 -echo "========================================" 1>&2 -echo 1>&2 - -echo "Subject: frop!" -echo "From: stephan@rename-it.nl" -echo "To: tss@example.com" -echo -echo "Frop!" diff --git a/tests/plugins/extprograms/errors.svtest b/tests/plugins/extprograms/errors.svtest deleted file mode 100644 index 148f4da..0000000 --- a/tests/plugins/extprograms/errors.svtest +++ /dev/null @@ -1,32 +0,0 @@ -require "vnd.dovecot.testsuite"; - -require "relational"; -require "comparator-i;ascii-numeric"; - -/* - * Invalid program names - */ - -test "Invalid Program Names" { - if test_script_compile "errors/programname.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { - test_fail "wrong number of errors reported"; - } -} - -/* - * Invalid arguments - */ - -test "Invalid Arguments" { - if test_script_compile "errors/arguments.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { - test_fail "wrong number of errors reported"; - } -} diff --git a/tests/plugins/extprograms/errors/arguments.sieve b/tests/plugins/extprograms/errors/arguments.sieve deleted file mode 100644 index 04f0aa0..0000000 --- a/tests/plugins/extprograms/errors/arguments.sieve +++ /dev/null @@ -1,5 +0,0 @@ -require "vnd.dovecot.pipe"; - -pipe :args "aaaa - aaaa" "frop"; - diff --git a/tests/plugins/extprograms/errors/programname.sieve b/tests/plugins/extprograms/errors/programname.sieve deleted file mode 100644 index 1d2d19c..0000000 --- a/tests/plugins/extprograms/errors/programname.sieve +++ /dev/null @@ -1,25 +0,0 @@ -require "variables"; -require "encoded-character"; -require "vnd.dovecot.pipe"; - -# Slash -pipe "../frop"; - -# More slashes -pipe "../../james/sieve/vacation"; - -# 0000-001F; [CONTROL CHARACTERS] -pipe "idiotic${unicode: 001a}"; - -# 007F; DELETE -pipe "idiotic${unicode: 007f}"; - -# 0080-009F; [CONTROL CHARACTERS] -pipe "idiotic${unicode: 0085}"; - -# 2028; LINE SEPARATOR -pipe "idiotic${unicode: 2028}"; - -# 2029; PARAGRAPH SEPARATOR -pipe "idiotic${unicode: 2029}"; - diff --git a/tests/plugins/extprograms/execute/command.svtest b/tests/plugins/extprograms/execute/command.svtest deleted file mode 100644 index 92c1fd1..0000000 --- a/tests/plugins/extprograms/execute/command.svtest +++ /dev/null @@ -1,27 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "vnd.dovecot.execute"; -require "variables"; - -test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.execute"; - -test "Basic" { - execute "program"; -} - -test "Input message" { - execute :pipe "program"; -} - -test "Input string" { - execute :input "DATA" "program"; -} - -test "Input variable" { - set "DATA" "DATA"; - execute :input "${DATA}" "program"; -} - -test "Output variable" { - execute :output "DATA" "program"; -} diff --git a/tests/plugins/extprograms/execute/errors.svtest b/tests/plugins/extprograms/execute/errors.svtest deleted file mode 100644 index b08e77c..0000000 --- a/tests/plugins/extprograms/execute/errors.svtest +++ /dev/null @@ -1,35 +0,0 @@ -require "vnd.dovecot.testsuite"; - -require "relational"; -require "comparator-i;ascii-numeric"; - -test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.execute"; - -/* - * Command syntax - */ - -test "Command syntax" { - if test_script_compile "errors/syntax.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "13" { - test_fail "wrong number of errors reported"; - } -} - -/* - * Variables - */ - -test "Variables" { - if test_script_compile "errors/variables.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { - test_fail "wrong number of errors reported"; - } -} diff --git a/tests/plugins/extprograms/execute/errors/syntax.sieve b/tests/plugins/extprograms/execute/errors/syntax.sieve deleted file mode 100644 index bd10ee5..0000000 --- a/tests/plugins/extprograms/execute/errors/syntax.sieve +++ /dev/null @@ -1,38 +0,0 @@ -require "vnd.dovecot.execute"; - -# 1: error: no arguments -execute; - -# 2: error: numeric argument -execute 1; - -# 3: error: tag argument -execute :frop; - -# 4: error: numeric second argument -execute "sdfd" 1; - -# 5: error: stringlist first argument -execute ["sdfd","werwe"] "sdfs"; - -# 6: error: too many arguments -execute "sdfs" "sdfd" "werwe"; - -# 7: error: inappropriate :copy argument -execute :copy "234234" ["324234", "23423"]; - -# 8: error: invalid :input argument; missing parameter -execute :input "frop"; - -# 9: error: invalid :input argument; invalid parameter -execute :input 1 "frop"; - -# 10: error: invalid :input argument; invalid parameter -execute :input ["23423","21342"] "frop"; - -# 11: error: invalid :input argument; invalid parameter -execute :input :frop "frop"; - -# 12: error: :output not allowed without variables extension -execute :output "${frop}" "frop"; - diff --git a/tests/plugins/extprograms/execute/errors/variables.sieve b/tests/plugins/extprograms/execute/errors/variables.sieve deleted file mode 100644 index 3d0b3e7..0000000 --- a/tests/plugins/extprograms/execute/errors/variables.sieve +++ /dev/null @@ -1,7 +0,0 @@ -require "vnd.dovecot.execute"; -require "variables"; - -# 1: invalid variable name -execute :output "wqwe-aeqwe" "frop"; - - diff --git a/tests/plugins/extprograms/execute/execute.svtest b/tests/plugins/extprograms/execute/execute.svtest deleted file mode 100644 index bd2d2d3..0000000 --- a/tests/plugins/extprograms/execute/execute.svtest +++ /dev/null @@ -1,104 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "vnd.dovecot.execute"; -require "vnd.dovecot.debug"; -require "variables"; -require "relational"; -require "environment"; - -test_set "message" text: -From: stephan@example.com -To: pipe@example.net -Subject: Frop! - -Frop! -. -; - -test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.execute"; -test_result_reset; - -test "Execute - bare" { - execute "program"; -} - -test_result_reset; -test "Execute - i/o" { - execute :input "FROP" :output "out" "frame"; - - if not string "${out}" "FRAMED { FROP }" { - test_fail "wrong string returned: ${out}"; - } -} - -test_result_reset; -test "Execute - i/o and arguments" { - execute :input "FROP" :output "out" "frame" ["FRIEP "]; - - if not string "${out}" "FRAMED FRIEP { FROP }" { - test_fail "wrong string returned: ${out}"; - } -} - -test_result_reset; -test "Execute - pipe" { - execute :pipe :output "msg" "cat"; - - if not string :contains "${msg}" "Subject: Frop!" { - test_fail "wrong string returned: ${out}"; - } -} - -test_result_reset; -test "Execute - env" { - test_set "envelope.from" "stephan@sub.example.com"; - test_set "envelope.to" "stephan@sub.example.net"; - test_set "envelope.orig_to" "all@sub.example.net"; - - execute :output "out" "env" "SENDER"; - if not string :is "${out}" "stephan@sub.example.com" { - test_fail "wrong SENDER env returned: '${out}'"; - } - - execute :output "out" "env" "RECIPIENT"; - if not string :is "${out}" "stephan@sub.example.net" { - test_fail "wrong RECIPIENT env returned: '${out}'"; - } - - execute :output "out" "env" "ORIG_RECIPIENT"; - if not string :is "${out}" "all@sub.example.net" { - test_fail "wrong ORIG_RECIPIENT env returned: '${out}'"; - } - - execute :output "out" "env" "HOST"; - if not environment :is "host" "${out}" { - test_fail "wrong HOST env returned: '${out}'"; - } - - execute :output "out" "env" "HOME"; - if string :count "eq" "${out}" "0" { - test_fail "empty HOME env returned"; - } - - execute :output "out" "env" "USER"; - if string :count "eq" "${out}" "0" { - test_fail "empty USER env returned"; - } -} - -test_result_reset; -test "Execute - used as test" { - if execute :pipe :output "msg" "dog" { - test_fail "execute action indicated success with invalid program"; - } - - if not execute :pipe :output "msg" "cat" { - test_fail "execute action indicated failure with valid program"; - } - - if not string :contains "${msg}" "Subject: Frop!" { - test_fail "wrong string returned: ${out}"; - } -} - - diff --git a/tests/plugins/extprograms/filter/command.svtest b/tests/plugins/extprograms/filter/command.svtest deleted file mode 100644 index 50f949a..0000000 --- a/tests/plugins/extprograms/filter/command.svtest +++ /dev/null @@ -1,10 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "vnd.dovecot.filter"; -require "variables"; - -test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.filter"; - -test "Basic" { - filter "program"; -} diff --git a/tests/plugins/extprograms/filter/errors.svtest b/tests/plugins/extprograms/filter/errors.svtest deleted file mode 100644 index 3461c01..0000000 --- a/tests/plugins/extprograms/filter/errors.svtest +++ /dev/null @@ -1,21 +0,0 @@ -require "vnd.dovecot.testsuite"; - -require "relational"; -require "comparator-i;ascii-numeric"; - -/* - * Command syntax - */ - -test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.filter"; - -test "Command syntax" { - if test_script_compile "errors/syntax.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { - test_fail "wrong number of errors reported"; - } -} diff --git a/tests/plugins/extprograms/filter/errors/syntax.sieve b/tests/plugins/extprograms/filter/errors/syntax.sieve deleted file mode 100644 index 00a3a23..0000000 --- a/tests/plugins/extprograms/filter/errors/syntax.sieve +++ /dev/null @@ -1,22 +0,0 @@ -require "vnd.dovecot.filter"; - -# 1: error: no arguments -filter; - -# 2: error: numeric argument -filter 1; - -# 3: error: tag argument -filter :frop; - -# 4: error: numeric second argument -filter "sdfd" 1; - -# 5: error: stringlist first argument -filter ["sdfd","werwe"] "sdfs"; - -# 6: error: too many arguments -filter "sdfd" "werwe" "sdfs"; - -# 7: error: inappropriate :copy argument -filter :try :copy "234234" ["324234", "23423"]; diff --git a/tests/plugins/extprograms/filter/execute.svtest b/tests/plugins/extprograms/filter/execute.svtest deleted file mode 100644 index a952c55..0000000 --- a/tests/plugins/extprograms/filter/execute.svtest +++ /dev/null @@ -1,180 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "vnd.dovecot.filter"; -require "vnd.dovecot.debug"; -require "variables"; -require "editheader"; -require "body"; -require "fileinto"; -require "mailbox"; - -test_set "message" text: -From: stephan@example.com -To: pipe@example.net -Subject: Frop! - -Frop! -. -; - -test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.filter"; -test_result_reset; - -test_result_reset; -test "Replace" { - if header :contains "subject" "replacement" { - test_fail "message already replaced"; - } - - filter "replace"; - - if not header :contains "subject" "replacement" { - test_fail "message not replaced"; - } -} - -test_result_reset; -test "Used as test" { - if filter "nonsense" { - test_fail "filter action indicated success with invalid program"; - } - - if not filter "replace" { - test_fail "filter action indicated failure with valid program"; - } - - if not header :contains "subject" "replacement" { - test_fail "message not replaced; filter not actually executed"; - } -} - -test_result_reset; -test "Modify" { - if anyof ( - body :contains "extra", - exists "x-frop") { - test_fail "message already modified"; - } - - if not header "subject" "Frop!" { - test_fail "message is wrong"; - } - - filter "modify"; - - if not header "subject" "Frop!" { - test_fail "message replaced erroneously"; - } - - if not header :contains "x-frop" "extra" { - test_fail "message header not modified"; - } - - if not body :contains "Extra" { - test_fail "message body not modified"; - } -} - -test_result_reset; -test "Editheader" { - if anyof ( exists "X-A", exists "X-B", exists "X-C", exists "X-D", - exists "X-E") { - test_fail "message already modified"; - } - - addheader "X-A" "1"; - if not header "X-A" "1" { - test_fail "X-A header missing"; - } - - fileinto :create "A"; - - filter "addheader" ["X-B", "2"]; - if not header "X-B" "2" { - test_fail "X-B header missing"; - } - - fileinto :create "B"; - - addheader "X-C" "3"; - if not header "X-C" "3" { - test_fail "X-C header missing"; - } - - fileinto :create "C"; - - filter "addheader" ["X-D", "4"]; - if not header "X-D" "4" { - test_fail "X-D header missing"; - } - - fileinto :create "D"; - - addheader "X-E" "5"; - if not header "X-E" "5" { - test_fail "X-E header missing"; - } - - fileinto :create "E"; - - if not test_result_execute { - test_fail "failed to execute result"; - } - - test_message :folder "A" 0; - - if not header "X-A" "1" { - test_fail "X-A header missing"; - } - if anyof ( - header "X-B" "2", header "X-C" "3", - header "X-D" "4", header "X-E" "5") { - test_fail "X-B, X-C, X-D or X-E header found"; - } - - test_message :folder "B" 0; - - if not header "X-B" "2" { - test_fail "X-B header missing"; - } - if anyof ( - header "X-C" "3", header "X-D" "4", header "X-E" "5") { - test_fail "X-C, X-D or X-E header found"; - } - - test_message :folder "C" 0; - - if not header "X-C" "3" { - test_fail "X-C header missing"; - } - if anyof (header "X-D" "4", header "X-E" "5") { - test_fail "X-D or X-E header found"; - } - - test_message :folder "D" 0; - - if not header "X-D" "4" { - test_fail "X-D header missing"; - } - if anyof (header "X-E" "5") { - test_fail "X-E header found"; - } - - test_message :folder "E" 0; - - if not header "X-A" "1" { - test_fail "X-A header missing in final message"; - } - if not header "X-B" "2" { - test_fail "X-B header missing in final message"; - } - if not header "X-C" "3" { - test_fail "X-C header missing in final message"; - } - if not header "X-D" "4" { - test_fail "X-D header missing in final message"; - } - if not header "X-E" "5" { - test_fail "X-E header missing in final message"; - } -} diff --git a/tests/plugins/extprograms/pipe/command.svtest b/tests/plugins/extprograms/pipe/command.svtest deleted file mode 100644 index dabd970..0000000 --- a/tests/plugins/extprograms/pipe/command.svtest +++ /dev/null @@ -1,10 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "vnd.dovecot.pipe"; - -test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.pipe"; - -test "Basic" { - pipe "program"; -} - diff --git a/tests/plugins/extprograms/pipe/errors.svtest b/tests/plugins/extprograms/pipe/errors.svtest deleted file mode 100644 index 4987fbc..0000000 --- a/tests/plugins/extprograms/pipe/errors.svtest +++ /dev/null @@ -1,58 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "variables"; - -require "relational"; -require "comparator-i;ascii-numeric"; - -/* - * Command syntax - */ - -test "Command syntax" { - if test_script_compile "errors/syntax.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { - test_fail "wrong number of errors reported"; - } -} - -/* Timeout */ - -test_set "message" text: -From: stephan@example.com -To: pipe@example.net -Subject: Frop! - -Frop! -. -; - -test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; -test_config_set "sieve_pipe_exec_timeout" "1s"; -test_config_reload :extension "vnd.dovecot.pipe"; -test_result_reset; - -test "Timeout" { - if not test_script_compile "errors/timeout.sieve" { - test_fail "compile failed"; - } - - if not test_script_run { - test_fail "execute failed"; - } - - if test_result_execute { - test_fail "pipe should have timed out"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { - test_fail "wrong number of errors reported"; - } - - if not test_error :index 1 :contains "failed to pipe" { - test_fail "wrong error reported"; - } - -} diff --git a/tests/plugins/extprograms/pipe/errors/syntax.sieve b/tests/plugins/extprograms/pipe/errors/syntax.sieve deleted file mode 100644 index 64d5310..0000000 --- a/tests/plugins/extprograms/pipe/errors/syntax.sieve +++ /dev/null @@ -1,22 +0,0 @@ -require "vnd.dovecot.pipe"; - -# 1: error: no arguments -pipe; - -# 2: error: numeric argument -pipe 1; - -# 3: error: tag argument -pipe :frop; - -# 4: error: numeric second argument -pipe "sdfd" 1; - -# 5: error: stringlist first argument -pipe ["sdfd","werwe"] "sdfs"; - -# 6: error: too many arguments -pipe "sdfd" "werwe" "sdfs"; - -# 7: error: inappropriate :copy argument -pipe :try :copy "234234" ["324234", "23423"]; diff --git a/tests/plugins/extprograms/pipe/errors/timeout.sieve b/tests/plugins/extprograms/pipe/errors/timeout.sieve deleted file mode 100644 index 7a940c8..0000000 --- a/tests/plugins/extprograms/pipe/errors/timeout.sieve +++ /dev/null @@ -1,3 +0,0 @@ -require "vnd.dovecot.pipe"; - -pipe "sleep10"; diff --git a/tests/plugins/extprograms/pipe/execute.svtest b/tests/plugins/extprograms/pipe/execute.svtest deleted file mode 100644 index 34b6798..0000000 --- a/tests/plugins/extprograms/pipe/execute.svtest +++ /dev/null @@ -1,56 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "vnd.dovecot.pipe"; -require "vnd.dovecot.debug"; -require "variables"; - -test_set "message" text: -From: stephan@example.com -To: pipe@example.net -Subject: Frop! - -Frop! -. -; - -/* Basic pipe */ - -test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; -test_config_reload :extension "vnd.dovecot.pipe"; -test_result_reset; - -test "Pipe" { - pipe "stderr" ["ONE", "TWO"]; - - if not test_result_execute { - test_fail "failed to pipe message to script"; - } -} - -/* Timeout */ - -test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; -test_config_set "sieve_pipe_exec_timeout" "3s"; -test_config_reload :extension "vnd.dovecot.pipe"; -test_result_reset; - -test "Timeout 3s" { - pipe "sleep2"; - - if not test_result_execute { - test_fail "failed to pipe message to script"; - } -} - -test_result_reset; -test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; -test_config_set "sieve_pipe_exec_timeout" "0"; -test_config_reload :extension "vnd.dovecot.pipe"; -test_result_reset; - -test "Timeout infinite" { - pipe "sleep2"; - - if not test_result_execute { - test_fail "failed to pipe message to script"; - } -} -- 1.7.10.2 From e1f42eba4e4f66962000b16929a85d4c90e40d9d Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Fri, 24 May 2013 13:07:23 +0200 Subject: [PATCH] managesieve-login: Fixed '[' ']' stupidity for response codes. Emerged when Sieve and ManageSieve were merged into Pigeonhole. diff --git a/src/managesieve-login/client.c b/src/managesieve-login/client.c index ec14114..b4220dc 100644 --- a/src/managesieve-login/client.c +++ b/src/managesieve-login/client.c @@ -372,9 +372,9 @@ void _client_send_response(struct client *client, str_append(line, oknobye); if (resp_code != NULL) { - str_append(line, " ["); + str_append(line, " ("); str_append(line, resp_code); - str_append_c(line, ']'); + str_append_c(line, ')'); } if ( msg != NULL ) -- 1.7.10.2 From 8e6fff6053e715ba8d8b3b08f0540ec399a962c8 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Fri, 24 May 2013 13:12:24 +0200 Subject: [PATCH] Merged changes from Pigeonhole v0.3 tree. diff --git a/INSTALL b/INSTALL index edfed5e..d584340 100644 --- a/INSTALL +++ b/INSTALL @@ -127,7 +127,8 @@ plugin section of the config file (default values are shown if applicable): The Pigeonhole Sieve interpreter can have plugins of its own. Using this setting, the used plugins can be specified. Check the Dovecot wiki (wiki2.dovecot.org) or the pigeonhole website (http://pigeonhole.dovecot.org) - for available plugins. + for available plugins. The sieve_extprograms plugin is included in this + release. sieve_user_log = The path to the file where the user log file is written. If not configured, a @@ -367,7 +368,7 @@ Sieve Interpreter - Extension Configuration configuration options. Refer to doc/extensions/include.txt for settings specific to the include extension. -- Spamtest and Virustest extensions: +- Spamtest and virustest extensions: Using the spamtest and virustest extensions (RFC 5235), the Sieve language provides a uniform and standardized command interface for evaluating spam and @@ -382,6 +383,29 @@ Sieve Interpreter - Extension Configuration configuration and are not enabled for use by default. Refer to doc/extensions/spamtest-virustest.txt for configuration information. +- Vnd.dovecot.duplicate extension: + + The vnd.dovecot.duplicate extension augments the Sieve filtering + implementation with a test that allows detecting and handling duplicate + message deliveries, e.g. as caused by mailinglists when people reply both to + the mailinglist and the user directly. + + The vnd.dovecot.duplicate extension requires explicit configuration and is not + enabled for use by default. Refer to doc/extensions/vnd.dovecot.duplicate.txt + for configuration information. + +- Extprograms plugin; + vnd.dovovecot.pipe, vnd.dovecot.filter, vnd.dovecot.execute extensions: + + The "sieve_extprograms" plugin provides extensions to the Sieve filtering + language adding new action commands for invoking a predefined set of external + programs. Messages can be piped to or filtered through those programs and + string data can be input to and retrieved from those programs. + + This plugin and the extensions it provides require explicit configuration and + are not enabled for use by default. Refer to doc/plugins/sieve_extprograms.txt + for more information. + Sieve Interpreter - Migration from CMUSieve (Dovecot v1.0/v1.1) --------------------------------------------------------------- @@ -621,7 +645,8 @@ interpreter on your particular platform. Note that the test suite is not available when this package is compiled against the Dovecot headers only. The test suite executes a list of test cases and halts when one of them fails. If it executes all test cases successfully, the test suite finishes. You can execute -the test suite using `make test`. +the basic test suite using `make test`, which does not include the plugins. You +can test the plugins using `make test-plugins`. A failing test case is always a bug and a report is greatly appreciated. diff --git a/Makefile.am b/Makefile.am index 6e91416..50d3ba8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,7 @@ ChangeLog: .hg/dirstate hg log --style=changelog > ChangeLog endif -# Testsuite tests +# Testsuite tests (FIXME: ugly) TESTSUITE_BIN = $(top_builddir)/src/testsuite/testsuite $(TESTSUITE_OPTIONS) @@ -76,6 +76,7 @@ test_cases = \ tests/extensions/include/variables.svtest \ tests/extensions/include/once.svtest \ tests/extensions/include/twice.svtest \ + tests/extensions/include/optional.svtest \ tests/extensions/include/rfc.svtest \ tests/extensions/include/execute.svtest \ tests/extensions/imap4flags/basic.svtest \ @@ -147,8 +148,26 @@ test_cases = \ $(test_cases): @$(TEST_BIN) $(top_srcdir)/$@ -.PHONY: $(test_cases) +TEST_EXTPROGRAMS_BIN = $(TEST_BIN) \ + -P src/plugins/sieve-extprograms/.libs/sieve_extprograms +extprograms_test_cases = \ + tests/plugins/extprograms/errors.svtest \ + tests/plugins/extprograms/pipe/command.svtest \ + tests/plugins/extprograms/pipe/errors.svtest \ + tests/plugins/extprograms/pipe/execute.svtest \ + tests/plugins/extprograms/filter/command.svtest \ + tests/plugins/extprograms/filter/errors.svtest \ + tests/plugins/extprograms/filter/execute.svtest \ + tests/plugins/extprograms/execute/command.svtest \ + tests/plugins/extprograms/execute/errors.svtest \ + tests/plugins/extprograms/execute/execute.svtest + +$(extprograms_test_cases): + @$(TEST_EXTPROGRAMS_BIN) $(top_srcdir)/$@ + +.PHONY: $(test_cases) $(extprograms_test_cases) test: $(test_cases) +test-plugins: $(extprograms_test_cases) check: check-am test all-am diff --git a/NEWS b/NEWS index 9c0842d..70c7929 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,23 @@ +v0.4.0 09-05-2013 Stephan Bosch + + + Added doveadm-sieve plugin that provides the possibility to synch Sieve + scripts using doveadm sync along with the user's mailboxes. + + Added the Sieve extprograms plugin to the main Pigeonhole package. It is + still a plugin, but it is now included so that a separate compile is no + longer necessary and distributors are likely to include it. The extprograms + plugin provides Sieve language extensions that allows executing + (administrator-controlled) external programs for message delivery, + message filtering and string manipulation. Refer to + doc/plugins/sieve_extprograms.txt for more information. + + Added debug message showing Pigeonhole version at initialization. Makes it + very clear that the plugin is properly loaded. + + Finished implementation of the Sieve include extension. It should now + fully conform to RFC 6609. The main addition is the new :optional tag which + makes the include command ignore missing included scripts without an error. + + Finished implementation of the Sieve environment extension as much as + possible. Environment items "location", "phase" and "domain" now also + return a usable value. + v0.3.5 09-05-2013 Stephan Bosch - Sieve editheader extension: fixed interaction with the Sieve body extension. @@ -29,7 +49,7 @@ v0.3.4 06-04-2013 Stephan Bosch sieve_vacation_dont_check_recipient is active. - Sieve tools: the -D option wasn't enabled and documented for all tools. - Siev dict script storage: fixed potential segfault occuring when dict - initialization fails. + initialization fails. - ManageSieve: fixed bug in skipping of CRLF at end of AUTHENTICATE command. - ManageSieve: fixed handling of unkown commands pre-login. - Fixed compile on Mageia Linux. diff --git a/README b/README index 4e3c00a..d4e68b4 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Pigeonhole for Dovecot v2.1 +Pigeonhole for Dovecot v2.2 Introduction ============ @@ -100,17 +100,15 @@ following list outlines the implementation status of each supported extension: copy (RFC 3894): fully supported. body (RFC 5173): almost fully supported, but the text body-transform - implementation is simple and some issues make it still not completely - RFC compliant. - environment (RFC 5183): basic support is provided (v0.1.5+). + implementation is simple. + environment (RFC 5183): fully supported (v0.4.0+). variables (RFC 5229): fully supported. vacation (RFC 5230): fully supported. + vacation-seconds (RFC 6131): fully supported (v0.2.3+). relational (RFC 5231): fully supported. imap4flags (RFC 5232): fully supported. subaddress (RFC 5233): fully supported, but with limited configurability. - spamtest and virustest (RFC 5235): fully supported (v0.1.16+), but - currently considered experimental. + spamtest and virustest (RFC 5235): fully supported (v0.1.16+). date (RFC 5260; Section 4): fully supported (v0.1.12+). editheader (RFC 5293): fully supported (v0.3.0+). reject (RFC 5429; Section 2.2): fully supported. @@ -121,8 +119,7 @@ following list outlines the implementation status of each supported extension: ihave (RFC 5463): fully supported (v0.2.4+). mailbox (RFC 5490; Section 3): fully supported (v0.1.10+), but ACL permissions are not verified for mailboxexists. - include (draft v05; not latest version): almost fully supported, but - interaction with ManageSieve is not in accordance with specification. + include (RFC 6609): fully supported (v0.4.0+) regex (draft v08; not latest version): almost fully supported, but UTF-8 is not supported. @@ -134,6 +131,22 @@ following list outlines the implementation status of each supported extension: The availability of these deprecated extensions is disabled by default. + The following Dovecot-specific Sieve extensions are available: + + vnd.dovecot.debug (v0.3.0+): + Allows logging debug messages + vnd.dovecot.duplicate (v0.3.1+): + Allows detecting duplicate message deliveries based on message ID and + other criteria. + vnd.dovecot.pipe (v0.4.0+; sieve_extprograms plugin): + Implements piping messages to a pre-defined set of external programs + vnd.dovecot.filter (v0.4.0+; sieve_extprograms plugin): + Implements filtering messages through a pre-defined set of external + programs + vnd.dovecot.execute (v0.4.0+; sieve_extprograms plugin): + Implements executing a pre-defined set of external programs with the + option to process string data through the external program + The following extensions are under development: ereject (RFC 5429; page 4): implemented, but currently equal to reject @@ -161,19 +174,23 @@ exist in the src/sieve-tools/ directory of this package. After installation, these are available at your $prefix/bin directory. The following commands are installed: -sievec - Compiles sieve scripts into a binary representation for later - execution. Refer to the next section on manually compiling Sieve - scripts. +sievec - Compiles sieve scripts into a binary representation for later + execution. Refer to the next section on manually compiling Sieve + scripts. + +sieve-test - This is a universal Sieve test tool for testing the effect of a + Sieve script on a particular message. It allows compiling, + running and testing Sieve scripts. It can either be used to + display the actions that would be performed on the provided test + message or it can be used to test the actual delivery of the + message and show the messages that would normally be sent through + SMTP. -sieve-test - This is a universal Sieve test tool for testing the effect of a - Sieve script on a particular message. It allows compiling, running - and testing Sieve scripts. It can either be used to display the - actions that would be performed on the provided test message or it - can be used to test the actual delivery of the message and show the - messages that would normally be sent through SMTP. +sieve-dump - Dumps the content of a Sieve binary file for (development) + debugging purposes. -sieve-dump - Dumps the content of a Sieve binary file for (development) - debugging purposes. +sieve-filter - Allow running Sieve filters on messages already stored in a + mailbox. When installed, man pages are also available for these commands. In this package the man pages are present in doc/man and can be viewed before install using diff --git a/TODO b/TODO index 81c8766..7771e43 100644 --- a/TODO +++ b/TODO @@ -1,28 +1,21 @@ -Active development is moved to Pigeonhole v0.4 for Dovecot v2.2. The v0.3.x -series for Dovecot v2.1 is maintained for bug fixes and small updates. Check -http://hg.rename-it.nl/dovecot-2.2-pigeonhole/raw-file/tip/TODO for the most -up-to-date TODO list. +Current activities: -Parallel plugin-based efforts: - -* Implement enotify xmpp method as a plugin. -* Implement metadata and servermetadata extensions as a plugin. - - Compiles against dovecot metadata plugin, as currently developed by - Dennis Schridde. - -Open TODO issues for this revision: - -* 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). * 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 infrastructure for loading a sequence of global scripts from + a database (for sieve_before/sieve_after). + +Parallel plugin-based efforts (on hold at the moment): + +* Implement enotify xmpp method as a plugin. +* Implement metadata and servermetadata extensions as a plugin. + - Update to native Dovecot metadata implementation once it is created. + +Next (mostly in order of descending priority/precedence): + * Implement index extension * Add normalize() method to comparators to normalize the string before matching (for efficiency). @@ -34,7 +27,6 @@ Open TODO issues for this revision: (posix regexes actually do support utf8, but only when locale is set accordingly) * Finish body extension: - - Implement proper :content "multipart" behavior - Build test cases for decoding MIME encodings to UTF-8 * Cleanup the test suite - Restructure test scripts @@ -51,6 +43,13 @@ Open TODO issues for this revision: - Verify outgoing mail addresses at runtime when necessary (e.g. after variables substitution) - Improve handling of invalid addresses in headers (requires Dovecot changes) +* Improve sieve_extprograms plugin: + - Redesign (forcible) local script termination. It should use SIGCHLD and + a ioloop-based timeout. + - Add facility to trigger a temporary failure condition when a program + fails rather than an implicit keep. + - Add a method to implicitly pass environment variables such as SENDER and + RECIPIENT through the script socket service. * Properly implement Sieve internationalization support (utf-8 handling), currently it is not complete: - Make this implementation fully conform section 2.7.2 of RFC5228 (Comparisons @@ -63,7 +62,8 @@ Open TODO issues for this revision: - Review the specification documents and check whether the given requirements are tested at least once. * Fix ManageSieve proxy to recognize response codes from the backend and forward - them to the user if appropriate/safe. + them to the user if appropriate/safe. Probably means implementing a proper + ManageSieve client library. * Test ManageSieve behavior thoroughly: - Test pipelined behavior - Test proxy authentication @@ -74,7 +74,7 @@ Open TODO issues for this revision: * Build a server with test mail accounts that processes lots and lots of mail (e.g. spam, mailing lists etc.) -* ## MAKE A FOURTH MAIN RELEASE (0.4.x) ## +Low priority items: * Implement extlists extension as a plugin * Enotify extension: detect use of variable values extracted from the message @@ -90,8 +90,8 @@ Open TODO issues for this revision: * Warn during compile if using non-existent folders. * Implement IMAP plugin for IMAPSieve support: - - Requires the same Sieve transaction support as the sieve-filter tool needs. - - Requires (IMAP) metadata support in Dovecot. + - Requires some sort of Sieve transaction support. + - Requires (IMAP) METADATA support in Dovecot. - This may include support for manually running a script on a set of messages through IMAP (no specification for something like this is available; we will have to provide our own) diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0c71710 --- /dev/null +++ b/configure.ac @@ -0,0 +1,139 @@ +AC_INIT([Pigeonhole], [0.4.0], [dovecot@dovecot.org], [dovecot-2.2-pigeonhole]) +AC_CONFIG_AUX_DIR([.]) +AC_CONFIG_SRCDIR([src]) +AC_CONFIG_MACRO_DIR([m4]) + +# Autoheader is not needed and does more harm than good for this package. However, it is +# tightly integrated in autoconf/automake and therefore it is difficult not to use it. As +# a workaround we give autoheader a dummy config header to chew on and we handle the +# real config header ourselves. +AC_CONFIG_HEADERS([dummy-config.h pigeonhole-config.h]) + +AC_DEFINE_UNQUOTED(PIGEONHOLE_NAME, "$PACKAGE_NAME", + [Define to the full name of Pigeonhole for Dovecot.]) +AC_DEFINE_UNQUOTED(PIGEONHOLE_VERSION, "$PACKAGE_VERSION", + [Define to the version of Pigeonhole for Dovecot.]) + +AM_INIT_AUTOMAKE([no-define foreign tar-ustar]) + +AM_MAINTAINER_MODE + +AC_PROG_CC +AC_PROG_CPP +AC_PROG_LIBTOOL + +# Couple with Dovecot +# + +DC_DOVECOT +DC_DOVECOT_MODULEDIR +LIBDOVECOT_INCLUDE="$LIBDOVECOT_INCLUDE $LIBDOVECOT_STORAGE_INCLUDE" +CFLAGS="$DOVECOT_CFLAGS" +LIBS="$DOVECOT_LIBS" +AC_SUBST(LIBDOVECOT_INCLUDE) + +# Define Sieve documentation install dir +# + +sieve_docdir='${dovecot_docdir}/sieve' +AC_SUBST(sieve_docdir) + +# Extensions under development +# + +AC_ARG_WITH(unfinished-features, +[AC_HELP_STRING([--with-unfinished-features], + [Build unfinished new features/extensions [default=no]])], + if test x$withval = xno || test x$withval = xauto; then + want_unfinished_features=$withval + else + want_unfinished_features=yes + fi, + want_unfinished_features=no) +AM_CONDITIONAL(BUILD_UNFINISHED, test "$want_unfinished_features" = "yes") + +if test "$want_unfinished_features" = "yes"; then + AC_DEFINE(HAVE_SIEVE_UNFINISHED,, + [Define to build unfinished features/extensions.]) +fi + +# +# + +AC_ARG_WITH(docs, +[ --with-docs Install documentation (default)], + if test x$withval = xno; then + want_docs=no + else + want_docs=yes + fi, + want_docs=yes) +AM_CONDITIONAL(BUILD_DOCS, test "$want_docs" = "yes") + +AC_ARG_ENABLE(valgrind, +[AC_HELP_STRING([--enable-valgrind], [Enable Valgrind memory leak checks in testsuite [default=no]])], + if test x$enableval = xno || test x$enableval = xauto; then + want_valgrind=$enableval + else + want_valgrind=yes + fi, + want_valgrind=no) +AM_CONDITIONAL(TESTSUITE_VALGRIND, test "$want_valgrind" = "yes") + +AC_ARG_WITH(managesieve, +[AC_HELP_STRING([--with-managesieve], + [Build ManageSieve service [default=yes]])], + if test x$withval = xno || test x$withval = xauto; then + want_managesieve=$withval + else + want_managesieve=yes + fi, + want_managesieve=yes) +AM_CONDITIONAL(BUILD_MANAGESIEVE, test "$want_managesieve" = "yes") + +AC_CONFIG_FILES([ +Makefile +doc/Makefile +doc/man/Makefile +doc/example-config/Makefile +doc/example-config/conf.d/Makefile +doc/rfc/Makefile +src/Makefile +src/lib-sieve/Makefile +src/lib-sieve/plugins/Makefile +src/lib-sieve/plugins/vacation/Makefile +src/lib-sieve/plugins/subaddress/Makefile +src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile +src/lib-sieve/plugins/relational/Makefile +src/lib-sieve/plugins/regex/Makefile +src/lib-sieve/plugins/imap4flags/Makefile +src/lib-sieve/plugins/copy/Makefile +src/lib-sieve/plugins/include/Makefile +src/lib-sieve/plugins/body/Makefile +src/lib-sieve/plugins/variables/Makefile +src/lib-sieve/plugins/enotify/Makefile +src/lib-sieve/plugins/enotify/mailto/Makefile +src/lib-sieve/plugins/notify/Makefile +src/lib-sieve/plugins/environment/Makefile +src/lib-sieve/plugins/mailbox/Makefile +src/lib-sieve/plugins/date/Makefile +src/lib-sieve/plugins/spamvirustest/Makefile +src/lib-sieve/plugins/ihave/Makefile +src/lib-sieve/plugins/editheader/Makefile +src/lib-sieve/plugins/vnd.dovecot/Makefile +src/lib-sieve/plugins/vnd.dovecot/debug/Makefile +src/lib-sieve/plugins/vnd.dovecot/duplicate/Makefile +src/lib-sieve-tool/Makefile +src/lib-sievestorage/Makefile +src/lib-managesieve/Makefile +src/plugins/Makefile +src/plugins/doveadm-sieve/Makefile +src/plugins/lda-sieve/Makefile +src/plugins/sieve-extprograms/Makefile +src/sieve-tools/Makefile +src/managesieve/Makefile +src/managesieve-login/Makefile +src/testsuite/Makefile +stamp.h]) + +AC_OUTPUT diff --git a/configure.in b/configure.in deleted file mode 100644 index 834aa41..0000000 --- a/configure.in +++ /dev/null @@ -1,137 +0,0 @@ -AC_INIT([Pigeonhole], [0.3.5], [dovecot@dovecot.org], [dovecot-2.1-pigeonhole]) -AC_CONFIG_AUX_DIR([.]) -AC_CONFIG_SRCDIR([src]) -AC_CONFIG_MACRO_DIR([m4]) - -# Autoheader is not needed and does more harm than good for this package. However, it is -# tightly integrated in autoconf/automake and therefore it is difficult not to use it. As -# a workaround we give autoheader a dummy config header to chew on and we handle the -# real config header ourselves. -AC_CONFIG_HEADERS([dummy-config.h pigeonhole-config.h]) - -AC_DEFINE_UNQUOTED(PIGEONHOLE_NAME, "$PACKAGE_NAME", - [Define to the full name of Pigeonhole for Dovecot.]) -AC_DEFINE_UNQUOTED(PIGEONHOLE_VERSION, "$PACKAGE_VERSION", - [Define to the version of Pigeonhole for Dovecot.]) - -AM_INIT_AUTOMAKE([no-define foreign tar-ustar]) - -AM_MAINTAINER_MODE - -AC_PROG_CC -AC_PROG_CPP -AC_PROG_LIBTOOL - -# Couple with Dovecot -# - -DC_DOVECOT -DC_DOVECOT_MODULEDIR -LIBDOVECOT_INCLUDE="$LIBDOVECOT_INCLUDE $LIBDOVECOT_STORAGE_INCLUDE" -CFLAGS="$DOVECOT_CFLAGS" -LIBS="$DOVECOT_LIBS" -AC_SUBST(LIBDOVECOT_INCLUDE) - -# Define Sieve documentation install dir -# - -sieve_docdir='${dovecot_docdir}/sieve' -AC_SUBST(sieve_docdir) - -# Extensions under development -# - -AC_ARG_WITH(unfinished-features, -[AC_HELP_STRING([--with-unfinished-features], - [Build unfinished new features/extensions [default=no]])], - if test x$withval = xno || test x$withval = xauto; then - want_unfinished_features=$withval - else - want_unfinished_features=yes - fi, - want_unfinished_features=no) -AM_CONDITIONAL(BUILD_UNFINISHED, test "$want_unfinished_features" = "yes") - -if test "$want_unfinished_features" = "yes"; then - AC_DEFINE(HAVE_SIEVE_UNFINISHED,, - [Define to build unfinished features/extensions.]) -fi - -# -# - -AC_ARG_WITH(docs, -[ --with-docs Install documentation (default)], - if test x$withval = xno; then - want_docs=no - else - want_docs=yes - fi, - want_docs=yes) -AM_CONDITIONAL(BUILD_DOCS, test "$want_docs" = "yes") - -AC_ARG_ENABLE(valgrind, -[AC_HELP_STRING([--enable-valgrind], [Enable Valgrind memory leak checks in testsuite [default=no]])], - if test x$enableval = xno || test x$enableval = xauto; then - want_valgrind=$enableval - else - want_valgrind=yes - fi, - want_valgrind=no) -AM_CONDITIONAL(TESTSUITE_VALGRIND, test "$want_valgrind" = "yes") - -AC_ARG_WITH(managesieve, -[AC_HELP_STRING([--with-managesieve], - [Build ManageSieve service [default=yes]])], - if test x$withval = xno || test x$withval = xauto; then - want_managesieve=$withval - else - want_managesieve=yes - fi, - want_managesieve=yes) -AM_CONDITIONAL(BUILD_MANAGESIEVE, test "$want_managesieve" = "yes") - -AC_CONFIG_FILES([ -Makefile -doc/Makefile -doc/man/Makefile -doc/example-config/Makefile -doc/example-config/conf.d/Makefile -doc/rfc/Makefile -src/Makefile -src/lib-sieve/Makefile -src/lib-sieve/plugins/Makefile -src/lib-sieve/plugins/vacation/Makefile -src/lib-sieve/plugins/subaddress/Makefile -src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile -src/lib-sieve/plugins/relational/Makefile -src/lib-sieve/plugins/regex/Makefile -src/lib-sieve/plugins/imap4flags/Makefile -src/lib-sieve/plugins/copy/Makefile -src/lib-sieve/plugins/include/Makefile -src/lib-sieve/plugins/body/Makefile -src/lib-sieve/plugins/variables/Makefile -src/lib-sieve/plugins/enotify/Makefile -src/lib-sieve/plugins/enotify/mailto/Makefile -src/lib-sieve/plugins/notify/Makefile -src/lib-sieve/plugins/environment/Makefile -src/lib-sieve/plugins/mailbox/Makefile -src/lib-sieve/plugins/date/Makefile -src/lib-sieve/plugins/spamvirustest/Makefile -src/lib-sieve/plugins/ihave/Makefile -src/lib-sieve/plugins/editheader/Makefile -src/lib-sieve/plugins/vnd.dovecot/Makefile -src/lib-sieve/plugins/vnd.dovecot/debug/Makefile -src/lib-sieve/plugins/vnd.dovecot/duplicate/Makefile -src/lib-sieve-tool/Makefile -src/lib-sievestorage/Makefile -src/lib-managesieve/Makefile -src/plugins/Makefile -src/plugins/lda-sieve/Makefile -src/sieve-tools/Makefile -src/managesieve/Makefile -src/managesieve-login/Makefile -src/testsuite/Makefile -stamp.h]) - -AC_OUTPUT diff --git a/doc/Makefile.am b/doc/Makefile.am index 58b1b6e..b5bb82a 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -10,5 +10,6 @@ endif EXTRA_DIST = \ devel \ extensions \ + plugins \ $(docfiles) diff --git a/doc/example-config/conf.d/90-sieve-extprograms.conf b/doc/example-config/conf.d/90-sieve-extprograms.conf new file mode 100644 index 0000000..17dcb77 --- /dev/null +++ b/doc/example-config/conf.d/90-sieve-extprograms.conf @@ -0,0 +1,44 @@ +# Sieve Extprograms plugin configuration + +# Don't forget to add the sieve_extprograms plugin to the sieve_plugins setting. +# Also enable the extensions you need (one or more of vnd.dovecot.pipe, +# vnd.dovecot.filter and vnd.dovecot.execute) by adding these to the +# sieve_extensions or sieve_global_extensions settings. Restricting these +# extensions to a global context using sieve_global_extensions is recommended. + +plugin { + + # The directory where the program sockets are located for the + # vnd.dovecot.pipe, vnd.dovecot.filter and vnd.dovecot.execute extension + # respectively. The name of each unix socket contained in that directory + # directly maps to a program-name referenced from the Sieve script. + #sieve_pipe_socket_dir = sieve-pipe + #sieve_filter_socket_dir = sieve-filter + #sieve_execute_socket_dir = sieve-execute + + # The directory where the scripts are located for direct execution by the + # vnd.dovecot.pipe, vnd.dovecot.filter and vnd.dovecot.execute extension + # respectively. The name of each script contained in that directory + # directly maps to a program-name referenced from the Sieve script. + #sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe + #sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter + #sieve_execute_bin_dir = /usr/lib/dovecot/sieve-execute +} + +# An example program service called 'do-something' to pipe messages to +#service do-something { + # Define the executed script as parameter to the sieve service + #executable = script /usr/lib/dovecot/sieve-pipe/do-something.sh + + # Use some unprivileged user for executing the program + #user = dovenull + + # The unix socket located in the sieve_pipe_socket_dir (as defined in the + # plugin {} section above) + #unix_listener sieve-pipe/do-something { + # LDA/LMTP must have access + # user = vmail + # mode = 0600 + #} +#} + diff --git a/doc/example-config/conf.d/90-sieve.conf b/doc/example-config/conf.d/90-sieve.conf index 128d9c3..1ebf9f3 100644 --- a/doc/example-config/conf.d/90-sieve.conf +++ b/doc/example-config/conf.d/90-sieve.conf @@ -68,6 +68,7 @@ plugin { # setting, the used plugins can be specified. Check the Dovecot wiki # (wiki2.dovecot.org) or the pigeonhole website # (http://pigeonhole.dovecot.org) for available plugins. + # The sieve_extprograms plugin is included in this release. #sieve_plugins = # The separator that is expected between the :user and :detail diff --git a/doc/example-config/conf.d/Makefile.am b/doc/example-config/conf.d/Makefile.am index 12a606c..8cea566 100644 --- a/doc/example-config/conf.d/Makefile.am +++ b/doc/example-config/conf.d/Makefile.am @@ -3,7 +3,8 @@ pkgsysconfdir = $(sysconfdir)/dovecot exampledir = $(dovecot_docdir)/example-config/conf.d example_DATA = \ 20-managesieve.conf \ - 90-sieve.conf + 90-sieve.conf \ + 90-sieve-extprograms.conf EXTRA_DIST = \ $(example_DATA) diff --git a/doc/man/pigeonhole.7.in b/doc/man/pigeonhole.7.in index f86d5e6..75db9ca 100644 --- a/doc/man/pigeonhole.7.in +++ b/doc/man/pigeonhole.7.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "PIGEONHOLE" 7 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "PIGEONHOLE" 7 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME pigeonhole \- Overview of the Pigeonhole project\(aqs Sieve support for the diff --git a/doc/man/sieve-dump.1.in b/doc/man/sieve-dump.1.in index faf886f..3ea65f7 100644 --- a/doc/man/sieve-dump.1.in +++ b/doc/man/sieve-dump.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-DUMP" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-DUMP" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME sieve\-dump \- Pigeonhole\(aqs Sieve script binary dump tool diff --git a/doc/man/sieve-filter.1.in b/doc/man/sieve-filter.1.in index 1566dfa..df3fc36 100644 --- a/doc/man/sieve-filter.1.in +++ b/doc/man/sieve-filter.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-FILTER" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-FILTER" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .SH NAME sieve\-filter \- Pigeonhole\(aqs Sieve mailbox filter tool diff --git a/doc/man/sieve-test.1.in b/doc/man/sieve-test.1.in index 946c5b7..4331250 100644 --- a/doc/man/sieve-test.1.in +++ b/doc/man/sieve-test.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVE\-TEST" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVE\-TEST" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .SH NAME sieve\-test \- Pigeonhole\(aqs Sieve script tester .\"------------------------------------------------------------------------ diff --git a/doc/man/sievec.1.in b/doc/man/sievec.1.in index 28ef9e3..9307800 100644 --- a/doc/man/sievec.1.in +++ b/doc/man/sievec.1.in @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010-2012 Pigeonhole authors, see the included COPYING file -.TH "SIEVEC" 1 "2012-01-07" "Pigeonhole for Dovecot v2.1" "Pigeonhole" +.\" Copyright (c) 2010-2013 Pigeonhole authors, see the included COPYING file +.TH "SIEVEC" 1 "2013-05-09" "Pigeonhole for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME sievec \- Pigeonhole\(aqs Sieve script compiler diff --git a/doc/plugins/sieve_extprograms.txt b/doc/plugins/sieve_extprograms.txt new file mode 100644 index 0000000..d2b59a6 --- /dev/null +++ b/doc/plugins/sieve_extprograms.txt @@ -0,0 +1,175 @@ +Sieve Extprograms plugin for Pigeonhole + +Relevant specifications +======================= + + doc/rfc/spec-bosch-sieve-extprograms.txt + +Introduction +============ + +Sieve (RFC 5228) is a highly extensible machine language specifically tailored +for internet message filtering. For the Dovecot Secure IMAP server, Sieve +support is provided by the Pigeonhole Sieve plugin. This package includes a +plugin for Pigeonhole called "sieve_extprograms", which extends the Sieve +filtering implementation with action commands for invoking a predefined set of +external programs. Messages can be piped to or filtered through those programs +and string data can be input to and retrieved from those programs. + +The Sieve language is explicitly designed to be powerful enough to be useful yet +limited in order to allow for a safe server-side filtering system. Therefore, +the base specification of the language makes it impossible for users to do +anything more complex (and dangerous) than write simple mail filters. One of the +consequences of this security-minded design is that users cannot execute +external programs from their mail filter. Particularly for server-side filtering +setups in which mail accounts have no corresponding system account, allowing the +execution of arbitrary programs from the mail filter can be a significant +security risk. However, such functionality can also be very useful, for instance +to easily implement a custom action or external effect that Sieve normally +cannot provide. + +The "sieve_extprograms" plugin provides an extension to the Sieve filtering +language adding new action commands for invoking a predefined set of external +programs. To mitigate the security concerns, the external programs cannot be +chosen arbitrarily; the available programs are restricted through administrator +configuration. + +This extension is specific to the Pigeonhole Sieve implementation for the +Dovecot Secure IMAP server. It will therefore most likely not be supported by +web interfaces or GUI-based Sieve editors. This extension is primarily meant for +use in small setups or global scripts that are managed by the systems +administrator. + +Implementation Status +--------------------- + +The "vnd.dovecot.pipe", "vnd.dovecot.filter" and "vnd.dovecot.execute" Sieve +language extensions introduced by this plugin are vendor-specific with draft +status and their implementation for Pigeonhole is experimental, which means that +the language extensions are still subject to change and that the current +implementation is not thoroughly tested. + +Configuration +============= + +The plugin is activated by adding it to the sieve_plugins setting: + +sieve_plugins = sieve_extprograms + +This plugin registers the "vnd.dovecot.pipe", "vnd.dovecot.filter" and +"vnd.dovecot.execute" extensions with the Sieve interpreter. However, these +extensions are not enabled by default and thus need to be enabled explicitly. It +is recommended to restrict the use of these extensions to global context by +adding these to the "sieve_global_extensions" setting. If personal user scripts +also need to directly access external programs, the extensions need to be added +to the "sieve_extensions" setting. + +The commands introduced by the Sieve language extensions in this plugin can +directly pipe a message or string data to an external program (typically a shell +script) by forking a new process. Alternatively, these can connect to a unix +socket behind which a Dovecot script service is listening to start the external +program, e.g. to execute as a different user or for added security. + +The program name specified for the new Sieve "pipe", "filter" and "execute" +commands is used to find the program or socket in a configured directory. +Separate directories are specified for the sockets and the directly executed +binaries. The socket directory is searched first. Since the use of "/" in +program names is prohibited, it is not possible to build a hierarchical +structure. + +Directly forked programs are executed with a limited set of environment +variables: HOME, USER, HOST, SENDER, RECIPIENT and ORIG_RECIPIENT. Programs +executed through the script-pipe socket service currently have no environment +set at all. + +If a shell script is expected to read a message or string data, it must fully +read the provided input until the data ends with EOF, otherwise the Sieve action +invoking the program will fail. The action will also fail when the shell script +returns a nonzero exit code. Standard output is available for returning a +message (for the filter command) or string data (for the execute command) to the +Sieve interpreter. Standard error is written to the LDA log file. + +The three extensions introduced by this plugin - "vnd.dovecot.pipe", +"vnd.dovecot.filter" and "vnd.dovecot.pipe" - each have separate but similar +configuration. The settings that specify a time period are specified in +s(econds), unless followed by a d(ay), h(our) or m(inute) specifier character. +The following configuration settings are used, for which "" in the +setting name is replaced by either "pipe", "filter" or "execute" depending on +which extension is being configured. + +sieve__socket_dir = + Points to a directory relative to the Dovecot base_dir where the plugin looks + for script service sockets. + +sieve__bin_dir = + Points to a directory where the plugin looks for programs (shell scripts) to + execute directly and pipe messages to. + +sieve__exec_timeout = 10s + Configures the maximum execution time after which the program is forcefully + terminated. + +Examples +-------- + +Example 1: socket service for "pipe" and "execute" + +plugin { + sieve = ~/.dovecot.sieve + + sieve_plugins = sieve_extprograms + sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute + + # pipe sockets in /var/run/dovecot/sieve-pipe + sieve_pipe_socket_dir = sieve-pipe + + # execute sockets in /var/run/dovecot/sieve-execute + sieve_execute_socket_dir = sieve-execute +} + +service sieve-pipe-script { + # This script is executed for each service connection + executable = script /usr/lib/dovecot/sieve-extprograms/sieve-pipe-action.sh + + # use some unprivileged user for execution + user = dovenull + + # socket name is program-name in Sieve (without sieve-pipe/ prefix) + unix_listener sieve-pipe/sieve-pipe-script { + } +} + +service sieve-execute-action { + # This script is executed for each service connection + executable = script /usr/lib/dovecot/sieve-extprograms/sieve-execute-action.sh + + # use some unprivileged user for execution + user = dovenull + + # socket name is program-name in Sieve (without sieve-execute/ prefix) + unix_listener sieve-execute/sieve-execute-action { + } +} + +Example 2: direct execution for "pipe" and "filter" + +plugin { + sieve = ~/.dovecot.sieve + + sieve_plugins = sieve_extprograms + sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.filter + + # This directory contains the scripts that are available for the pipe command. + sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe + + # This directory contains the scripts that are available for the filter + # command. + sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter +} + +Using +===== + +Refer to doc/rfc/spec-bosch-sieve-extprograms.txt for a specification of the +Sieve language extensions. + diff --git a/doc/rfc/include.rfc6609.txt b/doc/rfc/include.rfc6609.txt new file mode 100644 index 0000000..08620a5 --- /dev/null +++ b/doc/rfc/include.rfc6609.txt @@ -0,0 +1,787 @@ + + + + + + +Internet Engineering Task Force (IETF) C. Daboo +Request for Comments: 6609 Apple, Inc. +Category: Standards Track A. Stone +ISSN: 2070-1721 Serendipity + May 2012 + + + Sieve Email Filtering: Include Extension + +Abstract + + The Sieve Email Filtering "include" extension permits users to + include one Sieve script inside another. This can make managing + large scripts or multiple sets of scripts much easier, and allows a + site and its users to build up libraries of scripts. Users are able + to include their own personal scripts or site-wide scripts. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6609. + +Copyright Notice + + Copyright (c) 2012 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + +Daboo & Stone Standards Track [Page 1] + +RFC 6609 Sieve Extension: Include May 2012 + + +Table of Contents + + 1. Introduction and Overview .......................................2 + 2. Conventions Used in This Document ...............................2 + 3. Include Extension ...............................................3 + 3.1. General Considerations .....................................3 + 3.2. Control Structure "include" ................................4 + 3.3. Control Structure "return" .................................7 + 3.4. Interaction with the "variables" Extension .................8 + 3.4.1. Control Structure "global" ..........................8 + 3.4.2. Variables Namespace global .........................10 + 3.5. Interaction with Other Extensions .........................11 + 4. Security Considerations ........................................12 + 5. IANA Considerations ............................................12 + 6. References .....................................................13 + 6.1. Normative References ......................................13 + 6.2. Informative References ....................................13 + Appendix A. Acknowledgments .......................................14 + +1. Introduction and Overview + + It's convenient to be able to break Sieve [RFC5228] scripts down into + smaller components that can be reused in a variety of different + circumstances. For example, users may want to have a default script + and a special 'vacation' script, the latter being activated when the + user goes on vacation. In that case, the default actions should + continue to be run, but a vacation command should be executed first. + One option is to edit the default script to add or remove the + vacation command as needed. Another is to have a vacation script + that simply has a vacation command and then includes the default + script. + + This document defines the Sieve Email Filtering "include" extension, + which permits users to include one Sieve script inside another. + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + Conventions for notations are as in Sieve [RFC5228], Section 1.1. + + + + + + + + + +Daboo & Stone Standards Track [Page 2] + +RFC 6609 Sieve Extension: Include May 2012 + + + The following key phrases are used to describe scripts and script + execution: + + script + a valid Sieve script. + + script execution + an instance of a Sieve interpreter invoked for a given message + delivery, starting with the user's active script and continuing + through any included scripts until the final disposition of the + message (e.g., delivered, forwarded, discarded, rejected, etc.). + + immediate script + the individual Sieve script file being executed. + + including script + the individual Sieve script file that had an include statement + that included the immediate script. + +3. Include Extension + +3.1. General Considerations + + Sieve implementations that implement the "include", "return", and + "global" commands described below have an identifier of "include" for + use with the capability mechanism. If any of the "include", + "return", or "global" commands are used in a script, the "include" + capability MUST be listed in the "require" statement in that script. + + Sieve implementations need to track the use of actions in included + scripts so that implicit "keep" behavior can be properly determined + based on whether any actions have executed in any script. + + Sieve implementations are allowed to limit the total number of nested + included scripts, but MUST provide for a total of at least three + levels of nested scripts including the top-level script. An error + MUST be generated either when the script is uploaded to the Sieve + repository, or when the script is executed, if any nesting limit is + exceeded. If such an error is detected whilst processing a Sieve + script, an implicit "keep" action MUST be executed to prevent loss of + any messages. + + Sieve implementations MUST NOT allow recursive script inclusion. + Both direct recursion, where script A includes script A (itself), and + indirect recursion, where script A includes script B which includes + script A once again, are prohibited. + + + + + +Daboo & Stone Standards Track [Page 3] + +RFC 6609 Sieve Extension: Include May 2012 + + + Sieve implementations MUST generate an error at execution time if an + included script is a recursive inclusion. Implementations MUST NOT + generate errors for recursive includes at upload time, as this would + force an upload ordering requirement upon script authors and + generators. + + Sieve implementations MUST generate an error at execution time if an + included script does not exist, except when the ":optional" parameter + is specified. Implementations MUST NOT generate errors for scripts + missing at upload time, as this would force an upload ordering + requirement upon script authors and generators. + + If the Sieve "variables" extension [RFC5229] is present, an issue + arises with the "scope" of variables defined in scripts that may + include each other. For example, if a script defines the variable + "${status}" with one particular meaning or usage, and another defines + "${status}" with a different meaning, then if one script includes the + other there is an issue as to which "${status}" is being referenced. + To solve this problem, Sieve implementations MUST follow the scoping + rules defined in Section 3.4 and support the "global" command defined + there. + +3.2. Control Structure "include" + + Usage: include [LOCATION] [":once"] [":optional"] + + LOCATION = ":personal" / ":global" + + The "include" command takes an optional "location" parameter, an + optional ":once" parameter, an optional ":optional" parameter, and a + single string argument representing the name of the script to include + for processing at that point. Implementations MUST restrict script + names according to ManageSieve [RFC5804], Section 1.6. The script + name argument MUST be a constant string as defined in [RFC5229], + Section 3; implementations MUST NOT expand variables in the script + name argument. + + The "location" parameter MUST default to ":personal" if not + specified. The "location" parameter MUST NOT be specified more than + once. The "location" has the following meanings: + + :personal + Indicates that the named script is stored in the user's own + personal (private) Sieve repository. + + :global + Indicates that the named script is stored in a site-wide Sieve + repository, accessible to all users of the Sieve system. + + + +Daboo & Stone Standards Track [Page 4] + +RFC 6609 Sieve Extension: Include May 2012 + + + The ":once" parameter tells the interpreter only to include the named + script if it has not already been included at any other point during + script execution. If the script has already been included, + processing continues immediately following the "include" command. + Implementations MUST NOT generate an error if an "include :once" + command names a script whose inclusion would be recursive; in this + case, the script MUST be considered previously included, and + therefore "include :once" will not include it again. + + Note: It is RECOMMENDED that script authors and generators use the + ":once" parameter only when including a script that performs general + duties such as declaring global variables and making sanity checks of + the environment. + + The ":optional" parameter indicates that the script may be missing. + Ordinarily, an implementation MUST generate an error during execution + if an "include" command specifies a script that does not exist. When + ":optional" is specified, implementations MUST NOT generate an error + for a missing script, and MUST continue as if the "include" command + had not been present. + + The included script MUST be a valid Sieve script. Implementations + MUST validate that each script has its own "require" statements for + all optional capabilities used by that script. The scope of a + "require" statement is the script in which it immediately appears, + and neither inherits nor passes on capabilities to other scripts + during the course of execution. + + A "stop" command in an included script MUST stop all script + processing, including the processing of the scripts that include the + immediate one. The "return" command (described below) stops + processing of the immediate script only, and allows the scripts that + include it to continue. + + The "include" command MAY appear anywhere in a script where a control + structure is legal, and MAY be used within another control structure, + e.g., an "if" block. + + + + + + + + + + + + + + +Daboo & Stone Standards Track [Page 5] + +RFC 6609 Sieve Extension: Include May 2012 + + + Examples: + + The user has four scripts stored in their personal repository: + + "default" + + This is the default active script that includes several others. + + require ["include"]; + + include :personal "always_allow"; + include :global "spam_tests"; + include :personal "spam_tests"; + include :personal "mailing_lists"; + + Personal script "always_allow" + + This script special-cases some correspondent email addresses and + makes sure any message containing those addresses is always kept. + + if address :is "from" "boss@example.com" + { + keep; + } + elsif address :is "from" "ceo@example.com" + { + keep; + } + + Personal script "spam_tests" (uses "reject" [RFC5429]) + + This script does some user-specific spam tests to catch spam + messages not caught by the site-wide spam tests. + + require ["reject"]; + + if header :contains "Subject" "XXXX" + { + reject "Subject XXXX is unacceptable."; + } + elsif address :is "from" "money@example.com" + { + reject "Mail from this sender is unwelcome."; + } + + + + + + + +Daboo & Stone Standards Track [Page 6] + +RFC 6609 Sieve Extension: Include May 2012 + + + Personal script "mailing_lists" + + This script looks for messages from different mailing lists and + files each into a mailbox specific to the mailing list. + + require ["fileinto"]; + + if header :is "List-ID" "sieve.ietf.org" + { + fileinto "lists.sieve"; + } + elsif header :is "List-ID" "ietf-imapext.imc.org" + { + fileinto "lists.imapext"; + } + + There is one script stored in the global repository: + + Site script "spam_tests" (uses "reject" [RFC5429]) + + This script does some site-wide spam tests that any user at the + site can include in their own scripts at a suitable point. The + script content is kept up to date by the site administrator. + + require ["reject"]; + + if anyof (header :contains "Subject" "$$", + header :contains "Subject" "Make money") + { + reject "No thank you."; + } + +3.3. Control Structure "return" + + Usage: return + + The "return" command stops processing of the immediately included + script only and returns processing control to the script that + includes it. If used in the main script (i.e., not in an included + script), it has the same effect as the "stop" command, including the + appropriate "keep" action if no other actions have been executed up + to that point. + + + + + + + + + +Daboo & Stone Standards Track [Page 7] + +RFC 6609 Sieve Extension: Include May 2012 + + +3.4. Interaction with the "variables" Extension + + In order to avoid problems of variables in an included script + "overwriting" those from the script that includes it, this + specification requires that all variables defined in a script MUST be + kept "private" to the immediate script by default -- that is, they + are not "visible" to other scripts. This ensures that two script + authors cannot inadvertently cause problems by choosing the same name + for a variable. + + However, sometimes there is a need to make a variable defined in one + script available to others. This specification defines the new + command "global" to declare that a variable is shared among scripts. + Effectively, two namespaces are defined: one local to the immediate + script, and another shared among all scripts. Implementations MUST + allow a non-global variable to have the same name as a global + variable but have no interaction between them. + +3.4.1. Control Structure "global" + + Usage: global + + The "global" command accepts a string list argument that defines one + or more names of variables to be stored in the global variable space. + Each name MUST be a constant string and conform to the syntax of + variable-name as defined in the "variables" extension document + [RFC5229], Section 3. Match variables cannot be specified, and + namespace prefixes are not allowed. An invalid name MUST be detected + as a syntax error. + + The "global" command is only available when the script has both + "include" and "variables" in its require line. If the "global" + command appears when only "include" or only "variables" has been + required, an error MUST be generated when the script is uploaded. + + If a "global" command is given the name of a variable that has + previously been defined in the immediate script with "set", an error + MUST be generated either when the script is uploaded or at execution + time. + + If a "global" command lists a variable that has not been defined in + the "global" namespace, the name of the variable is now marked as + global, and any subsequent "set" command will set the value of the + variable in global scope. + + + + + + + +Daboo & Stone Standards Track [Page 8] + +RFC 6609 Sieve Extension: Include May 2012 + + + A variable has global scope in all scripts that have declared it with + the "global" command. If a script uses that variable name without + declaring it global, the name specifies a separate, non-global + variable within that script. + + Interpretation of a string containing a variable marked as global, + but without any value set, SHALL behave as any other access to an + unknown variable, as specified in the "variables" extension document + [RFC5229], Section 3 (i.e., evaluates to an empty string). + + Example: + + The active script + + The included script may contain repetitive code that is + effectively a subroutine that can be factored out. In this + script, the test that matches last will leave its value in the + test_mailbox variable, and the top-level script will file the + message into that mailbox. If no tests matched, the message will + be implicitly kept in the INBOX. + + require ["fileinto", "include", "variables", "relational"]; + global "test"; + global "test_mailbox"; + + set "test" "$$"; + include "subject_tests"; + + set "test" "Make money"; + include "subject_tests"; + + if string :count "eq" "${test_mailbox}" "1" + { + fileinto "${test_mailbox}"; + stop; + } + + + + + + + + + + + + + + + +Daboo & Stone Standards Track [Page 9] + +RFC 6609 Sieve Extension: Include May 2012 + + + Personal script "subject_tests" + + This script performs a number of tests against the message, sets + the global test_mailbox variable with a folder to file the message + into, and then falls back to the top-level script. + + + require ["include", "variables"]; + global ["test", "test_mailbox"]; + + if header :contains "Subject" "${test}" + { + set "test_mailbox" "spam-${test}"; + } + +3.4.2. Variables Namespace global + + In addition to the "global" command, this document defines the + variables namespace "global", in accordance with the "variables" + extension document [RFC5229], Section 3. The "global" namespace has + no sub-namespaces (e.g., 'set "global.data.from" "me@example.com";' + is not allowed). The variable-name part MUST be a valid identifier + (e.g., 'set "global.12" "value";' is not valid because "12" is not a + valid identifier). + + Note that the "variables" extension document [RFC5229], Section 3 + suggests that extensions should define a namespace that is the same + as its capability string (in this case, "include" rather than + "global"). Nevertheless, references to the "global" namespace + without a prior require statement for the "include" extension MUST + cause an error. + + Example: + + require ["variables", "include"]; + + set "global.i_am_on_vacation" "1"; + + Variables declared global and variables accessed via the "global" + namespace MUST each be one and the same. In the following example + script, we see the variable "i_am_on_vacation" used in a "global" + command, and again with the "global" namespace. Consider these as + two syntaxes with identical meaning. + + + + + + + + +Daboo & Stone Standards Track [Page 10] + +RFC 6609 Sieve Extension: Include May 2012 + + + Example: + + require ["variables", "include", "vacation"]; + global "i_am_on_vacation"; + + set "global.i_am_on_vacation" "1"; + + if string :is "${i_am_on_vacation}" "1" + { + vacation "It's true, I am on vacation."; + } + +3.5. Interaction with Other Extensions + + When "include" is used with the "editheader" extension [RFC5293], any + changes made to headers in a script MUST be propagated both to and + from included scripts. By way of example, if a script deletes one + header and adds another, then includes a second script, the included + script MUST NOT see the removed header, and MUST see the added + header. Likewise, if the included script adds or removes a header, + upon returning to the including script, subsequent actions MUST see + the added headers and MUST NOT see the removed headers. + + When "include" is used with the MIME extension [RFC5703] + "foreverypart" control structure, the included script MUST be + presented with the current MIME part as though it were the entire + message. A script SHALL NOT have any special control over the + control structure it was included from. The "break" command in an + included script is not valid on its own and may not terminate a + "foreverypart" iteration in another script. The included script can + use "return" to transfer control back to the including script. A + global variable can be used to convey results to the including + script. A "stop" in an included script, even within a "foreverypart" + loop, still halts all script execution, per Section 3.2. + + When "include" is used with the "reject" extension [RFC5429], calling + "reject" or "ereject" at any time sets the reject action on the + message, and continues script execution. Apropos of the MIME + extension, if an included script sees only a portion of the message + and calls a reject, it is the entire message and not the single MIME + part that carries the rejection. + + + + + + + + + + +Daboo & Stone Standards Track [Page 11] + +RFC 6609 Sieve Extension: Include May 2012 + + +4. Security Considerations + + Sieve implementations MUST ensure adequate security for the global + script repository to prevent unauthorized changes to global scripts. + For example, a site policy might enable only certain users with + administrative privileges to modify the global scripts. Sites are + advised against allowing all users to have write access to the sites' + global scripts. + + Sieve implementations MUST ensure that script names are checked for + validity and proper permissions prior to inclusion, in order to + prevent a malicious user from gaining access to files accessible to + the mail server software that should not be accessible to the user. + + Sieve implementations MUST ensure that script names are safe for use + with their storage system. An error MUST be generated either when + the script is uploaded or at execution time for a script including a + name that could be used as a vector to attack the storage system. By + way of example, the following include commands should be considered + hostile: 'include "./../..//etc/passwd"', 'include "foo$(`rm + star`)"'. + + Beyond these, the "include" extension does not raise any security + considerations that are not discussed in the base Sieve [RFC5228] + document and the "variables" extension document [RFC5229]. + +5. IANA Considerations + + The following template specifies the IANA registration of the Sieve + extension specified in this document: + + To: iana@iana.org + Subject: Registration of new Sieve extension + + Capability name: include + Description: adds the "include" command to execute other Sieve + scripts, the "return" action from an included + script, and the "global" command and "global" + variables namespace to access variables shared + among included scripts. + RFC number: this RFC + Contact address: the Sieve discussion list + + This information has been added to IANA's "Sieve Extensions" registry + (http://www.iana.org). + + + + + + +Daboo & Stone Standards Track [Page 12] + +RFC 6609 Sieve Extension: Include May 2012 + + +6. References + +6.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5228] Guenther, P., Ed., and T. Showalter, Ed., "Sieve: An Email + Filtering Language", RFC 5228, January 2008. + + [RFC5229] Homme, K., "Sieve Email Filtering: Variables Extension", + RFC 5229, January 2008. + + [RFC5804] Melnikov, A., Ed., and T. Martin, "A Protocol for Remotely + Managing Sieve Scripts", RFC 5804, July 2010. + +6.2. Informative References + + [RFC5293] Degener, J. and P. Guenther, "Sieve Email Filtering: + Editheader Extension", RFC 5293, August 2008. + + [RFC5429] Stone, A., Ed., "Sieve Email Filtering: Reject and + Extended Reject Extensions", RFC 5429, March 2009. + + [RFC5703] Hansen, T. and C. Daboo, "Sieve Email Filtering: MIME Part + Tests, Iteration, Extraction, Replacement, and Enclosure", + RFC 5703, October 2009. + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Stone Standards Track [Page 13] + +RFC 6609 Sieve Extension: Include May 2012 + + +Appendix A. Acknowledgments + + Thanks to Stephan Bosch, Ned Freed, Arnt Gulbrandsen, Tony Hansen, + Kjetil Torgrim Homme, Jeffrey Hutzelman, Barry Leiba, Alexey + Melnikov, Ken Murchison, Marc Mutz, and Rob Siemborski, for comments + and corrections. + +Authors' Addresses + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + Aaron Stone + Serendipity + 1817 California St. #104 + San Francisco, CA 94109 + USA + + EMail: aaron@serendipity.cx + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Stone Standards Track [Page 14] + diff --git a/doc/rfc/spec-bosch-sieve-extprograms.txt b/doc/rfc/spec-bosch-sieve-extprograms.txt new file mode 100644 index 0000000..dd6557b --- /dev/null +++ b/doc/rfc/spec-bosch-sieve-extprograms.txt @@ -0,0 +1,728 @@ + + + +Pigeonhole Project S. Bosch + October 12, 2012 + + + Sieve Email Filtering: Invoking External Programs + +Abstract + + The Sieve filtering language (RFC 5228) is explicitly designed to be + powerful enough to be useful yet limited in order to allow for a safe + filtering system. The base specification of the language makes it + impossible for users to do anything more complex (and dangerous) than + write simple mail filters. One of the consequences of this security- + minded design is that users cannot execute programs external to the + Sieve filter. However, this can be a very useful and flexible + feature for situations where Sieve cannot provide some uncommon + functionality by itself. This document updates the Sieve filtering + language with extensions that add support for invoking a predefined + set of external programs. Messages can be piped to or filtered + through those programs and string data can be input to and retrieved + from those programs. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Bosch [Page 1] + + Sieve External Programs October 2012 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. Conventions Used in This Document . . . . . . . . . . . . . . 3 + 3. Naming of External Programs . . . . . . . . . . . . . . . . . 3 + 4. Arguments for External Programs . . . . . . . . . . . . . . . 4 + 5. Action "pipe" . . . . . . . . . . . . . . . . . . . . . . . . 5 + 5.1. Interactions with Other Sieve Actions . . . . . . . . . . 5 + 5.2. Interaction with the Sieve "copy" Extension . . . . . . . 6 + 6. Action "filter" . . . . . . . . . . . . . . . . . . . . . . . 6 + 6.1. Interaction with Other Tests and Actions . . . . . . . . . 7 + 7. Action "execute" . . . . . . . . . . . . . . . . . . . . . . . 7 + 8. Actions "filter" and "execute" as Tests . . . . . . . . . . . 8 + 9. Sieve Capability Strings . . . . . . . . . . . . . . . . . . . 9 + 10. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 + 10.1. Example 1 . . . . . . . . . . . . . . . . . . . . . . . . 9 + 10.2. Example 2 . . . . . . . . . . . . . . . . . . . . . . . . 10 + 10.3. Example 3 . . . . . . . . . . . . . . . . . . . . . . . . 10 + 10.4. Example 4 . . . . . . . . . . . . . . . . . . . . . . . . 11 + 11. Security Considerations . . . . . . . . . . . . . . . . . . . 12 + 12. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 12.1. Normative References . . . . . . . . . . . . . . . . . . . 12 + 12.2. Informative References . . . . . . . . . . . . . . . . . . 13 + Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + +Bosch [Page 2] + + Sieve External Programs October 2012 + + +1. Introduction + + This is an extension to the Sieve filtering language defined by RFC + 5228 [SIEVE]. It adds commands for invoking a predefined set of + external programs. Messages can be piped to or filtered through + those programs and, alternatively, string data can be passed to and + retrieved from those programs. + + The Sieve language is explicitly designed to be powerful enough to be + useful yet limited in order to allow for a safe server-side filtering + system. Therefore, the base specification of the language makes it + impossible for users to do anything more complex (and dangerous) than + write simple mail filters. One of the consequences of this security- + minded design is that users cannot execute external programs from + their Sieve script. Particularly for server-side filtering setups in + which mail accounts have no corresponding system account, allowing + the execution of arbitrary programs from the mail filter can be a + significant security risk. However, such functionality can also be + very useful, for instance to easily implement a custom action or + external effect that Sieve normally cannot provide. + + This document updates the Sieve filtering language with an extension + to support invoking a predefined set of external programs using a set + of new commands. To mitigate the security concerns, the external + programs cannot be chosen arbitrarily; the available programs are + restricted through administrator configuration. + + This extension is specific to the Pigeonhole Sieve implementation for + the Dovecot Secure IMAP server. It will therefore most likely not be + supported by web interfaces and GUI-based Sieve editors. This + extension is primarily meant for use in small setups or global + scripts that are managed by the system's administrator. + + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [KEYWORDS]. + + Conventions for notations are as in [SIEVE] Section 1.1, including + use of the "Usage:" label for the definition of action and tagged + arguments syntax. + + +3. Naming of External Programs + + An external program is identified by a name. This MUST not + + + +Bosch [Page 3] + + Sieve External Programs October 2012 + + + correspond to a file system path or otherwise have the ability to + point to arbitrary programs on the system. The list of valid program + names MUST be limited, subject to administrator configuration. + + A program name is a sequence of Unicode characters encoded in UTF-8 + [UTF-8]. A program name MUST comply with Net-Unicode Definition + (Section 2 of [NET-UNICODE]), with the additional restriction of + prohibiting the following Unicode characters: + + o 0000-001F; [CONTROL CHARACTERS] + + o 002F; SLASH + + o 007F; DELETE + + o 0080-009F; [CONTROL CHARACTERS] + + o 2028; LINE SEPARATOR + + o 2029; PARAGRAPH SEPARATOR + + Program names MUST be at least one octet (and hence Unicode + character) long. Implementations MUST allow names of up to 128 + Unicode characters in length (which can take up to 512 octets when + encoded in UTF-8, not counting the terminating NUL), and MAY allow + longer names. A server that receives a program name longer than its + internal limit MUST reject the corresponding operation, in particular + it MUST NOT truncate the program name. + + Implementations MUST NOT allow variables to be expanded into the + program names; in other words, the "program-name" value MUST be a + constant string as defined in [VARIABLES], Section 3. + + +4. Arguments for External Programs + + Optionally, arguments can be passed to an external program. The + arguments are specified as a Sieve string list and are passed to the + external program in sequence. Implementations SHOULD NOT impose any + structure for these arguments; validity checks are the responsibility + of the external program. + + However, implementations SHOULD limit the maximum number of arguments + and the length of each argument. Implementations MUST accept at + least 16 arguments with a length of at least 1024 octets each, and + MAY allow more and longer arguments. Additionally, implementations + MAY restrict the use of certain control characters such as CR and LF, + if these can cause unexpected behavior or raise security concerns. + + + +Bosch [Page 4] + + Sieve External Programs October 2012 + + + Note that implementations MAY also implicitly pass other data, such + as the message envelope, to all executed programs avoiding the need + to pass this information explicitly through program arguments. + + +5. Action "pipe" + + Usage: "pipe" [":try"] + [] + + The "pipe" action executes the external program identified by the + "program-name" argument and pipes the message to it. Much like the + "fileinto" and "redirect" actions [SIEVE], this action is a + disposition-type action (it is intended to deliver the message) and + therefore it cancels Sieve's implicit keep (see Section 2.10.2 of + [SIEVE]) by default. + + The specified "program-name" argument MUST conform to the syntax and + restrictions defined in Section 3. A script MUST fail with an + appropriate error if it attempts to use the "filter" action with an + invalid, restricted or unknown program name. The optional + "arguments" argument lists the arguments that are passed to the + external program, as explained in Section 4. + + If the external program invoked by the "pipe" action fails to execute + or finishes execution with an error, script execution MUST fail with + an appropriate error (causing an implicit "keep" action to be + executed), unless the ":try" tag is specified. + + When the ":try" tag is specified, the "pipe" instruction will attempt + execution of the external program, but failure will not cause the + whole Sieve script execution to fail with an error. Instead, the + Sieve processing continues as if the "pipe" action was never + triggered. + + If the execution of the external program is unsuccessful, the "pipe" + action MUST NOT cancel the implicit keep. + +5.1. Interactions with Other Sieve Actions + + By default, the "pipe" action cancels the implicit keep, thereby + handing the responsibility for the message over to the external + program. This behavior can be overridden using the Sieve "copy" + extension [RFC3894] as described in Section 5.2. + + The "pipe" action can only be executed once per script for a + particular external program. A script MUST fail with an appropriate + error if it attempts to "pipe" messages to the same program multiple + + + +Bosch [Page 5] + + Sieve External Programs October 2012 + + + times. + + The "pipe" action is incompatible with the Sieve "reject" and + "ereject" actions [RFC5429]. + +5.2. Interaction with the Sieve "copy" Extension + + The Sieve "copy" extension [RFC3894] adds an optional ":copy" tagged + argument to the "fileinto" and "redirect" action commands. When this + tag is specified, these commands do not cancel the implicit "keep". + Instead, the requested action is performed in addition to whatever + else is happening to the message. + + When the "vnd.dovecot.pipe" extension is active, the "copy" extension + also adds the optional ":copy" tag to the "pipe" action command. + This has the familiar effect that when the ":copy" tag is specified, + the implicit "keep" will not be canceled by the "pipe" action. When + the "copy" extension is active, the syntax of the "pipe" action is + represented as follows: + + Usage: "pipe" [":copy"] [":try"] + [] + + +6. Action "filter" + + Usage: "filter" [] + + The "filter" action executes the external program identified by the + "program-name" argument and filters the message through it. This + means that the message is provided as input to the external program + and that the output of the external program is used as the new + message. This way, the entire message can be altered using the + external program. The "filter" action does not affect Sieve's + implicit keep. + + The specified "program-name" argument MUST conform to the syntax and + restrictions defined in Section 3. A script MUST fail with an + appropriate error if it attempts to use the "filter" action with an + invalid, restricted or unknown program name. The optional + "arguments" argument lists the arguments that are passed to the + external program, as explained in Section 4. + + If the external program fails to execute, finishes execution with an + error, or fails to provide message output, the "filter" action MUST + terminate and leave the message unchanged. No error condition is + raised, script processing continues, and prior or subsequent "filter" + actions are not affected. + + + +Bosch [Page 6] + + Sieve External Programs October 2012 + + +6.1. Interaction with Other Tests and Actions + + A successful "filter" action effectively changes the message, + potentially substituting the message in its entirety with a new + version. However, actions such as "reject" and "vacation" that + generate [MDN], [DSN], or similar disposition messages MUST do so + using the original, unmodified message. Similarly, if an error + terminates processing of the script, the original message MUST be + used when doing the implicit keep required by Section 2.10.6 of + [SIEVE]. All other actions that store, send, or alter the message + MUST do so with the current version of the message. This includes + the "filter" action itself. + + When a disposition-type action, such as "fileinto", "redirect" or + "pipe", is encountered, the current version of the message is "locked + in" for that disposition-type action. Whether the implementation + performs the action at that point or batches it for later, it MUST + perform the action on the message as it stood at the time, and MUST + NOT include subsequent changes encountered later in the script + processing. + + In addition, any tests done on the message and its parts will test + the message after all prior "filter" actions have been performed. + Because the implicit keep, if it is in effect, acts on the final + state of the message, all "filter" actions are performed before any + implicit keep. + + The "filter" action does not affect the applicability of other + actions; any action that was applicable before the "filter" + invocation is equally applicable to the changed message afterward. + + +7. Action "execute" + + Usage: "execute" [":input" / ":pipe"] + [":output" ] + [] + + The "execute" action executes the external program identified by the + "program-name" argument. Input to the program can be provided using + the ":input" or ":pipe" tags. If used in combination with the + "variables" extension [VARIABLES], the "execute" action can redirect + output from the program to the variable specified using the ":output" + tag. This way, string data can be passed to and retrieved from an + external program. The "execute" action does not change the message + in any way and it never affects Sieve's implicit keep. + + The specified "program-name" argument MUST conform to the syntax and + + + +Bosch [Page 7] + + Sieve External Programs October 2012 + + + restrictions defined in Section 3. A script MUST fail with an + appropriate error if it attempts to use the "execute" action with an + invalid, restricted or unknown program name. The optional + "arguments" argument lists the arguments that are passed to the + external program, as explained in Section 4. + + The ":input" and ":pipe" tags are mutually exclusive, because these + both specify input that is passed to the external program. + Specifying both for a single "execute" command MUST trigger a compile + error. The ":input" tag specifies a string that is passed to the + external script as input. This string may also contain variable + substitutions when the "variables" extension is active. If instead + the ":pipe" tag is specified, the current version of the message + itself is passed to the external program. If the ":input" and + ":pipe" tags are both omitted, no input is provided to the external + program. + + The ":output" tag specifies the variable to which the output of the + external program is to be redirected. If the ":output" tag is + omitted, any output from the external program is discarded. The + ":output" tag requires the "variables" [VARIABLES] extension to be + active. The use of the ":output" tag for the "execute" action + without the "variables" extension in the require line MUST trigger a + compile error. + + The "varname" parameter of the ":output" tag specifies the name of + the variable. It MUST be a constant string and it MUST conform to + the syntax of "variable-name" as defined in [VARIABLES], Section 3. + An invalid name MUST be detected as a syntax error. The referenced + variable MUST be compatible with the "set" command as described in + [VARIABLES], Section 4. This means that match variables cannot be + specified and that variable namespaces are only allowed when their + specification explicitly indicates compatibility with the "set" + command. Use of an incompatible variable MUST trigger a compile + error. + + The data actually stored in the variable MAY be truncated to conform + to an implementation-specific limit on variable length. If the + execution of the external program fails, the contents of the variable + referenced with ":output" MUST remain unchanged. + + +8. Actions "filter" and "execute" as Tests + + To simplify checking the successful invocation of the external + program, the "filter" and "execute" actions can also be used as + tests. As such, these will attempt to execute the requested external + program, and will evaluate to "true" if the program executed + + + +Bosch [Page 8] + + Sieve External Programs October 2012 + + + successfully and, if applicable, output was retrieved from it + successfully. The usage as a test is exactly the same as the usage + as an action: as a test it doubles as an action and a test of the + action's result at the same time. + + For the "execute" test, a "false" result is not necessarily equal to + actual failure: it may just mean that the executed program returned a + "false" result, e.g. an exit code higher than zero on Unix systems. + Note that any output from the external program is discarded when it + yields a "false" result. Similarly, for the "filter" test, programs + may return a "false" result to indicate that the message was not + changed. In that case the Sieve interpreter will not replace the + active message with an identical one, which is beneficial for + efficiency. The exact semantics of these tests thus partly depends + on the program being executed. + + To handle missing programs gracefully, implementations MAY let the + "filter" and "execute" tests evaluate to "false" if an unknown + program name is specified, instead of failing the script with an + error as would happen if used as an action. In any other case and + irrespective of whether the command is used as an action or a test, + passing invalid arguments to the "filter" or "execute" commands, such + as a syntactically invalid or restricted program name, MUST always + cause the script to fail with an appropriate error. + + +9. Sieve Capability Strings + + A Sieve implementation that defines the "pipe" action command will + advertise the capability string "vnd.dovecot.pipe". + + A Sieve implementation that defines the "filter" action command will + advertise the capability string "vnd.dovecot.filter". + + A Sieve implementation that defines the "execute" command will + advertise the capability string "vnd.dovecot.execute". + + +10. Examples + + The examples outlined in this section all refer to some external + program. These programs are imaginary and are only available when + the administrator would provide them. + +10.1. Example 1 + + The following example passes messages directed to a + "user-request@example.com" address to an external program called + + + +Bosch [Page 9] + + Sieve External Programs October 2012 + + + "request-handler". The "-request" part of the recipient address is + identified using the "subaddress" extension [SUBADDRESS]. If the + program is executed successfully, the message is considered delivered + and does not end up in the user's inbox. + + require [ "vnd.dovecot.pipe", "subaddress", "envelope" ]; + + if envelope :detail "to" "request" + { + pipe "request-handler"; + } + +10.2. Example 2 + + The following example copies messages addressed to a particular + recipient to a program called "printer". This program sends the + message to some printer. In this case it is configured for "A4" page + format and "draft" quality using the two arguments. Irrespective of + whether the message is printed or not, it is also always stored in + the user's inbox through Sieve's implicit keep action (which is not + canceled due to the specified :copy tag). + + require [ "vnd.dovecot.pipe", "copy" ]; + + if address "to" "snailmail@example.com" + { + pipe :copy "printer" ["A4", "draft"]; + } + +10.3. Example 3 + + The following example translates a message from Dutch to English if + appropriate. If the message's content language is indicated to be + Dutch, the message is filtered through an external program called + "translator" with arguments that request Dutch to English + translation. Dutch messages are translated and filed into a special + folder called "Translated". Other messages are delivered to the + user's inbox. + + require [ "vnd.dovecot.filter", "fileinto" ]; + + if header "content-language" "nl" + { + filter "translator" ["nl", "en"]; + fileinto "Translated"; + stop; + } + + + + +Bosch [Page 10] + + Sieve External Programs October 2012 + + + Note that (formerly) Dutch messages are filed into the "Translated" + folder, even when the "translator" program fails. In the following + modified example this is prevented by using the filter action as a + test: + + require [ "vnd.dovecot.filter", "fileinto" ]; + + if header "content-language" "nl" + { + if filter "translator" ["nl", "en"] + { + fileinto "Translated"; + stop; + } + } + + This way, messages only end up in the "Translated" folder when + translation was actually successful. + +10.4. Example 4 + + The following example determines whether the user is on vacation by + querying an external source. The vacation message is obtained from + the external source as well. The program that queries the external + source is called "onvacation" and it has one argument: the localpart + of the recipient address. The execute action is used as a test, + which will evaluate to "true" when the user is determined to be on + vacation. This means that the external program "onvacation" exits + with a failure when the user is not on vacation. Of course, a + vacation response is also not sent when the "onvacation" program + truly fails somehow. + + require [ "vnd.dovecot.execute", "vacation", "variables", + "envelope" ]; + + if envelope :localpart :matches "to" "*" + { + set "recipient" "${1}"; + } + + if execute :output "vacation_message" "onvacation" "${recipient}" + { + vacation "${vacation_message}"; + } + + + + + + + +Bosch [Page 11] + + Sieve External Programs October 2012 + + +11. Security Considerations + + Allowing users to execute programs external to the Sieve filter can + be a significant security risk, therefore the extensions presented in + this specification must be implemented with great care. The external + programs should execute with no more privileges than needed. + + Particularly the arguments passed to the external programs (see + Section 4) need to be handled with scrutiny. The external programs + need to check the arguments for validity and SHOULD NOT pass these to + system tools directly, as this may introduce the possibility of + various kinds of insertion attacks. External programs that work with + message content or string input from the Sieve script may have + similar security concerns. + + Unlike the Sieve interpreter itself, an external program can easily + consume a large amount of resources if not implemented carefully. + This can be triggered by coincidence or intentionally by an attacker. + Therefore, amount of resources available to the external programs + SHOULD be limited appropriately. For one, external programs MUST NOT + be allowed to execute indefinitely. + + For improved security, implementations MAY restrict the use of this + extension to administrator-controlled global Sieve scripts. In such + setups, the external programs are never called directly from the + user's personal script. For example, using the "include" extension + [INCLUDE], the user's personal script can include global scripts that + contain the actual external program invocations. This both abstracts + the details of external program invocation from the user's view and + it limits access to external programs to whatever the administrator + defines. + + +12. References + +12.1. Normative References + + [KEYWORDS] + Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [NET-UNICODE] + Klensin, J. and M. Padlipsky, "Unicode Format for Network + Interchange", RFC 5198, March 2008. + + [RFC3894] Degener, J., "Sieve Extension: Copying Without Side + Effects", RFC 3894, October 2004. + + + + +Bosch [Page 12] + + Sieve External Programs October 2012 + + + [SIEVE] Guenther, P. and T. Showalter, "Sieve: An Email Filtering + Language", RFC 5228, January 2008. + + [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", STD 63, RFC 3629, November 2003. + + [VARIABLES] + Homme, K., "Sieve Email Filtering: Variables Extension", + RFC 5229, January 2008. + +12.2. Informative References + + [DSN] Moore, K. and G. Vaudreuil, "An Extensible Message Format + for Delivery Status Notifications", RFC 3464, + January 2003. + + [INCLUDE] Daboo, C. and A. Stone, "Sieve Email Filtering: Include + Extension", RFC 6609, May 2012. + + [MDN] Hansen, T. and G. Vaudreuil, "Message Disposition + Notification", RFC 3798, May 2004. + + [RFC5429] Stone, A., "Sieve Email Filtering: Reject and Extended + Reject Extensions", RFC 5429, March 2009. + + [SUBADDRESS] + Murchison, K., "Sieve Email Filtering -- Subaddress + Extension", RFC 3598, September 2003. + + +Author's Address + + Stephan Bosch + Enschede + NL + + Email: stephan@rename-it.nl + + + + + + + + + + + + + + +Bosch [Page 13] + diff --git a/doc/rfc/xml/reference.DSN.xml b/doc/rfc/xml/reference.DSN.xml new file mode 100644 index 0000000..8868dd4 --- /dev/null +++ b/doc/rfc/xml/reference.DSN.xml @@ -0,0 +1,17 @@ + + + + + +An Extensible Message Format for Delivery Status Notifications + + + + + + +This memo defines a Multipurpose Internet Mail Extensions (MIME) content-type that may be used by a message transfer agent (MTA) or electronic mail gateway to report the result of an attempt to deliver a message to one or more recipients. This content-type is intended as a machine-processable replacement for the various types of delivery status notifications currently used in Internet electronic mail. Because many messages are sent between the Internet and other messaging systems (such as X.400 or the so-called "Local Area Network (LAN)-based" systems), the Delivery Status Notification (DSN) protocol is designed to be useful in a multi-protocol messaging environment. To this end, the protocol described in this memo provides for the carriage of "foreign" addresses and error codes, in addition to those normally used in Internet mail. Additional attributes may also be defined to support "tunneling" of foreign notifications through Internet mail. [STANDARDS-TRACK] + + + + diff --git a/doc/rfc/xml/reference.MDN.xml b/doc/rfc/xml/reference.MDN.xml new file mode 100644 index 0000000..553c1d4 --- /dev/null +++ b/doc/rfc/xml/reference.MDN.xml @@ -0,0 +1,17 @@ + + + + + +Message Disposition Notification + + + + + + +This memo defines a MIME content-type that may be used by a mail user agent (MUA) or electronic mail gateway to report the disposition of a message after it has been successfully delivered to a recipient. This content-type is intended to be machine-processable. Additional message headers are also defined to permit Message Disposition Notifications (MDNs) to be requested by the sender of a message. The purpose is to extend Internet Mail to support functionality often found in other messaging systems, such as X.400 and the proprietary "LAN-based" systems, and often referred to as "read receipts," "acknowledgements", or "receipt notifications." The intention is to do this while respecting privacy concerns, which have often been expressed when such functions have been discussed in the past. Because many messages are sent between the Internet and other messaging systems (such as X.400 or the proprietary "LAN-based" systems), the MDN protocol is designed to be useful in a multi-protocol messaging environment. To this end, the protocol described in this memo provides for the carriage of "foreign" addresses, in addition to those normally used in Internet Mail. Additional attributes may also be defined to support "tunneling" of foreign notifications through Internet Mail. [STANDARDS-TRACK] + + + + diff --git a/doc/rfc/xml/reference.NET-UNICODE.xml b/doc/rfc/xml/reference.NET-UNICODE.xml new file mode 100644 index 0000000..450c448 --- /dev/null +++ b/doc/rfc/xml/reference.NET-UNICODE.xml @@ -0,0 +1,17 @@ + + + + + +Unicode Format for Network Interchange + + + + + + +The Internet today is in need of a standardized form for the transmission of internationalized "text" information, paralleling the specifications for the use of ASCII that date from the early days of the ARPANET. This document specifies that format, using UTF-8 with normalization and specific line-ending sequences. [STANDARDS-TRACK] + + + + diff --git a/doc/rfc/xml/reference.RFC.3894.xml b/doc/rfc/xml/reference.RFC.3894.xml new file mode 100644 index 0000000..d24f25f --- /dev/null +++ b/doc/rfc/xml/reference.RFC.3894.xml @@ -0,0 +1,15 @@ + + + + + +Sieve Extension: Copying Without Side Effects + + + + +The Sieve scripting language allows users to control handling and disposal of their incoming e-mail. By default, an e-mail message that is processed by a Sieve script is saved in the owner's "inbox". Actions such as "fileinto" and "redirect" cancel this default behavior.</t><t> This document defines a new keyword parameter, ":copy", to be used with the Sieve "fileinto" and "redirect" actions. Adding ":copy" to an action suppresses cancellation of the default "inbox" save. It allows users to add commands to an existing script without changing the meaning of the rest of the script. [STANDARDS-TRACK] + + + + diff --git a/doc/rfc/xml/reference.RFC.5429.xml b/doc/rfc/xml/reference.RFC.5429.xml new file mode 100644 index 0000000..d0c35b0 --- /dev/null +++ b/doc/rfc/xml/reference.RFC.5429.xml @@ -0,0 +1,15 @@ + + + + + +Sieve Email Filtering: Reject and Extended Reject Extensions + + + + +This memo updates the definition of the Sieve mail filtering language "reject" extension, originally defined in RFC 3028.</t><t> A "Joe-job" is a spam run forged to appear as though it came from an innocent party, who is then generally flooded by automated bounces, Message Disposition Notifications (MDNs), and personal messages with complaints. The original Sieve "reject" action defined in RFC 3028 required use of MDNs for rejecting messages, thus contributing to the flood of Joe-job spam to victims of Joe-jobs.</t><t> This memo updates the definition of the "reject" action to allow messages to be refused during the SMTP transaction, and defines the "ereject" action to require messages to be refused during the SMTP transaction, if possible.</t><t> The "ereject" action is intended to replace the "reject" action wherever possible. The "ereject" action is similar to "reject", but will always favor protocol-level message rejection. [STANDARDS-TRACK] + + + + diff --git a/doc/rfc/xml/reference.SUBADDRESS.xml b/doc/rfc/xml/reference.SUBADDRESS.xml new file mode 100644 index 0000000..e8fb02a --- /dev/null +++ b/doc/rfc/xml/reference.SUBADDRESS.xml @@ -0,0 +1,15 @@ + + + + + +Sieve Email Filtering -- Subaddress Extension + + + + +On email systems that allow for "subaddressing" or "detailed addressing" (e.g., "ken+sieve@example.org"), it is sometimes desirable to make comparisons against these sub-parts of addresses. This document defines an extension to the Sieve mail filtering language that allows users to compare against the user and detail parts of an address. [STANDARDS-TRACK] + + + + diff --git a/doc/rfc/xml/reference.UTF-8.xml b/doc/rfc/xml/reference.UTF-8.xml new file mode 100644 index 0000000..663a5dd --- /dev/null +++ b/doc/rfc/xml/reference.UTF-8.xml @@ -0,0 +1,16 @@ + + + + + +UTF-8, a transformation format of ISO 10646 + + + + +ISO/IEC 10646-1 defines a large character set called the Universal Character Set (UCS) which encompasses most of the world's writing systems. The originally proposed encodings of the UCS, however, were not compatible with many current applications and protocols, and this has led to the development of UTF-8, the object of this memo. UTF-8 has the characteristic of preserving the full US-ASCII range, providing compatibility with file systems, parsers and other software that rely on US-ASCII values but are transparent to other values. This memo obsoletes and replaces RFC 2279. + + + + + diff --git a/doc/rfc/xml/spec-bosch-sieve-extprograms.xml b/doc/rfc/xml/spec-bosch-sieve-extprograms.xml new file mode 100644 index 0000000..ddb9bb0 --- /dev/null +++ b/doc/rfc/xml/spec-bosch-sieve-extprograms.xml @@ -0,0 +1,616 @@ + + + + + + + + + + + + + + + + + + + + +Sieve Email Filtering: Invoking External Programs + + + + +
+ + + Enschede + NL + + stephan@rename-it.nl +
+
+ + + +General +Pigeonhole Project +sieve +pipe +external program + + + +The Sieve filtering language (RFC 5228) is explicitly designed to be powerful +enough to be useful yet limited in order to allow for a safe filtering system. +The base specification of the language makes it impossible for users to do +anything more complex (and dangerous) than write simple mail filters. One of +the consequences of this security-minded design is that users cannot execute +programs external to the Sieve filter. However, this can be a very useful and +flexible feature for situations where Sieve cannot provide some uncommon +functionality by itself. This document updates the Sieve filtering language with +extensions that add support for invoking a predefined set of external programs. +Messages can be piped to or filtered through those programs and string data +can be input to and retrieved from those programs. + + +
+ + + +
+This is an extension to the Sieve filtering language defined by +RFC 5228. It adds commands for invoking +a predefined set of external programs. Messages can be piped to or filtered +through those programs and, alternatively, string data can be passed to and +retrieved from those programs. + + +The Sieve language is explicitly designed to be powerful enough to be useful +yet limited in order to allow for a safe server-side filtering system. Therefore, +the base specification of the language makes it impossible for users to do +anything more complex (and dangerous) than write simple mail filters. One of the +consequences of this security-minded design is that users cannot execute +external programs from their Sieve script. Particularly for server-side +filtering setups in which mail accounts have no corresponding system account, +allowing the execution of arbitrary programs from the mail filter can be a +significant security risk. However, such functionality can also be very useful, +for instance to easily implement a custom action or external effect that Sieve +normally cannot provide. + + +This document updates the Sieve filtering language with an extension to +support invoking a predefined set of external programs using a set of new +commands. To mitigate the security concerns, the external programs cannot +be chosen arbitrarily; the available programs are restricted through +administrator configuration. + + +This extension is specific to the Pigeonhole Sieve implementation for the +Dovecot Secure IMAP server. It will therefore most likely not be supported by +web interfaces and GUI-based Sieve editors. This extension is primarily meant +for use in small setups or global scripts that are managed by the system's +administrator. + +
+ +
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", +"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this +document are to be interpreted as described in . +Conventions for notations are as in Section 1.1, +including use of the "Usage:" label for the definition of action and tagged +arguments syntax. +
+ +
+An external program is identified by a name. This MUST not correspond to a +file system path or otherwise have the ability to point to arbitrary programs on +the system. The list of valid program names MUST be limited, subject to +administrator configuration. + + +A program name is a sequence of Unicode characters encoded in UTF-8 +. A program name MUST comply with Net-Unicode Definition +(Section 2 of ), with the additional restriction of +prohibiting the following Unicode characters: + + + +0000-001F; [CONTROL CHARACTERS] + +002F; SLASH + +007F; DELETE + +0080-009F; [CONTROL CHARACTERS] + +2028; LINE SEPARATOR + +2029; PARAGRAPH SEPARATOR + + + +Program names MUST be at least one octet (and hence Unicode character) long. +Implementations MUST allow names of up to 128 Unicode characters in length +(which can take up to 512 octets when encoded in UTF-8, not counting the +terminating NUL), and MAY allow longer names. A server that receives a program +name longer than its internal limit MUST reject the corresponding operation, +in particular it MUST NOT truncate the program name. + + +Implementations MUST NOT allow variables to be expanded into the program +names; in other words, the "program-name" value MUST be a constant string as +defined in , Section 3. + +
+ +
+Optionally, arguments can be passed to an external program. The arguments are +specified as a Sieve string list and are passed to the external program in +sequence. Implementations SHOULD NOT impose any structure for these arguments; +validity checks are the responsibility of the external program. + +However, implementations SHOULD limit the maximum number of arguments and the +length of each argument. Implementations MUST accept at least 16 arguments with +a length of at least 1024 octets each, and MAY allow more and longer arguments. +Additionally, implementations MAY restrict the use of certain control characters +such as CR and LF, if these can cause unexpected behavior or raise security +concerns. + +Note that implementations MAY also implicitly pass other data, such as the +message envelope, to all executed programs avoiding the need to pass this +information explicitly through program arguments. + +
+ +
+ + +
+ + [] +]]> +
+ +The "pipe" action executes the external program identified by the +"program-name" argument and pipes the message to it. Much like the "fileinto" +and "redirect" actions , this action is a disposition-type +action (it is intended to deliver the message) and therefore it cancels Sieve's +implicit keep (see Section 2.10.2 of ) by default. + + +The specified "program-name" argument MUST conform to the syntax and +restrictions defined in . A script MUST fail with an +appropriate error if it attempts to use the "filter" action with an invalid, +restricted or unknown program name. The optional "arguments" argument lists the +arguments that are passed to the external program, as explained in +. + + +If the external program invoked by the "pipe" action fails to execute or +finishes execution with an error, script execution MUST fail with an appropriate +error (causing an implicit "keep" action to be executed), unless the ":try" tag +is specified. + + +When the ":try" tag is specified, the "pipe" instruction will attempt +execution of the external program, but failure will not cause the whole Sieve +script execution to fail with an error. Instead, the Sieve processing continues +as if the "pipe" action was never triggered. + + +If the execution of the external program is unsuccessful, the "pipe" action +MUST NOT cancel the implicit keep. + + +
+ +By default, the "pipe" action cancels the implicit keep, thereby handing +the responsibility for the message over to the external program. This behavior +can be overridden using the Sieve "copy" extension as +described in . + + +The "pipe" action can only be executed once per script for a particular +external program. A script MUST fail with an appropriate error if it attempts +to "pipe" messages to the same program multiple times. + + +The "pipe" action is incompatible with the Sieve "reject" and "ereject" +actions . + +
+ +
+ +The Sieve "copy" extension adds an optional ":copy" +tagged argument to the "fileinto" and "redirect" action commands. When +this tag is specified, these commands do not cancel the implicit "keep". +Instead, the requested action is performed in addition to whatever else +is happening to the message. + + +When the "vnd.dovecot.pipe" extension is active, the "copy" extension also +adds the optional ":copy" tag to the "pipe" action command. This has the +familiar effect that when the ":copy" tag is specified, the implicit "keep" will +not be canceled by the "pipe" action. When the "copy" extension is active, the +syntax of the "pipe" action is represented as follows: + + + +
+ + [] +]]> +
+
+
+ +
+ +
+ [] +]]> +
+ +The "filter" action executes the external program identified by the +"program-name" argument and filters the message through it. This means that the +message is provided as input to the external program and that the output of the +external program is used as the new message. This way, the entire message can be +altered using the external program. The "filter" action does not affect Sieve's +implicit keep. + + +The specified "program-name" argument MUST conform to the syntax and +restrictions defined in . A script MUST fail with an +appropriate error if it attempts to use the "filter" action with an invalid, +restricted or unknown program name. The optional "arguments" argument lists the +arguments that are passed to the external program, as explained in +. + + +If the external program fails to execute, finishes execution with an error, +or fails to provide message output, the "filter" action MUST terminate and leave +the message unchanged. No error condition is raised, script processing +continues, and prior or subsequent "filter" actions are not affected. + + +
+A successful "filter" action effectively changes the message, potentially +substituting the message in its entirety with a new version. However, actions +such as "reject" and "vacation" that generate , +, or similar disposition messages MUST do so using the +original, unmodified message. Similarly, if an error terminates processing of +the script, the original message MUST be used when doing the implicit keep +required by Section 2.10.6 of . All other actions that +store, send, or alter the message MUST do so with the current version of the +message. This includes the "filter" action itself. + + +When a disposition-type action, such as "fileinto", "redirect" or "pipe", is +encountered, the current version of the message is "locked in" for that +disposition-type action. Whether the implementation performs the action at that +point or batches it for later, it MUST perform the action on the message as it +stood at the time, and MUST NOT include subsequent changes encountered later +in the script processing. + + +In addition, any tests done on the message and its parts will test the +message after all prior "filter" actions have been performed. Because the +implicit keep, if it is in effect, acts on the final state of the message, +all "filter" actions are performed before any implicit keep. + + +The "filter" action does not affect the applicability of other actions; any +action that was applicable before the "filter" invocation is equally applicable +to the changed message afterward. + + +
+
+ +
+ + +
+ / ":pipe"] + [":output" ] + [] +]]> +
+ +The "execute" action executes the external program identified by the +"program-name" argument. Input to the program can be provided using the +":input" or ":pipe" tags. If used in combination with the "variables" +extension , the "execute" action can redirect output +from the program to the variable specified using the ":output" tag. This way, +string data can be passed to and retrieved from an external program. The +"execute" action does not change the message in any way and it never affects +Sieve's implicit keep. + + +The specified "program-name" argument MUST conform to the syntax and +restrictions defined in . A script MUST fail with an +appropriate error if it attempts to use the "execute" action with an invalid, +restricted or unknown program name. The optional "arguments" argument lists the +arguments that are passed to the external program, as explained in +. + + +The ":input" and ":pipe" tags are mutually exclusive, because these both +specify input that is passed to the external program. Specifying both for a +single "execute" command MUST trigger a compile error. The ":input" tag +specifies a string that is passed to the external script as input. This string +may also contain variable substitutions when the "variables" extension is +active. If instead the ":pipe" tag is specified, the current version of the +message itself is passed to the external program. If the ":input" and ":pipe" +tags are both omitted, no input is provided to the external program. + + +The ":output" tag specifies the variable to which the output of the external +program is to be redirected. If the ":output" tag is omitted, any output from +the external program is discarded. The ":output" tag requires the "variables" + extension to be active. The use of the ":output" tag +for the "execute" action without the "variables" extension in the require line +MUST trigger a compile error. + + +The "varname" parameter of the ":output" tag specifies the name of the +variable. It MUST be a constant string and it MUST conform to the syntax of +"variable-name" as defined in , Section 3. An invalid +name MUST be detected as a syntax error. The referenced variable MUST be +compatible with the "set" command as described in , +Section 4. This means that match variables cannot be specified and that variable +namespaces are only allowed when their specification explicitly indicates +compatibility with the "set" command. Use of an incompatible variable MUST +trigger a compile error. + + +The data actually stored in the variable MAY be truncated to conform to an +implementation-specific limit on variable length. If the execution of the +external program fails, the contents of the variable referenced with ":output" +MUST remain unchanged. + + +
+ +
+To simplify checking the successful invocation of the external program, +the "filter" and "execute" actions can also be used as tests. As such, these +will attempt to execute the requested external program, and will evaluate to +"true" if the program executed successfully and, if applicable, output was +retrieved from it successfully. The usage as a test is exactly the same as the +usage as an action: as a test it doubles as an action and a test of the action's +result at the same time. + +For the "execute" test, a "false" result is not necessarily equal to actual +failure: it may just mean that the executed program returned a "false" result, +e.g. an exit code higher than zero on Unix systems. Note that any output from +the external program is discarded when it yields a "false" result. Similarly, +for the "filter" test, programs may return a "false" result to indicate that the +message was not changed. In that case the Sieve interpreter will not replace the +active message with an identical one, which is beneficial for efficiency. The +exact semantics of these tests thus partly depends on the program being +executed. + +To handle missing programs gracefully, implementations MAY let the "filter" +and "execute" tests evaluate to "false" if an unknown program name is +specified, instead of failing the script with an error as would happen if used +as an action. In any other case and irrespective of whether the command is used +as an action or a test, passing invalid arguments to the "filter" or "execute" +commands, such as a syntactically invalid or restricted program name, MUST +always cause the script to fail with an appropriate error. + +
+ +
+A Sieve implementation that defines the "pipe" action command +will advertise the capability string "vnd.dovecot.pipe". + + +A Sieve implementation that defines the "filter" action command +will advertise the capability string "vnd.dovecot.filter". + + +A Sieve implementation that defines the "execute" command will +advertise the capability string "vnd.dovecot.execute". + +
+ +
+ +The examples outlined in this section all refer to some external program. +These programs are imaginary and are only available when the administrator would +provide them. + + +
+The following example passes messages directed to a +"user-request@example.com" address to an external program called +"request-handler". The "-request" part of the recipient address is identified +using the "subaddress" extension . If the program is +executed successfully, the message is considered delivered and does not end up +in the user's inbox. + + + +
+ +
+
+ +
+ +The following example copies messages addressed to a particular recipient to +a program called "printer". This program sends the message to some printer. In +this case it is configured for "A4" page format and "draft" quality using the +two arguments. Irrespective of whether the message is printed or not, it is also +always stored in the user's inbox through Sieve's implicit keep action (which is +not canceled due to the specified :copy tag). + + + +
+ +
+
+ +
+ +The following example translates a message from Dutch to English if +appropriate. If the message's content language is indicated to be Dutch, the +message is filtered through an external program called "translator" with +arguments that request Dutch to English translation. Dutch messages are +translated and filed into a special folder called "Translated". Other messages +are delivered to the user's inbox. + + + +
+ +
+ +Note that (formerly) Dutch messages are filed into the "Translated" folder, +even when the "translator" program fails. In the following modified example this +is prevented by using the filter action as a test: + + + +
+ +
+This way, messages only end up in the "Translated" folder when translation +was actually successful. + +
+ +
+ +The following example determines whether the user is on vacation by querying +an external source. The vacation message is obtained from the external source +as well. The program that queries the external source is called "onvacation" and +it has one argument: the localpart of the recipient address. The execute action +is used as a test, which will evaluate to "true" when the user is determined to +be on vacation. This means that the external program "onvacation" exits with a +failure when the user is not on vacation. Of course, a vacation response is also +not sent when the "onvacation" program truly fails somehow. + + + +
+ +
+
+ +
+ +
+Allowing users to execute programs external to the Sieve filter can be a +significant security risk, therefore the extensions presented in this +specification must be implemented with great care. The external programs should +execute with no more privileges than needed. + +Particularly the arguments passed to the external programs (see +) need to be handled with scrutiny. The external +programs need to check the arguments for validity and SHOULD NOT pass these to +system tools directly, as this may introduce the possibility of various kinds of +insertion attacks. External programs that work with message content or string +input from the Sieve script may have similar security concerns. + +Unlike the Sieve interpreter itself, an external program can easily +consume a large amount of resources if not implemented carefully. This can be +triggered by coincidence or intentionally by an attacker. Therefore, the amount +of resources available to the external programs SHOULD be limited appropriately. +For one, external programs MUST NOT be allowed to execute indefinitely. + +For improved security, implementations MAY restrict the use of this extension +to administrator-controlled global Sieve scripts. In such setups, the external +programs are never called directly from the user's personal script. For example, +using the "include" extension , the +user's personal script can include global scripts that contain the actual +external program invocations. This both abstracts the details of external +program invocation from the user's view and it limits access to external +programs to whatever the administrator defines. + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/m4/dovecot.m4 b/m4/dovecot.m4 index 2095875..df0520b 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 6 +# serial 11 AC_DEFUN([DC_DOVECOT_MODULEDIR],[ AC_ARG_WITH(moduledir, @@ -76,6 +76,8 @@ AC_DEFUN([DC_DOVECOT],[ eval `grep '^LIBDOVECOT[[A-Z_]]*=' "$dovecotdir"/dovecot-config` if test "$use_install_dirs" = "no"; then + # the main purpose of these is to fix make distcheck for plugins + # other than that, they don't really make much sense dovecot_pkgincludedir='$(pkgincludedir)' dovecot_pkglibdir='$(pkglibdir)' dovecot_pkglibexecdir='$(libexecdir)/dovecot' @@ -84,10 +86,10 @@ AC_DEFUN([DC_DOVECOT],[ fi 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]) + AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS], [DOVECOT_SQL_LIBS], [DOVECOT_COMPRESS_LIBS]) + AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_SSL], [LIBDOVECOT_COMPRESS], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE]) + AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_SSL_DEPS], [LIBDOVECOT_COMPRESS_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS]) + AX_SUBST_L([LIBDOVECOT_INCLUDE], [LIBDOVECOT_LDA_INCLUDE], [LIBDOVECOT_DOVEADM_INCLUDE], [LIBDOVECOT_SERVICE_INCLUDE], [LIBDOVECOT_STORAGE_INCLUDE], [LIBDOVECOT_LOGIN_INCLUDE], [LIBDOVECOT_CONFIG_INCLUDE], [LIBDOVECOT_IMAP_INCLUDE]) DC_PLUGIN_DEPS ]) diff --git a/src/Makefile.am b/src/Makefile.am index a04a0ab..6f0d3ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,8 @@ + sieve_subdirs = \ lib-sieve \ + lib-sievestorage \ plugins \ lib-sieve-tool \ sieve-tools \ @@ -9,7 +11,6 @@ sieve_subdirs = \ if BUILD_MANAGESIEVE managesieve_subdirs = \ lib-managesieve \ - lib-sievestorage \ managesieve \ managesieve-login else diff --git a/src/lib-managesieve/managesieve-parser.c b/src/lib-managesieve/managesieve-parser.c index d084a71..b231375 100644 --- a/src/lib-managesieve/managesieve-parser.c +++ b/src/lib-managesieve/managesieve-parser.c @@ -620,7 +620,7 @@ static ssize_t quoted_string_istream_read(struct istream_private *stream) struct quoted_string_istream *qsstream = (struct quoted_string_istream *)stream; const unsigned char *data; - size_t i, dest, size; + size_t i, dest, size, avail; ssize_t ret = 0; bool slash; @@ -648,7 +648,7 @@ static ssize_t quoted_string_istream_read(struct istream_private *stream) } /* Allocate buffer space */ - if (!i_stream_get_buffer_space(stream, size, NULL)) + if (!i_stream_try_alloc(stream, size, &avail)) return -2; /* Parse quoted string content */ @@ -712,10 +712,16 @@ static ssize_t quoted_string_istream_read(struct istream_private *stream) return ret; } -static const struct stat *quoted_string_istream_stat +static int quoted_string_istream_stat (struct istream_private *stream, bool exact) { - return i_stream_stat(stream->parent, exact); + const struct stat *st; + + if (i_stream_stat(stream->parent, exact, &st) < 0) + return -1; + + stream->statbuf = *st; + return 0; } static struct istream *quoted_string_istream_create diff --git a/src/lib-sieve-tool/mail-raw.c b/src/lib-sieve-tool/mail-raw.c index 74074ec..7e9f2f8 100644 --- a/src/lib-sieve-tool/mail-raw.c +++ b/src/lib-sieve-tool/mail-raw.c @@ -9,7 +9,6 @@ #include "str-sanitize.h" #include "strescape.h" #include "safe-mkstemp.h" -#include "close-keep-errno.h" #include "mkdir-parents.h" #include "abspath.h" #include "message-address.h" @@ -88,7 +87,7 @@ static int seekable_fd_callback if (unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_error("unlink(%s) failed: %m", str_c(path)); - close_keep_errno(fd); + i_close_fd(&fd); return -1; } @@ -189,8 +188,13 @@ static struct mail_raw *mail_raw_create } if ( ret < 0 ) { - i_fatal("Can't open delivery mail as raw: %s", - mailbox_get_last_error(mailr->box, NULL)); + if ( mailfile == NULL ) { + i_fatal("Can't open delivery mail as raw: %s", + mailbox_get_last_error(mailr->box, NULL)); + } else { + i_fatal("Can't open delivery mail as raw (file=%s): %s", + mailfile, mailbox_get_last_error(mailr->box, NULL)); + } } mailr->trans = mailbox_transaction_begin(mailr->box, 0); diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c index 4293542..19f78e6 100644 --- a/src/lib-sieve-tool/sieve-tool.c +++ b/src/lib-sieve-tool/sieve-tool.c @@ -271,8 +271,10 @@ struct sieve_instance *sieve_tool_init_finish 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.hostname = my_hostdomain(); svenv.base_dir = tool->mail_user_dovecot->set->base_dir; + svenv.location = SIEVE_ENV_LOCATION_MS; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; /* Initialize Sieve Engine */ if ( (tool->svinst=sieve_init diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index f3c0a97..f1785b9 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -5,8 +5,8 @@ #include "ioloop.h" #include "str-sanitize.h" #include "istream.h" -#include "istream-crlf.h" #include "istream-header-filter.h" +#include "ostream.h" #include "rfc2822.h" @@ -318,11 +318,9 @@ static bool act_redirect_send const struct sieve_script_env *senv = aenv->scriptenv; const char *sender = sieve_message_get_sender(msgctx); const char *recipient = sieve_message_get_final_recipient(msgctx); - struct istream *input, *crlf_input; + struct istream *input; + struct ostream *output; void *smtp_handle; - FILE *f; - const unsigned char *data; - size_t size; /* Just to be sure */ if ( !sieve_smtp_available(senv) ) { @@ -335,32 +333,25 @@ static bool act_redirect_send return FALSE; /* Open SMTP transport */ - smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &f); + smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &output); /* Remove unwanted headers */ input = i_stream_create_header_filter - (input, HEADER_FILTER_EXCLUDE, hide_headers, - N_ELEMENTS(hide_headers), null_header_filter_callback, NULL); - - /* Make sure the message contains CRLF consistently */ - crlf_input = i_stream_create_crlf(input); - - /* Prepend sieve headers (should not affect signatures) */ - rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); - if ( recipient != NULL ) - rfc2822_header_field_write(f, "X-Sieve-Redirected-From", recipient); - - /* Pipe the message to the outgoing SMTP transport */ - while (i_stream_read_data(crlf_input, &data, &size, 0) > 0) { - if (fwrite(data, size, 1, f) == 0) - break; - i_stream_skip(crlf_input, size); - } + (input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, + N_ELEMENTS(hide_headers), *null_header_filter_callback, (void *)NULL); + + T_BEGIN { + string_t *hdr = t_str_new(256); - // FIXME: handle stream error. Currently, we have no means to abort here. + /* Prepend sieve headers (should not affect signatures) */ + rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); + if ( recipient != NULL ) + rfc2822_header_write(hdr, "X-Sieve-Redirected-From", recipient); + o_stream_send(output, str_data(hdr), str_len(hdr)); + } T_END; - i_stream_unref(&crlf_input); - i_stream_unref(&input); + o_stream_send_istream(output, input); + i_stream_unref(&input); /* Close SMTP transport */ if ( !sieve_smtp_close(senv, smtp_handle) ) { diff --git a/src/lib-sieve/edit-mail.c b/src/lib-sieve/edit-mail.c index a5871f3..fda48a8 100644 --- a/src/lib-sieve/edit-mail.c +++ b/src/lib-sieve/edit-mail.c @@ -14,6 +14,7 @@ #include "message-header-decode.h" #include "mail-user.h" #include "mail-storage-private.h" +#include "index-mail.h" #include "raw-storage.h" #include "rfc2822.h" @@ -535,11 +536,9 @@ static inline char *_header_decode /* hdr_data is already unfolded */ /* Decode MIME encoded-words. */ - if ( message_header_decode_utf8 - ((const unsigned char *)hdr_data, hdr_data_len, str, FALSE)) - return i_strdup(str_c(str)); - - return i_strndup(hdr_data, hdr_data_len); + message_header_decode_utf8 + ((const unsigned char *)hdr_data, hdr_data_len, str, FALSE); + return i_strdup(str_c(str)); } static int edit_mail_headers_parse @@ -771,7 +770,7 @@ void edit_mail_header_add message_header_encode(value, enc_value); - lines = rfc2822_header_field_append + lines = rfc2822_header_append (data, field_name, str_c(enc_value), edmail->crlf, &field->body_offset); /* Copy to new field */ @@ -1086,6 +1085,13 @@ static uint64_t edit_mail_get_modseq(struct mail *mail) return edmail->wrapped->v.get_modseq(&edmail->wrapped->mail); } +static uint64_t edit_mail_get_pvt_modseq(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_pvt_modseq(&edmail->wrapped->mail); +} + static int edit_mail_get_parts (struct mail *mail, struct message_part **parts_r) { @@ -1232,7 +1238,7 @@ static int edit_mail_get_headers struct _header_index *header_idx; struct _header_field_index *field_idx; const char *const *headers; - ARRAY_DEFINE(header_values, const char *); + ARRAY(const char *) header_values; if ( !edmail->modified || edmail->headers_head == NULL ) { /* Unmodified */ @@ -1405,6 +1411,13 @@ static void edit_mail_update_modseq(struct mail *mail, uint64_t min_modseq) edmail->wrapped->v.update_modseq(&edmail->wrapped->mail, min_modseq); } +static void edit_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.update_pvt_modseq(&edmail->wrapped->mail, min_pvt_modseq); +} + static void edit_mail_update_pop3_uidl(struct mail *mail, const char *uidl) { struct edit_mail *edmail = (struct edit_mail *)mail; @@ -1439,6 +1452,7 @@ static struct mail_vfuncs edit_mail_vfuncs = { edit_mail_get_keywords, edit_mail_get_keyword_indexes, edit_mail_get_modseq, + edit_mail_get_pvt_modseq, edit_mail_get_parts, edit_mail_get_date, edit_mail_get_received_date, @@ -1449,11 +1463,13 @@ static struct mail_vfuncs edit_mail_vfuncs = { edit_mail_get_headers, edit_mail_get_header_stream, edit_mail_get_stream, + index_mail_get_binary_stream, edit_mail_get_special, edit_mail_get_real_mail, edit_mail_update_flags, edit_mail_update_keywords, edit_mail_update_modseq, + edit_mail_update_pvt_modseq, edit_mail_update_pop3_uidl, edit_mail_expunge, edit_mail_set_cache_corrupted, @@ -1473,7 +1489,7 @@ struct edit_mail_istream { struct _header_field_index *cur_header; - unsigned int read_header; + unsigned int header_read:1; }; static void edit_mail_istream_destroy(struct iostream_private *stream) @@ -1485,17 +1501,146 @@ static void edit_mail_istream_destroy(struct iostream_private *stream) pool_unref(&edstream->pool); } +static ssize_t merge_from_parent +(struct edit_mail_istream *edstream, uoff_t parent_v_offset, + uoff_t parent_end_v_offset, uoff_t copy_v_offset) +{ + struct istream_private *stream = &edstream->istream; + uoff_t v_offset = stream->istream.v_offset; + buffer_t *buffer = edstream->buffer; + const unsigned char *data; + size_t pos, cur_pos; + ssize_t ret; + + if (v_offset < copy_v_offset) { + i_assert(stream->skip == 0); + if (buffer->used < copy_v_offset - v_offset) { + copy_v_offset = v_offset + buffer->used; + } + } + + /* If we are still merging it with our local buffer, we need to update the + * parent seek offset to point to where we left of. + */ + if (v_offset < copy_v_offset) { + parent_v_offset += buffer->used - (copy_v_offset - v_offset) + stream->pos; + if (parent_v_offset >= parent_end_v_offset) + return 0; + cur_pos = 0; + } else { + buffer_set_used_size(buffer, 0); + stream->pos -= stream->skip; + stream->skip = 0; + cur_pos = stream->pos; + } + + i_stream_seek(stream->parent, parent_v_offset); + + /* Read from parent */ + data = i_stream_get_data(stream->parent, &pos); + if (pos > cur_pos) + ret = 0; + else do { + if ((ret = i_stream_read(stream->parent)) == -2) + return -2; + + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + data = i_stream_get_data(stream->parent, &pos); + /* check again, in case the parent stream had been seeked + backwards and the previous read() didn't get us far + enough. */ + } while (pos <= cur_pos && ret > 0); + + /* Don't read beyond parent end offset */ + if (pos > (parent_end_v_offset - parent_v_offset)) + pos = parent_end_v_offset - parent_v_offset; + + if (v_offset < copy_v_offset) { + /* Merging with our local buffer; copying data from parent */ + if (pos > 0) { + ret = (ssize_t)(pos); + buffer_append(buffer, data, pos); + stream->buffer = buffer_get_data(buffer, &pos); + i_assert(ret > 0); + stream->pos = pos; + } else { + ret = (ret == 0 ? 0 : -1); + } + } else { + /* Just passing buffers from parent; no copying */ + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : + (ret == 0 ? 0 : -1); + stream->buffer = data; + stream->pos = pos; + } + + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +static ssize_t merge_modified_headers(struct edit_mail_istream *edstream) +{ + struct istream_private *stream = &edstream->istream; + struct edit_mail *edmail = edstream->mail; + size_t pos; + ssize_t ret = 0; + + if (edstream->cur_header != NULL) { + /* Merge remaining parent buffer, if any */ + if (edstream->buffer->used == 0 && stream->skip < stream->pos ) { + buffer_append(edstream->buffer, + stream->buffer + stream->skip, stream->pos - stream->skip); + } + + /* Add modified headers to buffer */ + while ( edstream->cur_header != NULL && edstream->buffer->used < 1024 ) { + buffer_append(edstream->buffer, edstream->cur_header->field->data, + edstream->cur_header->field->size); + + edstream->cur_header = edstream->cur_header->next; + + /* Stop at end of prepended headers if original header is left unparsed */ + if ( !edmail->headers_parsed + && edstream->cur_header == edmail->header_fields_appended ) + edstream->cur_header = NULL; + } + + if ( edstream->buffer->used > 0 ) { + /* Output current buffer */ + stream->buffer = buffer_get_data(edstream->buffer, &pos); + ret = (ssize_t)pos + stream->skip - stream->pos; + i_assert( ret >= 0 ); + stream->pos = pos; + stream->skip = 0; + + if ( ret != 0 ) + return ret; + + if ( edstream->buffer->used >= 1024 ) + return -2; + } + } + return 0; +} + static ssize_t edit_mail_istream_read(struct istream_private *stream) { struct edit_mail_istream *edstream = (struct edit_mail_istream *)stream; struct edit_mail *edmail = edstream->mail; - uoff_t parent_v_offset, hdr_size, v_offset = stream->istream.v_offset; - size_t pos; + uoff_t parent_v_offset, parent_end_v_offset, copy_v_offset; + uoff_t v_offset = stream->istream.v_offset; + uoff_t prep_hdr_size, hdr_size; ssize_t ret = 0; + if (stream->istream.eof) + return -1; + if ( edstream->buffer->used > 0 ) { if ( stream->skip > 0 ) { + /* Remove skipped data from buffer */ buffer_copy (edstream->buffer, 0, edstream->buffer, stream->skip, (size_t)-1); stream->pos -= stream->skip; @@ -1504,93 +1649,88 @@ static ssize_t edit_mail_istream_read(struct istream_private *stream) } } - if ( edstream->buffer->used > 0 || stream->pos - stream->skip == 0 ) { - if ( edstream->cur_header != NULL ) { - while ( edstream->cur_header != NULL && edstream->buffer->used < 1024 ) { - buffer_append(edstream->buffer, edstream->cur_header->field->data, - edstream->cur_header->field->size); - - edstream->cur_header = edstream->cur_header->next; - - if ( !edmail->headers_parsed - && edstream->cur_header == edmail->header_fields_appended ) - edstream->cur_header = NULL; - } - } - } - - if ( edstream->buffer->used > 0 ) { - stream->buffer = buffer_get_data(edstream->buffer, &pos); - ret = (ssize_t)pos + stream->skip - stream->pos; - i_assert( ret >= 0 ); - stream->pos = pos; - stream->skip = 0; - - if ( ret == 0 ) - return -2; - - return ret; + /* Merge prepended headers */ + if (edstream->cur_header != NULL) { + if ( (ret=merge_modified_headers(edstream)) != 0 ) + return ret; } - if ( !edmail->headers_parsed && edmail->header_fields_appended != NULL ) { + if ( !edmail->headers_parsed && !edstream->header_read && + edmail->header_fields_appended != NULL ) { /* Output headers from original stream */ - /* At what offset does the header end (not including LF of final empty line) + /* Size of the prepended header */ + prep_hdr_size = edmail->hdr_size.physical_size - + edmail->appended_hdr_size.physical_size; + + /* Offset of header end or appended header * Any final CR is dealt with later */ - hdr_size = edmail->wrapped_hdr_size.physical_size + - edmail->hdr_size.physical_size - - edmail->appended_hdr_size.physical_size - 1; + hdr_size = prep_hdr_size + edmail->wrapped_hdr_size.physical_size; - if ( v_offset < hdr_size ) { + if ( v_offset < hdr_size - 1 ) { parent_v_offset = stream->parent_start_offset + - (v_offset + edmail->appended_hdr_size.physical_size - - edmail->hdr_size.physical_size); - - i_stream_seek(stream->parent, parent_v_offset); + (v_offset - prep_hdr_size); + parent_end_v_offset = stream->parent_start_offset + + edmail->wrapped_hdr_size.physical_size - 1; + copy_v_offset = prep_hdr_size; - if ( (ret=i_stream_read_copy_from_parent(&stream->istream)) < 0 ) + if ( (ret=merge_from_parent(edstream, parent_v_offset, + parent_end_v_offset, copy_v_offset)) < 0 ) { return ret; + } if ( stream->pos >= hdr_size - 1 - v_offset ) { - /* Truncate buffer from original mail strictly to header */ - ret -= stream->pos - (hdr_size - v_offset); - stream->pos = hdr_size - v_offset; - /* Strip final CR too when it is present */ if ( stream->buffer[stream->pos-1] == '\r' ) { stream->pos--; ret--; + if (edstream->buffer->used > 0) + buffer_set_used_size(edstream->buffer, edstream->buffer->used-1); } i_assert(ret >= 0); + edstream->header_read = TRUE; edstream->cur_header = edmail->header_fields_appended; - if ( ret == 0 ) - return -2; } - return ret; + if (ret != 0) + return ret; + } + + /* Merge Appended headers */ + if (edstream->cur_header != NULL) { + if ( (ret=merge_modified_headers(edstream)) != 0 ) + return ret; } } - if ( !edmail->headers_parsed ) { - if ( v_offset < edmail->hdr_size.physical_size ) - return -2; + /* Header does not come from original mail at all */ + if ( edmail->headers_parsed ) { + parent_v_offset = stream->parent_start_offset + + (v_offset - edmail->hdr_size.physical_size) + + edmail->wrapped_hdr_size.physical_size - ( edmail->eoh_crlf ? 2 : 1); + copy_v_offset = edmail->hdr_size.physical_size; + + /* Header comes partially from original mail and headers are added between + header and body. + */ + } else if (edmail->header_fields_appended != NULL) { + parent_v_offset = stream->parent_start_offset + + (v_offset - edmail->hdr_size.physical_size); + copy_v_offset = edmail->hdr_size.physical_size + + edmail->wrapped_hdr_size.physical_size; - parent_v_offset = stream->parent_start_offset - + (v_offset - edmail->hdr_size.physical_size); + /* Header comes partially from original mail, but headers are only prepended. + */ } else { - if ( v_offset < edmail->hdr_size.physical_size ) - return -2; - parent_v_offset = stream->parent_start_offset - + edmail->wrapped_hdr_size.physical_size - + (v_offset - edmail->hdr_size.physical_size) - - ( edmail->eoh_crlf ? 2 : 1); + + (v_offset - edmail->hdr_size.physical_size); + copy_v_offset = edmail->hdr_size.physical_size; } - i_stream_seek(stream->parent, parent_v_offset); - return i_stream_read_copy_from_parent(&stream->istream); + return merge_from_parent + (edstream, parent_v_offset, (uoff_t)-1, copy_v_offset); } static void @@ -1635,6 +1775,8 @@ static void edit_mail_istream_seek struct edit_mail *edmail = edstream->mail; uoff_t offset; + edstream->header_read = FALSE; + /* The beginning */ if ( v_offset == 0 ) { stream_reset_to(edstream, 0); @@ -1683,6 +1825,8 @@ static void edit_mail_istream_seek return; } + edstream->header_read = TRUE; + /* Inside appended header */ offset = edmail->hdr_size.physical_size + edmail->wrapped_hdr_size.physical_size; @@ -1717,7 +1861,7 @@ edit_mail_istream_sync(struct istream_private *stream ATTR_UNUSED) i_panic("edit-mail istream sync() not implemented"); } -static const struct stat * +static int edit_mail_istream_stat(struct istream_private *stream, bool exact) { struct edit_mail_istream *edstream = @@ -1726,16 +1870,16 @@ edit_mail_istream_stat(struct istream_private *stream, bool exact) const struct stat *st; /* Stat the original stream */ - st = i_stream_stat(stream->parent, exact); - if (st == NULL || st->st_size == -1 || !exact) - return st; + if (i_stream_stat(stream->parent, exact, &st) < 0) + return -1; - /* Adjust stat data */ stream->statbuf = *st; + if (st->st_size == -1 || !exact) + return 0; if ( !edmail->headers_parsed ) { if ( !edmail->modified ) - return &stream->statbuf; + return 0; } else { stream->statbuf.st_size = edmail->wrapped_body_size.physical_size + ( edmail->eoh_crlf ? 2 : 1 ); @@ -1743,8 +1887,7 @@ edit_mail_istream_stat(struct istream_private *stream, bool exact) stream->statbuf.st_size += edmail->hdr_size.physical_size + edmail->body_size.physical_size; - - return &stream->statbuf; + return 0; } struct istream *edit_mail_istream_create diff --git a/src/lib-sieve/ext-encoded-character.c b/src/lib-sieve/ext-encoded-character.c index 3e42069..6055310 100644 --- a/src/lib-sieve/ext-encoded-character.c +++ b/src/lib-sieve/ext-encoded-character.c @@ -28,12 +28,8 @@ static bool ext_encoded_character_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); struct sieve_extension_def encoded_character_extension = { - "encoded-character", - NULL, NULL, - ext_encoded_character_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "encoded-character", + .validator_load = ext_encoded_character_validator_load, }; /* diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c index 5481d9a..f39bd10 100644 --- a/src/lib-sieve/ext-envelope.c +++ b/src/lib-sieve/ext-envelope.c @@ -48,12 +48,9 @@ static bool ext_envelope_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def envelope_extension = { - "envelope", - NULL, NULL, - ext_envelope_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(envelope_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "envelope", + .validator_load = ext_envelope_validator_load, + SIEVE_EXT_DEFINE_OPERATION(envelope_operation) }; static bool ext_envelope_validator_load @@ -183,7 +180,7 @@ static const struct sieve_envelope_part *_envelope_part_find static const struct sieve_address *const *_from_part_get_addresses (const struct sieve_runtime_env *renv) { - ARRAY_DEFINE(envelope_values, const struct sieve_address *); + ARRAY(const struct sieve_address *) envelope_values; const struct sieve_address *address = sieve_message_get_sender_address(renv->msgctx); @@ -202,7 +199,7 @@ static const struct sieve_address *const *_from_part_get_addresses static const char *const *_from_part_get_values (const struct sieve_runtime_env *renv) { - ARRAY_DEFINE(envelope_values, const char *); + ARRAY(const char *) envelope_values; t_array_init(&envelope_values, 2); @@ -218,7 +215,7 @@ static const char *const *_from_part_get_values static const struct sieve_address *const *_to_part_get_addresses (const struct sieve_runtime_env *renv) { - ARRAY_DEFINE(envelope_values, const struct sieve_address *); + ARRAY(const struct sieve_address *) envelope_values; const struct sieve_address *address = sieve_message_get_orig_recipient_address(renv->msgctx); @@ -237,7 +234,7 @@ static const struct sieve_address *const *_to_part_get_addresses static const char *const *_to_part_get_values (const struct sieve_runtime_env *renv) { - ARRAY_DEFINE(envelope_values, const char *); + ARRAY(const char *) envelope_values; t_array_init(&envelope_values, 2); @@ -253,7 +250,7 @@ static const char *const *_to_part_get_values static const char *const *_auth_part_get_values (const struct sieve_runtime_env *renv) { - ARRAY_DEFINE(envelope_values, const char *); + ARRAY(const char *) envelope_values; t_array_init(&envelope_values, 2); diff --git a/src/lib-sieve/ext-fileinto.c b/src/lib-sieve/ext-fileinto.c index e40ba39..e4648f9 100644 --- a/src/lib-sieve/ext-fileinto.c +++ b/src/lib-sieve/ext-fileinto.c @@ -44,12 +44,9 @@ static bool ext_fileinto_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def fileinto_extension = { - "fileinto", - NULL, NULL, - ext_fileinto_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(fileinto_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "fileinto", + .validator_load = ext_fileinto_validator_load, + SIEVE_EXT_DEFINE_OPERATION(fileinto_operation) }; static bool ext_fileinto_validator_load diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c index 5398d6e..4532677 100644 --- a/src/lib-sieve/ext-reject.c +++ b/src/lib-sieve/ext-reject.c @@ -56,12 +56,9 @@ static bool ext_reject_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def reject_extension = { - "reject", - NULL, NULL, - ext_reject_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(reject_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "reject", + .validator_load = ext_reject_validator_load, + SIEVE_EXT_DEFINE_OPERATION(reject_operation) }; static bool ext_reject_validator_load @@ -79,12 +76,9 @@ static bool ext_ereject_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def ereject_extension = { - "ereject", - NULL, NULL, - ext_ereject_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(ereject_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "ereject", + .validator_load = ext_ereject_validator_load, + SIEVE_EXT_DEFINE_OPERATION(ereject_operation) }; static bool ext_ereject_validator_load diff --git a/src/lib-sieve/plugins/body/ext-body-common.c b/src/lib-sieve/plugins/body/ext-body-common.c index 39cf96f..9addbe3 100644 --- a/src/lib-sieve/plugins/body/ext-body-common.c +++ b/src/lib-sieve/plugins/body/ext-body-common.c @@ -43,8 +43,8 @@ struct ext_body_part_cached { struct ext_body_message_context { pool_t pool; - ARRAY_DEFINE(cached_body_parts, struct ext_body_part_cached); - ARRAY_DEFINE(return_body_parts, struct ext_body_part); + ARRAY(struct ext_body_part_cached) cached_body_parts; + ARRAY(struct ext_body_part) return_body_parts; buffer_t *tmp_buffer; buffer_t *raw_body; }; @@ -83,6 +83,25 @@ static bool _is_wanted_content_type return FALSE; } +static bool _want_multipart_content_type +(const char * const *wanted_types) +{ + for (; *wanted_types != NULL; wanted_types++) { + if (**wanted_types == '\0') { + /* empty string matches everything */ + return TRUE; + } + + /* match only main type */ + if ( strncasecmp(*wanted_types, "multipart", 9) == 0 && + ( strlen(*wanted_types) == 9 || *(*wanted_types+9) == '/' ) ) + return TRUE; + } + + return FALSE; +} + + static bool ext_body_get_return_parts (struct ext_body_message_context *ctx, const char * const *wanted_types, bool decode_to_plain) @@ -201,9 +220,11 @@ static bool ext_body_parts_add_missing struct message_decoder_context *decoder; struct message_block block, decoded; struct message_part *parts, *prev_part = NULL; + ARRAY(struct message_part *) part_index; struct istream *input; unsigned int idx = 0; - bool save_body = FALSE, have_all; + bool save_body = FALSE, want_multipart, have_all; + int ret; /* First check whether any are missing */ if (ext_body_get_return_parts(ctx, content_types, decode_to_plain)) { @@ -214,18 +235,24 @@ static bool ext_body_parts_add_missing /* Get the message stream */ if ( mail_get_stream(mail, NULL, NULL, &input) < 0 ) return FALSE; - //if (mail_get_parts(mail, &parts) < 0) - // return FALSE; + + if (mail_get_parts(mail, &parts) < 0) + return FALSE; + + if ( (want_multipart=_want_multipart_content_type(content_types)) ) { + t_array_init(&part_index, 8); + } buffer_set_used_size(ctx->tmp_buffer, 0); /* Initialize body decoder */ - decoder = decode_to_plain ? message_decoder_init(FALSE) : NULL; - - //parser = message_parser_init_from_parts(parts, input, 0, 0); - parser = message_parser_init(ctx->pool, input, 0, 0); + decoder = decode_to_plain ? message_decoder_init(NULL, 0) : NULL; - while ( message_parser_parse_next_block(parser, &block) > 0 ) { + //parser = message_parser_init_from_parts(parts, input, 0, + //MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS); + parser = message_parser_init(ctx->pool, input, 0, + MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS); + while ( (ret = message_parser_parse_next_block(parser, &block)) > 0 ) { if ( block.part != prev_part ) { bool message_rfc822 = FALSE; @@ -237,8 +264,9 @@ static bool ext_body_parts_add_missing strcmp(body_part->content_type, "message/rfc822") == 0 ) { message_rfc822 = TRUE; } else { - if ( save_body ) + if ( save_body ) { ext_body_part_save(ctx, body_part, decoder != NULL); + } } } @@ -246,6 +274,30 @@ static bool ext_body_parts_add_missing body_part = array_idx_modifiable(&ctx->cached_body_parts, idx); body_part->content_type = "text/plain"; + /* Check whether this is the epilogue block of a wanted multipart part */ + if ( want_multipart ) { + array_idx_set(&part_index, idx, &block.part); + + if ( prev_part != NULL && prev_part->next != block.part && + block.part->parent != prev_part ) { + struct message_part *const *iparts; + unsigned int count, i; + + iparts = array_get(&part_index, &count); + for ( i = 0; i < count; i++ ) { + if ( iparts[i] == block.part ) { + const struct ext_body_part_cached *parent = + array_idx(&ctx->cached_body_parts, i); + body_part->content_type = parent->content_type; + body_part->have_body = TRUE; + save_body = _is_wanted_content_type + (content_types, body_part->content_type); + break; + } + } + } + } + /* If this is message/rfc822 content retain the enveloping part for * storing headers as content. */ diff --git a/src/lib-sieve/plugins/body/ext-body.c b/src/lib-sieve/plugins/body/ext-body.c index ee35598..6241780 100644 --- a/src/lib-sieve/plugins/body/ext-body.c +++ b/src/lib-sieve/plugins/body/ext-body.c @@ -53,12 +53,9 @@ static bool ext_body_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def body_extension = { - "body", - NULL, NULL, - ext_body_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(body_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "body", + .validator_load = ext_body_validator_load, + SIEVE_EXT_DEFINE_OPERATION(body_operation) }; static bool ext_body_validator_load diff --git a/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c b/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c index 453ede8..0d00b17 100644 --- a/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c +++ b/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c @@ -38,11 +38,8 @@ static bool ext_cmp_i_ascii_numeric_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def comparator_i_ascii_numeric_extension = { - "comparator-i;ascii-numeric", - NULL, NULL, - ext_cmp_i_ascii_numeric_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, + .name = "comparator-i;ascii-numeric", + .validator_load = ext_cmp_i_ascii_numeric_validator_load, SIEVE_EXT_DEFINE_OPERAND(my_comparator_operand) }; diff --git a/src/lib-sieve/plugins/copy/ext-copy.c b/src/lib-sieve/plugins/copy/ext-copy.c index 4086b9d..77faa4f 100644 --- a/src/lib-sieve/plugins/copy/ext-copy.c +++ b/src/lib-sieve/plugins/copy/ext-copy.c @@ -39,11 +39,8 @@ static bool ext_copy_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def copy_extension = { - "copy", - NULL, NULL, - ext_copy_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, + .name = "copy", + .validator_load = ext_copy_validator_load, SIEVE_EXT_DEFINE_OPERAND(copy_side_effect_operand) }; diff --git a/src/lib-sieve/plugins/date/ext-date-common.c b/src/lib-sieve/plugins/date/ext-date-common.c index 1c889d6..399eb80 100644 --- a/src/lib-sieve/plugins/date/ext-date-common.c +++ b/src/lib-sieve/plugins/date/ext-date-common.c @@ -4,6 +4,7 @@ #include "lib.h" #include "utc-offset.h" #include "str.h" +#include "iso8601-date.h" #include "message-date.h" #include "sieve-common.h" @@ -350,7 +351,7 @@ static const char *ext_date_julian_part_get int day = tm->tm_mday; int c, ya, jd; - /* Modified from RFC 5260 Appendix A */ + /* Modified from RFC 5260 Appendix A (refer to Errata) */ if ( month > 2 ) month -= 3; @@ -394,50 +395,18 @@ static const char *ext_date_time_part_get static const char *ext_date_iso8601_part_get (struct tm *tm, int zone_offset) { - const char *time_offset; - - /* - * RFC 3339: 5.6. Internet Date/Time Format - * - * The following profile of ISO 8601 [ISO8601] dates SHOULD be used in - * new protocols on the Internet. This is specified using the syntax - * description notation defined in [ABNF]. - * - * date-fullyear = 4DIGIT - * date-month = 2DIGIT ; 01-12 - * date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on - * ; month/year - * time-hour = 2DIGIT ; 00-23 - * time-minute = 2DIGIT ; 00-59 - * time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second - * ; rules - * time-secfrac = "." 1*DIGIT - * time-numoffset = ("+" / "-") time-hour ":" time-minute - * time-offset = "Z" / time-numoffset - * - * partial-time = time-hour ":" time-minute ":" time-second - * [time-secfrac] - * full-date = date-fullyear "-" date-month "-" date-mday - * full-time = partial-time time-offset - * - * date-time = full-date "T" full-time - * + /* From RFC: `The restricted ISO 8601 format is specified by the date-time + * ABNF production given in [RFC3339], Section 5.6, with the added + * restrictions that the letters "T" and "Z" MUST be in upper case, and + * a time zone offset of zero MUST be represented by "Z" and not "+00:00". */ - if ( zone_offset == 0 ) - time_offset = "Z"; - else { - int offset = zone_offset > 0 ? zone_offset : -zone_offset; + zone_offset = INT_MAX; - time_offset = t_strdup_printf - ("%c%02d:%02d", (zone_offset > 0 ? '+' : '-'), offset / 60, offset % 60); - } - - return t_strdup_printf("%04d-%02d-%02dT%02d:%02d:%02d%s", - tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - tm->tm_sec, time_offset); + return iso8601_date_create_tm(tm, zone_offset); } + static const char *ext_date_std11_part_get (struct tm *tm, int zone_offset) { diff --git a/src/lib-sieve/plugins/date/ext-date.c b/src/lib-sieve/plugins/date/ext-date.c index 6477908..c1b0d48 100644 --- a/src/lib-sieve/plugins/date/ext-date.c +++ b/src/lib-sieve/plugins/date/ext-date.c @@ -43,14 +43,10 @@ const struct sieve_operation_def *ext_date_operations[] = { }; const struct sieve_extension_def date_extension = { - "date", - NULL, NULL, - ext_date_validator_load, - NULL, - ext_date_interpreter_load, - NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "date", + .validator_load = ext_date_validator_load, + .interpreter_load = ext_date_interpreter_load, + SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations) }; static bool ext_date_validator_load diff --git a/src/lib-sieve/plugins/editheader/ext-editheader-common.c b/src/lib-sieve/plugins/editheader/ext-editheader-common.c index a6c2926..b8bf653 100644 --- a/src/lib-sieve/plugins/editheader/ext-editheader-common.c +++ b/src/lib-sieve/plugins/editheader/ext-editheader-common.c @@ -29,7 +29,7 @@ struct ext_editheader_header { struct ext_editheader_config { pool_t pool; - ARRAY_DEFINE(headers, struct ext_editheader_header); + ARRAY(struct ext_editheader_header) headers; size_t max_header_size; }; diff --git a/src/lib-sieve/plugins/editheader/ext-editheader.c b/src/lib-sieve/plugins/editheader/ext-editheader.c index b7a49c5..17139c6 100644 --- a/src/lib-sieve/plugins/editheader/ext-editheader.c +++ b/src/lib-sieve/plugins/editheader/ext-editheader.c @@ -45,13 +45,11 @@ static bool ext_editheader_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def editheader_extension = { - "editheader", - ext_editheader_load, - ext_editheader_unload, - ext_editheader_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "editheader", + .load = ext_editheader_load, + .unload = ext_editheader_unload, + .validator_load = ext_editheader_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations) }; static bool ext_editheader_validator_load diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h index cf80361..d1ccf84 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h +++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h @@ -22,7 +22,7 @@ extern const struct sieve_extension_capabilities notify_capabilities; struct ext_enotify_context { const struct sieve_extension *var_ext; - ARRAY_DEFINE(notify_methods, struct sieve_enotify_method); + ARRAY(struct sieve_enotify_method) notify_methods; }; diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c index 7603fab..5310f58 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify.c +++ b/src/lib-sieve/plugins/enotify/ext-enotify.c @@ -46,11 +46,10 @@ static bool ext_enotify_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def enotify_extension = { - "enotify", - ext_enotify_load, - ext_enotify_unload, - ext_enotify_validator_load, - NULL, NULL, NULL, NULL, NULL, + .name = "enotify", + .load = ext_enotify_load, + .unload = ext_enotify_unload, + .validator_load = ext_enotify_validator_load, SIEVE_EXT_DEFINE_OPERATIONS(ext_enotify_operations), SIEVE_EXT_DEFINE_OPERAND(encodeurl_operand) }; diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c index 6b662da..a358011 100644 --- a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c +++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c @@ -23,6 +23,7 @@ #include "str.h" #include "ioloop.h" #include "str-sanitize.h" +#include "ostream.h" #include "message-date.h" #include "mail-storage.h" @@ -386,9 +387,11 @@ static bool ntfy_mailto_send const char *body = mtctx->uri->body; string_t *to, *cc; const struct uri_mailto_recipient *recipients; + const struct uri_mailto_header_field *headers; void *smtp_handle; - unsigned int count, i; - FILE *f; + struct ostream *output; + string_t *msg; + unsigned int count, i, hcount, h; const char *outmsgid; /* Get recipients */ @@ -461,74 +464,81 @@ static bool ntfy_mailto_send } } - /* Send message to all recipients */ - for ( i = 0; i < count; i++ ) { - const struct uri_mailto_header_field *headers; - unsigned int h, hcount; + msg = t_str_new(512); + outmsgid = sieve_message_get_new_id(nenv->svinst); + + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Message-ID", outmsgid); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); + + rfc2822_header_utf8_printf(msg, "From", "%s", from); + + if ( to != NULL ) + rfc2822_header_utf8_printf(msg, "To", "%s", str_c(to)); + + if ( cc != NULL ) + rfc2822_header_utf8_printf(msg, "Cc", "%s", str_c(cc)); + + rfc2822_header_printf(msg, "Auto-Submitted", + "auto-notified; owner-email=\"%s\"", recipient); + rfc2822_header_write(msg, "Precedence", "bulk"); + + /* Set importance */ + switch ( nact->importance ) { + case 1: + rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); + rfc2822_header_write(msg, "Importance", "High"); + break; + case 3: + rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); + rfc2822_header_write(msg, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); + rfc2822_header_write(msg, "Importance", "Normal"); + break; + } - smtp_handle = sieve_smtp_open - (senv, recipients[i].normalized, from_smtp, &f); - outmsgid = sieve_message_get_new_id(nenv->svinst); - - rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_field_write(f, "Message-ID", outmsgid); - rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); - rfc2822_header_field_utf8_printf(f, "Subject", "%s", subject); - - rfc2822_header_field_utf8_printf(f, "From", "%s", from); - - if ( to != NULL ) - rfc2822_header_field_utf8_printf(f, "To", "%s", str_c(to)); - - if ( cc != NULL ) - rfc2822_header_field_utf8_printf(f, "Cc", "%s", str_c(cc)); - - rfc2822_header_field_printf(f, "Auto-Submitted", - "auto-notified; owner-email=\"%s\"", recipient); - rfc2822_header_field_write(f, "Precedence", "bulk"); - - /* Set importance */ - switch ( nact->importance ) { - case 1: - rfc2822_header_field_write(f, "X-Priority", "1 (Highest)"); - rfc2822_header_field_write(f, "Importance", "High"); - break; - case 3: - rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)"); - rfc2822_header_field_write(f, "Importance", "Low"); - break; - case 2: - default: - rfc2822_header_field_write(f, "X-Priority", "3 (Normal)"); - rfc2822_header_field_write(f, "Importance", "Normal"); - break; - } + /* Add custom headers */ + + headers = array_get(&mtctx->uri->headers, &hcount); + for ( h = 0; h < hcount; h++ ) { + const char *name = rfc2822_header_field_name_sanitize(headers[h].name); - /* Add custom headers */ + rfc2822_header_write(msg, name, headers[h].body); + } - headers = array_get(&mtctx->uri->headers, &hcount); - for ( h = 0; h < hcount; h++ ) { - const char *name = rfc2822_header_field_name_sanitize(headers[h].name); + /* Generate message body */ - rfc2822_header_field_write(f, name, headers[h].body); + rfc2822_header_write(msg, "MIME-Version", "1.0"); + if ( body != NULL ) { + if (_contains_8bit(body)) { + rfc2822_header_write + (msg, "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + } else { + rfc2822_header_write + (msg, "Content-Type", "text/plain; charset=us-ascii"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); } + str_printfa(msg, "\r\n%s\r\n", body); - /* Generate message body */ - if ( body != NULL ) { - if (_contains_8bit(body)) { - rfc2822_header_field_write(f, "MIME-Version", "1.0"); - rfc2822_header_field_write - (f, "Content-Type", "text/plain; charset=UTF-8"); - rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); - } + } else { + rfc2822_header_write + (msg, "Content-Type", "text/plain; charset=US-ASCII"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); - fprintf(f, "\r\n"); - fprintf(f, "%s\r\n", body); + str_append(msg, "\r\nNotification of new message.\r\n"); + } - } else { - fprintf(f, "\r\n"); - fprintf(f, "Notification of new message.\r\n"); - } + /* Send message to all recipients */ + for ( i = 0; i < count; i++ ) { + smtp_handle = sieve_smtp_open + (senv, recipients[i].normalized, from_smtp, &output); + + o_stream_send(output, str_data(msg), str_len(msg)); if ( sieve_smtp_close(senv, smtp_handle) ) { sieve_enotify_global_info(nenv, @@ -552,6 +562,7 @@ static bool ntfy_mailto_action_execute const char *const *headers; const char *sender = sieve_message_get_sender(nenv->msgctx); const char *recipient = sieve_message_get_final_recipient(nenv->msgctx); + bool result; /* Is the recipient unset? */ @@ -578,7 +589,11 @@ static bool ntfy_mailto_action_execute } } - return ntfy_mailto_send(nenv, nact, recipient); + T_BEGIN { + result = ntfy_mailto_send(nenv, nact, recipient); + } T_END; + + return result; } diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c index 35308f6..e3a946e 100644 --- a/src/lib-sieve/plugins/environment/ext-environment-common.c +++ b/src/lib-sieve/plugins/environment/ext-environment-common.c @@ -10,7 +10,8 @@ #include "ext-environment-common.h" struct ext_environment_context { - struct hash_table *environment_items; + HASH_TABLE(const char *, + const struct sieve_environment_item *) environment_items; }; /* @@ -36,8 +37,7 @@ static void ext_environment_item_register (struct ext_environment_context *ectx, const struct sieve_environment_item *item) { - hash_table_insert - (ectx->environment_items, (void *) item->name, (void *) item); + hash_table_insert(ectx->environment_items, item->name, item); } void sieve_ext_environment_item_register @@ -61,8 +61,8 @@ bool ext_environment_init unsigned int i; - ectx->environment_items = hash_table_create - (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + hash_table_create + (&ectx->environment_items, default_pool, 0, str_hash, strcmp); for ( i = 0; i < core_env_items_count; i++ ) { ext_environment_item_register(ectx, core_env_items[i]); @@ -94,8 +94,7 @@ const char *ext_environment_item_get_value struct ext_environment_context *ectx = (struct ext_environment_context *) ext->context; const struct sieve_environment_item *item = - (const struct sieve_environment_item *) - hash_table_lookup(ectx->environment_items, name); + hash_table_lookup(ectx->environment_items, name); if ( item == NULL ) return NULL; @@ -103,11 +102,8 @@ const char *ext_environment_item_get_value if ( item->value != NULL ) return item->value; - if ( item->get_value != NULL ) { - const char *value = item->get_value(ext->svinst, senv); - - return ( value == NULL ? "" : value ); - } + if ( item->get_value != NULL ) + return item->get_value(ext->svinst, senv); return NULL; } @@ -121,10 +117,17 @@ const char *ext_environment_item_get_value * The primary DNS domain associated with the Sieve execution context, usually * but not always a proper suffix of the host name. */ + +static const char *envit_domain_get_value +(struct sieve_instance *svinst, + const struct sieve_script_env *senv ATTR_UNUSED) +{ + return svinst->domainname; +} + const struct sieve_environment_item domain_env_item = { - "domain", - NULL, - NULL, + .name = "domain", + .get_value = envit_domain_get_value, }; /* "host": @@ -134,15 +137,15 @@ const struct sieve_environment_item domain_env_item = { */ static const char *envit_host_get_value -(struct sieve_instance *svinst, const struct sieve_script_env *senv ATTR_UNUSED) +(struct sieve_instance *svinst, + const struct sieve_script_env *senv ATTR_UNUSED) { return svinst->hostname; } const struct sieve_environment_item host_env_item = { - "host", - NULL, - envit_host_get_value, + .name = "host", + .get_value = envit_host_get_value, }; /* "location": @@ -152,13 +155,30 @@ const struct sieve_environment_item host_env_item = { * service that is evaluating the script. Possible values are: * "MTA" - the Sieve script is being evaluated by a Message Transfer Agent * "MDA" - evaluation is being performed by a Mail Delivery Agent - * "MUA" - evaluation is being performed by a Mail User Agent + * "MUA" - evaluation is being performed by a Mail User Agent (right...) * "MS" - evaluation is being performed by a Message Store */ + +static const char *envit_location_get_value +(struct sieve_instance *svinst, + const struct sieve_script_env *senv ATTR_UNUSED) +{ + switch ( svinst->env_location ) { + case SIEVE_ENV_LOCATION_MDA: + return "MDA"; + case SIEVE_ENV_LOCATION_MTA: + return "MTA"; + case SIEVE_ENV_LOCATION_MS: + return "MS"; + default: + break; + } + return NULL; +} + const struct sieve_environment_item location_env_item = { - "location", - NULL, - NULL, + .name = "location", + .get_value = envit_location_get_value }; /* "phase": @@ -169,20 +189,36 @@ const struct sieve_environment_item location_env_item = { * taken place. */ +static const char *envit_phase_get_value +(struct sieve_instance *svinst, + const struct sieve_script_env *senv ATTR_UNUSED) +{ + switch ( svinst->delivery_phase ) { + case SIEVE_DELIVERY_PHASE_PRE: + return "pre"; + case SIEVE_DELIVERY_PHASE_DURING: + return "during"; + case SIEVE_DELIVERY_PHASE_POST: + return "post"; + default: + break; + } + return NULL; +} + const struct sieve_environment_item phase_env_item = { - "phase", - NULL, - NULL, + .name = "phase", + .get_value = envit_phase_get_value }; /* "name": * * The product name associated with the Sieve interpreter. */ + const struct sieve_environment_item name_env_item = { - "name", - PIGEONHOLE_NAME" Sieve", - NULL, + .name = "name", + .value = PIGEONHOLE_NAME" Sieve" }; /* "version": @@ -193,9 +229,8 @@ const struct sieve_environment_item name_env_item = { */ const struct sieve_environment_item version_env_item = { - "version", - PIGEONHOLE_VERSION, - NULL, + .name = "version", + .value = PIGEONHOLE_VERSION, }; diff --git a/src/lib-sieve/plugins/environment/ext-environment.c b/src/lib-sieve/plugins/environment/ext-environment.c index d90b597..402e0a5 100644 --- a/src/lib-sieve/plugins/environment/ext-environment.c +++ b/src/lib-sieve/plugins/environment/ext-environment.c @@ -32,13 +32,11 @@ static bool ext_environment_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def environment_extension = { - "environment", - ext_environment_init, - ext_environment_deinit, - ext_environment_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "environment", + .load = ext_environment_init, + .unload = ext_environment_deinit, + .validator_load = ext_environment_validator_load, + SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation) }; static bool ext_environment_validator_load diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-binary.c b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c index ee86035..f348675 100644 --- a/src/lib-sieve/plugins/ihave/ext-ihave-binary.c +++ b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c @@ -48,7 +48,7 @@ struct ext_ihave_binary_context { struct sieve_binary *binary; struct sieve_binary_block *block; - ARRAY_DEFINE(missing_extensions, const char *); + ARRAY(const char *) missing_extensions; }; static struct ext_ihave_binary_context *ext_ihave_binary_create_context diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-common.h b/src/lib-sieve/plugins/ihave/ext-ihave-common.h index 29f2246..a85d5f0 100644 --- a/src/lib-sieve/plugins/ihave/ext-ihave-common.h +++ b/src/lib-sieve/plugins/ihave/ext-ihave-common.h @@ -35,7 +35,7 @@ extern const struct sieve_operation_def error_operation; */ struct ext_ihave_ast_context { - ARRAY_DEFINE(missing_extensions, const char *); + ARRAY(const char *) missing_extensions; }; struct ext_ihave_ast_context *ext_ihave_get_ast_context diff --git a/src/lib-sieve/plugins/ihave/ext-ihave.c b/src/lib-sieve/plugins/ihave/ext-ihave.c index 8def774..125eb9f 100644 --- a/src/lib-sieve/plugins/ihave/ext-ihave.c +++ b/src/lib-sieve/plugins/ihave/ext-ihave.c @@ -34,15 +34,11 @@ static bool ext_ihave_generator_load const struct sieve_extension_def ihave_extension = { "ihave", - NULL, NULL, - ext_ihave_validator_load, - ext_ihave_generator_load, - NULL, - ext_ihave_binary_load, - ext_ihave_binary_dump, - NULL, - SIEVE_EXT_DEFINE_OPERATION(error_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .validator_load = ext_ihave_validator_load, + .generator_load = ext_ihave_generator_load, + .binary_load = ext_ihave_binary_load, + .binary_dump = ext_ihave_binary_dump, + SIEVE_EXT_DEFINE_OPERATION(error_operation) }; static bool ext_ihave_validator_load diff --git a/src/lib-sieve/plugins/ihave/tst-ihave.c b/src/lib-sieve/plugins/ihave/tst-ihave.c index 853474a..a73fdfb 100644 --- a/src/lib-sieve/plugins/ihave/tst-ihave.c +++ b/src/lib-sieve/plugins/ihave/tst-ihave.c @@ -51,7 +51,7 @@ static bool tst_ihave_validate struct sieve_ast_argument *stritem; enum sieve_compile_flags cpflags = sieve_validator_compile_flags(valdtr); bool no_global = ( (cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0 ); - ARRAY_DEFINE(capabilities, struct _capability); + ARRAY(struct _capability) capabilities; struct _capability capability; const struct _capability *caps; unsigned int i, count; diff --git a/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c b/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c index f5b3910..fb477ed 100644 --- a/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c +++ b/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c @@ -49,12 +49,9 @@ static bool ext_imap4flags_interpreter_load sieve_size_t *address); const struct sieve_extension_def imap4flags_extension = { - "imap4flags", - NULL, NULL, - ext_imap4flags_validator_load, - NULL, - ext_imap4flags_interpreter_load, - NULL, NULL, NULL, + .name = "imap4flags", + .validator_load = ext_imap4flags_validator_load, + .interpreter_load = ext_imap4flags_interpreter_load, SIEVE_EXT_DEFINE_OPERATIONS(imap4flags_operations), SIEVE_EXT_DEFINE_OPERAND(flags_side_effect_operand) }; diff --git a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c index 558987a..64a893d 100644 --- a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c +++ b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c @@ -77,15 +77,10 @@ static bool ext_imapflags_interpreter_load sieve_size_t *address); const struct sieve_extension_def imapflags_extension = { - "imapflags", - ext_imapflags_load, - NULL, - ext_imapflags_validator_load, - NULL, - ext_imapflags_interpreter_load, - NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "imapflags", + .load = ext_imapflags_load, + .validator_load = ext_imapflags_validator_load, + .interpreter_load = ext_imapflags_interpreter_load }; static bool ext_imapflags_load diff --git a/src/lib-sieve/plugins/imap4flags/tag-flags.c b/src/lib-sieve/plugins/imap4flags/tag-flags.c index 068fa2c..001f69c 100644 --- a/src/lib-sieve/plugins/imap4flags/tag-flags.c +++ b/src/lib-sieve/plugins/imap4flags/tag-flags.c @@ -187,7 +187,7 @@ static bool tag_flags_generate /* Context data */ struct seff_flags_context { - ARRAY_DEFINE(keywords, const char *); + ARRAY(const char *) keywords; enum mail_flags flags; }; diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c index 7264115..6247a67 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -23,7 +23,7 @@ * Include command * * Syntax: - * include [LOCATION] + * include [LOCATION] [":once"] [":optional"] * * [LOCATION]: * ":personal" / ":global" @@ -74,11 +74,11 @@ const struct sieve_operation_def include_operation = { struct cmd_include_context_data { enum ext_include_script_location location; - bool location_assigned; - - bool include_once; struct sieve_script *script; + enum ext_include_flags flags; + + unsigned int location_assigned:1; }; /* @@ -103,17 +103,25 @@ static const struct sieve_argument_def include_global_tag = { NULL, NULL, NULL }; -static bool cmd_include_validate_once_tag +static bool cmd_include_validate_boolean_tag (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd); static const struct sieve_argument_def include_once_tag = { "once", NULL, - cmd_include_validate_once_tag, + cmd_include_validate_boolean_tag, + NULL, NULL, NULL +}; + +static const struct sieve_argument_def include_optional_tag = { + "optional", + NULL, + cmd_include_validate_boolean_tag, NULL, NULL, NULL }; + /* * Tag validation */ @@ -147,14 +155,17 @@ static bool cmd_include_validate_location_tag return TRUE; } -static bool cmd_include_validate_once_tag +static bool cmd_include_validate_boolean_tag (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, struct sieve_command *cmd) { struct cmd_include_context_data *ctx_data = (struct cmd_include_context_data *) cmd->data; - ctx_data->include_once = TRUE; + if ( sieve_argument_is(*arg, include_once_tag) ) + ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE; + else + ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL; /* Delete this tag (for now) */ *arg = sieve_ast_arguments_detach(*arg, 1); @@ -173,6 +184,7 @@ static bool cmd_include_registered sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0); sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0); sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0); return TRUE; } @@ -204,7 +216,7 @@ static bool cmd_include_validate struct sieve_script *script; const char *script_location, *script_name; enum sieve_error error = SIEVE_ERROR_NONE; - bool include = TRUE; + int ret; /* Check argument */ if ( !sieve_validate_positional_argument @@ -251,33 +263,47 @@ static bool cmd_include_validate (this_ext->svinst, script_location, script_name, sieve_validator_error_handler(valdtr), &error); - if ( script == NULL ) { + ret = 0; + if ( script != NULL ) + ret = sieve_script_open(script, &error); + + if ( script == NULL || ret < 0 ) { if ( error != SIEVE_ERROR_NOT_FOUND ) { + if ( script != NULL ) + sieve_script_unref(&script); return FALSE; + + /* Not found */ } else { enum sieve_compile_flags cpflags = sieve_validator_compile_flags(valdtr); - if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) { + if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) { + /* :optional */ + + } else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) { + /* Script is being uploaded */ sieve_argument_validate_warning(valdtr, arg, "included %s script '%s' does not exist (ignored during upload)", ext_include_script_location_name(ctx_data->location), str_sanitize(script_name, 80)); - include = FALSE; + ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD; + } else { + /* Should have existed */ sieve_argument_validate_error(valdtr, arg, "included %s script '%s' does not exist", ext_include_script_location_name(ctx_data->location), str_sanitize(script_name, 80)); + if ( script != NULL ) + sieve_script_unref(&script); return FALSE; } } } - if ( include ) { - ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); - ctx_data->script = script; - } + ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); + ctx_data->script = script; (void)sieve_ast_arguments_detach(arg, 1); return TRUE; @@ -293,26 +319,20 @@ static bool cmd_include_generate struct cmd_include_context_data *ctx_data = (struct cmd_include_context_data *) cmd->data; const struct ext_include_script_info *included; - unsigned int flags = ctx_data->include_once; int ret; - /* Upon upload ctx_data->script may be NULL if the script was not found. We - * don't emit any code for this include command in that case. + /* Compile (if necessary) and include the script into the binary. + * This yields the id of the binary block containing the compiled byte code. */ - if ( ctx_data->script != NULL ) { - /* Compile (if necessary) and include the script into the binary. - * This yields the id of the binary block containing the compiled byte code. - */ - if ( (ret=ext_include_generate_include - (cgenv, cmd, ctx_data->location, ctx_data->script, &included, - ctx_data->include_once)) < 0 ) - return FALSE; - - if ( ret > 0 ) { - (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); - (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); - (void)sieve_binary_emit_byte(cgenv->sblock, flags); - } + if ( (ret=ext_include_generate_include + (cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script, + &included)) < 0 ) + return FALSE; + + if ( ret > 0 ) { + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); + (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); + (void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags); } return TRUE; @@ -371,7 +391,8 @@ static int opc_include_execute return SIEVE_EXEC_BIN_CORRUPT; } - return ext_include_execute_include(renv, include_id, flags & 0x01); + return ext_include_execute_include + (renv, include_id, (enum ext_include_flags)flags); } diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index a06d340..2483be7 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -53,8 +53,9 @@ struct ext_include_binary_context { struct sieve_binary *binary; struct sieve_binary_block *dependency_block; - struct hash_table *included_scripts; - ARRAY_DEFINE(include_index, struct ext_include_script_info *); + HASH_TABLE(struct sieve_script *, + struct ext_include_script_info *) included_scripts; + ARRAY(struct ext_include_script_info *) include_index; struct sieve_variable_scope_binary *global_vars; @@ -70,9 +71,8 @@ static struct ext_include_binary_context *ext_include_binary_create_context p_new(pool, struct ext_include_binary_context, 1); ctx->binary = sbin; - ctx->included_scripts = hash_table_create(default_pool, pool, 0, - (hash_callback_t *) sieve_script_hash, - (hash_cmp_callback_t *) sieve_script_cmp); + hash_table_create(&ctx->included_scripts, pool, 0, + sieve_script_hash, sieve_script_cmp); p_array_init(&ctx->include_index, pool, 128); sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx); @@ -122,23 +122,24 @@ struct ext_include_binary_context *ext_include_binary_init */ const struct ext_include_script_info *ext_include_binary_script_include -(struct ext_include_binary_context *binctx, struct sieve_script *script, - enum ext_include_script_location location, struct sieve_binary_block *inc_block) +(struct ext_include_binary_context *binctx, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, struct sieve_binary_block *inc_block) { pool_t pool = sieve_binary_pool(binctx->binary); struct ext_include_script_info *incscript; incscript = p_new(pool, struct ext_include_script_info, 1); incscript->id = array_count(&binctx->include_index)+1; - incscript->script = script; incscript->location = location; + incscript->flags = flags; + incscript->script = script; incscript->block = inc_block; /* Unreferenced on binary_free */ sieve_script_ref(script); - hash_table_insert - (binctx->included_scripts, (void *) script, (void *) incscript); + hash_table_insert(binctx->included_scripts, script, incscript); array_append(&binctx->include_index, &incscript, 1); return incscript; @@ -148,7 +149,7 @@ bool ext_include_binary_script_is_included (struct ext_include_binary_context *binctx, struct sieve_script *script, const struct ext_include_script_info **script_info_r) { - struct ext_include_script_info *incscript = (struct ext_include_script_info *) + struct ext_include_script_info *incscript = hash_table_lookup(binctx->included_scripts, script); if ( incscript == NULL ) @@ -175,8 +176,7 @@ const struct ext_include_script_info *ext_include_binary_script_get_included const struct ext_include_script_info *ext_include_binary_script_get (struct ext_include_binary_context *binctx, struct sieve_script *script) { - return (struct ext_include_script_info *) - hash_table_lookup(binctx->included_scripts, script); + return hash_table_lookup(binctx->included_scripts, script); } unsigned int ext_include_binary_script_get_count @@ -222,9 +222,15 @@ static bool ext_include_binary_save for ( i = 0; i < script_count; i++ ) { struct ext_include_script_info *incscript = scripts[i]; - sieve_binary_emit_unsigned(sblock, sieve_binary_block_get_id(incscript->block)); + if ( incscript->block != NULL ) { + sieve_binary_emit_unsigned + (sblock, sieve_binary_block_get_id(incscript->block)); + } else { + sieve_binary_emit_unsigned(sblock, 0); + } sieve_binary_emit_byte(sblock, incscript->location); sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script)); + sieve_binary_emit_byte(sblock, incscript->flags); sieve_script_binary_write_metadata(incscript->script, sblock); } @@ -269,17 +275,19 @@ static bool ext_include_binary_open /* Read dependencies */ for ( i = 0; i < depcount; i++ ) { unsigned int inc_block_id; - struct sieve_binary_block *inc_block; - unsigned int location; + struct sieve_binary_block *inc_block = NULL; + unsigned int location, flags; string_t *script_name; const char *script_location; struct sieve_script *script; + enum sieve_error error; int ret; if ( !sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) || !sieve_binary_read_byte(sblock, &offset, &location) || - !sieve_binary_read_string(sblock, &offset, &script_name) ) { + !sieve_binary_read_string(sblock, &offset, &script_name) || + !sieve_binary_read_byte(sblock, &offset, &flags) ) { /* Binary is corrupt, recompile */ sieve_sys_error(svinst, "include: failed to read included script " @@ -288,7 +296,8 @@ static bool ext_include_binary_open return FALSE; } - if ( (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) { + if ( inc_block_id != 0 && + (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) { sieve_sys_error(svinst, "include: failed to find block %d for included script " "from dependency block %d of binary %s", inc_block_id, block_id, @@ -305,18 +314,55 @@ static bool ext_include_binary_open return FALSE; } - /* Can we find/open the script dependency ? */ + /* Can we find the script dependency ? */ script_location = ext_include_get_script_location (ext, location, str_c(script_name)); - if ( script_location == NULL || (script=sieve_script_create - (ext->svinst, script_location, str_c(script_name), NULL, NULL)) == NULL ) - { + if ( script_location == NULL ) { /* No, recompile */ return FALSE; } - if ( (ret=sieve_script_binary_read_metadata(script, sblock, &offset)) - < 0 ) { + /* Can we open the script dependency ? */ + script = sieve_script_create + (ext->svinst, script_location, str_c(script_name), NULL, &error); + if ( script == NULL ) { + /* No, recompile */ + return FALSE; + } + if ( sieve_script_open(script, &error) < 0 ) { + if ( error != SIEVE_ERROR_NOT_FOUND ) { + /* No, recompile */ + sieve_script_unref(&script); + return FALSE; + } + + if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) { + /* Not supposed to be missing, recompile */ + if ( svinst->debug ) { + sieve_sys_debug(svinst, + "include: script '%s' included in binary %s is missing, " + "so recompile", str_c(script_name), sieve_binary_path(sbin)); + } + sieve_script_unref(&script); + return FALSE; + } + + } else if (inc_block == NULL) { + /* Script exists, but it is missing from the binary, recompile no matter + * what. + */ + if ( svinst->debug ) { + sieve_sys_debug(svinst, + "include: script '%s' is missing in binary %s, but is now available, " + "so recompile", str_c(script_name), sieve_binary_path(sbin)); + } + sieve_script_unref(&script); + return FALSE; + } + + /* Can we read script metadata ? */ + 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 " @@ -330,7 +376,7 @@ static bool ext_include_binary_open binctx->outdated = TRUE; (void)ext_include_binary_script_include - (binctx, script, location, inc_block); + (binctx, location, flags, script, inc_block); sieve_script_unref(&script); } @@ -360,16 +406,14 @@ static void ext_include_binary_free struct ext_include_binary_context *binctx = (struct ext_include_binary_context *) context; struct hash_iterate_context *hctx; - void *key, *value; + struct sieve_script *script; + struct ext_include_script_info *incscript; /* Release references to all included script objects */ 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; - + while ( hash_table_iterate + (hctx, binctx->included_scripts, &script, &incscript) ) sieve_script_unref(&incscript->script); - } hash_table_iterate_deinit(&hctx); hash_table_destroy(&binctx->included_scripts); @@ -389,31 +433,38 @@ bool ext_include_binary_dump struct ext_include_binary_context *binctx = ext_include_binary_get_context(ext, sbin); struct hash_iterate_context *hctx; - void *key, *value; + struct sieve_script *script; + struct ext_include_script_info *incscript; if ( !ext_include_variables_dump(denv, binctx->global_vars) ) return FALSE; 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; - unsigned int block_id = sieve_binary_block_get_id(incscript->block); + while ( hash_table_iterate + (hctx, binctx->included_scripts, &script, &incscript) ) { - sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)", - ext_include_script_location_name(incscript->location), - sieve_script_name(incscript->script), block_id); + if ( incscript->block == NULL ) { + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script)); - denv->sblock = incscript->block; - denv->cdumper = sieve_code_dumper_create(denv); + } else { + unsigned int block_id = sieve_binary_block_get_id(incscript->block); - if ( denv->cdumper == NULL ) - return FALSE; + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script), block_id); - sieve_code_dumper_run(denv->cdumper); - sieve_code_dumper_free(&(denv->cdumper)); - } + denv->sblock = incscript->block; + denv->cdumper = sieve_code_dumper_create(denv); + + if ( denv->cdumper == NULL ) + return FALSE; + sieve_code_dumper_run(denv->cdumper); + sieve_code_dumper_free(&(denv->cdumper)); + } + } hash_table_iterate_deinit(&hctx); return TRUE; diff --git a/src/lib-sieve/plugins/include/ext-include-binary.h b/src/lib-sieve/plugins/include/ext-include-binary.h index 8cd304d..0910f92 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.h +++ b/src/lib-sieve/plugins/include/ext-include-binary.h @@ -30,18 +30,19 @@ struct sieve_variable_scope_binary *ext_include_binary_get_global_scope */ struct ext_include_script_info { - unsigned int id; + unsigned int id; - struct sieve_script *script; - enum ext_include_script_location location; + struct sieve_script *script; + enum ext_include_flags flags; + enum ext_include_script_location location; - struct sieve_binary_block *block; + struct sieve_binary_block *block; }; const struct ext_include_script_info *ext_include_binary_script_include - (struct ext_include_binary_context *binctx, struct sieve_script *script, - enum ext_include_script_location location, - struct sieve_binary_block *block); + (struct ext_include_binary_context *binctx, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, struct sieve_binary_block *inc_block); bool ext_include_binary_script_is_included (struct ext_include_binary_context *binctx, struct sieve_script *script, const struct ext_include_script_info **script_info_r); diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index 0324f49..833c355 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -43,7 +43,7 @@ static inline struct ext_include_generator_context * /* Interpreter context */ struct ext_include_interpreter_global { - ARRAY_DEFINE(included_scripts, struct sieve_script *); + ARRAY(struct sieve_script *) included_scripts; struct sieve_variable_scope_binary *var_scope; struct sieve_variable_storage *var_storage; @@ -454,8 +454,9 @@ struct sieve_variable_storage *ext_include_interpreter_get_global_variables int ext_include_generate_include (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, - enum ext_include_script_location location, struct sieve_script *script, - const struct ext_include_script_info **included_r, bool once) + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, + const struct ext_include_script_info **included_r) { const struct sieve_extension *this_ext = cmd->ext; struct ext_include_context *ext_ctx = @@ -489,7 +490,7 @@ int ext_include_generate_include } /* Check for circular include */ - if ( !once ) { + if ( (flags & EXT_INCLUDE_FLAG_ONCE) == 0 ) { pctx = ctx; while ( pctx != NULL ) { if ( sieve_script_equals(pctx->script, script) ) { @@ -515,12 +516,19 @@ int ext_include_generate_include binctx = ext_include_binary_init(this_ext, sbin, cgenv->ast); /* Is the script already compiled into the current binary? */ - if ( !ext_include_binary_script_is_included(binctx, script, &included) ) - { - struct sieve_binary_block *inc_block; + if ( ext_include_binary_script_is_included(binctx, script, &included) ) { + /* Yes, only update flags */ + if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) + flags &= ~EXT_INCLUDE_FLAG_OPTIONAL; + if ( (flags & EXT_INCLUDE_FLAG_ONCE) == 0 ) // for consistency + flags &= ~EXT_INCLUDE_FLAG_ONCE; + + } else { const char *script_name = sieve_script_name(script); enum sieve_compile_flags cpflags = cgenv->flags; + /* No, include new script */ + /* Check whether include limit is exceeded */ if ( ext_include_binary_script_get_count(binctx) >= ext_ctx->max_includes ) { @@ -530,57 +538,69 @@ int ext_include_generate_include return -1; } - /* No, allocate a new block in the binary and mark the script as included. + /* Allocate a new block in the binary and mark the script as included. */ - inc_block = sieve_binary_block_create(sbin); - included = ext_include_binary_script_include - (binctx, script, location, inc_block); - - /* Parse */ - if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) { - sieve_command_generate_error(gentr, cmd, - "failed to parse included script '%s'", str_sanitize(script_name, 80)); - return -1; - } + if ( !sieve_script_is_open(script) ) { + /* Just making an empty entry to mark a missing script */ + i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 || + (flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0); + included = ext_include_binary_script_include + (binctx, location, flags, script, NULL); + result = 0; + + } else { + struct sieve_binary_block *inc_block = sieve_binary_block_create(sbin); + + /* Real include */ + included = ext_include_binary_script_include + (binctx, location, flags, script, inc_block); + + /* Parse */ + if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) { + sieve_command_generate_error(gentr, cmd, + "failed to parse included script '%s'", str_sanitize(script_name, 80)); + return -1; + } - /* Included scripts inherit global variable scope */ - (void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast); + /* Included scripts inherit global variable scope */ + (void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast); - if ( location == EXT_INCLUDE_LOCATION_GLOBAL ) + if ( location == EXT_INCLUDE_LOCATION_GLOBAL ) cpflags &= ~SIEVE_RUNTIME_FLAG_NOGLOBAL; - else + else cpflags |= SIEVE_RUNTIME_FLAG_NOGLOBAL; - /* Validate */ - if ( !sieve_validate(ast, ehandler, cpflags, NULL) ) { - sieve_command_generate_error(gentr, cmd, - "failed to validate included script '%s'", - str_sanitize(script_name, 80)); - sieve_ast_unref(&ast); - return -1; - } + /* Validate */ + if ( !sieve_validate(ast, ehandler, cpflags, NULL) ) { + sieve_command_generate_error(gentr, cmd, + "failed to validate included script '%s'", + str_sanitize(script_name, 80)); + sieve_ast_unref(&ast); + return -1; + } + + /* Generate + * + * FIXME: It might not be a good idea to recurse code generation for + * included scripts. + */ + subgentr = sieve_generator_create(ast, ehandler, cpflags); + ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script); + + if ( sieve_generator_run(subgentr, &inc_block) == NULL ) { + sieve_command_generate_error(gentr, cmd, + "failed to generate code for included script '%s'", + str_sanitize(script_name, 80)); + result = -1; + } - /* Generate - * - * FIXME: It might not be a good idea to recurse code generation for - * included scripts. - */ - subgentr = sieve_generator_create(ast, ehandler, cpflags); - ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script); + sieve_generator_free(&subgentr); - if ( sieve_generator_run(subgentr, &inc_block) == NULL ) { - sieve_command_generate_error(gentr, cmd, - "failed to generate code for included script '%s'", - str_sanitize(script_name, 80)); - result = -1; + /* Cleanup */ + sieve_ast_unref(&ast); } - - sieve_generator_free(&subgentr); - - /* Cleanup */ - sieve_ast_unref(&ast); } - + if ( result > 0 ) *included_r = included; return result; diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index 217ef27..f2857da 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -21,6 +21,12 @@ struct ext_include_binary_context; * Types */ +enum ext_include_flags { // stored in one byte + EXT_INCLUDE_FLAG_ONCE = 0x01, + EXT_INCLUDE_FLAG_OPTIONAL = 0x02, + EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD = 0x04 +}; + enum ext_include_script_location { EXT_INCLUDE_LOCATION_PERSONAL, EXT_INCLUDE_LOCATION_GLOBAL, @@ -120,7 +126,7 @@ static inline struct ext_include_context *ext_include_get_context struct ext_include_ast_context { struct sieve_variable_scope *global_vars; - ARRAY_DEFINE(included_scripts, struct sieve_script *); + ARRAY(struct sieve_script *) included_scripts; }; struct ext_include_ast_context *ext_include_create_ast_context @@ -144,8 +150,9 @@ void ext_include_register_generator_context int ext_include_generate_include (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, - enum ext_include_script_location location, struct sieve_script *script, - const struct ext_include_script_info **included_r, bool once); + enum ext_include_script_location location, + enum ext_include_flags flags, struct sieve_script *script, + const struct ext_include_script_info **included_r); /* Interpreter context */ diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c index 41fb71e..f765df1 100644 --- a/src/lib-sieve/plugins/include/ext-include.c +++ b/src/lib-sieve/plugins/include/ext-include.c @@ -63,17 +63,19 @@ static bool ext_include_binary_load /* Extension objects */ const struct sieve_extension_def include_extension = { - "include", - ext_include_load, - ext_include_unload, - ext_include_validator_load, - ext_include_generator_load, - ext_include_interpreter_load, - ext_include_binary_load, - ext_include_binary_dump, - ext_include_code_dump, - SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "include", + .version = 1, + + .load = ext_include_load, + .unload = ext_include_unload, + .validator_load = ext_include_validator_load, + .generator_load = ext_include_generator_load, + .interpreter_load = ext_include_interpreter_load, + .binary_load = ext_include_binary_load, + .binary_dump = ext_include_binary_dump, + .code_dump = ext_include_code_dump, + + SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations) }; static bool ext_include_validator_load diff --git a/src/lib-sieve/plugins/mailbox/ext-mailbox.c b/src/lib-sieve/plugins/mailbox/ext-mailbox.c index c565627..9a8879e 100644 --- a/src/lib-sieve/plugins/mailbox/ext-mailbox.c +++ b/src/lib-sieve/plugins/mailbox/ext-mailbox.c @@ -32,10 +32,8 @@ static bool ext_mailbox_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def mailbox_extension = { - "mailbox", - NULL, NULL, - ext_mailbox_validator_load, - NULL, NULL, NULL, NULL, NULL, + .name = "mailbox", + .validator_load = ext_mailbox_validator_load, SIEVE_EXT_DEFINE_OPERATION(mailboxexists_operation), SIEVE_EXT_DEFINE_OPERAND(mailbox_create_operand) }; diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c index 84d8ea3..8496623 100644 --- a/src/lib-sieve/plugins/notify/cmd-notify.c +++ b/src/lib-sieve/plugins/notify/cmd-notify.c @@ -6,6 +6,7 @@ #include "str.h" #include "ioloop.h" #include "str-sanitize.h" +#include "ostream.h" #include "message-date.h" #include "mail-storage.h" @@ -690,8 +691,10 @@ static bool act_notify_send const struct ext_notify_recipient *recipients; void *smtp_handle; unsigned int count, i; - FILE *f; + struct ostream *output; + string_t *msg; const char *outmsgid; + size_t hdr_size; /* Get recipients */ recipients = array_get(&act->recipients, &count); @@ -708,59 +711,68 @@ static bool act_notify_send return TRUE; } - /* Send message to all recipients */ - for ( i = 0; i < count; i++ ) { - - if ( sieve_message_get_sender(aenv->msgctx) != NULL ) - smtp_handle = sieve_smtp_open - (senv, recipients[i].normalized, senv->postmaster_address, &f); - else - smtp_handle = sieve_smtp_open - (senv, recipients[i].normalized, NULL, &f); + /* Compose common headers */ + msg = t_str_new(512); + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + + /* Set importance */ + switch ( act->importance ) { + case 1: + rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); + rfc2822_header_write(msg, "Importance", "High"); + break; + case 3: + rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); + rfc2822_header_write(msg, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); + rfc2822_header_write(msg, "Importance", "Normal"); + break; + } - outmsgid = sieve_message_get_new_id(aenv->svinst); + rfc2822_header_printf(msg, "From", + "Postmaster <%s>", senv->postmaster_address); - rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_field_write(f, "Message-ID", outmsgid); - rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); + rfc2822_header_write(msg, "Subject", "[SIEVE] New mail notification"); - /* Set importance */ - switch ( act->importance ) { - case 1: - rfc2822_header_field_write(f, "X-Priority", "1 (Highest)"); - rfc2822_header_field_write(f, "Importance", "High"); - break; - case 3: - rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)"); - rfc2822_header_field_write(f, "Importance", "Low"); - break; - case 2: - default: - rfc2822_header_field_write(f, "X-Priority", "3 (Normal)"); - rfc2822_header_field_write(f, "Importance", "Normal"); - break; - } + rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (notify)"); + rfc2822_header_write(msg, "Precedence", "bulk"); - rfc2822_header_field_printf(f, "From", "%s", - t_strdup_printf("Postmaster <%s>", senv->postmaster_address)); + rfc2822_header_write(msg, "MIME-Version", "1.0"); + if (contains_8bit(act->message)) { + rfc2822_header_write(msg, + "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + } else { + rfc2822_header_write(msg, + "Content-Type", "text/plain; charset=us-ascii"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); + } - rfc2822_header_field_printf(f, "To", "%s", recipients[i].full); + hdr_size = str_len(msg); - rfc2822_header_field_write(f, "Subject", "[SIEVE] New mail notification"); + /* Send message to all recipients */ + for ( i = 0; i < count; i++ ) { + if ( sieve_message_get_sender(aenv->msgctx) != NULL ) + smtp_handle = sieve_smtp_open + (senv, recipients[i].normalized, senv->postmaster_address, &output); + else + smtp_handle = sieve_smtp_open + (senv, recipients[i].normalized, NULL, &output); - rfc2822_header_field_write(f, "Auto-Submitted", "auto-generated (notify)"); - rfc2822_header_field_write(f, "Precedence", "bulk"); + str_truncate(msg, hdr_size); - if (contains_8bit(act->message)) { - rfc2822_header_field_write(f, "MIME-Version", "1.0"); - rfc2822_header_field_write(f, - "Content-Type", "text/plain; charset=UTF-8"); - rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); - } + outmsgid = sieve_message_get_new_id(aenv->svinst); + rfc2822_header_write(msg, "Message-ID", outmsgid); + rfc2822_header_write(msg, "To", recipients[i].full); /* Generate message body */ - fprintf(f, "\r\n"); - fprintf(f, "%s\r\n", act->message); + str_printfa(msg, "\r\n%s\r\n", act->message); + + o_stream_send(output, str_data(msg), str_len(msg)); if ( sieve_smtp_close(senv, smtp_handle) ) { sieve_result_global_log(aenv, @@ -785,6 +797,7 @@ static bool act_notify_commit (const struct ext_notify_action *) action->context; const struct sieve_message_data *msgdata = aenv->msgdata; const char *const *headers; + bool result; /* Is the message an automatic reply ? */ if ( mail_get_headers @@ -803,7 +816,11 @@ static bool act_notify_commit } } - return act_notify_send(aenv, act); + T_BEGIN { + result = act_notify_send(aenv, act); + } T_END; + + return result; } diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c index bd06dd4..04ee634 100644 --- a/src/lib-sieve/plugins/notify/ext-notify-common.c +++ b/src/lib-sieve/plugins/notify/ext-notify-common.c @@ -175,7 +175,7 @@ static buffer_t *cmd_notify_extract_body_text return NULL; /* Initialize body decoder */ - decoder = message_decoder_init(FALSE); + decoder = message_decoder_init(NULL, 0); parser = message_parser_init(mctx->pool, input, 0, 0); is_text = TRUE; diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c index c4088cb..e119bc0 100644 --- a/src/lib-sieve/plugins/notify/ext-notify.c +++ b/src/lib-sieve/plugins/notify/ext-notify.c @@ -41,13 +41,9 @@ static bool ext_notify_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def notify_extension = { - "notify", - NULL, - NULL, - ext_notify_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations), - SIEVE_EXT_DEFINE_NO_OPERANDS, + .name = "notify", + .validator_load = ext_notify_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations) }; /* diff --git a/src/lib-sieve/plugins/regex/ext-regex.c b/src/lib-sieve/plugins/regex/ext-regex.c index c7a50f7..dfa14bc 100644 --- a/src/lib-sieve/plugins/regex/ext-regex.c +++ b/src/lib-sieve/plugins/regex/ext-regex.c @@ -49,11 +49,8 @@ static bool ext_regex_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def regex_extension = { - "regex", - NULL, NULL, - ext_regex_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, + .name = "regex", + .validator_load = ext_regex_validator_load, SIEVE_EXT_DEFINE_OPERAND(regex_match_type_operand) }; diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c index aee20f4..4710285 100644 --- a/src/lib-sieve/plugins/regex/mcht-regex.c +++ b/src/lib-sieve/plugins/regex/mcht-regex.c @@ -178,7 +178,7 @@ struct mcht_regex_key { }; struct mcht_regex_context { - ARRAY_DEFINE(reg_expressions, struct mcht_regex_key); + ARRAY(struct mcht_regex_key) reg_expressions; regmatch_t *pmatch; size_t nmatch; unsigned int all_compiled:1; diff --git a/src/lib-sieve/plugins/relational/ext-relational.c b/src/lib-sieve/plugins/relational/ext-relational.c index c7a433c..cd229dc 100644 --- a/src/lib-sieve/plugins/relational/ext-relational.c +++ b/src/lib-sieve/plugins/relational/ext-relational.c @@ -36,11 +36,8 @@ static bool ext_relational_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def relational_extension = { - "relational", - NULL, NULL, - ext_relational_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, + .name = "relational", + .validator_load = ext_relational_validator_load, SIEVE_EXT_DEFINE_OPERAND(rel_match_type_operand) }; diff --git a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c index 0ec424e..42dda49 100644 --- a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c +++ b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c @@ -68,33 +68,27 @@ static bool ext_spamvirustest_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def spamtest_extension = { - "spamtest", - ext_spamvirustest_load, - ext_spamvirustest_unload, - ext_spamvirustest_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(spamtest_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "spamtest", + .load = ext_spamvirustest_load, + .unload = ext_spamvirustest_unload, + .validator_load = ext_spamvirustest_validator_load, + SIEVE_EXT_DEFINE_OPERATION(spamtest_operation) }; const struct sieve_extension_def spamtestplus_extension = { - "spamtestplus", - ext_spamvirustest_load, - ext_spamvirustest_unload, - ext_spamvirustest_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(spamtest_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "spamtestplus", + .load = ext_spamvirustest_load, + .unload = ext_spamvirustest_unload, + .validator_load = ext_spamvirustest_validator_load, + SIEVE_EXT_DEFINE_OPERATION(spamtest_operation) }; const struct sieve_extension_def virustest_extension = { - "virustest", - ext_spamvirustest_load, - ext_spamvirustest_unload, - ext_spamvirustest_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(virustest_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "virustest", + .load = ext_spamvirustest_load, + .unload = ext_spamvirustest_unload, + .validator_load = ext_spamvirustest_validator_load, + SIEVE_EXT_DEFINE_OPERATION(virustest_operation) }; /* diff --git a/src/lib-sieve/plugins/subaddress/ext-subaddress.c b/src/lib-sieve/plugins/subaddress/ext-subaddress.c index 2a33494..248c499 100644 --- a/src/lib-sieve/plugins/subaddress/ext-subaddress.c +++ b/src/lib-sieve/plugins/subaddress/ext-subaddress.c @@ -57,12 +57,10 @@ static bool ext_subaddress_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def subaddress_extension = { - "subaddress", - ext_subaddress_load, - ext_subaddress_unload, - ext_subaddress_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, + .name = "subaddress", + .load = ext_subaddress_load, + .unload = ext_subaddress_unload, + .validator_load = ext_subaddress_validator_load, SIEVE_EXT_DEFINE_OPERAND(subaddress_operand) }; diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c index 348d8d9..e7196d1 100644 --- a/src/lib-sieve/plugins/vacation/cmd-vacation.c +++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c @@ -7,6 +7,7 @@ #include "md5.h" #include "hostpid.h" #include "str-sanitize.h" +#include "ostream.h" #include "message-address.h" #include "message-date.h" #include "ioloop.h" @@ -693,7 +694,7 @@ static int ext_vacation_operation_execute /* Normalize all addresses */ if ( addresses != NULL ) { - ARRAY_DEFINE(norm_addresses, const char *); + ARRAY(const char *) norm_addresses; string_t *raw_address; int ret; @@ -901,7 +902,8 @@ static bool act_vacation_send const struct sieve_message_data *msgdata = aenv->msgdata; const struct sieve_script_env *senv = aenv->scriptenv; void *smtp_handle; - FILE *f; + struct ostream *output; + string_t *msg; const char *outmsgid; const char *const *headers; const char *subject; @@ -932,31 +934,32 @@ static bool act_vacation_send /* Open smtp session */ - smtp_handle = sieve_smtp_open(senv, reply_to, NULL, &f); + smtp_handle = sieve_smtp_open(senv, reply_to, NULL, &output); outmsgid = sieve_message_get_new_id(aenv->svinst); /* Produce a proper reply */ - rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_field_write(f, "Message-ID", outmsgid); - rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); + msg = t_str_new(512); + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Message-ID", outmsgid); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); if ( ctx->from != NULL && *(ctx->from) != '\0' ) - rfc2822_header_field_utf8_printf(f, "From", "%s", ctx->from); + rfc2822_header_utf8_printf(msg, "From", "%s", ctx->from); else if ( reply_from != NULL ) - rfc2822_header_field_printf(f, "From", "<%s>", reply_from); + rfc2822_header_printf(msg, "From", "<%s>", reply_from); else - rfc2822_header_field_printf(f, "From", "Postmaster <%s>", senv->postmaster_address); + rfc2822_header_printf(msg, "From", "Postmaster <%s>", senv->postmaster_address); - /* FIXME: If From header of message has same address, we should use that in - * stead properly include the phrase part. + /* FIXME: If From header of message has same address, we should use that + * instead to properly include the phrase part. */ - rfc2822_header_field_printf(f, "To", "<%s>", reply_to); + rfc2822_header_printf(msg, "To", "<%s>", reply_to); if ( _contains_8bit(subject) ) - rfc2822_header_field_utf8_printf(f, "Subject", "%s", subject); + rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); else - rfc2822_header_field_printf(f, "Subject", "%s", subject); + rfc2822_header_printf(msg, "Subject", "%s", subject); /* Compose proper in-reply-to and references headers */ @@ -964,29 +967,30 @@ static bool act_vacation_send (aenv->msgdata->mail, "references", &headers); if ( msgdata->id != NULL ) { - rfc2822_header_field_write(f, "In-Reply-To", msgdata->id); + rfc2822_header_write(msg, "In-Reply-To", msgdata->id); if ( ret >= 0 && headers[0] != NULL ) - rfc2822_header_field_write - (f, "References", t_strconcat(headers[0], " ", msgdata->id, NULL)); + rfc2822_header_write + (msg, "References", t_strconcat(headers[0], " ", msgdata->id, NULL)); else - rfc2822_header_field_write(f, "References", msgdata->id); + rfc2822_header_write(msg, "References", msgdata->id); } else if ( ret >= 0 && headers[0] != NULL ) { - rfc2822_header_field_write(f, "References", headers[0]); + rfc2822_header_write(msg, "References", headers[0]); } - rfc2822_header_field_write(f, "Auto-Submitted", "auto-replied (vacation)"); - rfc2822_header_field_write(f, "Precedence", "bulk"); + rfc2822_header_write(msg, "Auto-Submitted", "auto-replied (vacation)"); + rfc2822_header_write(msg, "Precedence", "bulk"); - rfc2822_header_field_write(f, "MIME-Version", "1.0"); + rfc2822_header_write(msg, "MIME-Version", "1.0"); if ( !ctx->mime ) { - rfc2822_header_field_write(f, "Content-Type", "text/plain; charset=utf-8"); - rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); - fprintf(f, "\r\n"); + rfc2822_header_write(msg, "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + str_append(msg, "\r\n"); } - fprintf(f, "%s\r\n", ctx->reason); + str_printfa(msg, "%s\r\n", ctx->reason); + o_stream_send(output, str_data(msg), str_len(msg)); /* Close smtp session */ if ( !sieve_smtp_close(senv, smtp_handle) ) { @@ -1032,6 +1036,7 @@ static bool act_vacation_commit const char *const *hdsp; const char *const *headers; const char *reply_from = NULL, *orig_recipient = NULL; + bool result; /* Is the recipient unset? */ @@ -1214,7 +1219,11 @@ static bool act_vacation_commit /* Send the message */ - if ( act_vacation_send(aenv, ctx, sender, reply_from) ) { + T_BEGIN { + result = act_vacation_send(aenv, ctx, sender, reply_from); + } T_END; + + if ( result ) { sieve_number_t seconds; sieve_result_global_log(aenv, "sent vacation response to <%s>", @@ -1232,8 +1241,6 @@ static bool act_vacation_commit sieve_action_duplicate_mark (senv, dupl_hash, sizeof(dupl_hash), ioloop_time + seconds); } - - return TRUE; } return TRUE; diff --git a/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c b/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c index 1693fe0..0d99241 100644 --- a/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c +++ b/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c @@ -30,13 +30,9 @@ static bool ext_vacation_seconds_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def vacation_seconds_extension = { - "vacation-seconds", - ext_vacation_seconds_load, - NULL, - ext_vacation_seconds_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "vacation-seconds", + .load = ext_vacation_seconds_load, + .validator_load = ext_vacation_seconds_validator_load, }; bool ext_vacation_seconds_load diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c index 58f4a0c..9dc212d 100644 --- a/src/lib-sieve/plugins/vacation/ext-vacation.c +++ b/src/lib-sieve/plugins/vacation/ext-vacation.c @@ -32,13 +32,11 @@ static bool ext_vacation_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def vacation_extension = { - "vacation", - ext_vacation_load, - ext_vacation_unload, - ext_vacation_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(vacation_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "vacation", + .load = ext_vacation_load, + .unload = ext_vacation_unload, + .validator_load = ext_vacation_validator_load, + SIEVE_EXT_DEFINE_OPERATION(vacation_operation) }; static bool ext_vacation_validator_load diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c index 328e82f..a1d807c 100644 --- a/src/lib-sieve/plugins/variables/cmd-set.c +++ b/src/lib-sieve/plugins/variables/cmd-set.c @@ -73,7 +73,7 @@ const struct sieve_operation_def cmd_set_operation = { */ struct cmd_set_context { - ARRAY_DEFINE(modifiers, const struct sieve_variables_modifier *); + ARRAY(const struct sieve_variables_modifier *) modifiers; }; /* diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c index 4ba886f..f4bd3bd 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-common.c +++ b/src/lib-sieve/plugins/variables/ext-variables-common.c @@ -47,8 +47,8 @@ struct sieve_variable_scope { struct sieve_variable *error_var; - struct hash_table *variables; - ARRAY_DEFINE(variable_index, struct sieve_variable *); + HASH_TABLE(const char *, struct sieve_variable *) variables; + ARRAY(struct sieve_variable *) variable_index; }; struct sieve_variable_scope_binary { @@ -78,8 +78,7 @@ struct sieve_variable_scope *sieve_variable_scope_create scope->svinst = svinst; scope->ext = ext; - scope->variables = hash_table_create - (default_pool, pool, 0, strcase_hash, (hash_cmp_callback_t *)strcasecmp); + hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp); p_array_init(&scope->variable_index, pool, 128); return scope; @@ -133,8 +132,7 @@ struct sieve_variable *sieve_variable_scope_declare new_var->identifier = p_strdup(scope->pool, identifier); new_var->index = array_count(&scope->variable_index); - hash_table_insert - (scope->variables, (void *) new_var->identifier, (void *) new_var); + hash_table_insert(scope->variables, new_var->identifier, new_var); array_append(&scope->variable_index, &new_var, 1); return new_var; @@ -145,8 +143,7 @@ struct sieve_variable *sieve_variable_scope_get_variable { struct sieve_variable *var; - var = (struct sieve_variable *) - hash_table_lookup(scope->variables, identifier); + var = hash_table_lookup(scope->variables, identifier); if ( var == NULL && declare ) { var = sieve_variable_scope_declare(scope, identifier); @@ -163,8 +160,7 @@ struct sieve_variable *sieve_variable_scope_import new_var = p_new(scope->pool, struct sieve_variable, 1); memcpy(new_var, var, sizeof(struct sieve_variable)); - hash_table_insert - (scope->variables, (void *) new_var->identifier, (void *) new_var); + hash_table_insert(scope->variables, new_var->identifier, new_var); /* Not entered into the index because it is an external variable * (This can be done unlimited; only limited by the size of the external scope) @@ -188,13 +184,10 @@ struct sieve_variable_scope_iter *sieve_variable_scope_iterate_init bool sieve_variable_scope_iterate (struct sieve_variable_scope_iter *iter, struct sieve_variable **var_r) { - void *key, *value; + const char *key; - if ( !hash_table_iterate(iter->hctx, &key, &value) ) - return FALSE; - - *var_r = (struct sieve_variable *) value; - return TRUE; + return hash_table_iterate + (iter->hctx, iter->scope->variables, &key, var_r); } void sieve_variable_scope_iterate_deinit @@ -405,7 +398,7 @@ struct sieve_variable_storage { struct sieve_variable_scope *scope; struct sieve_variable_scope_binary *scope_bin; unsigned int max_size; - ARRAY_DEFINE(var_values, string_t *); + ARRAY(string_t *) var_values; }; struct sieve_variable_storage *sieve_variable_storage_create @@ -690,7 +683,7 @@ struct ext_variables_interpreter_context { struct sieve_variable_scope_binary *local_scope_bin; struct sieve_variable_storage *local_storage; - ARRAY_DEFINE(ext_storages, struct sieve_variable_storage *); + ARRAY(struct sieve_variable_storage *) ext_storages; }; static void ext_variables_interpreter_free diff --git a/src/lib-sieve/plugins/variables/ext-variables-dump.c b/src/lib-sieve/plugins/variables/ext-variables-dump.c index 05baaec..1228991 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-dump.c +++ b/src/lib-sieve/plugins/variables/ext-variables-dump.c @@ -30,7 +30,7 @@ const struct sieve_code_dumper_extension variables_dump_extension = { struct ext_variables_dump_context { struct sieve_variable_scope *local_scope; - ARRAY_DEFINE(ext_scopes, struct sieve_variable_scope *); + ARRAY(struct sieve_variable_scope *) ext_scopes; }; static void ext_variables_code_dumper_free diff --git a/src/lib-sieve/plugins/variables/ext-variables.c b/src/lib-sieve/plugins/variables/ext-variables.c index 61d12fe..56c2c57 100644 --- a/src/lib-sieve/plugins/variables/ext-variables.c +++ b/src/lib-sieve/plugins/variables/ext-variables.c @@ -57,13 +57,11 @@ static bool ext_variables_validator_load (const struct sieve_extension *ext, struct sieve_validator *validator); const struct sieve_extension_def variables_extension = { - "variables", - NULL, NULL, - ext_variables_validator_load, - ext_variables_generator_load, - ext_variables_interpreter_load, - NULL, NULL, - ext_variables_code_dump, + .name = "variables", + .validator_load = ext_variables_validator_load, + .generator_load = ext_variables_generator_load, + .interpreter_load = ext_variables_interpreter_load, + .code_dump = ext_variables_code_dump, SIEVE_EXT_DEFINE_OPERATIONS(ext_variables_operations), SIEVE_EXT_DEFINE_OPERANDS(ext_variables_operands) }; diff --git a/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c b/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c index 0be9f56..1e15b93 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c +++ b/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c @@ -40,14 +40,10 @@ static bool ext_debug_interpreter_load const struct sieve_extension_def debug_extension = { - "vnd.dovecot.debug", - NULL, NULL, - ext_debug_validator_load, - NULL, - ext_debug_interpreter_load, - NULL, NULL, NULL, + .name = "vnd.dovecot.debug", + .validator_load = ext_debug_validator_load, + .interpreter_load = ext_debug_interpreter_load, SIEVE_EXT_DEFINE_OPERATION(debug_log_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS }; static bool ext_debug_validator_load diff --git a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c index ccf947e..836baaf 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c +++ b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c @@ -135,7 +135,7 @@ struct ext_duplicate_handle { }; struct ext_duplicate_context { - ARRAY_DEFINE(handles, struct ext_duplicate_handle); + ARRAY(struct ext_duplicate_handle) handles; unsigned int nohandle_duplicate:1; unsigned int nohandle_checked:1; @@ -154,7 +154,7 @@ int ext_duplicate_check struct act_duplicate_mark_data *act; struct md5_context ctx; - if ( !sieve_action_duplicate_check_available(senv) || value == NULL ) + if ( !sieve_action_duplicate_check_available(senv) || value == NULL ) return 0; /* Get context; find out whether duplicate was checked earlier */ @@ -179,7 +179,7 @@ int ext_duplicate_check return ( record->duplicate ? 1 : 0 ); } } - } + } result_pool = sieve_result_pool(renv->result); act = p_new(result_pool, struct act_duplicate_mark_data, 1); @@ -216,7 +216,7 @@ int ext_duplicate_check rctx->nohandle_checked = TRUE; } else { struct ext_duplicate_handle *record; - + if ( msg_pool == NULL ) msg_pool = sieve_message_context_pool(renv->msgctx); if ( !array_is_created(&rctx->handles) ) diff --git a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c index b08a7e8..b91c78e 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c +++ b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate.c @@ -29,13 +29,11 @@ static bool ext_duplicate_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def duplicate_extension = { - "vnd.dovecot.duplicate", - ext_duplicate_load, - ext_duplicate_unload, - ext_duplicate_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation), - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "vnd.dovecot.duplicate", + .load = ext_duplicate_load, + .unload = ext_duplicate_unload, + .validator_load = ext_duplicate_validator_load, + SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation) }; /* diff --git a/src/lib-sieve/rfc2822.c b/src/lib-sieve/rfc2822.c index 8611416..27f94d4 100644 --- a/src/lib-sieve/rfc2822.c +++ b/src/lib-sieve/rfc2822.c @@ -116,11 +116,10 @@ const char *rfc2822_header_field_name_sanitize(const char *name) */ /* FIXME: This should be collected into a Dovecot API for composing internet - * mail messages. These functions now use FILE * output streams, but this should - * be changed to proper dovecot streams. + * mail messages. */ -unsigned int rfc2822_header_field_append +unsigned int rfc2822_header_append (string_t *header, const char *name, const char *body, bool crlf, uoff_t *body_offset_r) { @@ -144,7 +143,8 @@ unsigned int rfc2822_header_field_append /* Add field body; fold it if necessary and account for existing folding */ while ( *bp != '\0' ) { - while ( *bp != '\0' && nlp == NULL && (wp == NULL || line_len < max_line) ) { + while ( *bp != '\0' && nlp == NULL && + (wp == NULL || line_len < max_line) ) { if ( *bp == ' ' || *bp == '\t' ) { wp = bp; } else if ( *bp == '\r' || *bp == '\n' ) { @@ -208,70 +208,29 @@ unsigned int rfc2822_header_field_append return lines; } -static int rfc2822_header_field_write_real -(FILE *f, const char *name, const char *body, size_t size) +void rfc2822_header_printf +(string_t *header, const char *name, const char *fmt, ...) { - string_t *header = t_str_new(strlen(name) + size + 256); - - (void)rfc2822_header_field_append(header, name, body, TRUE, NULL); - - if ( !fwrite(str_data(header), str_len(header), 1, f) != 1 ) - return -1; - - return str_len(header); -} - -int rfc2822_header_field_write -(FILE *f, const char *name, const char *body) -{ - int ret; - - T_BEGIN { - ret = rfc2822_header_field_write_real(f, name, body, strlen(body)); - } T_END; - - return ret; -} - -int rfc2822_header_field_printf -(FILE *f, const char *name, const char *body_fmt, ...) -{ - string_t *body; + const char *body; va_list args; - int ret; - - T_BEGIN { - body = t_str_new(256); - va_start(args, body_fmt); - str_vprintfa(body, body_fmt, args); - va_end(args); + va_start(args, fmt); + body = t_strdup_vprintf(fmt, args); + va_end(args); - ret = rfc2822_header_field_write_real - (f, name, (const char *) str_data(body), str_len(body)); - } T_END; - - return ret; + rfc2822_header_write(header, name, body); } -int rfc2822_header_field_utf8_printf -(FILE *f, const char *name, const char *body_fmt, ...) +void rfc2822_header_utf8_printf +(string_t *header, const char *name, const char *fmt, ...) { - string_t *body; + string_t *body = t_str_new(256); va_list args; - int ret; - - T_BEGIN { - body = t_str_new(256); - - va_start(args, body_fmt); - message_header_encode(t_strdup_vprintf(body_fmt, args), body); - va_end(args); - ret = rfc2822_header_field_write_real - (f, name, (const char *) str_data(body), str_len(body)); - } T_END; + va_start(args, fmt); + message_header_encode(t_strdup_vprintf(fmt, args), body); + va_end(args); - return ret; + rfc2822_header_write(header, name, str_c(body)); } diff --git a/src/lib-sieve/rfc2822.h b/src/lib-sieve/rfc2822.h index 49b5c72..2a943cf 100644 --- a/src/lib-sieve/rfc2822.h +++ b/src/lib-sieve/rfc2822.h @@ -27,17 +27,19 @@ const char *rfc2822_header_field_name_sanitize(const char *name); * Message composition */ -unsigned int rfc2822_header_field_append +unsigned int rfc2822_header_append (string_t *header, const char *name, const char *body, bool crlf, uoff_t *body_offset_r); -int rfc2822_header_field_write - (FILE *f, const char *name, const char *body); +static inline void rfc2822_header_write +(string_t *header, const char *name, const char *body) +{ + (void)rfc2822_header_append(header, name, body, TRUE, NULL); +} -int rfc2822_header_field_printf - (FILE *f, const char *name, const char *body_fmt, ...) ATTR_FORMAT(3, 4); - -int rfc2822_header_field_utf8_printf - (FILE *f, const char *name, const char *body_fmt, ...) ATTR_FORMAT(3, 4); +void rfc2822_header_printf + (string_t *header, const char *name, const char *fmt, ...) ATTR_FORMAT(3, 4); +void rfc2822_header_utf8_printf + (string_t *header, const char *name, const char *fmt, ...) ATTR_FORMAT(3, 4); #endif /* __RFC2822_H */ diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index 1f6c091..e39db49 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -10,6 +10,7 @@ #include "unichar.h" #include "istream.h" #include "istream-header-filter.h" +#include "ostream.h" #include "mail-deliver.h" #include "mail-storage.h" #include "message-date.h" @@ -748,13 +749,11 @@ static bool sieve_action_do_reject_mail const struct sieve_script_env *senv = aenv->scriptenv; const struct sieve_message_data *msgdata = aenv->msgdata; struct istream *input; + struct ostream *output; void *smtp_handle; - struct message_size hdr_size; - FILE *f; const char *new_msgid, *boundary; - const unsigned char *data; const char *header; - size_t size; + string_t *hdr; int ret; /* Just to be sure */ @@ -764,81 +763,84 @@ static bool sieve_action_do_reject_mail return TRUE; } - smtp_handle = sieve_smtp_open(senv, sender, NULL, &f); + smtp_handle = sieve_smtp_open(senv, sender, NULL, &output); new_msgid = sieve_message_get_new_id(svinst); boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname); - rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_field_write(f, "Message-ID", new_msgid); - rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); - rfc2822_header_field_printf(f, "From", "Mail Delivery Subsystem <%s>", + hdr = t_str_new(512); + rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(hdr, "Message-ID", new_msgid); + rfc2822_header_write(hdr, "Date", message_date_create(ioloop_time)); + rfc2822_header_printf(hdr, "From", "Mail Delivery Subsystem <%s>", senv->postmaster_address); - rfc2822_header_field_printf(f, "To", "<%s>", sender); - rfc2822_header_field_write(f, "Subject", "Automatically rejected mail"); - rfc2822_header_field_write(f, "Auto-Submitted", "auto-replied (rejected)"); - rfc2822_header_field_write(f, "Precedence", "bulk"); - - rfc2822_header_field_write(f, "MIME-Version", "1.0"); - rfc2822_header_field_printf(f, "Content-Type", - "multipart/report; report-type=disposition-notification;\n" + rfc2822_header_printf(hdr, "To", "<%s>", sender); + rfc2822_header_write(hdr, "Subject", "Automatically rejected mail"); + rfc2822_header_write(hdr, "Auto-Submitted", "auto-replied (rejected)"); + rfc2822_header_write(hdr, "Precedence", "bulk"); + + rfc2822_header_write(hdr, "MIME-Version", "1.0"); + rfc2822_header_printf(hdr, "Content-Type", + "multipart/report; report-type=disposition-notification;\r\n" "boundary=\"%s\"", boundary); - fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + str_append(hdr, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); /* Human readable status report */ - fprintf(f, "--%s\r\n", boundary); - fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n"); - fprintf(f, "Content-Disposition: inline\r\n"); - fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n"); + str_printfa(hdr, "--%s\r\n", boundary); + rfc2822_header_write(hdr, "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_write(hdr, "Content-Disposition", "inline"); + rfc2822_header_write(hdr, "Content-Transfer-Encoding", "8bit"); - fprintf(f, "Your message to <%s> was automatically rejected:\r\n" + str_printfa(hdr, "\r\nYour message to <%s> was automatically rejected:\r\n" "%s\r\n", recipient, reason); /* MDN status report */ - fprintf(f, "--%s\r\n" - "Content-Type: message/disposition-notification\r\n\r\n", boundary); - fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", + str_printfa(hdr, "--%s\r\n", boundary); + 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", svinst->hostname); if (mail_get_first_header(msgdata->mail, "Original-Recipient", &header) > 0) - fprintf(f, "Original-Recipient: rfc822; %s\r\n", header); - fprintf(f, "Final-Recipient: rfc822; %s\r\n", recipient); + rfc2822_header_printf(hdr, "Original-Recipient", "rfc822; %s", header); + rfc2822_header_printf(hdr, "Final-Recipient", "rfc822; %s", recipient); if ( msgdata->id != NULL ) - fprintf(f, "Original-Message-ID: %s\r\n", msgdata->id); - fprintf(f, "Disposition: " - "automatic-action/MDN-sent-automatically; deleted\r\n"); - fprintf(f, "\r\n"); + rfc2822_header_write(hdr, "Original-Message-ID", msgdata->id); + rfc2822_header_write(hdr, "Disposition", + "automatic-action/MDN-sent-automatically; deleted"); + str_append(hdr, "\r\n"); /* original message's headers */ - fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); - - if (mail_get_stream(msgdata->mail, &hdr_size, NULL, &input) == 0) { - /* Note: If you add more headers, they need to be sorted. - * We'll drop Content-Type because we're not including the message - * body, and having a multipart Content-Type may confuse some - * MIME parsers when they don't see the message boundaries. - */ - static const char *const exclude_headers[] = { - "Content-Type" - }; - - input = i_stream_create_header_filter(input, - HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_HIDE_BODY, - exclude_headers, N_ELEMENTS(exclude_headers), - null_header_filter_callback, NULL); - - while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { - if (fwrite(data, size, 1, f) == 0) - break; - i_stream_skip(input, size); - } - i_stream_unref(&input); - - i_assert(ret != 0); - } - - fprintf(f, "\r\n\r\n--%s--\r\n", boundary); + str_printfa(hdr, "--%s\r\n", boundary); + rfc2822_header_write(hdr, "Content-Type", "message/rfc822"); + str_append(hdr, "\r\n"); + o_stream_send(output, str_data(hdr), str_len(hdr)); + + if (mail_get_hdr_stream(msgdata->mail, NULL, &input) == 0) { + /* Note: If you add more headers, they need to be sorted. + We'll drop Content-Type because we're not including the message + body, and having a multipart Content-Type may confuse some + MIME parsers when they don't see the message boundaries. */ + static const char *const exclude_headers[] = { + "Content-Type" + }; + + input = i_stream_create_header_filter(input, + HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | + HEADER_FILTER_HIDE_BODY, exclude_headers, + N_ELEMENTS(exclude_headers), + *null_header_filter_callback, (void *)NULL); + + ret = o_stream_send_istream(output, input); + i_stream_unref(&input); + + i_assert(ret != 0); + } + + str_truncate(hdr, 0); + str_printfa(hdr, "\r\n\r\n--%s--\r\n", boundary); + o_stream_send(output, str_data(hdr), str_len(hdr)); if ( !sieve_smtp_close(senv, smtp_handle) ) { sieve_result_global_error(aenv, @@ -856,12 +858,18 @@ bool sieve_action_reject_mail const char *sender, const char *recipient, const char *reason) { const struct sieve_script_env *senv = aenv->scriptenv; + bool result; - if ( senv->reject_mail != NULL ) { - return ( senv->reject_mail(senv, recipient, reason) >= 0 ); - } + T_BEGIN { + if ( senv->reject_mail != NULL ) { + result = + ( senv->reject_mail(senv, recipient, reason) >= 0 ); + } else { + result = sieve_action_do_reject_mail(aenv, sender, recipient, reason); + } + } T_END; - return sieve_action_do_reject_mail(aenv, sender, recipient, reason); + return result; } diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c index 2f154b9..c962395 100644 --- a/src/lib-sieve/sieve-address-parts.c +++ b/src/lib-sieve/sieve-address-parts.c @@ -44,12 +44,8 @@ static bool addrp_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def address_part_extension = { - "@address-parts", - NULL, NULL, - addrp_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS /* Defined as core operand */ + .name = "@address-parts", + .validator_load = addrp_validator_load }; /* diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index fcea092..c5fdd0e 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -48,8 +48,8 @@ struct sieve_ast { struct sieve_ast_node *root; - ARRAY_DEFINE(linked_extensions, const struct sieve_extension *); - ARRAY_DEFINE(extensions, struct sieve_ast_extension_reg); + ARRAY(const struct sieve_extension *) linked_extensions; + ARRAY(struct sieve_ast_extension_reg) extensions; }; struct sieve_ast *sieve_ast_create diff --git a/src/lib-sieve/sieve-binary-file.c b/src/lib-sieve/sieve-binary-file.c index cb611a7..0f68b37 100644 --- a/src/lib-sieve/sieve-binary-file.c +++ b/src/lib-sieve/sieve-binary-file.c @@ -233,11 +233,11 @@ static bool _sieve_binary_save sizeof(struct sieve_binary_block_index) * blk_count, &block_index) ) return FALSE; - /* Create block containing all used extensions - * FIXME: Per-extension this should also store binary version numbers. - */ + /* Create block containing all used extensions */ + ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); i_assert( ext_block != NULL ); + sieve_binary_block_clear(ext_block); ext_count = array_count(&sbin->linked_extensions); sieve_binary_emit_unsigned(ext_block, ext_count); @@ -248,6 +248,8 @@ static bool _sieve_binary_save sieve_binary_emit_cstring (ext_block, sieve_extension_name((*ext)->extension)); + sieve_binary_emit_unsigned + (ext_block, sieve_extension_version((*ext)->extension)); sieve_binary_emit_unsigned(ext_block, (*ext)->block_id); } @@ -719,38 +721,48 @@ static bool _read_block_index_record return TRUE; } -static bool _read_extensions(struct sieve_binary_block *sblock) +static int _read_extensions(struct sieve_binary_block *sblock) { struct sieve_binary *sbin = sblock->sbin; sieve_size_t offset = 0; unsigned int i, count; - bool result = TRUE; + bool result = 1; if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) - return FALSE; + return -1; - for ( i = 0; result && i < count; i++ ) { + for ( i = 0; result > 0 && i < count; i++ ) { T_BEGIN { string_t *extension; const struct sieve_extension *ext; + unsigned int version; if ( sieve_binary_read_string(sblock, &offset, &extension) ) { ext = sieve_extension_get_by_name(sbin->svinst, str_c(extension)); if ( ext == NULL ) { sieve_sys_error(sbin->svinst, - "binary open: binary %s requires unknown extension '%s'", + "binary open: binary %s requires unknown extension `%s'", sbin->path, str_sanitize(str_c(extension), 128)); - result = FALSE; + result = 0; } else { struct sieve_binary_extension_reg *ereg = NULL; (void) sieve_binary_extension_register(sbin, ext, &ereg); - if ( !sieve_binary_read_unsigned(sblock, &offset, &ereg->block_id) ) - result = FALSE; + if ( !sieve_binary_read_unsigned(sblock, &offset, &version) || + !sieve_binary_read_unsigned(sblock, &offset, &ereg->block_id) ) { + result = -1; + } else if ( !sieve_extension_version_is(ext, version) ) { + sieve_sys_debug(sbin->svinst, + "binary open: binary %s was compiled with different version " + "of the `%s' extension (compiled v%d, expected v%d;" + "automatically fixed when re-compiled)", sbin->path, + sieve_extension_name(ext), version, sieve_extension_version(ext)); + result = 0; + } } } else - result = FALSE; + result = -1; } T_END; } @@ -764,6 +776,7 @@ static bool _sieve_binary_open(struct sieve_binary *sbin) const struct sieve_binary_header *header; struct sieve_binary_block *ext_block; unsigned int i, blk_count; + int ret; /* Verify header */ @@ -838,10 +851,12 @@ 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); + } else if ( (ret=_read_extensions(ext_block)) <= 0 ) { + if ( ret < 0 ) { + 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-private.h b/src/lib-sieve/sieve-binary-private.h index 969bbe0..c87f541 100644 --- a/src/lib-sieve/sieve-binary-private.h +++ b/src/lib-sieve/sieve-binary-private.h @@ -99,15 +99,15 @@ struct sieve_binary { * context data to the binary object in memory. This is stored in these * registration objects as well. */ - ARRAY_DEFINE(extensions, struct sieve_binary_extension_reg *); - ARRAY_DEFINE(extension_index, struct sieve_binary_extension_reg *); - ARRAY_DEFINE(linked_extensions, struct sieve_binary_extension_reg *); + ARRAY(struct sieve_binary_extension_reg *) extensions; + ARRAY(struct sieve_binary_extension_reg *) extension_index; + ARRAY(struct sieve_binary_extension_reg *) linked_extensions; /* Attributes of a loaded binary */ const char *path; /* Blocks */ - ARRAY_DEFINE(blocks, struct sieve_binary_block *); + ARRAY(struct sieve_binary_block *) blocks; }; struct sieve_binary *sieve_binary_create diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h index 2b5a432..b1e9afc 100644 --- a/src/lib-sieve/sieve-binary.h +++ b/src/lib-sieve/sieve-binary.h @@ -12,8 +12,8 @@ * Config */ -#define SIEVE_BINARY_VERSION_MAJOR 0 -#define SIEVE_BINARY_VERSION_MINOR 4 +#define SIEVE_BINARY_VERSION_MAJOR 1 +#define SIEVE_BINARY_VERSION_MINOR 0 /* * Binary object diff --git a/src/lib-sieve/sieve-code-dumper.c b/src/lib-sieve/sieve-code-dumper.c index 7410c65..77e25b3 100644 --- a/src/lib-sieve/sieve-code-dumper.c +++ b/src/lib-sieve/sieve-code-dumper.c @@ -46,7 +46,7 @@ struct sieve_code_dumper { struct sieve_binary_debug_reader *dreader; - ARRAY_DEFINE(extensions, struct sieve_code_dumper_extension_reg); + ARRAY(struct sieve_code_dumper_extension_reg) extensions; }; struct sieve_code_dumper *sieve_code_dumper_create diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index b9671e8..ee0f785 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -157,6 +157,7 @@ struct sieve_instance { /* System environment */ const char *hostname; + const char *domainname; const char *base_dir; /* User environment */ @@ -181,6 +182,8 @@ struct sieve_instance { /* Plugin modules */ struct sieve_plugin *plugins; + enum sieve_env_location env_location; + enum sieve_delivery_phase delivery_phase; /* Limits */ size_t max_script_size; diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c index bd1b416..5f83631 100644 --- a/src/lib-sieve/sieve-comparators.c +++ b/src/lib-sieve/sieve-comparators.c @@ -39,12 +39,8 @@ static bool cmp_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def comparator_extension = { - "@comparators", - NULL, NULL, - cmp_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS /* Defined as core operand */ + .name = "@comparators", + .validator_load = cmp_validator_load }; /* diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c index ad11171..0a5e2ce 100644 --- a/src/lib-sieve/sieve-error.c +++ b/src/lib-sieve/sieve-error.c @@ -1247,7 +1247,7 @@ struct sieve_varexpand_ehandler { struct sieve_error_handler handler; const char *format; - ARRAY_DEFINE(table, struct var_expand_table); + ARRAY(struct var_expand_table) table; }; static const char *ATTR_FORMAT(3, 0) _expand_message diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index 0c1ae2d..bc3c4a2 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -31,9 +31,9 @@ static struct sieve_extension *_sieve_extension_register */ struct sieve_extension_registry { - ARRAY_DEFINE(extensions, struct sieve_extension *); - struct hash_table *extension_index; - struct hash_table *capabilities_index; + ARRAY(struct sieve_extension *) extensions; + HASH_TABLE(const char *, struct sieve_extension *) extension_index; + HASH_TABLE(const char *, struct sieve_capability_registration *) capabilities_index; /* Core language 'extensions' */ const struct sieve_extension *comparator_extension; @@ -41,7 +41,7 @@ struct sieve_extension_registry { const struct sieve_extension *address_part_extension; /* Preloaded extensions */ - ARRAY_DEFINE(preloaded_extensions, const struct sieve_extension *); + ARRAY(const struct sieve_extension *) preloaded_extensions; }; /* @@ -59,17 +59,11 @@ extern const struct sieve_extension_def address_part_extension; /* FIXME: This is stupid. Define a comparator-* extension and be done with it */ static const struct sieve_extension_def comparator_i_octet_extension = { - "comparator-i;octet", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "comparator-i;octet", }; static const struct sieve_extension_def comparator_i_ascii_casemap_extension = { - "comparator-i;ascii-casemap", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "comparator-i;ascii-casemap", }; /* @@ -327,8 +321,8 @@ static void sieve_extension_registry_init(struct sieve_instance *svinst) struct sieve_extension_registry *ext_reg = svinst->ext_reg; p_array_init(&ext_reg->extensions, svinst->pool, 50); - ext_reg->extension_index = hash_table_create - (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + hash_table_create + (&ext_reg->extension_index, default_pool, 0, str_hash, strcmp); } static void sieve_extension_registry_deinit(struct sieve_instance *svinst) @@ -337,7 +331,7 @@ static void sieve_extension_registry_deinit(struct sieve_instance *svinst) struct sieve_extension * const *exts; unsigned int i, ext_count; - if ( ext_reg->extension_index == NULL ) return; + if ( !hash_table_is_created(ext_reg->extension_index) ) return; exts = array_get_modifiable(&ext_reg->extensions, &ext_count); for ( i = 0; i < ext_count; i++ ) { @@ -368,7 +362,7 @@ static struct sieve_extension *_sieve_extension_register bool load, bool required) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - struct sieve_extension *ext = (struct sieve_extension *) + struct sieve_extension *ext = hash_table_lookup(ext_reg->extension_index, extdef->name); /* Register extension if it is not registered already */ @@ -385,8 +379,7 @@ static struct sieve_extension *_sieve_extension_register ext->def = extdef; ext->svinst = svinst; - hash_table_insert - (ext_reg->extension_index, (void *) extdef->name, (void *) ext); + hash_table_insert(ext_reg->extension_index, extdef->name, ext); /* Re-register it if it were previously unregistered * (not going to happen) @@ -479,8 +472,7 @@ const struct sieve_extension *sieve_extension_get_by_name if ( strlen(name) > 128 ) return NULL; - ext = (const struct sieve_extension *) - hash_table_lookup(ext_reg->extension_index, name); + ext = hash_table_lookup(ext_reg->extension_index, name); if ( ext == NULL || ext->def == NULL || (!ext->enabled && !ext->required)) return NULL; @@ -566,8 +558,8 @@ void sieve_extensions_set_string (struct sieve_instance *svinst, const char *ext_string, bool global) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - ARRAY_DEFINE(enabled_extensions, const struct sieve_extension *); - ARRAY_DEFINE(disabled_extensions, const struct sieve_extension *); + ARRAY(const struct sieve_extension *) enabled_extensions; + ARRAY(const struct sieve_extension *) disabled_extensions; const struct sieve_extension *const *ext_enabled; const struct sieve_extension *const *ext_disabled; struct sieve_extension **exts; @@ -611,8 +603,7 @@ void sieve_extensions_set_string if ( *name == '@' ) ext = NULL; else - ext = (const struct sieve_extension *) - hash_table_lookup(ext_reg->extension_index, name); + ext = hash_table_lookup(ext_reg->extension_index, name); if ( ext == NULL || ext->def == NULL ) { sieve_sys_warning(svinst, @@ -719,15 +710,15 @@ void sieve_capability_registry_init(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - ext_reg->capabilities_index = hash_table_create - (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + hash_table_create + (&ext_reg->capabilities_index, default_pool, 0, str_hash, strcmp); } void sieve_capability_registry_deinit(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; - if ( ext_reg->capabilities_index == NULL ) return; + if ( !hash_table_is_created(ext_reg->capabilities_index) ) return; hash_table_destroy(&svinst->ext_reg->capabilities_index); } @@ -744,8 +735,7 @@ void sieve_extension_capabilities_register reg->ext = ext; reg->capabilities = cap; - hash_table_insert - (ext_reg->capabilities_index, (void *) cap->name, (void *) reg); + hash_table_insert(ext_reg->capabilities_index, cap->name, reg); } void sieve_extension_capabilities_unregister @@ -753,15 +743,13 @@ void sieve_extension_capabilities_unregister { struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; struct hash_iterate_context *hictx; - void *key = NULL, *value = NULL; + const char *name; + struct sieve_capability_registration *reg; hictx = hash_table_iterate_init(ext_reg->capabilities_index); - while ( hash_table_iterate(hictx, &key, &value) ) { - struct sieve_capability_registration *reg = - (struct sieve_capability_registration *) value; - + while ( hash_table_iterate(hictx, ext_reg->capabilities_index, &name, ®) ) { if ( reg->ext == ext ) - hash_table_remove(ext_reg->capabilities_index, key); + hash_table_remove(ext_reg->capabilities_index, name); } hash_table_iterate_deinit(&hictx); } @@ -771,8 +759,7 @@ const char *sieve_extension_capabilities_get_string { struct sieve_extension_registry *ext_reg = svinst->ext_reg; const struct sieve_capability_registration *cap_reg = - (const struct sieve_capability_registration *) - hash_table_lookup(ext_reg->capabilities_index, cap_name); + hash_table_lookup(ext_reg->capabilities_index, cap_name); const struct sieve_extension_capabilities *cap; if ( cap_reg == NULL || cap_reg->capabilities == NULL ) diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h index e1607c8..d1bcf5a 100644 --- a/src/lib-sieve/sieve-extensions.h +++ b/src/lib-sieve/sieve-extensions.h @@ -23,6 +23,9 @@ struct sieve_extension_objects { struct sieve_extension_def { const char *name; + /* Version */ + unsigned int version; + /* Registration */ bool (*load)(const struct sieve_extension *ext, void **context); void (*unload)(const struct sieve_extension *ext); @@ -50,6 +53,8 @@ struct sieve_extension_def { struct sieve_extension_objects operands; }; +/* Defining opcodes and operands */ + #define SIEVE_EXT_DEFINE_NO_OBJECTS \ { NULL, 0 } #define SIEVE_EXT_DEFINE_OBJECT(OBJ) \ @@ -60,6 +65,20 @@ struct sieve_extension_def { #define SIEVE_EXT_GET_OBJECTS_COUNT(ext, field) \ ext->field->count; +#define SIEVE_EXT_DEFINE_NO_OPERATIONS \ + .operations = SIEVE_EXT_DEFINE_NO_OBJECTS +#define SIEVE_EXT_DEFINE_OPERATION(OP) \ + .operations = SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_OPERATIONS(OPS) \ + .operations = SIEVE_EXT_DEFINE_OBJECTS(OPS) + +#define SIEVE_EXT_DEFINE_NO_OPERANDS \ + .operands = SIEVE_EXT_DEFINE_NO_OBJECTS +#define SIEVE_EXT_DEFINE_OPERAND(OP) \ + .operands = SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_OPERANDS(OPS) \ + .operands = SIEVE_EXT_DEFINE_OBJECTS(OPS) + /* * Extension instance */ @@ -81,21 +100,13 @@ struct sieve_extension { #define sieve_extension_is(ext, definition) \ ( (ext)->def == &(definition) ) #define sieve_extension_name(ext) \ - (ext)->def->name + ((ext)->def->name) #define sieve_extension_name_is(ext, _name) \ ( strcmp((ext)->def->name, (_name)) == 0 ) - -/* - * Defining opcodes and operands - */ - -#define SIEVE_EXT_DEFINE_NO_OPERATIONS SIEVE_EXT_DEFINE_NO_OBJECTS -#define SIEVE_EXT_DEFINE_OPERATION(OP) SIEVE_EXT_DEFINE_OBJECT(OP) -#define SIEVE_EXT_DEFINE_OPERATIONS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) - -#define SIEVE_EXT_DEFINE_NO_OPERANDS SIEVE_EXT_DEFINE_NO_OBJECTS -#define SIEVE_EXT_DEFINE_OPERAND(OP) SIEVE_EXT_DEFINE_OBJECT(OP) -#define SIEVE_EXT_DEFINE_OPERANDS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) +#define sieve_extension_version(ext) \ + ((ext)->def->version) +#define sieve_extension_version_is(ext, _version) \ + ((ext)->def->version == (_version)) /* * Extensions init/deinit diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c index 5faa656..fdbf7e5 100644 --- a/src/lib-sieve/sieve-generator.c +++ b/src/lib-sieve/sieve-generator.c @@ -72,7 +72,7 @@ struct sieve_generator { struct sieve_codegen_env genenv; struct sieve_binary_debug_writer *dwriter; - ARRAY_DEFINE(ext_contexts, void *); + ARRAY(void *) ext_contexts; }; struct sieve_generator *sieve_generator_create diff --git a/src/lib-sieve/sieve-generator.h b/src/lib-sieve/sieve-generator.h index 86f4875..30a7426 100644 --- a/src/lib-sieve/sieve-generator.h +++ b/src/lib-sieve/sieve-generator.h @@ -75,7 +75,7 @@ const void *sieve_generator_extension_get_context struct sieve_jumplist { pool_t pool; struct sieve_binary_block *block; - ARRAY_DEFINE(jumps, sieve_size_t); + ARRAY(sieve_size_t) jumps; }; struct sieve_jumplist *sieve_jumplist_create diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c index 6bb1988..42409c1 100644 --- a/src/lib-sieve/sieve-interpreter.c +++ b/src/lib-sieve/sieve-interpreter.c @@ -45,7 +45,7 @@ struct sieve_interpreter { pool_t pool; /* Runtime data for extensions */ - ARRAY_DEFINE(extensions, struct sieve_interpreter_extension_reg); + ARRAY(struct sieve_interpreter_extension_reg) extensions; sieve_size_t reset_vector; diff --git a/src/lib-sieve/sieve-lexer.c b/src/lib-sieve/sieve-lexer.c index 51db5b6..7218b64 100644 --- a/src/lib-sieve/sieve-lexer.c +++ b/src/lib-sieve/sieve-lexer.c @@ -71,13 +71,12 @@ const struct sieve_lexer *sieve_lexer_create const struct stat *st; /* Open script as stream */ - stream = sieve_script_open(script, error_r); - if ( stream == NULL ) + if ( sieve_script_get_stream(script, &stream, error_r) < 0 ) return NULL; /* Check script size */ - st = i_stream_stat(stream, TRUE); - if ( st != NULL && st->st_size > 0 && svinst->max_script_size > 0 && + if ( i_stream_stat(stream, TRUE, &st) >= 0 && st->st_size > 0 && + svinst->max_script_size > 0 && (uoff_t)st->st_size > svinst->max_script_size ) { sieve_error(ehandler, sieve_script_name(script), "sieve script is too large (max %"PRIuSIZE_T" bytes)", @@ -121,7 +120,6 @@ void sieve_lexer_free(const struct sieve_lexer **lexer) i_stream_unref(&scanner->input); - sieve_script_close(scanner->script); sieve_script_unref(&scanner->script); sieve_error_handler_unref(&scanner->ehandler); diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c index 0a7ccba..150a82f 100644 --- a/src/lib-sieve/sieve-match-types.c +++ b/src/lib-sieve/sieve-match-types.c @@ -29,7 +29,7 @@ struct sieve_match_values { pool_t pool; - ARRAY_DEFINE(values, string_t *); + ARRAY(string_t *) values; unsigned count; }; @@ -52,12 +52,8 @@ static bool mtch_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def match_type_extension = { - "@match-types", - NULL, NULL, - mtch_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS + .name = "@match-types", + .validator_load = mtch_validator_load }; /* diff --git a/src/lib-sieve/sieve-message.c b/src/lib-sieve/sieve-message.c index 53c1eba..df90d6e 100644 --- a/src/lib-sieve/sieve-message.c +++ b/src/lib-sieve/sieve-message.c @@ -71,10 +71,10 @@ struct sieve_message_context { /* Message versioning */ struct mail_user *raw_mail_user; - ARRAY_DEFINE(versions, struct sieve_message_version); + ARRAY(struct sieve_message_version) versions; /* Context data for extensions */ - ARRAY_DEFINE(ext_contexts, void *); + ARRAY(void *) ext_contexts; unsigned int edit_snapshot:1; unsigned int substitute_snapshot:1; diff --git a/src/lib-sieve/sieve-plugins.c b/src/lib-sieve/sieve-plugins.c index 520f12d..2fdcf42 100644 --- a/src/lib-sieve/sieve-plugins.c +++ b/src/lib-sieve/sieve-plugins.c @@ -77,7 +77,7 @@ void sieve_plugins_load path = MODULEDIR"/sieve"; memset(&mod_set, 0, sizeof(mod_set)); - mod_set.version = PIGEONHOLE_VERSION; + mod_set.abi_version = PIGEONHOLE_VERSION; mod_set.require_init_funcs = TRUE; mod_set.debug = FALSE; diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c index 90d5cde..a67dc4e 100644 --- a/src/lib-sieve/sieve-result.c +++ b/src/lib-sieve/sieve-result.c @@ -76,7 +76,7 @@ struct sieve_result { struct sieve_instance *svinst; /* Context data for extensions */ - ARRAY_DEFINE(ext_contexts, void *); + ARRAY(void *) ext_contexts; struct sieve_error_handler *ehandler; @@ -91,7 +91,8 @@ struct sieve_result { struct sieve_result_action *last_attempted_action; - struct hash_table *action_contexts; + HASH_TABLE(const struct sieve_action_def *, + struct sieve_result_action_context *) action_contexts; }; struct sieve_result *sieve_result_create @@ -129,7 +130,6 @@ struct sieve_result *sieve_result_create result->first_action = NULL; result->last_action = NULL; - result->action_contexts = NULL; return result; } @@ -147,7 +147,7 @@ void sieve_result_unref(struct sieve_result **result) sieve_message_context_unref(&(*result)->action_env.msgctx); - if ( (*result)->action_contexts != NULL ) + if ( hash_table_is_created((*result)->action_contexts) ) hash_table_destroy(&(*result)->action_contexts); if ( (*result)->ehandler != NULL ) @@ -326,12 +326,10 @@ void sieve_result_add_implicit_side_effect to_action = to_keep ? &act_store : to_action; - if ( result->action_contexts == NULL ) { - result->action_contexts = hash_table_create - (default_pool, result->pool, 0, NULL, NULL); + if ( !hash_table_is_created(result->action_contexts) ) { + hash_table_create_direct(&result->action_contexts, result->pool, 0); } else { - actctx = (struct sieve_result_action_context *) - hash_table_lookup(result->action_contexts, to_action); + actctx = hash_table_lookup(result->action_contexts, to_action); } if ( actctx == NULL ) { @@ -340,8 +338,7 @@ void sieve_result_add_implicit_side_effect actctx->action = to_action; actctx->seffects = sieve_side_effects_list_create(result); - hash_table_insert(result->action_contexts, (void *) to_action, - (void *) actctx); + hash_table_insert(result->action_contexts, to_action, actctx); } seffect.object.def = &seff_def->obj_def; @@ -623,12 +620,11 @@ static int _sieve_result_add_action result->action_count++; /* Apply any implicit side effects */ - if ( result->action_contexts != NULL ) { + if ( hash_table_is_created(result->action_contexts) ) { struct sieve_result_action_context *actctx; /* Check for implicit side effects to this particular action */ - actctx = (struct sieve_result_action_context *) - hash_table_lookup(result->action_contexts, + actctx = hash_table_lookup(result->action_contexts, ( keep ? &act_store : act_def )); if ( actctx != NULL ) { @@ -793,12 +789,11 @@ static void sieve_result_print_implicit_side_effects bool dummy = TRUE; /* Print any implicit side effects if applicable */ - if ( result->action_contexts != NULL ) { + if ( hash_table_is_created(result->action_contexts) ) { struct sieve_result_action_context *actctx; /* Check for implicit side effects to keep action */ - actctx = (struct sieve_result_action_context *) - hash_table_lookup(rpenv->result->action_contexts, &act_store); + actctx = hash_table_lookup(rpenv->result->action_contexts, &act_store); if ( actctx != NULL && actctx->seffects != NULL ) sieve_result_print_side_effects @@ -963,12 +958,11 @@ static bool _sieve_result_implicit_keep } /* Apply any implicit side effects if applicable */ - if ( !rollback && result->action_contexts != NULL ) { + if ( !rollback && hash_table_is_created(result->action_contexts) ) { struct sieve_result_action_context *actctx; /* Check for implicit side effects to keep action */ - actctx = (struct sieve_result_action_context *) - hash_table_lookup(result->action_contexts, act_keep.def); + actctx = hash_table_lookup(result->action_contexts, act_keep.def); if ( actctx != NULL && actctx->seffects != NULL ) rsef_first = actctx->seffects->first_effect; diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c index 7975105..6260528 100644 --- a/src/lib-sieve/sieve-script-dict.c +++ b/src/lib-sieve/sieve-script-dict.c @@ -49,7 +49,18 @@ static struct sieve_script *sieve_dict_script_alloc(void) return &script->script; } -static int sieve_dict_script_create +static void sieve_dict_script_free(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 int sieve_dict_script_open (struct sieve_script *_script, const char *data, const char *const *options, enum sieve_error *error_r) { @@ -57,7 +68,7 @@ static int sieve_dict_script_create struct sieve_instance *svinst = _script->svinst; struct sieve_error_handler *ehandler = _script->ehandler; const char *username = NULL, *name = _script->name; - const char *path; + const char *path, *error; int ret; if ( options != NULL ) { @@ -105,12 +116,12 @@ static int sieve_dict_script_create } script->dict_uri = p_strdup(_script->pool, data); - script->dict = dict_init - (script->dict_uri, DICT_DATA_TYPE_STRING, username, svinst->base_dir); - if ( script->dict == NULL ) { + ret = dict_init(script->dict_uri, DICT_DATA_TYPE_STRING, username, + svinst->base_dir, &script->dict, &error); + if ( ret < 0 ) { 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); + "for user `%s': %s", data, username, error); *error_r = SIEVE_ERROR_TEMP_FAIL; return -1; } @@ -153,19 +164,9 @@ static int sieve_dict_script_create 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) +static int sieve_dict_script_get_stream +(struct sieve_script *_script, struct istream **stream_r, + enum sieve_error *error_r) { struct sieve_dict_script *script = (struct sieve_dict_script *)_script; struct sieve_instance *svinst = _script->svinst; @@ -193,21 +194,11 @@ static struct istream *sieve_dict_script_open "not found at path %s", script->data_id, name, path); } *error_r = SIEVE_ERROR_TEMP_FAIL; - return NULL; + return -1; } - 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; + *stream_r = i_stream_create_from_data(script->data, strlen(script->data)); + return 0; } static int sieve_dict_script_binary_read_metadata @@ -269,18 +260,18 @@ static int sieve_dict_script_binary_save static bool sieve_dict_script_equals (const struct sieve_script *_script, const struct sieve_script *_other) { - struct sieve_dict_script *script = (struct sieve_dict_script *)_script; - struct sieve_dict_script *other = (struct sieve_dict_script *)_other; + struct sieve_dict_script *script = (struct sieve_dict_script *)_script; + struct sieve_dict_script *other = (struct sieve_dict_script *)_other; - if ( script == NULL || other == NULL ) - return FALSE; + if ( script == NULL || other == NULL ) + return FALSE; - if ( strcmp(script->dict_uri, other->dict_uri) != 0 ) + if ( strcmp(script->dict_uri, other->dict_uri) != 0 ) return FALSE; i_assert( _script->name != NULL && _other->name != NULL ); - return ( strcmp(_script->name, _other->name) == 0 ); + return ( strcmp(_script->name, _other->name) == 0 ); } @@ -288,11 +279,11 @@ 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_free, sieve_dict_script_open, - sieve_dict_script_close, + + sieve_dict_script_get_stream, sieve_dict_script_binary_read_metadata, sieve_dict_script_binary_write_metadata, diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c index cc9cf64..18df9fb 100644 --- a/src/lib-sieve/sieve-script-file.c +++ b/src/lib-sieve/sieve-script-file.c @@ -113,7 +113,7 @@ static struct sieve_script *sieve_file_script_alloc(void) return &script->script; } -static int sieve_file_script_create +static int sieve_file_script_open (struct sieve_script *_script, const char *path, const char *const *options, enum sieve_error *error_r) { @@ -248,8 +248,9 @@ static int sieve_file_script_create return ( success ? 0 : -1 ); } -static struct istream *sieve_file_script_open -(struct sieve_script *_script, enum sieve_error *error_r) +static int sieve_file_script_get_stream +(struct sieve_script *_script, struct istream **stream_r, + enum sieve_error *error_r) { struct sieve_file_script *script = (struct sieve_file_script *)_script; struct sieve_instance *svinst = _script->svinst; @@ -261,7 +262,7 @@ static struct istream *sieve_file_script_open if ( (fd=open(script->path, O_RDONLY)) < 0 ) { sieve_file_script_handle_error(_script, script->path, name, error_r); - return NULL; + return -1; } if ( fstat(fd, &st) != 0 ) { @@ -292,7 +293,8 @@ static struct istream *sieve_file_script_open } } - return result; + *stream_r = result; + return 0; } static int sieve_file_script_get_size @@ -356,11 +358,11 @@ 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_get_stream, sieve_file_script_binary_read_metadata, NULL, diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h index 9570b9c..753d64a 100644 --- a/src/lib-sieve/sieve-script-private.h +++ b/src/lib-sieve/sieve-script-private.h @@ -12,17 +12,16 @@ 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); + 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 (*open) + (struct sieve_script *script, const char *data, + const char *const *options, enum sieve_error *error_r); + int (*get_stream) + (struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r); + int (*binary_read_metadata) (struct sieve_script *_script, struct sieve_binary_block *sblock, sieve_size_t *offset); @@ -53,18 +52,20 @@ struct sieve_script { struct sieve_error_handler *ehandler; const char *name; + const char *data; const char *location; const char *bin_dir; /* Stream */ struct istream *stream; + + unsigned int open:1; }; -struct sieve_script *sieve_script_init +void sieve_script_init (struct sieve_script *script, struct sieve_instance *svinst, const struct sieve_script *script_class, const char *data, - const char *name, struct sieve_error_handler *ehandler, - enum sieve_error *error_r); + const char *name, struct sieve_error_handler *ehandler); int sieve_script_setup_bindir (struct sieve_script *script, mode_t mode); diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index 50c35dd..ced71c9 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -180,51 +180,19 @@ static bool sieve_script_location_parse return TRUE; } -struct sieve_script *sieve_script_init +void sieve_script_init (struct sieve_script *script, struct sieve_instance *svinst, - const struct sieve_script *script_class, const char *data, const char *name, - struct sieve_error_handler *ehandler, enum sieve_error *error_r) + const struct sieve_script *script_class, const char *data, + const char *name, struct sieve_error_handler *ehandler) { - enum sieve_error error; - const char *const *options = NULL; - const char *location = NULL, *parse_error = NULL; - - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NONE; - script->script_class = script_class; script->refcount = 1; script->svinst = svinst; - script->ehandler = ehandler; - + script->data = p_strdup_empty(script->pool, data); script->name = p_strdup_empty(script->pool, name); - 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); - if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; - return NULL; - } - - 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 { - *error_r = error; - } - return NULL; - } - - i_assert( script->location != NULL ); - sieve_error_handler_ref(ehandler); - return script; } struct sieve_script *sieve_script_create @@ -257,36 +225,111 @@ struct sieve_script *sieve_script_create else script_class = NULL; - if ( script_class == NULL ) - i_error("Unknown sieve script driver module: %s", driver); + if ( script_class == NULL ) { + sieve_sys_error(svinst, + "Unknown sieve script driver module: %s", driver); + } } T_END; } - if ( script_class == NULL ) + if ( script_class == NULL ) { + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_TEMP_FAIL; return NULL; + } 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; + sieve_script_init(script, svinst, script_class, data, name, ehandler); + script->location = p_strdup(script->pool, location); + return script; +} + +int sieve_script_open +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_instance *svinst = script->svinst; + struct sieve_error_handler *ehandler = script->ehandler; + enum sieve_error error; + const char *const *options = NULL; + const char *location = NULL, *parse_error = NULL; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + if ( script->open ) + return 0; + + if ( !sieve_script_location_parse + (script, script->data, &location, &options, &parse_error) ) { + sieve_critical(svinst, ehandler, NULL, + "failed to access sieve script", "failed to parse script location: %s", + parse_error); + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_TEMP_FAIL; + return -1; + } + + script->location = NULL; + if ( script->v.open(script, location, options, &error) < 0 ) { + if ( error_r == NULL ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) + sieve_error(ehandler, script->name, "sieve script does not exist"); + } else { + *error_r = error; + } + return -1; } + i_assert( script->location != NULL ); + i_assert( script->name != NULL ); + script->open = TRUE; + return 0; +} + +int sieve_script_open_as +(struct sieve_script *script, const char *name, enum sieve_error *error_r) +{ + if ( sieve_script_open(script, error_r) < 0 ) + return -1; + + /* override name */ + script->name = p_strdup(script->pool, name); + return 0; +} + +struct sieve_script *sieve_script_create_open +(struct sieve_instance *svinst, const char *location, const char *name, + struct sieve_error_handler *ehandler, enum sieve_error *error_r) +{ + struct sieve_script *script; + + script = sieve_script_create(svinst, location, name, ehandler, error_r); + if ( script == NULL ) + return NULL; + + if ( sieve_script_open(script, error_r) < 0 ) { + sieve_script_unref(&script); + return NULL; + } + return script; } -struct sieve_script *sieve_script_create_as +struct sieve_script *sieve_script_create_open_as (struct sieve_instance *svinst, const char *location, const char *name, struct sieve_error_handler *ehandler, enum sieve_error *error_r) { struct sieve_script *script; - if ( (script=sieve_script_create(svinst, location, NULL, ehandler, error_r)) - == NULL ) + script = sieve_script_create(svinst, location, name, ehandler, error_r); + if ( script == NULL ) return NULL; - /* override name */ - script->name = p_strdup(script->pool, name); + if ( sieve_script_open_as(script, name, error_r) < 0 ) { + sieve_script_unref(&script); + return NULL; + } + return script; } @@ -338,6 +381,7 @@ struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) { + struct istream *stream; int ret; if ( script->v.get_size != NULL ) { @@ -346,31 +390,42 @@ int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) } /* Try getting size from the stream */ - if ( script->stream == NULL && sieve_script_open(script, NULL) == NULL ) + if ( script->stream == NULL && + sieve_script_get_stream(script, &stream, NULL) < 0 ) return -1; return i_stream_get_size(script->stream, TRUE, size_r); } +bool sieve_script_is_open(const struct sieve_script *script) +{ + return script->open; +} + /* * Stream management */ -struct istream *sieve_script_open -(struct sieve_script *script, enum sieve_error *error_r) +int sieve_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) { enum sieve_error error; + int ret; if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; - if ( script->stream == NULL ) { - T_BEGIN { - script->stream = script->v.open(script, &error); - } T_END; + if ( script->stream != NULL ) { + *stream_r = script->stream; + return 0; } - if ( script->stream == NULL ) { + T_BEGIN { + ret = script->v.get_stream(script, &script->stream, &error); + } T_END; + + if ( ret < 0 ) { if ( error_r == NULL ) { if ( error == SIEVE_ERROR_NOT_FOUND ) { sieve_error(script->ehandler, script->name, @@ -379,23 +434,11 @@ struct istream *sieve_script_open } else { *error_r = error; } + return -1; } - return script->stream; -} - -void sieve_script_close(struct sieve_script *script) -{ - if ( script->stream != NULL ) - return; - - i_stream_unref(&script->stream); - - if ( script->v.close != NULL ) { - T_BEGIN { - script->v.close(script); - } T_END; - } + *stream_r = script->stream; + return 0; } /* @@ -425,6 +468,8 @@ bool sieve_script_equals unsigned int sieve_script_hash(const struct sieve_script *script) { + i_assert( script->name != NULL ); + return str_hash(script->name); } diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h index 09b7d7d..74c02b8 100644 --- a/src/lib-sieve/sieve-script.h +++ b/src/lib-sieve/sieve-script.h @@ -26,13 +26,22 @@ ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *); struct sieve_script *sieve_script_create (struct sieve_instance *svinst, const char *location, const char *name, struct sieve_error_handler *ehandler, enum sieve_error *error_r); -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); void sieve_script_ref(struct sieve_script *script); void sieve_script_unref(struct sieve_script **script); +int sieve_script_open + (struct sieve_script *script, enum sieve_error *error_r); +int sieve_script_open_as + (struct sieve_script *script, const char *name, enum sieve_error *error_r); + +struct sieve_script *sieve_script_create_open + (struct sieve_instance *svinst, const char *location, const char *name, + struct sieve_error_handler *ehandler, enum sieve_error *error_r); +struct sieve_script *sieve_script_create_open_as + (struct sieve_instance *svinst, const char *location, const char *name, + struct sieve_error_handler *ehandler, enum sieve_error *error_r); + /* * Accessors */ @@ -41,6 +50,8 @@ const char *sieve_script_name(const struct sieve_script *script); const char *sieve_script_location(const struct sieve_script *script); struct sieve_instance *sieve_script_svinst(const struct sieve_script *script); +bool sieve_script_is_open(const struct sieve_script *script); + /* * Saving/loading Sieve binaries */ @@ -61,10 +72,9 @@ int sieve_script_binary_save * Stream management */ -struct istream *sieve_script_open - (struct sieve_script *script, enum sieve_error *error_r); -void sieve_script_close(struct sieve_script *script); - +int sieve_script_get_stream + (struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r); int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r); /* diff --git a/src/lib-sieve/sieve-smtp.c b/src/lib-sieve/sieve-smtp.c index e203cbd..b4102a4 100644 --- a/src/lib-sieve/sieve-smtp.c +++ b/src/lib-sieve/sieve-smtp.c @@ -14,12 +14,12 @@ bool sieve_smtp_available void *sieve_smtp_open (const struct sieve_script_env *senv, const char *destination, - const char *return_path, FILE **file_r) + const char *return_path, struct ostream **output_r) { if ( senv->smtp_open == NULL || senv->smtp_close == NULL ) return NULL; - return senv->smtp_open(senv, destination, return_path, file_r); + return senv->smtp_open(senv, destination, return_path, output_r); } bool sieve_smtp_close diff --git a/src/lib-sieve/sieve-smtp.h b/src/lib-sieve/sieve-smtp.h index 5ee286d..63b7cbe 100644 --- a/src/lib-sieve/sieve-smtp.h +++ b/src/lib-sieve/sieve-smtp.h @@ -11,7 +11,7 @@ bool sieve_smtp_available void *sieve_smtp_open (const struct sieve_script_env *senv, const char *destination, - const char *return_path, FILE **file_r); + const char *return_path, struct ostream **output_r); bool sieve_smtp_close (const struct sieve_script_env *senv, void *handle); diff --git a/src/lib-sieve/sieve-stringlist.c b/src/lib-sieve/sieve-stringlist.c index 4c8a703..01e9c5d 100644 --- a/src/lib-sieve/sieve-stringlist.c +++ b/src/lib-sieve/sieve-stringlist.c @@ -17,7 +17,7 @@ int sieve_stringlist_read_all const char * const **list_r) { if ( strlist->read_all == NULL ) { - ARRAY_DEFINE(items, const char *); + ARRAY(const char *) items; string_t *item; int ret; diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h index d8380ab..375a0f7 100644 --- a/src/lib-sieve/sieve-types.h +++ b/src/lib-sieve/sieve-types.h @@ -31,14 +31,39 @@ enum sieve_flag { SIEVE_FLAG_HOME_RELATIVE = (1 << 0), }; +/* Sieve evaluation can be performed at various different points as messages + are processed. */ +enum sieve_env_location { + /* Unknown */ + SIEVE_ENV_LOCATION_UNKNOWN = 0, + /* "MDA" - evaluation is being performed by a Mail Delivery Agent */ + SIEVE_ENV_LOCATION_MDA, + /* "MTA" - the Sieve script is being evaluated by a Message Transfer Agent */ + SIEVE_ENV_LOCATION_MTA, + /* "MS" - evaluation is being performed by a Message Store */ + SIEVE_ENV_LOCATION_MS +}; + +/* The point relative to final delivery where the Sieve script is being + evaluated. */ +enum sieve_delivery_phase { + SIEVE_DELIVERY_PHASE_UNKNOWN = 0, + SIEVE_DELIVERY_PHASE_PRE, + SIEVE_DELIVERY_PHASE_DURING, + SIEVE_DELIVERY_PHASE_POST, +}; + struct sieve_environment { const char *hostname; - const char *base_dir; + const char *domainname; + const char *base_dir; const char *username; const char *home_dir; enum sieve_flag flags; + enum sieve_env_location location; + enum sieve_delivery_phase delivery_phase; }; /* @@ -168,7 +193,7 @@ struct sieve_script_env { /* Interface for sending mail */ void *(*smtp_open) (const struct sieve_script_env *senv, const char *destination, - const char *return_path, FILE **file_r); + const char *return_path, struct ostream **output_r); bool (*smtp_close)(const struct sieve_script_env *senv, void *handle); /* Interface for marking and checking duplicates */ diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index 0c866d3..9763f62 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -48,9 +48,9 @@ struct sieve_command_registration { const struct sieve_command_def *cmd_def; const struct sieve_extension *ext; - ARRAY_DEFINE(normal_tags, struct sieve_tag_registration *); - ARRAY_DEFINE(instanced_tags, struct sieve_tag_registration *); - ARRAY_DEFINE(persistent_tags, struct sieve_tag_registration *); + ARRAY(struct sieve_tag_registration *) normal_tags; + ARRAY(struct sieve_tag_registration *) instanced_tags; + ARRAY(struct sieve_tag_registration *) persistent_tags; }; /* Default (literal) arguments */ @@ -93,9 +93,9 @@ struct sieve_validator { /* Registries */ - struct hash_table *commands; + HASH_TABLE(const char *, struct sieve_command_registration *) commands; - ARRAY_DEFINE(extensions, struct sieve_validator_extension_reg); + ARRAY(struct sieve_validator_extension_reg) extensions; /* This is currently a wee bit ugly and needs more thought */ struct sieve_default_argument default_arguments[SAT_COUNT]; @@ -179,8 +179,8 @@ struct sieve_validator *sieve_validator_create sieve_extensions_get_count(valdtr->svinst)); /* Setup command registry */ - valdtr->commands = hash_table_create - (default_pool, pool, 0, strcase_hash, (hash_cmp_callback_t *)strcasecmp); + hash_table_create + (&valdtr->commands, pool, 0, strcase_hash, strcasecmp); sieve_validator_register_core_commands(valdtr); sieve_validator_register_core_tests(valdtr); @@ -306,8 +306,7 @@ static struct sieve_command_registration * sieve_validator_find_command_registration (struct sieve_validator *valdtr, const char *command) { - return (struct sieve_command_registration *) - hash_table_lookup(valdtr->commands, command); + return hash_table_lookup(valdtr->commands, command); } static struct sieve_command_registration *_sieve_validator_register_command @@ -320,7 +319,7 @@ static struct sieve_command_registration *_sieve_validator_register_command cmd_reg->cmd_def = cmd_def; cmd_reg->ext = ext; - hash_table_insert(valdtr->commands, (void *) identifier, (void *) cmd_reg); + hash_table_insert(valdtr->commands, identifier, cmd_reg); return cmd_reg; } @@ -1402,7 +1401,7 @@ struct sieve_validator_object_reg { struct sieve_validator_object_registry { struct sieve_validator *valdtr; - ARRAY_DEFINE(registrations, struct sieve_validator_object_reg); + ARRAY(struct sieve_validator_object_reg) registrations; }; struct sieve_validator_object_registry *sieve_validator_object_registry_get diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index e5d38f3..c7bfd0a 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -49,6 +49,7 @@ struct sieve_instance *sieve_init struct sieve_instance *svinst; unsigned long long int uint_setting; size_t size_setting; + const char *domain; pool_t pool; /* Create Sieve engine instance */ @@ -58,14 +59,44 @@ struct sieve_instance *sieve_init 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; + svinst->env_location = env->location; + svinst->delivery_phase = env->delivery_phase; + + /* Determine domain */ + if ( env->domainname != NULL && *(env->domainname) != '\0' ) { + domain = env->domainname; + } else { + /* Fall back to parsing username localpart@domain */ + domain = svinst->username == NULL ? NULL : + strchr(svinst->username, '@'); + if ( domain == NULL || *(domain+1) == '\0' ) { + /* Fall back to parsing hostname host.domain */ + domain = ( env->hostname != NULL ? strchr(env->hostname, '.') : NULL ); + if ( domain == NULL || *(domain+1) == '\0' + || strchr(domain+1, '.') == NULL ) { + /* Fall back to bare hostname */ + domain = env->hostname; + } else { + domain++; + } + } else { + domain++; + } + } + svinst->hostname = p_strdup_empty(pool, env->hostname); + svinst->domainname = p_strdup(pool, domain); sieve_errors_init(svinst); + if ( debug ) { + sieve_sys_debug(svinst, "%s version %s initializing", + PIGEONHOLE_NAME, PIGEONHOLE_VERSION); + } + /* Read limits from configuration */ svinst->max_script_size = SIEVE_DEFAULT_MAX_SCRIPT_SIZE; @@ -255,7 +286,7 @@ struct sieve_binary *sieve_compile struct sieve_script *script; struct sieve_binary *sbin; - if ( (script = sieve_script_create + if ( (script = sieve_script_create_open (svinst, script_location, script_name, ehandler, error_r)) == NULL ) return NULL; @@ -375,10 +406,8 @@ struct sieve_binary *sieve_open struct sieve_binary *sbin; /* First open the scriptfile itself */ - script = sieve_script_create - (svinst, script_location, script_name, ehandler, error_r); - - if ( script == NULL ) { + if ( (script=sieve_script_create_open + (svinst, script_location, script_name, ehandler, error_r)) == NULL ) { /* Failed */ return NULL; } diff --git a/src/lib-sievestorage/Makefile.am b/src/lib-sievestorage/Makefile.am index 181971d..c9f3b82 100644 --- a/src/lib-sievestorage/Makefile.am +++ b/src/lib-sievestorage/Makefile.am @@ -1,11 +1,11 @@ -noinst_LIBRARIES = libsievestorage.a +noinst_LTLIBRARIES = libsievestorage.la INCLUDES = \ $(LIBDOVECOT_INCLUDE) \ -I$(top_srcdir) \ -I$(top_srcdir)/src/lib-sieve -libsievestorage_a_SOURCES = \ +libsievestorage_la_SOURCES = \ sieve-storage-save.c \ sieve-storage-script.c \ sieve-storage-list.c \ diff --git a/src/lib-sievestorage/sieve-storage-list.c b/src/lib-sievestorage/sieve-storage-list.c index 214d778..72b5962 100644 --- a/src/lib-sievestorage/sieve-storage-list.c +++ b/src/lib-sievestorage/sieve-storage-list.c @@ -45,7 +45,7 @@ struct sieve_list_context *sieve_storage_list_init T_BEGIN { /* Get the name of the active script */ - if ( sieve_storage_get_active_scriptfile(storage, &active) < 0) { + if ( sieve_storage_active_script_get_file(storage, &active) < 0) { ctx = NULL; } else { pool = pool_alloconly_create("sieve_list_context", 4096); diff --git a/src/lib-sievestorage/sieve-storage-private.h b/src/lib-sievestorage/sieve-storage-private.h index 4b2dc54..5133026 100644 --- a/src/lib-sievestorage/sieve-storage-private.h +++ b/src/lib-sievestorage/sieve-storage-private.h @@ -9,14 +9,6 @@ #include "sieve-storage.h" - -enum sieve_storage_flags { - /* Print debugging information while initializing the storage */ - SIEVE_STORAGE_FLAG_DEBUG = 0x01, - /* Use CRLF linefeeds when saving mails. */ - SIEVE_STORAGE_FLAG_SAVE_CRLF = 0x02, -}; - #define SIEVE_READ_BLOCK_SIZE (1024*8) /* How often to scan tmp/ directory for old files (based on dir's atime) */ @@ -38,19 +30,20 @@ struct sieve_storage { char *name; char *dir; - bool debug; /* Private */ char *active_path; char *active_fname; char *link_path; char *error; - char *user; /* name of user accessing the storage */ + char *username; /* name of user accessing the storage */ mode_t dir_create_mode; mode_t file_create_mode; gid_t file_create_gid; + struct mailbox *inbox; + uint64_t max_scripts; uint64_t max_storage; @@ -58,10 +51,19 @@ struct sieve_storage { struct sieve_error_handler *ehandler; enum sieve_storage_flags flags; + time_t prev_mtime; }; struct sieve_script *sieve_storage_script_init_from_path (struct sieve_storage *storage, const char *path, const char *scriptname); +void sieve_storage_inbox_script_attribute_set + (struct sieve_storage *storage, const char *name); +void sieve_storage_inbox_script_attribute_rename + (struct sieve_storage *storage, const char *oldname, const char *newname); +void sieve_storage_inbox_script_attribute_unset + (struct sieve_storage *storage, const char *name); + + #endif diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c index b4bece5..ad8039f 100644 --- a/src/lib-sievestorage/sieve-storage-save.c +++ b/src/lib-sievestorage/sieve-storage-save.c @@ -9,6 +9,7 @@ #include "ostream.h" #include "str.h" #include "eacces-error.h" +#include "safe-mkstemp.h" #include "sieve-script.h" #include "sieve-script-file.h" @@ -36,6 +37,8 @@ struct sieve_save_context { int fd; const char *tmp_path; + time_t mtime; + unsigned int failed:1; unsigned int moving:1; unsigned int finished:1; @@ -204,6 +207,7 @@ sieve_storage_save_init(struct sieve_storage *storage, ctx->storage = storage; ctx->scriptname = p_strdup(pool, scriptname); ctx->scriptobject = NULL; + ctx->mtime = (time_t)-1; T_BEGIN { ctx->fd = sieve_storage_create_tmp(storage, scriptname, &path); @@ -278,6 +282,12 @@ int sieve_storage_save_finish(struct sieve_save_context *ctx) return ( ctx->failed ? -1 : 0 ); } +void sieve_storage_save_set_mtime +(struct sieve_save_context *ctx, time_t mtime) +{ + ctx->mtime = mtime; +} + static void sieve_storage_save_destroy(struct sieve_save_context **ctx) { if ((*ctx)->scriptobject != NULL) @@ -324,7 +334,7 @@ bool sieve_storage_save_will_activate const char *scriptname; int ret; - ret = sieve_storage_get_active_scriptname(ctx->storage, &scriptname); + ret = sieve_storage_active_script_get_name(ctx->storage, &scriptname); if ( ret > 0 ) { /* Is the requested script active? */ result = ( strcmp(ctx->scriptname, scriptname) == 0 ); @@ -334,23 +344,48 @@ bool sieve_storage_save_will_activate return result; } -int sieve_storage_save_commit(struct sieve_save_context **ctx) +static void sieve_storage_update_mtime(const char *path, time_t mtime) +{ + struct utimbuf times = { .actime = mtime, .modtime = mtime }; + + if ( utime(path, ×) < 0 ) { + switch ( errno ) { + case ENOENT: + break; + case EACCES: + i_error("sieve-storage: %s", eacces_error_get("utime", path)); + break; + default: + i_error("sieve-storage: utime(%s) failed: %m", path); + } + } +} + +int sieve_storage_save_commit(struct sieve_save_context **_ctx) { + struct sieve_save_context *ctx = *_ctx; + struct sieve_storage *storage = ctx->storage; const char *dest_path; bool failed = FALSE; - i_assert((*ctx)->output == NULL); - i_assert((*ctx)->finished); - i_assert((*ctx)->scriptname != NULL); + i_assert(ctx->output == NULL); + i_assert(ctx->finished); + i_assert(ctx->scriptname != NULL); T_BEGIN { - dest_path = t_strconcat((*ctx)->storage->dir, "/", - sieve_scriptfile_from_name((*ctx)->scriptname), NULL); + dest_path = t_strconcat(storage->dir, "/", + sieve_scriptfile_from_name(ctx->scriptname), NULL); - failed = !sieve_storage_script_move((*ctx), dest_path); + failed = !sieve_storage_script_move(ctx, dest_path); + if ( ctx->mtime != (time_t)-1 ) + sieve_storage_update_mtime(dest_path, ctx->mtime); } T_END; - sieve_storage_save_destroy(ctx); + /* set INBOX mailbox attribute */ + if ( !failed ) + sieve_storage_inbox_script_attribute_set(storage, ctx->scriptname); + + sieve_storage_save_destroy(_ctx); return ( failed ? -1 : 0 ); } @@ -368,3 +403,57 @@ void sieve_storage_save_cancel(struct sieve_save_context **ctx) sieve_storage_save_destroy(ctx); } + +int sieve_storage_save_as_active_script(struct sieve_storage *storage, + struct istream *input, time_t mtime) +{ + int fd; + string_t *temp_path; + struct ostream *output; + + temp_path = t_str_new(256); + str_append(temp_path, storage->active_path); + str_append_c(temp_path, '.'); + fd = safe_mkstemp_hostpid + (temp_path, storage->file_create_mode, (uid_t)-1, (gid_t)-1); + if ( fd < 0 ) { + if ( errno == EACCES ) { + sieve_storage_set_critical(storage, + "failed to create temporary file: %s", + eacces_error_get_creating("open", str_c(temp_path))); + } else { + sieve_storage_set_critical(storage, + "failed to create temporary file: open(%s) failed: %m", + str_c(temp_path)); + } + return -1; + } + + output = o_stream_create_fd(fd, 0, FALSE); + if (o_stream_send_istream(output, input) < 0) { + sieve_storage_set_critical(storage, + "o_stream_send_istream(%s) failed: %m", str_c(temp_path)); + o_stream_destroy(&output); + (void)unlink(str_c(temp_path)); + return -1; + } + o_stream_destroy(&output); + + if (rename(str_c(temp_path), storage->active_path) < 0) { + if ( ENOSPACE(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_SPACE, "Not enough disk space"); + } else if ( errno == EACCES ) { + sieve_storage_set_critical(storage, + "%s", eacces_error_get("rename", storage->active_path)); + } else { + sieve_storage_set_critical(storage, + "rename(%s, %s) failed: %m", str_c(temp_path), storage->active_path); + } + } else { + sieve_storage_update_mtime(storage->active_path, mtime); + } + + (void)unlink(str_c(temp_path)); + return 0; +} diff --git a/src/lib-sievestorage/sieve-storage-save.h b/src/lib-sievestorage/sieve-storage-save.h index d3d3232..491b5ca 100644 --- a/src/lib-sievestorage/sieve-storage-save.h +++ b/src/lib-sievestorage/sieve-storage-save.h @@ -22,9 +22,18 @@ struct sieve_script *sieve_storage_save_get_tempscript bool sieve_storage_save_will_activate (struct sieve_save_context *ctx); +void sieve_storage_save_set_mtime + (struct sieve_save_context *ctx, time_t mtime); + void sieve_storage_save_cancel(struct sieve_save_context **ctx); int sieve_storage_save_commit(struct sieve_save_context **ctx); +/* Saves input directly as a regular file at the active script path. + * This is needed for the doveadm-sieve plugin. + */ +int sieve_storage_save_as_active_script(struct sieve_storage *storage, + struct istream *input, time_t mtime); + #endif diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c index 134fcf5..55f6756 100644 --- a/src/lib-sievestorage/sieve-storage-script.c +++ b/src/lib-sievestorage/sieve-storage-script.c @@ -41,7 +41,7 @@ struct sieve_script *sieve_storage_script_init_from_path /* Prevent initializing the active script link as a script when it * resides in the sieve storage directory. */ - if ( *(storage->link_path) == '\0' ) { + if ( scriptname != NULL && *(storage->link_path) == '\0' ) { const char *fname; fname = strrchr(path, '/'); @@ -63,18 +63,18 @@ struct sieve_script *sieve_storage_script_init_from_path st_script->file.script.pool = pool; st_script->storage = storage; - if ( sieve_script_init + 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); + scriptname, sieve_storage_get_error_handler(storage)); - if ( error == SIEVE_ERROR_NOT_FOUND ) - sieve_storage_set_error(storage, error, "Script does not exist."); + if ( sieve_script_open(&st_script->file.script, &error) < 0 ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) + sieve_storage_set_error(storage, error, "Script does not exist."); + pool_unref(&pool); + return NULL; + } - return NULL; + return &st_script->file.script; } struct sieve_script *sieve_storage_script_init @@ -126,18 +126,20 @@ static int sieve_storage_read_active_link ret = readlink(storage->active_path, linkbuf, sizeof(linkbuf)); if ( ret < 0 ) { - if (errno == EINVAL) { + if ( errno == EINVAL ) { /* Our symlink is no symlink. Report 'no active script'. * Activating a script will automatically resolve this, so * there is no need to panic on this one. */ - i_warning - ("sieve-storage: Active sieve script symlink %s is no symlink.", - storage->active_path); + if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { + i_warning + ("sieve-storage: Active sieve script symlink %s is no symlink.", + storage->active_path); + } return 0; } - if (errno == ENOENT ) { + if ( errno == ENOENT ) { /* Symlink not found */ return 0; } @@ -154,7 +156,7 @@ static int sieve_storage_read_active_link return 1; } -static const char *sieve_storage_parse_link +static const char *sieve_storage_parse_active_link (struct sieve_storage *storage, const char *link, const char **scriptname_r) { const char *fname, *scriptname, *scriptpath; @@ -197,7 +199,7 @@ static const char *sieve_storage_parse_link return fname; } -int sieve_storage_get_active_scriptfile +int sieve_storage_active_script_get_file (struct sieve_storage *storage, const char **file_r) { const char *link, *scriptfile; @@ -210,7 +212,7 @@ int sieve_storage_get_active_scriptfile return ret; /* Parse the link */ - scriptfile = sieve_storage_parse_link(storage, link, NULL); + scriptfile = sieve_storage_parse_active_link(storage, link, NULL); if (scriptfile == NULL) { /* Obviously someone has been playing with our symlink, @@ -224,7 +226,7 @@ int sieve_storage_get_active_scriptfile return 1; } -int sieve_storage_get_active_scriptname +int sieve_storage_active_script_get_name (struct sieve_storage *storage, const char **name_r) { const char *link; @@ -236,7 +238,7 @@ int sieve_storage_get_active_scriptname if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) return ret; - if ( sieve_storage_parse_link(storage, link, name_r) == NULL ) { + if ( sieve_storage_parse_active_link(storage, link, name_r) == NULL ) { /* Obviously someone has been playing with our symlink, * ignore this situation and report 'no active script'. * Activation should fix this situation. @@ -247,7 +249,13 @@ int sieve_storage_get_active_scriptname return 1; } -struct sieve_script *sieve_storage_get_active_script +const char *sieve_storage_active_script_get_path + (struct sieve_storage *storage) +{ + return storage->active_path; +} + +struct sieve_script *sieve_storage_active_script_get (struct sieve_storage *storage) { struct sieve_script *script; @@ -268,7 +276,7 @@ struct sieve_script *sieve_storage_get_active_script } /* Parse the link */ - scriptfile = sieve_storage_parse_link(storage, link, NULL); + scriptfile = sieve_storage_parse_active_link(storage, link, NULL); if (scriptfile == NULL) { /* Obviously someone has been playing with our symlink, @@ -289,6 +297,55 @@ struct sieve_script *sieve_storage_get_active_script return script; } +int sieve_storage_active_script_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + struct stat st; + + /* Try direct lstat first */ + if (lstat(storage->active_path, &st) == 0) { + if (!S_ISLNK(st.st_mode)) { + *last_change_r = st.st_mtime; + return 0; + } + } + /* Check error */ + else if (errno != ENOENT) { + sieve_storage_set_critical(storage, "lstat(%s) failed: %m", + storage->active_path); + } + + /* Fall back to statting storage directory */ + return sieve_storage_get_last_change(storage, last_change_r); +} + +int +sieve_storage_active_script_is_no_link(struct sieve_storage *storage) +{ + struct stat st; + + /* Stat the file */ + if ( lstat(storage->active_path, &st) != 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to stat active sieve script symlink (%s): %m.", + storage->active_path); + return -1; + } + return 0; + } + + if ( S_ISLNK( st.st_mode ) ) + return 0; + if ( !S_ISREG( st.st_mode ) ) { + sieve_storage_set_critical( storage, + "Active sieve script file '%s' is no symlink nor a regular file.", + storage->active_path ); + return -1; + } + return 1; +} + int sieve_storage_script_is_active(struct sieve_script *script) { struct sieve_storage_script *st_script = @@ -297,7 +354,7 @@ int sieve_storage_script_is_active(struct sieve_script *script) int ret = 0; T_BEGIN { - ret = sieve_storage_get_active_scriptfile(st_script->storage, &afile); + ret = sieve_storage_active_script_get_file(st_script->storage, &afile); if ( ret > 0 ) { /* Is the requested script active? */ @@ -334,6 +391,10 @@ int sieve_storage_script_delete(struct sieve_script **script) } } + /* unset INBOX mailbox attribute */ + if ( ret >= 0 ) + sieve_storage_inbox_script_attribute_unset(storage, (*script)->name); + /* Always deinitialize the script object */ sieve_script_unref(script); return ret; @@ -341,6 +402,7 @@ int sieve_storage_script_delete(struct sieve_script **script) static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) { + bool debug = ( (storage->flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 ); struct stat st; /* Stat the file */ @@ -355,7 +417,7 @@ static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) } if ( S_ISLNK( st.st_mode ) ) { - if ( storage->debug ) + if ( debug ) i_debug( "sieve-storage: nothing to rescue %s.", storage->active_path); return TRUE; /* Nothing to rescue */ } @@ -391,7 +453,7 @@ static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage) return FALSE; } -int sieve_storage_deactivate(struct sieve_storage *storage) +int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime) { int ret; @@ -406,10 +468,12 @@ int sieve_storage_deactivate(struct sieve_storage *storage) sieve_storage_set_critical(storage, "sieve_storage_deactivate(): " "error on unlink(%s): %m", storage->active_path); return -1; - } else + } else { return 0; + } } + sieve_storage_set_modified(storage, mtime); return 1; } @@ -469,7 +533,7 @@ static int sieve_storage_replace_active_link return 1; } -static int _sieve_storage_script_activate(struct sieve_script *script) +static int _sieve_storage_script_activate(struct sieve_script *script, time_t mtime) { struct sieve_storage_script *st_script = (struct sieve_storage_script *) script; @@ -484,7 +548,7 @@ static int _sieve_storage_script_activate(struct sieve_script *script) * resolves automatically. This step is only necessary to provide a * proper return value indicating whether the script was already active. */ - ret = sieve_storage_get_active_scriptfile(storage, &afile); + ret = sieve_storage_active_script_get_file(storage, &afile); /* Is the requested script already active? */ if ( ret <= 0 || strcmp(st_script->file.filename, afile) != 0 ) @@ -528,15 +592,16 @@ static int _sieve_storage_script_activate(struct sieve_script *script) } } + sieve_storage_set_modified(storage, mtime); return activated; } -int sieve_storage_script_activate(struct sieve_script *script) +int sieve_storage_script_activate(struct sieve_script *script, time_t mtime) { int ret; T_BEGIN { - ret = _sieve_storage_script_activate(script); + ret = _sieve_storage_script_activate(script, mtime); } T_END; return ret; @@ -548,7 +613,7 @@ int sieve_storage_script_rename struct sieve_storage_script *st_script = (struct sieve_storage_script *) script; struct sieve_storage *storage = st_script->storage; - const char *newpath, *newfile, *link_path; + const char *oldname = script->name, *newpath, *newfile, *link_path; int ret = 0; /* Check script name */ @@ -619,6 +684,10 @@ int sieve_storage_script_rename } } T_END; + /* rename INBOX mailbox attribute */ + if ( ret >= 0 && oldname != NULL ) + sieve_storage_inbox_script_attribute_rename(storage, oldname, newname); + return ret; } diff --git a/src/lib-sievestorage/sieve-storage-script.h b/src/lib-sievestorage/sieve-storage-script.h index 06fdf16..6186c8e 100644 --- a/src/lib-sievestorage/sieve-storage-script.h +++ b/src/lib-sievestorage/sieve-storage-script.h @@ -11,24 +11,24 @@ struct sieve_script *sieve_storage_script_init (struct sieve_storage *storage, const char *scriptname); -int sieve_storage_get_active_scriptfile +int sieve_storage_active_script_get_file (struct sieve_storage *storage, const char **file_r); -int sieve_storage_get_active_scriptname +int sieve_storage_active_script_get_name (struct sieve_storage *storage, const char **name_r); - -struct sieve_script *sieve_storage_get_active_script +const char *sieve_storage_active_script_get_path + (struct sieve_storage *storage); +int sieve_storage_active_script_is_no_link(struct sieve_storage *storage); +struct sieve_script *sieve_storage_active_script_get (struct sieve_storage *storage); +int sieve_storage_active_script_get_last_change + (struct sieve_storage *storage, time_t *last_change_r); +int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime); int sieve_storage_script_is_active(struct sieve_script *script); - +int sieve_storage_script_activate(struct sieve_script *script, time_t mtime); int sieve_storage_script_delete(struct sieve_script **script); - -int sieve_storage_deactivate(struct sieve_storage *storage); - -int sieve_storage_script_activate(struct sieve_script *script); - int sieve_storage_script_rename -(struct sieve_script *script, const char *newname); + (struct sieve_script *script, const char *newname); #endif diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c index 87a5a0d..eb6a4a4 100644 --- a/src/lib-sievestorage/sieve-storage.c +++ b/src/lib-sievestorage/sieve-storage.c @@ -7,6 +7,7 @@ #include "mkdir-parents.h" #include "eacces-error.h" #include "unlink-old-files.h" +#include "mail-storage-private.h" #include "sieve.h" #include "sieve-common.h" @@ -22,6 +23,7 @@ #include #include #include +#include #define SIEVE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT @@ -201,13 +203,36 @@ static int check_tmp(const char *path) return 1; } +static int _sieve_storage_open_inbox +(struct mail_user *user, struct mailbox **box_r) +{ + struct mail_namespace *ns; + struct mailbox *box; + enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS; + enum mail_error error; + + ns = mail_namespace_find_inbox(user->namespaces); + *box_r = box = mailbox_alloc(ns->list, "INBOX", flags); + if (mailbox_open(box) == 0) + return 0; + + i_warning("sieve-storage: " + "Failed to open user INBOX for attribute modifications: %s", + mailbox_get_last_error(box, &error)); + return -1; +} + static struct sieve_storage *_sieve_storage_create -(struct sieve_instance *svinst, const char *user, const char *home, bool debug) +(struct sieve_instance *svinst, struct mail_user *user, const char *home, + enum sieve_storage_flags flags) { pool_t pool; struct sieve_storage *storage; + struct mailbox *inbox = NULL; + bool debug = ( (flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 ); const char *tmp_dir, *link_path, *path; const char *sieve_data, *active_path, *active_fname, *storage_dir; + const char *username = user->username; mode_t dir_create_mode, file_create_mode; gid_t file_create_gid; const char *file_create_gid_origin; @@ -262,7 +287,8 @@ static struct sieve_storage *_sieve_storage_create path = home_expand_tilde(active_path, home); if ( path == NULL ) { i_error("sieve-storage: userdb(%s) didn't return a home directory " - "for substitition in active script path (sieve=%s)", user, active_path); + "for substitition in active script path (sieve=%s)", + username, active_path); return NULL; } @@ -365,7 +391,7 @@ static struct sieve_storage *_sieve_storage_create if ( path == NULL ) { i_error("sieve-storage: userdb(%s) didn't return a home directory " "for substitition in storage root directory (sieve_dir=%s)", - user, storage_dir); + username, storage_dir); return NULL; } @@ -378,6 +404,17 @@ static struct sieve_storage *_sieve_storage_create "using sieve script storage directory: %s", storage_dir); } + /* get the storage mtime before we modify it ourself. FIXME: do this + later, only just before modifying the sieve dir */ + struct stat st; + if (stat(storage_dir, &st) < 0) { + if (errno != ENOENT) { + i_error("stat(%s) failed: %m", storage_dir); + return NULL; + } + st.st_mtime = 0; + } + /* Get permissions */ sieve_storage_get_permissions @@ -400,6 +437,10 @@ static struct sieve_storage *_sieve_storage_create file_create_gid_origin, debug) < 0 ) return NULL; + /* Open user's INBOX for attribute updates if necessary */ + if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) + (void)_sieve_storage_open_inbox(user, &inbox); + /* * Create storage object */ @@ -407,17 +448,20 @@ static struct sieve_storage *_sieve_storage_create pool = pool_alloconly_create("sieve-storage", 512+256); storage = p_new(pool, struct sieve_storage, 1); storage->svinst = svinst; - storage->debug = debug; + storage->flags = flags; storage->pool = pool; storage->dir = p_strdup(pool, storage_dir); - storage->user = p_strdup(pool, user); + storage->username = p_strdup(pool, username); storage->active_path = p_strdup(pool, active_path); storage->active_fname = p_strdup(pool, active_fname); + storage->prev_mtime = st.st_mtime; storage->dir_create_mode = dir_create_mode; storage->file_create_mode = file_create_mode; storage->file_create_gid = file_create_gid; + storage->inbox = inbox; + /* Get the path to be prefixed to the script name in the symlink pointing * to the active script. */ @@ -455,30 +499,69 @@ static struct sieve_storage *_sieve_storage_create (unsigned long long int) storage->max_scripts); } } - return storage; } struct sieve_storage *sieve_storage_create -(struct sieve_instance *svinst, const char *user, const char *home, bool debug) +(struct sieve_instance *svinst, struct mail_user *user, const char *home, + enum sieve_storage_flags flags) { struct sieve_storage *storage; T_BEGIN { - storage = _sieve_storage_create(svinst, user, home, debug); + storage = _sieve_storage_create(svinst, user, home, flags); } T_END; return storage; } - void sieve_storage_free(struct sieve_storage *storage) { + if (storage->inbox != NULL) + mailbox_free(&storage->inbox); sieve_error_handler_unref(&storage->ehandler); pool_unref(&storage->pool); } +int sieve_storage_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + *last_change_r = storage->prev_mtime; + return 0; +} + +void sieve_storage_set_modified +(struct sieve_storage *storage, time_t mtime) +{ + struct utimbuf times; + time_t cur_mtime; + + if ( mtime != (time_t)-1 ) { + if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 && + cur_mtime > mtime ) + return; + } else { + mtime = ioloop_time; + } + + times.actime = mtime; + times.modtime = mtime; + if ( utime(storage->dir, ×) < 0 ) { + switch ( errno ) { + case ENOENT: + break; + case EACCES: + i_error("sieve-storage: %s", eacces_error_get("utime", storage->dir)); + break; + default: + i_error("sieve-storage: utime(%s) failed: %m", storage->dir); + } + } else { + storage->prev_mtime = mtime; + } +} + /* Error handling */ struct sieve_error_handler *sieve_storage_get_error_handler @@ -540,20 +623,6 @@ void sieve_storage_set_error storage->error_code = error; } -void sieve_storage_set_internal_error(struct sieve_storage *storage) -{ - struct tm *tm; - char str[256]; - - tm = localtime(&ioloop_time); - - i_free(storage->error); - storage->error_code = SIEVE_ERROR_TEMP_FAIL; - storage->error = - strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? - i_strdup(str) : i_strdup(CRITICAL_MSG); -} - void sieve_storage_set_critical (struct sieve_storage *storage, const char *fmt, ...) { @@ -561,14 +630,31 @@ void sieve_storage_set_critical sieve_storage_clear_error(storage); if (fmt != NULL) { - va_start(va, fmt); - i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va)); - va_end(va); - - /* critical errors may contain sensitive data, so let user - see only "Internal error" with a timestamp to make it - easier to look from log files the actual error message. */ - sieve_storage_set_internal_error(storage); + i_free(storage->error); + storage->error_code = SIEVE_ERROR_TEMP_FAIL; + + if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { + struct tm *tm; + char str[256]; + + va_start(va, fmt); + i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va)); + va_end(va); + + /* critical errors may contain sensitive data, so let user + see only "Internal error" with a timestamp to make it + easier to look from log files the actual error message. */ + tm = localtime(&ioloop_time); + storage->error = + strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + i_strdup(str) : i_strdup(CRITICAL_MSG); + } else { + /* no user is involved while synchronizing, so do it the + normal way */ + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + } } } @@ -586,4 +672,73 @@ const char *sieve_storage_get_last_error return storage->error != NULL ? storage->error : "Unknown error"; } +/* + * INBOX attributes + */ + +static void sieve_storage_inbox_transaction_finish +(struct sieve_storage *storage, struct mailbox_transaction_context **t) +{ + struct mailbox *inbox = storage->inbox; + + if (mailbox_transaction_commit(t) < 0) { + enum mail_error error; + + i_warning("sieve-storage: Failed to update INBOX attributes: %s", + mail_storage_get_last_error(mailbox_get_storage(inbox), &error)); + } +} + +void sieve_storage_inbox_script_attribute_set +(struct sieve_storage *storage, const char *name) +{ + struct mailbox_transaction_context *t; + const char *key; + + if (storage->inbox == NULL) + return; + + key = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); + t = mailbox_transaction_begin(storage->inbox, 0); + mail_index_attribute_set(t->itrans, TRUE, key, ioloop_time, 0); + sieve_storage_inbox_transaction_finish(storage, &t); +} + +void sieve_storage_inbox_script_attribute_rename +(struct sieve_storage *storage, const char *oldname, const char *newname) +{ + struct mailbox_transaction_context *t; + const char *oldkey, *newkey; + + if (storage->inbox == NULL) + return; + + oldkey = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL); + newkey = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL); + + t = mailbox_transaction_begin(storage->inbox, 0); + mail_index_attribute_unset(t->itrans, TRUE, oldkey, ioloop_time); + mail_index_attribute_set(t->itrans, TRUE, newkey, ioloop_time, 0); + sieve_storage_inbox_transaction_finish(storage, &t); +} + +void sieve_storage_inbox_script_attribute_unset +(struct sieve_storage *storage, const char *name) +{ + struct mailbox_transaction_context *t; + const char *key; + + if (storage->inbox == NULL) + return; + + key = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); + + t = mailbox_transaction_begin(storage->inbox, 0); + mail_index_attribute_unset(t->itrans, TRUE, key, ioloop_time); + sieve_storage_inbox_transaction_finish(storage, &t); +} diff --git a/src/lib-sievestorage/sieve-storage.h b/src/lib-sievestorage/sieve-storage.h index d98c4a2..81bfc80 100644 --- a/src/lib-sievestorage/sieve-storage.h +++ b/src/lib-sievestorage/sieve-storage.h @@ -5,12 +5,32 @@ #define __SIEVE_STORAGE_H #include "lib.h" +#include "mail-storage.h" #include "mail-user.h" #include "sieve.h" +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"sieve/" +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" + +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' + +enum sieve_storage_flags { + /* Print debugging information */ + SIEVE_STORAGE_FLAG_DEBUG = 0x01, + /* This storage is used for synchronization (and not normal ManageSieve) + */ + SIEVE_STORAGE_FLAG_SYNCHRONIZING = 0x02 +}; + struct sieve_storage *sieve_storage_create - (struct sieve_instance *svinst, const char *user, const char *home, bool debug); + (struct sieve_instance *svinst, struct mail_user *user, const char *home, + enum sieve_storage_flags flags); void sieve_storage_free(struct sieve_storage *storage); struct sieve_error_handler *sieve_storage_get_error_handler @@ -27,9 +47,12 @@ void sieve_storage_set_error void sieve_storage_set_critical(struct sieve_storage *storage, const char *fmt, ...) ATTR_FORMAT(2, 3); -void sieve_storage_set_internal_error(struct sieve_storage *storage); - const char *sieve_storage_get_last_error (struct sieve_storage *storage, enum sieve_error *error_r); +int sieve_storage_get_last_change + (struct sieve_storage *storage, time_t *last_change_r); +void sieve_storage_set_modified + (struct sieve_storage *storage, time_t mtime); + #endif diff --git a/src/managesieve-login/client-authenticate.c b/src/managesieve-login/client-authenticate.c index 32b89fd..7ebdd41 100644 --- a/src/managesieve-login/client-authenticate.c +++ b/src/managesieve-login/client-authenticate.c @@ -4,14 +4,12 @@ #include "login-common.h" #include "base64.h" #include "buffer.h" -#include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" -#include "time-util.h" #include "auth-client.h" #include "managesieve-parser.h" @@ -49,17 +47,20 @@ const char *client_authenticate_get_capabilities return str_c(str); } -bool managesieve_client_auth_handle_reply -(struct client *client, const struct client_auth_reply *reply) +void managesieve_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply, const char *text) { struct managesieve_client *msieve_client = - (struct managesieve_client *) client; - const char *timestamp, *msg; - - if ( reply->host != NULL ) { - string_t *resp_code; - const char *reason; - + (struct managesieve_client *)client; + string_t *referral; + + switch (result) { + case CLIENT_AUTH_RESULT_SUCCESS: + /* nothing to be done for IMAP */ + break; + case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS: + case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN: /* MANAGESIEVE referral [nologin] referral host=.. [port=..] [destuser=..] @@ -69,56 +70,41 @@ bool managesieve_client_auth_handle_reply OK [...] "Logged in, but you should use this server instead." .. [REFERRAL ..] Reason from auth server */ - resp_code = t_str_new(128); - str_printfa(resp_code, "REFERRAL sieve://%s;AUTH=%s@%s", + referral = t_str_new(128); + str_printfa(referral, "REFERRAL sieve://%s;AUTH=%s@%s", reply->destuser, client->auth_mech_name, reply->host); if ( reply->port != 4190 ) - str_printfa(resp_code, ":%u", reply->port); + str_printfa(referral, ":%u", reply->port); - if ( reply->reason == NULL ) { - if ( reply->nologin ) - reason = "Try this server instead."; - else - reason = "Logged in, but you should use " - "this server instead."; + if ( result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS ) { + client_send_okresp(client, str_c(referral), text);; } else { - reason = reply->reason; + client_send_noresp(client, str_c(referral), text); } - - if ( !reply->nologin ) { - client_send_okresp(client, str_c(resp_code), reason); - client_destroy_success(client, "Login with referral"); - return TRUE; - } - client_send_noresp(client, str_c(resp_code), reason); - } else if (!reply->nologin) { - /* normal login/failure */ - return FALSE; - } else if (reply->reason != NULL) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - reply->reason); - } else if (reply->temp) { - timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time); - msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]", - my_hostname, timestamp); - client_send_line(client, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, msg); - } else if (reply->authz_failure) { - client_send_line(client, CLIENT_CMD_REPLY_AUTHZ_FAILED, - "Authorization failed"); - } else { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + break; + case CLIENT_AUTH_RESULT_ABORTED: + client_send_no(client, text); + break; + case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: + client_send_noresp(client, "ALERT", text); + break; + case CLIENT_AUTH_RESULT_AUTHZFAILED: + client_send_no(client, text); + break; + case CLIENT_AUTH_RESULT_TEMPFAIL: + client_send_noresp(client, "TRYLATER", text); + break; + case CLIENT_AUTH_RESULT_SSL_REQUIRED: + client_send_noresp(client, "ENCRYPT-NEEDED", text); + break; + case CLIENT_AUTH_RESULT_AUTHFAILED: + default: + client_send_no(client, text); + break; } - i_assert(reply->nologin); - msieve_client->auth_response_input = NULL; managesieve_parser_reset(msieve_client->parser); - - if ( !client->destroyed ) - client_auth_failed(client); - return TRUE; } void managesieve_client_auth_send_challenge @@ -262,7 +248,7 @@ static int managesieve_client_auth_read_response return 1; } -int managesieve_client_auth_parse_response(struct client *client) +void managesieve_client_auth_parse_response(struct client *client) { struct managesieve_client *msieve_client = (struct managesieve_client *) client; @@ -272,18 +258,21 @@ int managesieve_client_auth_parse_response(struct client *client) if ( (ret=managesieve_client_auth_read_response(msieve_client, FALSE, &error)) < 0 ) { if ( error != NULL ) - sasl_server_auth_failed(client, error); - return -1; + client_auth_fail(client, error); + return; } - if ( ret == 0 ) return 0; + if ( ret == 0 ) return; if ( strcmp(str_c(client->auth_response), "*") == 0 ) { - sasl_server_auth_abort(client); - return -1; + client_auth_abort(client); + return; } - return 1; + client_auth_respond(client, str_c(client->auth_response)); + + memset(str_c_modifiable(client->auth_response), 0, + str_len(client->auth_response)); } int cmd_authenticate diff --git a/src/managesieve-login/client-authenticate.h b/src/managesieve-login/client-authenticate.h index f8ca147..18e8841 100644 --- a/src/managesieve-login/client-authenticate.h +++ b/src/managesieve-login/client-authenticate.h @@ -9,12 +9,13 @@ struct managesieve_arg; const char *client_authenticate_get_capabilities (struct client *client); -bool managesieve_client_auth_handle_reply(struct client *client, - const struct client_auth_reply *reply); +void managesieve_client_auth_result + (struct client *client, enum client_auth_result result, + const struct client_auth_reply *reply, const char *text); void managesieve_client_auth_send_challenge (struct client *client, const char *data); -int managesieve_client_auth_parse_response +void managesieve_client_auth_parse_response (struct client *client); int cmd_authenticate diff --git a/src/managesieve-login/client.c b/src/managesieve-login/client.c index b4220dc..055b33a 100644 --- a/src/managesieve-login/client.c +++ b/src/managesieve-login/client.c @@ -105,6 +105,15 @@ static int cmd_starttls return 1; } +static void managesieve_client_notify_starttls +(struct client *client, bool success, const char *text) +{ + if ( success ) + client_send_ok(client, text); + else + client_send_no(client, text); +} + static int cmd_noop (struct managesieve_client *client, const struct managesieve_arg *args) @@ -325,7 +334,7 @@ static void managesieve_client_destroy(struct client *client) managesieve_parser_destroy(&managesieve_client->parser); } -static void managesieve_client_send_greeting(struct client *client) +static void managesieve_client_notify_auth_ready(struct client *client) { /* Cork the stream to send the capability data as a single tcp frame * Some naive clients break if we don't. @@ -337,7 +346,6 @@ static void managesieve_client_send_greeting(struct client *client) client_send_ok(client, client->set->login_greeting); o_stream_uncork(client->output); - client->greeting_sent = TRUE; } static void managesieve_client_starttls(struct client *client) @@ -363,13 +371,15 @@ static void managesieve_client_starttls(struct client *client) o_stream_uncork(client->output); } -void _client_send_response(struct client *client, - const char *oknobye, const char *resp_code, const char *msg) +static void +client_send_reply_raw(struct client *client, + const char *prefix, const char *resp_code, + const char *text) { T_BEGIN { string_t *line = t_str_new(256); - str_append(line, oknobye); + str_append(line, prefix); if (resp_code != NULL) { str_append(line, " ("); @@ -377,10 +387,9 @@ void _client_send_response(struct client *client, str_append_c(line, ')'); } - if ( msg != NULL ) - { + if ( text != NULL ) { str_append_c(line, ' '); - managesieve_quote_append_string(line, msg, TRUE); + managesieve_quote_append_string(line, text, TRUE); } str_append(line, "\r\n"); @@ -389,42 +398,43 @@ void _client_send_response(struct client *client, } T_END; } -static void managesieve_client_send_line -(struct client *client, enum client_cmd_reply reply, const char *text) +void client_send_reply_code +(struct client *client, enum managesieve_cmd_reply reply, const char *resp_code, + const char *text) { - const char *resp_code = NULL; const char *prefix = "NO"; switch (reply) { - case CLIENT_CMD_REPLY_OK: + case MANAGESIEVE_CMD_REPLY_OK: prefix = "OK"; break; - case CLIENT_CMD_REPLY_AUTH_FAILED: - break; - case CLIENT_CMD_REPLY_AUTHZ_FAILED: - break; - case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP: - resp_code = "TRYLATER"; + case MANAGESIEVE_CMD_REPLY_NO: break; - case CLIENT_CMD_REPLY_AUTH_FAIL_REASON: - break; - case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL: - resp_code = "ENCRYPT-NEEDED"; - break; - case CLIENT_CMD_REPLY_BAD: - prefix = "NO"; - break; - case CLIENT_CMD_REPLY_BYE: + case MANAGESIEVE_CMD_REPLY_BYE: prefix = "BYE"; break; - case CLIENT_CMD_REPLY_STATUS: - return; - case CLIENT_CMD_REPLY_STATUS_BAD: - prefix = "NO"; - break; } - _client_send_response(client, prefix, resp_code, text); + client_send_reply_raw(client, prefix, resp_code, text); +} + +void client_send_reply +(struct client *client, enum managesieve_cmd_reply reply, const char *text) +{ + client_send_reply_code(client, reply, NULL, text); +} + +static void +managesieve_client_notify_disconnect +(struct client *client, enum client_disconnect_reason reason, const char *text) +{ + if ( reason == CLIENT_DISCONNECT_SYSTEM_SHUTDOWN ) { + client_send_reply_code + (client, MANAGESIEVE_CMD_REPLY_BYE, "TRYLATER", text); + } else { + client_send_reply_code + (client, MANAGESIEVE_CMD_REPLY_BYE, NULL, text); + } } static void managesieve_login_preinit(void) @@ -445,15 +455,18 @@ static struct client_vfuncs managesieve_client_vfuncs = { managesieve_client_alloc, managesieve_client_create, managesieve_client_destroy, - managesieve_client_send_greeting, + managesieve_client_notify_auth_ready, + managesieve_client_notify_disconnect, + NULL, + managesieve_client_notify_starttls, managesieve_client_starttls, managesieve_client_input, - managesieve_client_send_line, - managesieve_client_auth_handle_reply, managesieve_client_auth_send_challenge, managesieve_client_auth_parse_response, + managesieve_client_auth_result, managesieve_proxy_reset, - managesieve_proxy_parse_line + managesieve_proxy_parse_line, + managesieve_proxy_error }; static const struct login_binary managesieve_login_binary = { diff --git a/src/managesieve-login/client.h b/src/managesieve-login/client.h index fd5a526..b3449c5 100644 --- a/src/managesieve-login/client.h +++ b/src/managesieve-login/client.h @@ -4,7 +4,7 @@ #ifndef __CLIENT_H #define __CLIENT_H -#include "network.h" +#include "net.h" #include "client-common.h" /* maximum length for managesieve command line. */ @@ -36,21 +36,32 @@ struct managesieve_client { bool client_skip_line(struct managesieve_client *client); -void _client_send_response(struct client *client, - const char *oknobye, const char *resp_code, const char *msg); - -#define client_send_ok(client, msg) \ - _client_send_response(client, "OK", NULL, msg) -#define client_send_no(client, msg) \ - _client_send_response(client, "NO", NULL, msg) -#define client_send_bye(client, msg) \ - _client_send_response(client, "BYE", NULL, msg) - -#define client_send_okresp(client, resp_code, msg) \ - _client_send_response(client, "OK", resp_code, msg) -#define client_send_noresp(client, resp_code, msg) \ - _client_send_response(client, "NO", resp_code, msg) -#define client_send_byeresp(client, resp_code, msg) \ - _client_send_response(client, "BYE", resp_code, msg) +enum managesieve_cmd_reply { + MANAGESIEVE_CMD_REPLY_OK, + MANAGESIEVE_CMD_REPLY_NO, + MANAGESIEVE_CMD_REPLY_BYE +}; + +void client_send_reply(struct client *client, + enum managesieve_cmd_reply reply, const char *text); + +void client_send_reply_code(struct client *client, + enum managesieve_cmd_reply reply, const char *resp_code, + const char *text); + +#define client_send_ok(client, text) \ + client_send_reply(client, MANAGESIEVE_CMD_REPLY_OK, text) +#define client_send_no(client, text) \ + client_send_reply(client, MANAGESIEVE_CMD_REPLY_NO, text) +#define client_send_bye(client, text) \ + client_send_reply(client, MANAGESIEVE_CMD_REPLY_BYE, text) + +#define client_send_okresp(client, resp_code, text) \ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_OK, resp_code, text) +#define client_send_noresp(client, resp_code, text) \ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, resp_code, text) +#define client_send_byeresp(client, resp_code, text) \ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_BYE, resp_code, text) + #endif /* __CLIENT_H */ diff --git a/src/managesieve-login/managesieve-login-settings-plugin.c b/src/managesieve-login/managesieve-login-settings-plugin.c index 78296d0..64b0acb 100644 --- a/src/managesieve-login/managesieve-login-settings-plugin.c +++ b/src/managesieve-login/managesieve-login-settings-plugin.c @@ -28,7 +28,7 @@ static void (*next_hook_config_parser_begin)(struct config_parser_context *ctx) static void managesieve_login_config_parser_begin(struct config_parser_context *ctx); -const char *managesieve_login_settings_version = DOVECOT_VERSION; +const char *managesieve_login_settings_version = DOVECOT_ABI_VERSION; void managesieve_login_settings_init(struct module *module ATTR_UNUSED) { @@ -199,9 +199,18 @@ static void managesieve_login_config_set } static void managesieve_login_config_parser_begin(struct config_parser_context *ctx) -{ - if (*ctx->module != '\0' && strcmp(ctx->module, "managesieve-login") != 0) - return; +{ + const char *const *module = ctx->modules; + + if ( module != NULL && *module != NULL ) { + while ( *module != NULL ) { + if ( strcmp(*module, "managesieve-login") == 0 ) + break; + module++; + } + if ( *module == NULL ) + return; + } if ( !capability_dumped ) { (void)capability_dump(); diff --git a/src/managesieve-login/managesieve-proxy.c b/src/managesieve-login/managesieve-proxy.c index 676f932..5c8e547 100644 --- a/src/managesieve-login/managesieve-proxy.c +++ b/src/managesieve-login/managesieve-proxy.c @@ -366,3 +366,8 @@ void managesieve_proxy_reset(struct client *client ATTR_UNUSED) /* struct managesieve_client *msieve_client = (struct managesieve_client *) client; */ } + +void managesieve_proxy_error(struct client *client, const char *text) +{ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, "TRYLATER", text); +} diff --git a/src/managesieve-login/managesieve-proxy.h b/src/managesieve-login/managesieve-proxy.h index d2a8aac..0048f54 100644 --- a/src/managesieve-login/managesieve-proxy.h +++ b/src/managesieve-login/managesieve-proxy.h @@ -7,4 +7,6 @@ void managesieve_proxy_reset(struct client *client); int managesieve_proxy_parse_line(struct client *client, const char *line); +void managesieve_proxy_error(struct client *client, const char *text); + #endif diff --git a/src/managesieve/Makefile.am b/src/managesieve/Makefile.am index 43bab10..a523ffe 100644 --- a/src/managesieve/Makefile.am +++ b/src/managesieve/Makefile.am @@ -24,7 +24,7 @@ managesieve_LDFLAGS = -export-dynamic libs = \ managesieve-settings.lo \ $(top_builddir)/src/lib-managesieve/libmanagesieve.a \ - $(top_builddir)/src/lib-sievestorage/libsievestorage.a \ + $(top_builddir)/src/lib-sievestorage/libsievestorage.la \ $(top_builddir)/src/lib-sieve/libdovecot-sieve.la managesieve_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c index c194559..08bd8f3 100644 --- a/src/managesieve/cmd-getscript.c +++ b/src/managesieve/cmd-getscript.c @@ -105,9 +105,8 @@ bool cmd_getscript(struct client_command_context *cmd) return cmd_getscript_finish(ctx); } - ctx->script_stream = sieve_script_open(ctx->script, &error); - - if ( ctx->script_stream == NULL ) { + if ( sieve_script_get_stream + (ctx->script, &ctx->script_stream, &error) < 0 ) { if ( error == SIEVE_ERROR_NOT_FOUND ) sieve_storage_set_error(client->storage, error, "Script does not exist."); ctx->failed = TRUE; diff --git a/src/managesieve/cmd-putscript.c b/src/managesieve/cmd-putscript.c index c26fa03..8ab8a4d 100644 --- a/src/managesieve/cmd-putscript.c +++ b/src/managesieve/cmd-putscript.c @@ -43,9 +43,8 @@ struct cmd_putscript_context { static void cmd_putscript_finish(struct cmd_putscript_context *ctx); static bool cmd_putscript_continue_script(struct client_command_context *cmd); -static void client_input_putscript(void *context) +static void client_input_putscript(struct client *client) { - struct client *client = context; struct client_command_context *cmd = &client->cmd; i_assert(!client->destroyed); diff --git a/src/managesieve/cmd-setactive.c b/src/managesieve/cmd-setactive.c index 06fbf8d..7558767 100644 --- a/src/managesieve/cmd-setactive.c +++ b/src/managesieve/cmd-setactive.c @@ -68,7 +68,7 @@ bool cmd_setactive(struct client_command_context *cmd) /* Refresh activation no matter what; this can also resolve some erroneous * situations. */ - ret = sieve_storage_script_activate(script); + ret = sieve_storage_script_activate(script, (time_t)-1); if ( ret < 0 ) { client_send_storage_error(client, storage); } else { @@ -90,7 +90,7 @@ bool cmd_setactive(struct client_command_context *cmd) /* ... deactivate */ } else { - ret = sieve_storage_deactivate(storage); + ret = sieve_storage_deactivate(storage, (time_t)-1); if ( ret < 0 ) client_send_storage_error(client, storage); diff --git a/src/managesieve/main.c b/src/managesieve/main.c index 80d47f3..5f18f94 100644 --- a/src/managesieve/main.c +++ b/src/managesieve/main.c @@ -191,7 +191,7 @@ login_client_connected(const struct master_login_client *client, input.userdb_fields = extra_fields; input.session_id = client->session_id; - buffer_create_const_data(&input_buf, client->data, + buffer_create_from_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) { diff --git a/src/managesieve/managesieve-capabilities.c b/src/managesieve/managesieve-capabilities.c index 2e447d5..fd8ffd6 100644 --- a/src/managesieve/managesieve-capabilities.c +++ b/src/managesieve/managesieve-capabilities.c @@ -22,7 +22,7 @@ */ struct plugin_settings { - ARRAY_DEFINE(plugin_envs, const char *); + ARRAY(const char *) plugin_envs; }; static const struct setting_parser_info **plugin_set_roots; diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c index 1f8e9c8..de97723 100644 --- a/src/managesieve/managesieve-client.c +++ b/src/managesieve/managesieve-client.c @@ -6,7 +6,7 @@ #include "llist.h" #include "str.h" #include "hostpid.h" -#include "network.h" +#include "net.h" #include "istream.h" #include "ostream.h" #include "var-expand.h" @@ -62,13 +62,17 @@ static struct sieve_storage *client_get_storage const struct managesieve_settings *set) { struct sieve_storage *storage; + enum sieve_storage_flags flags = 0; const char *home; if ( mail_user_get_home(user, &home) <= 0 ) home = NULL; + if ( set->mail_debug ) + flags |= SIEVE_STORAGE_FLAG_DEBUG; + storage = sieve_storage_create - (svinst, user->username, home, set->mail_debug); + (svinst, user, home, flags); if (storage == NULL) { struct tm *tm; diff --git a/src/managesieve/managesieve-commands.c b/src/managesieve/managesieve-commands.c index b100643..34531c5 100644 --- a/src/managesieve/managesieve-commands.c +++ b/src/managesieve/managesieve-commands.c @@ -29,7 +29,7 @@ static const struct command managesieve_base_commands[] = { #define MANAGESIEVE_COMMANDS_COUNT N_ELEMENTS(managesieve_base_commands) -static ARRAY_DEFINE(managesieve_commands, struct command); +static ARRAY(struct command) managesieve_commands; static bool commands_unsorted; void command_register(const char *name, command_func_t *func) diff --git a/src/managesieve/managesieve-settings.c b/src/managesieve/managesieve-settings.c index f9c825e..f22b404 100644 --- a/src/managesieve/managesieve-settings.c +++ b/src/managesieve/managesieve-settings.c @@ -167,4 +167,4 @@ static bool managesieve_settings_verify /* */ -const char *managesieve_settings_version = DOVECOT_VERSION; +const char *managesieve_settings_version = DOVECOT_ABI_VERSION; diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 4623241..c8f8d97 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1 +1 @@ -SUBDIRS = lda-sieve +SUBDIRS = doveadm-sieve lda-sieve sieve-extprograms diff --git a/src/plugins/doveadm-sieve/Makefile.am b/src/plugins/doveadm-sieve/Makefile.am new file mode 100644 index 0000000..ea1d992 --- /dev/null +++ b/src/plugins/doveadm-sieve/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sievestorage \ + $(LIBDOVECOT_INCLUDE) + +doveadm_moduledir = $(dovecot_moduledir)/doveadm +lib10_doveadm_sieve_plugin_la_LDFLAGS = -module -avoid-version + +doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la + +lib10_doveadm_sieve_plugin_la_SOURCES = \ + doveadm-sieve-plugin.c +lib10_doveadm_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ + $(top_builddir)/src/lib-sievestorage/libsievestorage.la diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c new file mode 100644 index 0000000..bfe7a3b --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c @@ -0,0 +1,706 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "istream.h" +#include "istream-concat.h" +#include "sieve-script.h" +#include "sieve-script-file.h" +#include "sieve-storage.h" +#include "sieve-storage-list.h" +#include "sieve-storage-save.h" +#include "sieve-storage-script.h" +#include "mail-storage-private.h" + +#define SIEVE_MAIL_CONTEXT(obj) \ + MODULE_CONTEXT(obj, sieve_storage_module) +#define SIEVE_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, sieve_user_module) + +struct sieve_mail_user { + union mail_user_module_context module_ctx; + + struct sieve_instance *svinst; + struct sieve_storage *sieve_storage; +}; + +struct sieve_mailbox_attribute_iter { + struct mailbox_attribute_iter iter; + struct mailbox_attribute_iter *super; + + struct sieve_list_context *sieve_list; + string_t *name; + + bool failed; + bool have_active; +}; + +void doveadm_sieve_plugin_init(struct module *module); +void doveadm_sieve_plugin_deinit(void); + +static MODULE_CONTEXT_DEFINE_INIT(sieve_storage_module, + &mail_storage_module_register); +static MODULE_CONTEXT_DEFINE_INIT(sieve_user_module, + &mail_user_module_register); + +const char *doveadm_sieve_plugin_version = DOVECOT_ABI_VERSION; + +static const char * +mail_sieve_get_setting(void *context, const char *identifier) +{ + struct mail_user *mail_user = context; + + return mail_user_plugin_getenv(mail_user, identifier); +} + +static const struct sieve_callbacks mail_sieve_callbacks = { + NULL, + mail_sieve_get_setting +}; + +static void mail_sieve_user_deinit(struct mail_user *user) +{ + struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); + + sieve_storage_free(suser->sieve_storage); + sieve_deinit(&suser->svinst); + + suser->module_ctx.super.deinit(user); +} + +static int +mail_sieve_user_init +(struct mail_user *user, struct sieve_storage **svstorage_r) +{ + struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); + enum sieve_storage_flags storage_flags = SIEVE_STORAGE_FLAG_SYNCHRONIZING; + struct mail_user_vfuncs *v = user->vlast; + struct sieve_environment svenv; + + if (suser != NULL) { + *svstorage_r = suser->sieve_storage; + return 0; + } + + /* Delayed initialization of sieve storage until it's actually needed */ + memset(&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; + + suser = p_new(user->pool, struct sieve_mail_user, 1); + suser->module_ctx.super = *v; + user->vlast = &suser->module_ctx.super; + v->deinit = mail_sieve_user_deinit; + + if (user->mail_debug) + storage_flags |= SIEVE_STORAGE_FLAG_DEBUG; + + suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks, + user, user->mail_debug); + suser->sieve_storage = sieve_storage_create(suser->svinst, user, + svenv.home_dir, storage_flags); + + MODULE_CONTEXT_SET(user, sieve_user_module, suser); + *svstorage_r = suser->sieve_storage; + return 0; +} + +static int sieve_attribute_unset_script(struct mail_storage *storage, + struct sieve_storage *svstorage, + const char *scriptname) +{ + struct sieve_script *script; + const char *errstr; + enum sieve_error error; + int ret = 0; + + script = sieve_storage_script_init(svstorage, scriptname); + ret = script == NULL ? -1 : + sieve_storage_script_delete(&script); + if (ret < 0) { + errstr = sieve_storage_get_last_error(svstorage, &error); + if (error == SIEVE_ERROR_NOT_FOUND) { + /* already deleted, ignore */ + return 0; + } + mail_storage_set_critical(storage, + "Failed to delete Sieve script '%s': %s", scriptname, + errstr); + return -1; + } + return 0; +} + +static int +sieve_attribute_set_active(struct mail_storage *storage, + struct sieve_storage *svstorage, + const struct mail_attribute_value *value) +{ + const char *scriptname; + struct sieve_script *script; + int ret; + + if (mailbox_attribute_value_to_string(storage, value, &scriptname) < 0) + return -1; + i_assert(scriptname[0] == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK); + scriptname++; + + if (scriptname == NULL) { + /* don't affect non-link active script */ + if ((ret=sieve_storage_active_script_is_no_link(svstorage)) != 0) { + if (ret < 0) { + mail_storage_set_internal_error(storage); + return -1; + } + return 0; + } + + /* deactivate current script */ + if (sieve_storage_deactivate(svstorage, value->last_change) < 0) { + mail_storage_set_critical(storage, + "Failed to deactivate Sieve: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + return 0; + } + + /* activate specified script */ + script = sieve_storage_script_init(svstorage, scriptname); + ret = script == NULL ? -1 : + sieve_storage_script_activate(script, value->last_change); + if (ret < 0) { + mail_storage_set_critical(storage, + "Failed to activate Sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + } + if (script != NULL) + sieve_script_unref(&script); + sieve_storage_set_modified(svstorage, value->last_change); + return ret; +} + +static int +sieve_attribute_unset_active_script(struct mail_storage *storage, + struct sieve_storage *svstorage, time_t last_change) +{ + int ret; + + if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) { + if (ret < 0) + mail_storage_set_internal_error(storage); + return ret; + } + + if (sieve_storage_deactivate(svstorage, last_change) < 0) { + mail_storage_set_critical(storage, + "Failed to deactivate sieve: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + return 0; +} + +static int +sieve_attribute_set_active_script(struct mail_storage *storage, + struct sieve_storage *svstorage, + const struct mail_attribute_value *value) +{ + struct istream *input; + + if (value->value != NULL) { + input = i_stream_create_from_data(value->value, strlen(value->value)); + } else if (value->value_stream != NULL) { + input = value->value_stream; + i_stream_ref(input); + } else { + return sieve_attribute_unset_active_script(storage, svstorage, value->last_change); + } + /* skip over the 'S' type */ + i_stream_skip(input, 1); + + if (sieve_storage_save_as_active_script + (svstorage, input, value->last_change) < 0) { + mail_storage_set_critical(storage, + "Failed to save active sieve script: %s", + sieve_storage_get_last_error(svstorage, NULL)); + i_stream_unref(&input); + return -1; + } + + sieve_storage_set_modified(svstorage, value->last_change); + i_stream_unref(&input); + return 0; +} + +static int +sieve_attribute_set_default(struct mail_storage *storage, + struct sieve_storage *svstorage, + const struct mail_attribute_value *value) +{ + const unsigned char *data; + size_t size; + ssize_t ret; + char type; + + if (value->value != NULL) { + type = value->value[0]; + } else { + ret = i_stream_read_data(value->value_stream, &data, &size, 0); + if (ret == -1) { + mail_storage_set_critical(storage, "read(%s) failed: %m", + i_stream_get_name(value->value_stream)); + return -1; + } + i_assert(ret > 0); + type = data[0]; + } + if (type == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK) + return sieve_attribute_set_active(storage, svstorage, value); + if (type == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT) + return sieve_attribute_set_active_script(storage, svstorage, value); + mail_storage_set_error(storage, MAIL_ERROR_PARAMS, + "Invalid value for default sieve attribute"); + return -1; +} + +static int +sieve_attribute_set_sieve(struct mail_storage *storage, + const char *key, + const struct mail_attribute_value *value) +{ + struct sieve_storage *svstorage; + struct sieve_save_context *save_ctx; + struct istream *input; + const char *scriptname; + int ret = 0; + + if (mail_sieve_user_init(storage->user, &svstorage) < 0) + return -1; + + if (strcmp(key, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT) == 0) + return sieve_attribute_set_default(storage, svstorage, value); + if (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)) != 0) { + mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, + "Nonexistent sieve attribute"); + return -1; + } + scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); + + if (value->value != NULL) { + input = i_stream_create_from_data(value->value, + strlen(value->value)); + save_ctx = sieve_storage_save_init(svstorage, scriptname, input); + i_stream_unref(&input); + } else if (value->value_stream != NULL) { + input = value->value_stream; + save_ctx = sieve_storage_save_init(svstorage, scriptname, input); + } else { + return sieve_attribute_unset_script(storage, svstorage, scriptname); + } + + sieve_storage_save_set_mtime(save_ctx, value->last_change); + + if (save_ctx == NULL) { + /* save initialization failed */ + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + while (i_stream_read(input) > 0) { + if (sieve_storage_save_continue(save_ctx) < 0) { + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + ret = -1; + break; + } + } + i_assert(input->eof); + if (input->stream_errno != 0) { + errno = input->stream_errno; + mail_storage_set_critical(storage, + "Saving sieve script: read(%s) failed: %m", + i_stream_get_name(input)); + ret = -1; + } + if (ret == 0 && sieve_storage_save_finish(save_ctx) < 0) { + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + ret = -1; + } + if (ret < 0) + sieve_storage_save_cancel(&save_ctx); + else if (sieve_storage_save_commit(&save_ctx) < 0) { + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + ret = -1; + } + return ret; +} + +static int +sieve_attribute_set(struct mailbox_transaction_context *t, + enum mail_attribute_type type, const char *key, + const struct mail_attribute_value *value) +{ + struct mail_user *user = t->box->storage->user; + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(t->box); + time_t ts = value->last_change != 0 ? value->last_change : ioloop_time; + + if (t->box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE && + strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) == 0) { + if (sieve_attribute_set_sieve(t->box->storage, key, value) < 0) + return -1; + if (user->mail_debug) + i_debug("doveadm-sieve: Assigned value for key `%s'", key); + /* FIXME: set value len to sieve script size / active name + length */ + if (value->value != NULL || value->value_stream != NULL) + mail_index_attribute_set(t->itrans, TRUE, key, ts, 0); + else + mail_index_attribute_unset(t->itrans, TRUE, key, ts); + return 0; + } + return sbox->super.attribute_set(t, type, key, value); +} + +static int +sieve_attribute_retrieve_script(struct mail_storage *storage, + struct sieve_storage *svstorage, struct sieve_script *script, + bool add_type_prefix, + struct mail_attribute_value *value_r, const char **errorstr_r) +{ + char type = MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT; + struct istream *input, *inputs[3]; + const struct stat *st; + enum sieve_error error; + + if (script == NULL) + *errorstr_r = sieve_storage_get_last_error(svstorage, &error); + else if (sieve_script_get_stream(script, &input, &error) < 0) + sieve_script_unref(&script); + + if (script == NULL) { + if (error == SIEVE_ERROR_NOT_FOUND) { + /* already deleted, but return the last_change */ + (void)sieve_storage_get_last_change(svstorage, + &value_r->last_change); + return 0; + } + *errorstr_r = sieve_storage_get_last_error(svstorage, &error); + return -1; + } + if (i_stream_stat(input, FALSE, &st) < 0) { + mail_storage_set_critical(storage, + "stat(%s) failed: %m", i_stream_get_name(input)); + } else { + value_r->last_change = st->st_mtime; + } + if (!add_type_prefix) { + i_stream_ref(input); + value_r->value_stream = input; + } else { + inputs[0] = i_stream_create_from_data(&type, 1); + inputs[1] = input; + inputs[2] = NULL; + value_r->value_stream = i_stream_create_concat(inputs); + } + sieve_script_unref(&script); + return 1; +} + +static int +sieve_attribute_get_active_script(struct mail_storage *storage, + struct sieve_storage *svstorage, + struct mail_attribute_value *value_r) +{ + struct sieve_script *script; + const char *errstr; + int ret; + + if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) { + if (ret == 0 && sieve_storage_active_script_get_last_change + (svstorage, &value_r->last_change) < 0) { + ret = -1; + } + if (ret < 0) + mail_storage_set_internal_error(storage); + return ret; + } + + if ((script=sieve_storage_active_script_get(svstorage)) == NULL) + return 0; + if ((ret=sieve_attribute_retrieve_script + (storage, svstorage, script, TRUE, value_r, &errstr)) < 0) { + mail_storage_set_critical(storage, + "Failed to access active sieve script: %s", errstr); + } + return ret; +} + +static int +sieve_attribute_get_default(struct mail_storage *storage, + struct sieve_storage *svstorage, + struct mail_attribute_value *value_r) +{ + const char *scriptname; + int ret; + + ret = sieve_storage_active_script_get_name(svstorage, &scriptname); + if (ret == 0) + return sieve_attribute_get_active_script(storage, svstorage, value_r); + + if (ret > 0) { + value_r->value = t_strdup_printf("%c%s", + MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK, scriptname); + if (sieve_storage_active_script_get_last_change + (svstorage, &value_r->last_change) < 0) + ret = -1; + } + if (ret < 0) + mail_storage_set_internal_error(storage); + return ret; +} + +static int +sieve_attribute_get_sieve(struct mail_storage *storage, const char *key, + struct mail_attribute_value *value_r) +{ + struct sieve_storage *svstorage; + struct sieve_script *script; + const char *scriptname, *errstr; + int ret; + + if (mail_sieve_user_init(storage->user, &svstorage) < 0) + return -1; + + if (strcmp(key, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT) == 0) + return sieve_attribute_get_default(storage, svstorage, value_r); + if (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)) != 0) + return 0; + if ((value_r->flags & MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS) == 0) { + mail_storage_set_error(storage, MAIL_ERROR_PARAMS, + "Sieve attributes are available only as streams"); + return -1; + } + scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); + script = sieve_storage_script_init(svstorage, scriptname); + if ((ret=sieve_attribute_retrieve_script + (storage, svstorage, script, FALSE, value_r, &errstr)) < 0) { + mail_storage_set_critical(storage, + "Failed to access sieve script '%s': %s", + scriptname, errstr); + } + return ret; +} + +static int +sieve_attribute_get(struct mailbox_transaction_context *t, + enum mail_attribute_type type, const char *key, + struct mail_attribute_value *value_r) +{ + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(t->box); + struct mail_user *user = t->box->storage->user; + int ret; + + if (t->box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE && + strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) == 0) { + + ret = sieve_attribute_get_sieve(t->box->storage, key, value_r); + if (ret >= 0 && user->mail_debug) { + struct tm *tm = localtime(&value_r->last_change); + char str[256]; + const char *timestamp = ""; + + if (strftime(str, sizeof(str), + " (last change: %Y-%m-%d %H:%M:%S)", tm) > 0) + timestamp = str; + + if (ret > 0) { + i_debug("doveadm-sieve: Retrieved value for key `%s'%s", + key, timestamp); + } else { + i_debug("doveadm-sieve: Value missing for key `%s'%s", + key, timestamp); + } + } + return ret; + } + return sbox->super.attribute_get(t, type, key, value_r); +} + +static int +sieve_attribute_iter_script_init(struct sieve_mailbox_attribute_iter *siter) +{ + struct mail_user *user = siter->iter.box->storage->user; + struct sieve_storage *svstorage; + + if (user->mail_debug) + i_debug("doveadm-sieve: Iterating Sieve mailbox attributes"); + + if (mail_sieve_user_init(user, &svstorage) < 0) + return -1; + + siter->sieve_list = sieve_storage_list_init(svstorage); + if (siter->sieve_list == NULL) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + siter->name = str_new(default_pool, 128); + str_append(siter->name, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); + return 0; +} + +static struct mailbox_attribute_iter * +sieve_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, + const char *prefix) +{ + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(box); + struct sieve_mailbox_attribute_iter *siter; + + siter = i_new(struct sieve_mailbox_attribute_iter, 1); + siter->iter.box = box; + siter->super = sbox->super.attribute_iter_init(box, type, prefix); + + if (box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE && + strncmp(prefix, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, + strlen(prefix)) == 0) { + if (sieve_attribute_iter_script_init(siter) < 0) + siter->failed = TRUE; + } + return &siter->iter; +} + +static const char * +sieve_attribute_iter_next_script(struct sieve_mailbox_attribute_iter *siter) +{ + struct mail_user *user = siter->iter.box->storage->user; + struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); + struct sieve_storage *svstorage = suser->sieve_storage; + const char *scriptname; + bool active; + int ret; + + /* Iterate through all scripts in sieve_dir */ + while ((scriptname = sieve_storage_list_next(siter->sieve_list, &active)) + != NULL) { + if (active) + siter->have_active = TRUE; + str_truncate(siter->name, strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)); + str_append(siter->name, scriptname); + return str_c(siter->name); + } + if (sieve_storage_list_deinit(&siter->sieve_list) < 0) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + siter->failed = TRUE; + return NULL; + } + + /* Check whether active script is a proper symlink or a regular file */ + if ((ret=sieve_storage_active_script_is_no_link(svstorage)) < 0) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return NULL; + } + + /* Regular file */ + if (ret > 0) + return MAILBOX_ATTRIBUTE_SIEVE_DEFAULT; + + /* Symlink or none active */ + return siter->have_active ? MAILBOX_ATTRIBUTE_SIEVE_DEFAULT : NULL; +} + +static const char * +sieve_attribute_iter_next(struct mailbox_attribute_iter *iter) +{ + struct sieve_mailbox_attribute_iter *siter = + (struct sieve_mailbox_attribute_iter *)iter; + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(iter->box); + struct mail_user *user = iter->box->storage->user; + const char *key; + + if (siter->sieve_list != NULL) { + if ((key = sieve_attribute_iter_next_script(siter)) != NULL) { + if (user->mail_debug) { + i_debug("doveadm-sieve: Iterating Sieve mailbox attribute: %s", key); + } + return key; + } + } + return sbox->super.attribute_iter_next(siter->super); +} + +static int +sieve_attribute_iter_deinit(struct mailbox_attribute_iter *iter) +{ + struct sieve_mailbox_attribute_iter *siter = + (struct sieve_mailbox_attribute_iter *)iter; + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(iter->box); + int ret = siter->failed ? -1 : 0; + + if (siter->super != NULL) { + if (sbox->super.attribute_iter_deinit(siter->super) < 0) + ret = -1; + } + if (siter->sieve_list != NULL) + (void)sieve_storage_list_deinit(&siter->sieve_list); + if (siter->name != NULL) + str_free(&siter->name); + i_free(siter); + return ret; +} + +static void sieve_mailbox_allocated(struct mailbox *box) +{ + struct mailbox_vfuncs *v = box->vlast; + union mailbox_module_context *sbox; + + /* attribute syncing is done via INBOX */ + if (!box->inbox_user) + return; + + sbox = p_new(box->pool, union mailbox_module_context, 1); + sbox->super = *v; + box->vlast = &sbox->super; + v->attribute_set = sieve_attribute_set; + v->attribute_get = sieve_attribute_get; + v->attribute_iter_init = sieve_attribute_iter_init; + v->attribute_iter_next = sieve_attribute_iter_next; + v->attribute_iter_deinit = sieve_attribute_iter_deinit; + MODULE_CONTEXT_SET_SELF(box, sieve_storage_module, sbox); +} + +static struct mail_storage_hooks doveadm_sieve_mail_storage_hooks = { + .mailbox_allocated = sieve_mailbox_allocated +}; + +void doveadm_sieve_plugin_init(struct module *module) +{ + mail_storage_hooks_add_forced(module, &doveadm_sieve_mail_storage_hooks); +} + +void doveadm_sieve_plugin_deinit(void) +{ + /* the hooks array is freed already */ + /*mail_storage_hooks_remove(&doveadm_sieve_mail_storage_hooks);*/ +} diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index c5d4174..77a1850 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -70,12 +70,13 @@ static const struct sieve_callbacks lda_sieve_callbacks = { static void *lda_sieve_smtp_open (const struct sieve_script_env *senv, const char *destination, - const char *return_path, FILE **file_r) + const char *return_path, struct ostream **output_r) { struct mail_deliver_context *dctx = (struct mail_deliver_context *) senv->script_context; - return (void *) smtp_client_open(dctx->set, destination, return_path, file_r); + return (void *)smtp_client_open + (dctx->set, destination, return_path, output_r); } static bool lda_sieve_smtp_close @@ -87,7 +88,8 @@ static bool lda_sieve_smtp_close } static int lda_sieve_reject_mail -(const struct sieve_script_env *senv, const char *recipient, const char *reason) +(const struct sieve_script_env *senv, const char *recipient, + const char *reason) { struct mail_deliver_context *dctx = (struct mail_deliver_context *) senv->script_context; @@ -215,7 +217,7 @@ static int lda_sieve_multiscript_get_scripts files = array_get(&script_files, &count); for ( i = 0; i < count; i++ ) { - struct sieve_script *script = sieve_script_create + struct sieve_script *script = sieve_script_create_open (svinst, files[i], NULL, ehandler, &error); if ( script == NULL ) { @@ -531,6 +533,12 @@ static int lda_sieve_multiscript_execute /* Open */ + if ( debug ) { + sieve_sys_debug + (svinst, "opening script %d of %d from %s", i+1, count, + sieve_script_location(script)); + } + if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) { compile_error = TRUE; break; @@ -538,9 +546,10 @@ static int lda_sieve_multiscript_execute /* Execute */ - if ( debug ) + if ( debug ) { sieve_sys_debug (svinst, "executing script from %s", sieve_get_source(sbin)); + } more = sieve_multiscript_run(mscript, sbin, ehandler, rtflags, final); @@ -613,6 +622,8 @@ static int lda_sieve_deliver_mail svenv.hostname = mdctx->set->hostname; svenv.base_dir = mdctx->dest_user->set->base_dir; svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + svenv.location = SIEVE_ENV_LOCATION_MDA; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING; svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug); @@ -643,7 +654,7 @@ static int lda_sieve_deliver_mail user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user); if ( user_location != NULL ) { - srctx.user_script = sieve_script_create_as + srctx.user_script = sieve_script_create_open_as (svinst, user_location, "main script", master_ehandler, &error); if ( srctx.user_script == NULL ) { @@ -667,7 +678,7 @@ static int lda_sieve_deliver_mail 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 + srctx.main_script = sieve_script_create_open_as (svinst, default_location, "main script", master_ehandler, &error); if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND && @@ -868,7 +879,7 @@ static int lda_sieve_deliver_mail * Plugin interface */ -const char *sieve_plugin_version = DOVECOT_VERSION; +const char *sieve_plugin_version = DOVECOT_ABI_VERSION; const char sieve_plugin_binary_dependency[] = "lda lmtp"; void sieve_plugin_init(void) diff --git a/src/plugins/sieve-extprograms/Makefile.am b/src/plugins/sieve-extprograms/Makefile.am new file mode 100644 index 0000000..945ad8b --- /dev/null +++ b/src/plugins/sieve-extprograms/Makefile.am @@ -0,0 +1,38 @@ +sieve_plugindir = $(moduledir)/sieve + +sieve_plugin_LTLIBRARIES = lib90_sieve_extprograms_plugin.la + +lib90_sieve_extprograms_plugin_la_LDFLAGS = -module -avoid-version + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/plugins/copy \ + -I$(top_srcdir)/src/lib-sieve/plugins/variables \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +commands = \ + cmd-pipe.c \ + cmd-filter.c \ + cmd-execute.c + +extensions = \ + ext-pipe.c \ + ext-filter.c \ + ext-execute.c + +lib90_sieve_extprograms_plugin_la_SOURCES = \ + script-client-local.c \ + script-client-remote.c \ + script-client.c \ + $(commands) \ + $(extensions) \ + sieve-extprograms-common.c \ + sieve-extprograms-plugin.c + +noinst_HEADERS = \ + script-client.h \ + script-client-private.h \ + sieve-extprograms-common.h \ + sieve-extprograms-plugin.h + diff --git a/src/plugins/sieve-extprograms/cmd-execute.c b/src/plugins/sieve-extprograms/cmd-execute.c new file mode 100644 index 0000000..0a71f36 --- /dev/null +++ b/src/plugins/sieve-extprograms/cmd-execute.c @@ -0,0 +1,452 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "sieve-extprograms-common.h" + +/* Execute command + * + * Syntax: + * "execute" [":input" / ":pipe"] + * [":output" ] + * [] + * + */ + +static bool cmd_execute_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_execute_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_execute = { + "execute", + SCT_HYBRID, + -1, /* We check positional arguments ourselves */ + 0, FALSE, FALSE, + cmd_execute_registered, + NULL, + sieve_extprogram_command_validate, + NULL, + cmd_execute_generate, + NULL, +}; + +/* + * Tagged arguments + */ + +static bool cmd_execute_validate_input_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool cmd_execute_generate_input_tag +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +static bool cmd_execute_validate_output_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def execute_input_tag = { + "input", + NULL, + cmd_execute_validate_input_tag, + NULL, NULL, + cmd_execute_generate_input_tag +}; + +static const struct sieve_argument_def execute_pipe_tag = { + "pipe", + NULL, + cmd_execute_validate_input_tag, + NULL, NULL, + cmd_execute_generate_input_tag +}; + +static const struct sieve_argument_def execute_output_tag = { + "output", + NULL, + cmd_execute_validate_output_tag, + NULL, NULL, NULL +}; + + +/* + * Execute operation + */ + +static bool cmd_execute_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_execute_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_execute_operation = { + "EXECUTE", &execute_extension, + 0, + cmd_execute_operation_dump, + cmd_execute_operation_execute +}; + +/* Codes for optional operands */ + +enum cmd_execute_optional { + OPT_END, + OPT_INPUT, + OPT_OUTPUT +}; + +/* + * Tag validation + */ + +static bool cmd_execute_validate_input_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + if ( (bool) cmd->data ) { + sieve_argument_validate_error(valdtr, *arg, + "multiple :input or :pipe arguments specified for the %s %s", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + cmd->data = (void *) TRUE; + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + if ( sieve_argument_is(tag, execute_input_tag) ) { + /* Check syntax: + * :input + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, FALSE) ) { + return FALSE; + } + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + } + + return TRUE; +} + +static bool cmd_execute_validate_output_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *) cmd->ext->context; + + if ( ext_config == NULL || ext_config->var_ext == NULL || + !sieve_ext_variables_is_active(ext_config->var_ext, valdtr) ) { + sieve_argument_validate_error(valdtr,*arg, + "the %s %s only allows for the specification of an " + ":output argument when the variables extension is active", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + if ( !sieve_variable_argument_activate + (ext_config->var_ext, valdtr, cmd, *arg, TRUE) ) + return FALSE; + + (*arg)->argument->id_code = tag->argument->id_code; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_execute_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &execute_input_tag, OPT_INPUT); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &execute_pipe_tag, OPT_INPUT); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &execute_output_tag, OPT_OUTPUT); + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_execute_generate_input_tag +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + if ( arg->parameters == NULL ) { + sieve_opr_omitted_emit(cgenv->sblock); + return TRUE; + } + + return sieve_generate_argument_parameters(cgenv, cmd, arg); +} + +static bool cmd_execute_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_execute_operation); + + /* Emit is_test flag */ + sieve_binary_emit_byte(cgenv->sblock, ( cmd->ast_node->type == SAT_TEST )); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + /* Emit a placeholder when the argument is missing */ + if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) + sieve_opr_omitted_emit(cgenv->sblock); + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_execute_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + unsigned int is_test = 0; + + /* Read is_test flag */ + if ( !sieve_binary_read_byte(denv->sblock, address, &is_test) ) + return FALSE; + + sieve_code_dumpf(denv, "EXECUTE (%s)", (is_test ? "test" : "command")); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + bool opok = TRUE; + + if ( (opt=sieve_action_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_INPUT: + opok = sieve_opr_string_dump_ex(denv, address, "input", "PIPE"); + break; + case OPT_OUTPUT: + opok = sieve_opr_string_dump(denv, address, "output"); + break; + default: + return FALSE; + } + + if ( !opok ) return FALSE; + } + + if ( !sieve_opr_string_dump(denv, address, "program-name") ) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int cmd_execute_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + int opt_code = 0; + unsigned int is_test = 0; + struct sieve_stringlist *args_list = NULL; + string_t *pname = NULL, *input = NULL; + struct sieve_variable_storage *var_storage = NULL; + unsigned int var_index; + bool have_input = FALSE; + const char *program_name = NULL; + const char *const *args = NULL; + enum sieve_error error = SIEVE_ERROR_NONE; + buffer_t *outbuf = NULL; + struct sieve_extprogram *sprog = NULL; + int ret; + + /* + * Read operands + */ + + /* The is_test flag */ + if ( !sieve_binary_read_byte(renv->sblock, address, &is_test) ) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_action_opr_optional_read + (renv, address, &opt_code, &ret, &slist)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_INPUT: + if ( (ret=sieve_opr_string_read_ex + (renv, address, "input", TRUE, &input, NULL)) <= 0 ) + return ret; + have_input = TRUE; + break; + case OPT_OUTPUT: + if ( (ret=sieve_variable_operand_read + (renv, address, "output", &var_storage, &var_index)) <= 0 ) + return ret; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( ret <= 0 ) return ret; + } + + /* Fixed operands */ + + if ( (ret=sieve_extprogram_command_read_operands + (renv, address, &pname, &args_list)) <= 0 ) + return ret; + + program_name = str_c(pname); + if ( args_list != NULL && + sieve_stringlist_read_all(args_list, pool_datastack_create(), &args) < 0 ) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "execute action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute program `%s'", str_sanitize(program_name, 128)); + + sprog = sieve_extprogram_create + (this_ext, renv->scriptenv, renv->msgdata, "execute", program_name, args, + &error); + if ( sprog != NULL ) { + if ( var_storage != NULL ) { + // FIXME: limit output size + struct ostream *outdata; + + outbuf = buffer_create_dynamic(pool_datastack_create(), 1024); + outdata = o_stream_create_buffer(outbuf); + sieve_extprogram_set_output(sprog, outdata); + o_stream_unref(&outdata); + } + + if ( input == NULL && have_input ) { + ret = sieve_extprogram_set_input_mail + (sprog, sieve_message_get_mail(renv->msgctx)); + } else if ( input != NULL ) { + struct istream *indata = + i_stream_create_from_data(str_data(input), str_len(input)); + sieve_extprogram_set_input(sprog, indata); + i_stream_unref(&indata); + ret = 1; + } + + if ( ret >= 0 ) + ret = sieve_extprogram_run(sprog); + sieve_extprogram_destroy(&sprog); + } else { + ret = -1; + } + + if ( ret > 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "executed program successfully"); + + if ( var_storage != NULL ) { + string_t *var; + + if ( sieve_variable_get_modifiable(var_storage, var_index, &var) ) { + str_truncate(var, 0); + str_append_str(var, outbuf); + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "assigned output variable"); + } // FIXME: handle failure + } + + } else if ( ret < 0 ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) { + sieve_runtime_error(renv, NULL, + "execute action: program `%s' not found", + str_sanitize(program_name, 80)); + } else { + sieve_extprogram_exec_error(renv->ehandler, + sieve_runtime_get_full_command_location(renv), + "execute action: failed to execute to program `%s'", + str_sanitize(program_name, 80)); + } + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute action: program indicated false result"); + } + + if ( outbuf != NULL ) + buffer_free(&outbuf); + + if ( is_test ) + sieve_interpreter_set_test_result(renv->interp, ( ret > 0 )); + + return SIEVE_EXEC_OK; +} + diff --git a/src/plugins/sieve-extprograms/cmd-filter.c b/src/plugins/sieve-extprograms/cmd-filter.c new file mode 100644 index 0000000..c57935b --- /dev/null +++ b/src/plugins/sieve-extprograms/cmd-filter.c @@ -0,0 +1,283 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" +#include "ostream.h" +#include "safe-mkstemp.h" +#include "mail-user.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "sieve-extprograms-common.h" + +/* Filter command + * + * Syntax: + * "filter" [] + * + */ + +static bool cmd_filter_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_filter = { + "filter", + SCT_HYBRID, + -1, /* We check positional arguments ourselves */ + 0, FALSE, FALSE, + NULL, NULL, + sieve_extprogram_command_validate, + NULL, + cmd_filter_generate, + NULL, +}; + +/* + * Filter operation + */ + +static bool cmd_filter_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_filter_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_filter_operation = { + "FILTER", &filter_extension, + 0, + cmd_filter_operation_dump, + cmd_filter_operation_execute +}; + +/* + * Code generation + */ + +static bool cmd_filter_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_filter_operation); + + /* Emit is_test flag */ + sieve_binary_emit_byte(cgenv->sblock, ( cmd->ast_node->type == SAT_TEST )); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + /* Emit a placeholder when the argument is missing */ + if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) + sieve_opr_omitted_emit(cgenv->sblock); + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_filter_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int is_test = 0; + + /* Read is_test flag */ + if ( !sieve_binary_read_byte(denv->sblock, address, &is_test) ) + return FALSE; + + sieve_code_dumpf(denv, "FILTER (%s)", (is_test ? "test" : "command")); + sieve_code_descend(denv); + + /* Dump optional operands */ + if ( sieve_action_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + if ( !sieve_opr_string_dump(denv, address, "program-name") ) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int cmd_filter_get_tempfile +(const struct sieve_runtime_env *renv) +{ + struct sieve_instance *svinst = renv->svinst; + struct mail_user *mail_user = renv->scriptenv->user; + string_t *path; + int fd; + + path = t_str_new(128); + mail_user_set_get_temp_prefix(path, mail_user->set); + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) { + sieve_sys_error(svinst, "filter action: " + "safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + + /* We just want the fd, unlink it */ + if (unlink(str_c(path)) < 0) { + /* Shouldn't happen.. */ + sieve_sys_error(svinst, "filter action: " + "unlink(%s) failed: %m", str_c(path)); + if ( close(fd) < 0 ) { + sieve_sys_error(svinst, "filter action: " + "close(%s) failed after error: %m", str_c(path)); + } + return -1; + } + + return fd; +} + +static int cmd_filter_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + unsigned int is_test = 0; + struct sieve_stringlist *args_list = NULL; + enum sieve_error error = SIEVE_ERROR_NONE; + string_t *pname = NULL; + const char *program_name = NULL; + const char *const *args = NULL; + int tmp_fd = -1; + int ret; + + /* + * Read operands + */ + + /* The is_test flag */ + + if ( !sieve_binary_read_byte(renv->sblock, address, &is_test) ) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Optional operands */ + + if ( sieve_action_opr_optional_read(renv, address, NULL, &ret, NULL) != 0 ) + return ret; + + /* Fixed operands */ + + if ( (ret=sieve_extprogram_command_read_operands + (renv, address, &pname, &args_list)) <= 0 ) + return ret; + + program_name = str_c(pname); + if ( args_list != NULL && + sieve_stringlist_read_all(args_list, pool_datastack_create(), &args) < 0 ) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "filter action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute program `%s'", str_sanitize(program_name, 128)); + + ret = 1; + if ( (tmp_fd=cmd_filter_get_tempfile(renv)) < 0 ) { + ret = -1; + } + + if ( ret > 0 ) { + struct sieve_extprogram *sprog = sieve_extprogram_create + (this_ext, renv->scriptenv, renv->msgdata, "filter", program_name, args, + &error); + + if ( sprog != NULL && sieve_extprogram_set_input_mail + (sprog, sieve_message_get_mail(renv->msgctx)) >= 0 ) { + struct ostream *outdata = + o_stream_create_fd(tmp_fd, 0, FALSE); + sieve_extprogram_set_output(sprog, outdata); + o_stream_unref(&outdata); + + ret = sieve_extprogram_run(sprog); + } else { + ret = -1; + } + + if ( sprog != NULL ) + sieve_extprogram_destroy(&sprog); + } + + if ( ret > 0 ) { + struct istream *newmsg; + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "executed program successfully"); + + newmsg = i_stream_create_fd(tmp_fd, (size_t)-1, TRUE); + + if ( (ret=sieve_message_substitute(renv->msgctx, newmsg)) >= 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "changed message"); + } else { + sieve_runtime_critical(renv, NULL, "filter action", + "filter action: failed to substitute message"); + } + + i_stream_unref(&newmsg); + + } else { + if ( tmp_fd >= 0 ) { + if ( close(tmp_fd) < 0 ) { + sieve_sys_error + (renv->svinst, "filter action: close(temp_file) failed: %m"); + } + } + + if ( ret < 0 ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) { + sieve_runtime_error(renv, NULL, + "filter action: program `%s' not found", + str_sanitize(program_name, 80)); + } else { + sieve_extprogram_exec_error(renv->ehandler, + sieve_runtime_get_full_command_location(renv), + "filter action: failed to execute to program `%s'", + str_sanitize(program_name, 80)); + } + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "filter action: program indicated false result"); + } + } + + if ( is_test ) + sieve_interpreter_set_test_result(renv->interp, ( ret > 0 )); + + return SIEVE_EXEC_OK; +} + diff --git a/src/plugins/sieve-extprograms/cmd-pipe.c b/src/plugins/sieve-extprograms/cmd-pipe.c new file mode 100644 index 0000000..94f05ce --- /dev/null +++ b/src/plugins/sieve-extprograms/cmd-pipe.c @@ -0,0 +1,388 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-extprograms-common.h" + +/* Pipe command + * + * Syntax: + * pipe [":copy"] [":try"] [] + * + */ + +static bool cmd_pipe_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_pipe_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_pipe = { + "pipe", + SCT_COMMAND, + -1, /* We check positional arguments ourselves */ + 0, FALSE, FALSE, + cmd_pipe_registered, + NULL, + sieve_extprogram_command_validate, + NULL, + cmd_pipe_generate, + NULL, +}; + +/* + * Tagged arguments + */ + +static const struct sieve_argument_def pipe_try_tag = { + "try", + NULL, NULL, NULL, NULL, NULL +}; + +/* + * Pipe operation + */ + +static bool cmd_pipe_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_pipe_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_pipe_operation = { + "PIPE", &pipe_extension, 0, + cmd_pipe_operation_dump, + cmd_pipe_operation_execute +}; + +/* Codes for optional operands */ + +enum cmd_pipe_optional { + OPT_END, + OPT_TRY +}; + +/* + * Pipe action + */ + +/* Forward declarations */ + +static int act_pipe_check_duplicate + (const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void act_pipe_print + (const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static bool act_pipe_commit + (const struct sieve_action *action, + const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); + +/* Action object */ + +const struct sieve_action_def act_pipe = { + "pipe", + SIEVE_ACTFLAG_TRIES_DELIVER, + NULL, + act_pipe_check_duplicate, + NULL, + act_pipe_print, + NULL, NULL, + act_pipe_commit, + NULL +}; + +/* Action context information */ + +struct ext_pipe_action { + const char *program_name; + const char * const *args; + bool try; +}; + +/* + * Command registration + */ + +static bool cmd_pipe_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &pipe_try_tag, OPT_TRY); + + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_pipe_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_pipe_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + /* Emit a placeholder when the argument is missing */ + if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) + sieve_opr_omitted_emit(cgenv->sblock); + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_pipe_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "PIPE"); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + bool opok = TRUE; + + if ( (opt=sieve_action_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_TRY: + sieve_code_dumpf(denv, "try"); + break; + default: + return FALSE; + } + + if ( !opok ) return FALSE; + } + + if ( !sieve_opr_string_dump(denv, address, "program-name") ) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int cmd_pipe_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + struct ext_pipe_action *act; + pool_t pool; + int opt_code = 0; + struct sieve_stringlist *args_list = NULL; + string_t *pname = NULL; + bool try = FALSE; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_action_opr_optional_read + (renv, address, &opt_code, &ret, &slist)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_TRY: + try = TRUE; + ret = SIEVE_EXEC_OK; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( ret <= 0 ) return ret; + } + + /* Fixed operands */ + + if ( (ret=sieve_extprogram_command_read_operands + (renv, address, &pname, &args_list)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "pipe action"); + + /* Compose action */ + + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct ext_pipe_action, 1); + + if ( args_list != NULL && + sieve_stringlist_read_all(args_list, pool, &act->args) < 0 ) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + act->program_name = p_strdup(pool, str_c(pname)); + act->try = try; + + if ( sieve_result_add_action + (renv, this_ext, &act_pipe, slist, (void *) act, 0, TRUE) < 0 ) { + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} + +/* + * Action + */ + +/* Runtime verification */ + +static int act_pipe_check_duplicate +(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + struct ext_pipe_action *new_act, *old_act; + + if ( act->context == NULL || act_other->context == NULL ) + return 0; + + new_act = (struct ext_pipe_action *) act->context; + old_act = (struct ext_pipe_action *) act_other->context; + + if ( strcmp(new_act->program_name, old_act->program_name) == 0 ) { + sieve_runtime_error(renv, act->location, + "duplicate pipe \"%s\" action not allowed " + "(previously triggered one was here: %s)", + new_act->program_name, act_other->location); + return -1; + } + + return 0; +} + +/* Result printing */ + +static void act_pipe_print +(const struct sieve_action *action, const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + const struct ext_pipe_action *act = + (const struct ext_pipe_action *) action->context; + + sieve_result_action_printf + ( rpenv, "pipe message to external program '%s':", act->program_name); + + /* Print main method parameters */ + + sieve_result_printf + ( rpenv, " => try : %s\n", (act->try ? "yes" : "no") ); + + /* FIXME: print args */ + + /* Finish output with an empty line */ + + sieve_result_printf(rpenv, "\n"); +} + +/* Result execution */ + +static bool act_pipe_commit +(const struct sieve_action *action, const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED, bool *keep) +{ + const struct ext_pipe_action *act = + (const struct ext_pipe_action *) action->context; + enum sieve_error error = SIEVE_ERROR_NONE; + struct mail *mail = ( action->mail != NULL ? + action->mail : sieve_message_get_mail(aenv->msgctx) ); + struct sieve_extprogram *sprog; + int ret; + + sprog = sieve_extprogram_create + (action->ext, aenv->scriptenv, aenv->msgdata, "pipe", + act->program_name, act->args, &error); + if ( sprog != NULL && sieve_extprogram_set_input_mail(sprog, mail) >= 0 ) { + ret = sieve_extprogram_run(sprog); + } else { + ret = -1; + } + if ( sprog != NULL ) + sieve_extprogram_destroy(&sprog); + + if ( ret > 0 ) { + sieve_result_global_log(aenv, "pipe action: " + "piped message to program `%s'", str_sanitize(act->program_name, 128)); + } else { + if ( ret < 0 ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) { + sieve_result_error(aenv, "pipe action: " + "failed to pipe message to program: program `%s' not found", + str_sanitize(act->program_name, 80)); + } else { + sieve_extprogram_exec_error(aenv->ehandler, NULL, + "pipe action: failed to pipe message to program `%s'", + str_sanitize(act->program_name, 80)); + } + } else { + sieve_extprogram_exec_error(aenv->ehandler, NULL, + "pipe action: failed to execute to program `%s'", + str_sanitize(act->program_name, 80)); + } + + if ( act->try ) return TRUE; + + return FALSE; + } + + *keep = FALSE; + return TRUE; +} + + + + + + + diff --git a/src/plugins/sieve-extprograms/ext-execute.c b/src/plugins/sieve-extprograms/ext-execute.c new file mode 100644 index 0000000..057ac18 --- /dev/null +++ b/src/plugins/sieve-extprograms/ext-execute.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.execute + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: spec-bosch-sieve-extprograms + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" + +#include "sieve-extprograms-common.h" + +/* + * Extension + */ + +static bool ext_execute_load(const struct sieve_extension *ext, void **context); +static void ext_execute_unload(const struct sieve_extension *ext); +static bool ext_execute_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def execute_extension = { + .name = "vnd.dovecot.execute", + .load = ext_execute_load, + .unload = ext_execute_unload, + .validator_load = ext_execute_validator_load, + SIEVE_EXT_DEFINE_OPERATION(cmd_execute_operation) +}; + +/* + * Context + */ + +static bool ext_execute_load(const struct sieve_extension *ext, void **context) +{ + if ( *context != NULL ) { + ext_execute_unload(ext); + *context = NULL; + } + + *context = (void *)sieve_extprograms_config_init(ext); + return TRUE; +} + +static void ext_execute_unload(const struct sieve_extension *ext) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)ext->context; + + if ( ext_config == NULL ) return; + + sieve_extprograms_config_deinit(&ext_config); +} + +/* + * Validation + */ + +static bool ext_execute_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &cmd_execute); + + return TRUE; +} diff --git a/src/plugins/sieve-extprograms/ext-filter.c b/src/plugins/sieve-extprograms/ext-filter.c new file mode 100644 index 0000000..572dd31 --- /dev/null +++ b/src/plugins/sieve-extprograms/ext-filter.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.filter + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: spec-bosch-sieve-extprograms + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" + +#include "sieve-extprograms-common.h" + +/* + * Extension + */ + +static bool ext_filter_load(const struct sieve_extension *ext, void **context); +static void ext_filter_unload(const struct sieve_extension *ext); +static bool ext_filter_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def filter_extension = { + .name = "vnd.dovecot.filter", + .load = ext_filter_load, + .unload = ext_filter_unload, + .validator_load = ext_filter_validator_load, + SIEVE_EXT_DEFINE_OPERATION(cmd_filter_operation), +}; + +/* + * Context + */ + +static bool ext_filter_load(const struct sieve_extension *ext, void **context) +{ + if ( *context != NULL ) { + ext_filter_unload(ext); + *context = NULL; + } + + *context = (void *)sieve_extprograms_config_init(ext); + return TRUE; +} + +static void ext_filter_unload(const struct sieve_extension *ext) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)ext->context; + + if ( ext_config == NULL ) return; + + sieve_extprograms_config_deinit(&ext_config); +} + +/* + * Validation + */ + +static bool ext_filter_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &cmd_filter); + + return TRUE; +} diff --git a/src/plugins/sieve-extprograms/ext-pipe.c b/src/plugins/sieve-extprograms/ext-pipe.c new file mode 100644 index 0000000..aa9203f --- /dev/null +++ b/src/plugins/sieve-extprograms/ext-pipe.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.pipe + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: spec-bosch-sieve-extprograms + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" + +#include "sieve-extprograms-common.h" + +/* + * Extension + */ + +static bool ext_pipe_load(const struct sieve_extension *ext, void **context); +static void ext_pipe_unload(const struct sieve_extension *ext); +static bool ext_pipe_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def pipe_extension = { + .name = "vnd.dovecot.pipe", + .load = ext_pipe_load, + .unload = ext_pipe_unload, + .validator_load = ext_pipe_validator_load, + SIEVE_EXT_DEFINE_OPERATION(cmd_pipe_operation), +}; + +/* + * Context + */ + +static bool ext_pipe_load(const struct sieve_extension *ext, void **context) +{ + if ( *context != NULL ) { + ext_pipe_unload(ext); + *context = NULL; + } + + *context = (void *)sieve_extprograms_config_init(ext); + return TRUE; +} + +static void ext_pipe_unload(const struct sieve_extension *ext) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)ext->context; + + if ( ext_config == NULL ) return; + + sieve_extprograms_config_deinit(&ext_config); +} + +/* + * Validation + */ + +static bool ext_pipe_validator_extension_validate + (const struct sieve_extension *ext, struct sieve_validator *valdtr, + void *context, struct sieve_ast_argument *require_arg); + +const struct sieve_validator_extension pipe_validator_extension = { + &pipe_extension, + ext_pipe_validator_extension_validate, + NULL +}; + +static bool ext_pipe_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &cmd_pipe); + + /* Register extension to validator */ + sieve_validator_extension_register + (valdtr, ext, &pipe_validator_extension, NULL); + + return TRUE; +} + +static bool ext_pipe_validator_extension_validate +(const struct sieve_extension *ext, struct sieve_validator *valdtr, + void *context ATTR_UNUSED, struct sieve_ast_argument *require_arg ATTR_UNUSED) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *) ext->context; + + if ( ext_config != NULL && ext_config->copy_ext != NULL ) { + /* Register :copy command tag */ + sieve_ext_copy_register_tag + (valdtr, ext_config->copy_ext, cmd_pipe.identifier); + } + return TRUE; +} + + diff --git a/src/plugins/sieve-extprograms/script-client-local.c b/src/plugins/sieve-extprograms/script-client-local.c new file mode 100644 index 0000000..6d5bb6c --- /dev/null +++ b/src/plugins/sieve-extprograms/script-client-local.c @@ -0,0 +1,300 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "env-util.h" +#include "execv-const.h" +#include "array.h" +#include "net.h" +#include "istream.h" +#include "ostream.h" + +#include "script-client-private.h" + +#include +#include +#include +#include +#include +#include + + +struct script_client_local { + struct script_client client; + + pid_t pid; +}; + +static void exec_child +(const char *bin_path, const char *const *args, const char *const *envs, + int in_fd, int out_fd) +{ + ARRAY_TYPE(const_string) exec_args; + + if ( in_fd < 0 ) { + in_fd = open("/dev/null", O_RDONLY); + + if ( in_fd == -1 ) + i_fatal("open(/dev/null) failed: %m"); + } + + if ( out_fd < 0 ) { + out_fd = open("/dev/null", O_WRONLY); + + if ( out_fd == -1 ) + i_fatal("open(/dev/null) failed: %m"); + } + + if ( dup2(in_fd, STDIN_FILENO) < 0 ) + i_fatal("dup2(stdin) failed: %m"); + if ( dup2(out_fd, STDOUT_FILENO) < 0 ) + i_fatal("dup2(stdout) failed: %m"); + + /* Close all fds */ + if ( close(in_fd) < 0 ) + i_error("close(in_fd) failed: %m"); + if ( (out_fd != in_fd) && close(out_fd) < 0 ) + i_error("close(out_fd) failed: %m"); + + t_array_init(&exec_args, 16); + array_append(&exec_args, &bin_path, 1); + if ( args != NULL ) { + for (; *args != NULL; args++) + array_append(&exec_args, args, 1); + } + (void)array_append_space(&exec_args); + + env_clean(); + if ( envs != NULL ) { + for (; *envs != NULL; envs++) + env_put(*envs); + } + + args = array_idx(&exec_args, 0); + execvp_const(args[0], args); +} + +static int script_client_local_connect +(struct script_client *sclient) +{ + struct script_client_local *slclient = + (struct script_client_local *) sclient; + int fd[2] = { -1, -1 }; + + if ( sclient->input != NULL || sclient->output != NULL ) { + if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) { + i_error("socketpair() failed: %m"); + return -1; + } + } + + if ( (slclient->pid = fork()) == (pid_t)-1 ) { + i_error("fork() failed: %m"); + if ( fd[0] >= 0 && close(fd[0]) < 0 ) { + i_error("close(pipe_fd[0]) failed: %m"); + } + if ( fd[1] >= 0 && close(fd[1]) < 0 ) { + i_error("close(pipe_fd[1]) failed: %m"); + } + return -1; + } + + if ( slclient->pid == 0 ) { + unsigned int count; + const char *const *envs = NULL; + + /* child */ + if ( fd[1] >= 0 && close(fd[1]) < 0 ) { + i_error("close(pipe_fd[1]) failed: %m"); + } + + if ( array_is_created(&sclient->envs) ) + envs = array_get(&sclient->envs, &count); + + exec_child(sclient->path, sclient->args, envs, + ( sclient->input != NULL ? fd[0] : -1 ), + ( sclient->output != NULL ? fd[0] : -1 )); + i_unreached(); + } + + /* parent */ + if ( fd[0] >= 0 && close(fd[0]) < 0 ) { + i_error("close(pipe_fd[0]) failed: %m"); + } + + if ( fd[1] >= 0 ) { + net_set_nonblock(fd[1], TRUE); + sclient->fd_in = ( sclient->output != NULL ? fd[1] : -1 ); + sclient->fd_out = ( sclient->input != NULL ? fd[1] : -1 ); + } + script_client_init_streams(sclient); + return script_client_script_connected(sclient); +} + +static int script_client_local_close_output(struct script_client *sclient) +{ + /* Shutdown output; script stdin will get EOF */ + if ( sclient->fd_out >= 0 && shutdown(sclient->fd_out, SHUT_WR) < 0 ) { + i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path); + return -1; + } + sclient->fd_out = -1; + return 1; +} + +static int script_client_local_disconnect +(struct script_client *sclient, bool force) +{ + struct script_client_local *slclient = + (struct script_client_local *) sclient; + pid_t pid = slclient->pid; + time_t runtime, timeout = 0; + int status; + + i_assert( pid >= 0 ); + slclient->pid = -1; + + /* Calculate timeout */ + runtime = ioloop_time - sclient->start_time; + if ( !force && sclient->set->input_idle_timeout_secs > 0 && + runtime < sclient->set->input_idle_timeout_secs ) + timeout = sclient->set->input_idle_timeout_secs - runtime; + + if ( sclient->debug ) { + i_debug("waiting for program `%s' to finish after %llu seconds", + sclient->path, (unsigned long long int)runtime); + } + + /* Wait for child to exit */ + force = force || + (timeout == 0 && sclient->set->input_idle_timeout_secs > 0); + if ( !force ) + alarm(timeout); + if ( force || waitpid(pid, &status, 0) < 0 ) { + if ( !force && errno != EINTR ) { + i_error("waitpid(%s) failed: %m", sclient->path); + (void)kill(pid, SIGKILL); + return -1; + } + + /* Timed out */ + force = TRUE; + if ( sclient->error == SCRIPT_CLIENT_ERROR_NONE ) + sclient->error = SCRIPT_CLIENT_ERROR_RUN_TIMEOUT; + if ( sclient->debug ) { + i_debug("program `%s' execution timed out after %llu seconds: " + "sending TERM signal", sclient->path, + (unsigned long long int)sclient->set->input_idle_timeout_secs); + } + + /* Kill child gently first */ + if ( kill(pid, SIGTERM) < 0 ) { + i_error("failed to send SIGTERM signal to program `%s'", sclient->path); + (void)kill(pid, SIGKILL); + return -1; + } + + /* Wait for it to die (give it some more time) */ + alarm(5); + if ( waitpid(pid, &status, 0) < 0 ) { + if ( errno != EINTR ) { + i_error("waitpid(%s) failed: %m", sclient->path); + (void)kill(pid, SIGKILL); + return -1; + } + + /* Timed out again */ + if ( sclient->debug ) { + i_debug("program `%s' execution timed out: sending KILL signal", + sclient->path); + } + + /* Kill it brutally now */ + if ( kill(pid, SIGKILL) < 0 ) { + i_error("failed to send SIGKILL signal to program `%s'", + sclient->path); + return -1; + } + + /* Now it will die immediately */ + if ( waitpid(pid, &status, 0) < 0 ) { + i_error("waitpid(%s) failed: %m", sclient->path); + return -1; + } + } + } + + /* Evaluate child exit status */ + sclient->exit_code = -1; + if ( WIFEXITED(status) ) { + /* Exited */ + int exit_code = WEXITSTATUS(status); + + if ( exit_code != 0 ) { + i_info("program `%s' terminated with non-zero exit code %d", + sclient->path, exit_code); + sclient->exit_code = 0; + return 0; + } + + sclient->exit_code = 1; + return 1; + + } else if ( WIFSIGNALED(status) ) { + /* Killed with a signal */ + + if ( force ) { + i_error("program `%s' was forcibly terminated with signal %d", + sclient->path, WTERMSIG(status)); + } else { + i_error("program `%s' terminated abnormally, signal %d", + sclient->path, WTERMSIG(status)); + } + return -1; + + } else if ( WIFSTOPPED(status) ) { + /* Stopped */ + i_error("program `%s' stopped, signal %d", + sclient->path, WSTOPSIG(status)); + return -1; + } + + /* Something else */ + i_error("program `%s' terminated abnormally, return status %d", + sclient->path, status); + return -1; +} + +static void script_client_local_failure +(struct script_client *sclient, enum script_client_error error) +{ + switch ( error ) { + case SCRIPT_CLIENT_ERROR_RUN_TIMEOUT: + i_error("program `%s' execution timed out (> %d secs)", + sclient->path, sclient->set->input_idle_timeout_secs); + break; + default: + break; + } +} + +struct script_client *script_client_local_create +(const char *bin_path, const char *const *args, + const struct script_client_settings *set) +{ + struct script_client_local *sclient; + pool_t pool; + + pool = pool_alloconly_create("script client local", 1024); + sclient = i_new(struct script_client_local, 1); + script_client_init(&sclient->client, pool, bin_path, args, set); + sclient->client.connect = script_client_local_connect; + sclient->client.close_output = script_client_local_close_output; + sclient->client.disconnect = script_client_local_disconnect; + sclient->client.failure = script_client_local_failure; + sclient->pid = -1; + + return &sclient->client; +} + diff --git a/src/plugins/sieve-extprograms/script-client-private.h b/src/plugins/sieve-extprograms/script-client-private.h new file mode 100644 index 0000000..9396b41 --- /dev/null +++ b/src/plugins/sieve-extprograms/script-client-private.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SCRIPT_CLIENT_PRIVATE_H +#define __SCRIPT_CLIENT_PRIVATE_H + +#include "script-client.h" + +enum script_client_error { + SCRIPT_CLIENT_ERROR_NONE, + SCRIPT_CLIENT_ERROR_CONNECT_TIMEOUT, + SCRIPT_CLIENT_ERROR_RUN_TIMEOUT, + SCRIPT_CLIENT_ERROR_IO, + SCRIPT_CLIENT_ERROR_UNKNOWN +}; + +struct script_client { + pool_t pool; + const struct script_client_settings *set; + + char *path; + const char **args; + ARRAY_TYPE(const_string) envs; + + int fd_in, fd_out; + struct io *io; + struct ioloop *ioloop; + struct timeout *to; + time_t start_time; + + struct istream *input, *script_input; + struct ostream *output, *script_output; + + enum script_client_error error; + int exit_code; + + int (*connect)(struct script_client *sclient); + int (*close_output)(struct script_client *sclient); + int (*disconnect)(struct script_client *sclient, bool force); + void (*failure) + (struct script_client *sclient, enum script_client_error error); + + unsigned int debug:1; + unsigned int disconnected:1; +}; + +void script_client_init + (struct script_client *sclient, pool_t pool, const char *path, + const char *const *args, const struct script_client_settings *set); + +void script_client_init_streams(struct script_client *sclient); + +int script_client_script_connected(struct script_client *sclient); + +void script_client_fail + (struct script_client *sclient, enum script_client_error error); + +#endif + diff --git a/src/plugins/sieve-extprograms/script-client-remote.c b/src/plugins/sieve-extprograms/script-client-remote.c new file mode 100644 index 0000000..f437e04 --- /dev/null +++ b/src/plugins/sieve-extprograms/script-client-remote.c @@ -0,0 +1,319 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "net.h" +#include "write-full.h" +#include "eacces-error.h" +#include "istream-private.h" +#include "ostream.h" + +#include "script-client-private.h" + +#include +#include +#include + +/* + * Script client input stream + */ + +struct script_client_istream { + struct istream_private istream; + + struct stat statbuf; + + struct script_client *client; +}; + +static void script_client_istream_destroy(struct iostream_private *stream) +{ + struct script_client_istream *scstream = + (struct script_client_istream *)stream; + + i_stream_unref(&scstream->istream.parent); +} + +static ssize_t script_client_istream_read(struct istream_private *stream) +{ + struct script_client_istream *scstream = + (struct script_client_istream *)stream; + size_t pos, reserved; + ssize_t ret; + + i_stream_skip(stream->parent, stream->skip); + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(stream->parent, &pos); + + if ( stream->buffer != NULL && pos >= 1 ) { + /* retain/hide potential return code at end of buffer */ + reserved = ( stream->buffer[pos-1] == '\n' && pos > 1 ? 2 : 1 ); + pos -= reserved; + } + + if (pos > stream->pos) { + ret = 0; + } else if ( stream->parent->eof ) { + stream->istream.eof = TRUE; + ret = -1; + } else do { + if ((ret = i_stream_read(stream->parent)) == -2) + return -2; /* input buffer full */ + + stream->istream.stream_errno = stream->parent->stream_errno; + stream->buffer = i_stream_get_data(stream->parent, &pos); + + if ( stream->parent->eof ) { + /* Check return code at EOF */ + if ( stream->buffer != NULL && pos >= 2 && + stream->buffer[pos-1] == '\n' ) { + switch ( stream->buffer[pos-2] ) { + case '+': + scstream->client->exit_code = 1; + break; + case '-': + scstream->client->exit_code = 0; + default: + scstream->client->exit_code = -1; + } + } else { + scstream->client->exit_code = -1; + } + } + + if ( ret == 0 || (ret < 0 && !stream->parent->eof) ) break; + + if ( stream->buffer != NULL && pos >= 1 ) { + /* retain/hide potential return code at end of buffer */ + reserved = ( stream->buffer[pos-1] == '\n' && pos > 1 ? 2 : 1 ); + + pos -= reserved; + + if ( ret > 0 ) { + ret = ( (size_t)ret > reserved ? ret - reserved : 0 ); + } + } + + if ( ret <= 0 && stream->parent->eof ) { + /* Parent EOF and not more data to return; EOF here as well */ + stream->istream.eof = TRUE; + ret = -1; + } + } while ( ret == 0 ); + + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : (ret == 0 ? 0 : -1); + stream->pos = pos; + + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + + return ret; +} + +static void ATTR_NORETURN script_client_istream_sync +(struct istream_private *stream ATTR_UNUSED) +{ + i_panic("script_client_istream sync() not implemented"); +} + +static int script_client_istream_stat +(struct istream_private *stream, bool exact) +{ + struct script_client_istream *scstream = + (struct script_client_istream *)stream; + const struct stat *st; + int ret; + + /* Stat the original stream */ + ret = i_stream_stat(stream->parent, exact, &st); + if (ret < 0 || st->st_size == -1 || !exact) + return ret; + + scstream->statbuf = *st; + scstream->statbuf.st_size = -1; + + return ret; +} + +static struct istream *script_client_istream_create +(struct script_client *script_client, struct istream *input) +{ + struct script_client_istream *scstream; + + scstream = i_new(struct script_client_istream, 1); + scstream->client = script_client; + + scstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + scstream->istream.iostream.destroy = script_client_istream_destroy; + scstream->istream.read = script_client_istream_read; + scstream->istream.sync = script_client_istream_sync; + scstream->istream.stat = script_client_istream_stat; + + scstream->istream.istream.readable_fd = FALSE; + scstream->istream.istream.blocking = input->blocking; + scstream->istream.istream.seekable = FALSE; + + i_stream_seek(input, 0); + + return i_stream_create(&scstream->istream, input, -1); +} + +/* + * Script client + */ + +struct script_client_remote { + struct script_client client; + + unsigned int noreply:1; +}; + +static void script_client_remote_connected(struct script_client *sclient) +{ + struct script_client_remote *slclient = + (struct script_client_remote *)sclient; + const char **args = sclient->args; + string_t *str; + + io_remove(&sclient->io); + script_client_init_streams(sclient); + + if ( !slclient->noreply ) { + sclient->script_input = script_client_istream_create + (sclient, sclient->script_input); + } + + str = t_str_new(1024); + str_append(str, "VERSION\tscript\t3\t0\n"); + if ( slclient->noreply ) + str_append(str, "noreply\n"); + else + str_append(str, "-\n"); + if ( args != NULL ) { + for (; *args != NULL; args++) { + str_append(str, *args); + str_append_c(str, '\n'); + } + } + str_append_c(str, '\n'); + + if ( o_stream_send + (sclient->script_output, str_data(str), str_len(str)) < 0 ) { + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return; + } + + (void)script_client_script_connected(sclient); +} + +static int script_client_remote_connect(struct script_client *sclient) +{ + struct script_client_remote *slclient = + (struct script_client_remote *)sclient; + int fd; + + if ((fd = net_connect_unix(sclient->path)) < 0) { + switch (errno) { + case EAGAIN: + case ECONNREFUSED: + // FIXME: retry; + return -1; + case EACCES: + i_error("%s", eacces_error_get("net_connect_unix", sclient->path)); + return -1; + default: + i_error("net_connect_unix(%s) failed: %m", sclient->path); + return -1; + } + } + + net_set_nonblock(fd, TRUE); + + sclient->fd_in = ( slclient->noreply && sclient->output == NULL ? -1 : fd ); + sclient->fd_out = fd; + sclient->io = io_add(fd, IO_WRITE, script_client_remote_connected, sclient); + return 1; +} + +static int script_client_remote_close_output(struct script_client *sclient) +{ + /* Shutdown output; script stdin will get EOF */ + if ( shutdown(sclient->fd_out, SHUT_WR) < 0 ) { + i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path); + return -1; + } + + return 1; +} + +static int script_client_remote_disconnect +(struct script_client *sclient, bool force) +{ + struct script_client_remote *slclient = + (struct script_client_remote *)sclient; + int ret = 0; + + if ( sclient->error == SCRIPT_CLIENT_ERROR_NONE && !slclient->noreply && + sclient->script_input != NULL && !force) { + const unsigned char *data; + size_t size; + + /* Skip any remaining script output and parse the exit code */ + while ((ret = i_stream_read_data + (sclient->script_input, &data, &size, 0)) > 0) { + i_stream_skip(sclient->script_input, size); + } + + /* Get exit code */ + if ( !sclient->script_input->eof ) + ret = -1; + else + ret = sclient->exit_code; + } else { + ret = 1; + } + + return ret; +} + +static void script_client_remote_failure +(struct script_client *sclient, enum script_client_error error) +{ + switch ( error ) { + case SCRIPT_CLIENT_ERROR_CONNECT_TIMEOUT: + i_error("program `%s' socket connection timed out (> %d msecs)", + sclient->path, sclient->set->client_connect_timeout_msecs); + break; + case SCRIPT_CLIENT_ERROR_RUN_TIMEOUT: + i_error("program `%s' execution timed out (> %d secs)", + sclient->path, sclient->set->input_idle_timeout_secs); + break; + default: + break; + } +} + +struct script_client *script_client_remote_create +(const char *socket_path, const char *const *args, + const struct script_client_settings *set, bool noreply) +{ + struct script_client_remote *sclient; + pool_t pool; + + pool = pool_alloconly_create("script client remote", 1024); + sclient = p_new(pool, struct script_client_remote, 1); + script_client_init(&sclient->client, pool, socket_path, args, set); + sclient->client.connect = script_client_remote_connect; + sclient->client.close_output = script_client_remote_close_output; + sclient->client.disconnect = script_client_remote_disconnect; + sclient->client.failure = script_client_remote_failure; + sclient->noreply = noreply; + + return &sclient->client; +} + diff --git a/src/plugins/sieve-extprograms/script-client.c b/src/plugins/sieve-extprograms/script-client.c new file mode 100644 index 0000000..06a5d5d --- /dev/null +++ b/src/plugins/sieve-extprograms/script-client.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "array.h" +#include "istream-private.h" +#include "ostream.h" + +#include "script-client-private.h" + +#include + +#define MAX_OUTBUF_SIZE 16384 + +static void script_client_timeout(struct script_client *sclient) +{ + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_RUN_TIMEOUT); +} + +static void script_client_connect_timeout(struct script_client *sclient) +{ + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_CONNECT_TIMEOUT); +} + +static int script_client_connect(struct script_client *sclient) +{ + if (sclient->set->client_connect_timeout_msecs != 0) { + sclient->to = timeout_add + (sclient->set->client_connect_timeout_msecs, + script_client_connect_timeout, sclient); + } + + if ( sclient->connect(sclient) < 0 ) { + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return -1; + } + + return 1; +} + +static void script_client_disconnect +(struct script_client *sclient, bool force) +{ + int ret, error = FALSE; + + if ( sclient->ioloop != NULL ) + io_loop_stop(sclient->ioloop); + + if ( sclient->disconnected ) + return; + + if ( (ret=sclient->close_output(sclient)) < 0 ) + error = TRUE; + + if ( (ret=sclient->disconnect(sclient, force)) < 0 ) + error = TRUE; + + if ( sclient->script_input != NULL ) + i_stream_destroy(&sclient->script_input); + if ( sclient->script_output != NULL ) + o_stream_destroy(&sclient->script_output); + + if ( sclient->to != NULL ) + timeout_remove(&sclient->to); + if ( sclient->io != NULL ) + io_remove(&sclient->io); + + if (sclient->fd_in != -1 && close(sclient->fd_in) < 0) + i_error("close(%s) failed: %m", sclient->path); + if (sclient->fd_out != -1 && sclient->fd_out != sclient->fd_out) + i_error("close(%s/out) failed: %m", sclient->path); + sclient->fd_in = sclient->fd_out = -1; + + sclient->disconnected = TRUE; + if (error && sclient->error == SCRIPT_CLIENT_ERROR_NONE ) { + sclient->error = SCRIPT_CLIENT_ERROR_UNKNOWN; + } +} + +void script_client_fail +(struct script_client *sclient, enum script_client_error error) +{ + if ( sclient->error != SCRIPT_CLIENT_ERROR_NONE ) + return; + + sclient->error = error; + script_client_disconnect(sclient, TRUE); + + sclient->failure(sclient, error); +} + +static int script_client_script_output(struct script_client *sclient) +{ + struct istream *input = sclient->input; + struct ostream *output = sclient->script_output; + const unsigned char *data; + size_t size; + int ret = 0; + + if ((ret = o_stream_flush(output)) <= 0) { + if (ret < 0) + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return ret; + } + + if ( input != NULL && output != NULL ) { + do { + while ( (data=i_stream_get_data(input, &size)) != NULL ) { + ssize_t sent; + + if ( (sent=o_stream_send(output, data, size)) < 0 ) { + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return -1; + } + + if ( sent == 0 ) + return 0; + i_stream_skip(input, sent); + } + } while ( (ret=i_stream_read(input)) > 0 ); + + if ( ret == 0 ) { + // FIXME: not supposed to happen; returning 0 will poll the input stream + return 0; + } + + if ( ret < 0 ) { + if ( !input->eof ) { + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return -1; + } else if ( !i_stream_have_bytes_left(input) ) { + i_stream_unref(&sclient->input); + input = NULL; + + if ( (ret = o_stream_flush(output)) <= 0 ) { + if ( ret < 0 ) + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return ret; + } + } + } + } + + if ( input == NULL ) { + o_stream_unref(&sclient->script_output); + + if ( sclient->script_input == NULL ) { + script_client_disconnect(sclient, FALSE); + } else { + sclient->close_output(sclient); + } + return 0; + } + + return 1; +} + +static void script_client_script_input(struct script_client *sclient) +{ + struct istream *input = sclient->script_input; + struct ostream *output = sclient->output; + const unsigned char *data; + size_t size; + int ret = 0; + + if ( input != NULL ) { + while ( (ret=i_stream_read_data(input, &data, &size, 0)) > 0 ) { + if ( output != NULL ) { + ssize_t sent; + + if ( (sent=o_stream_send(output, data, size)) < 0 ) { + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + return; + } + size = (size_t)sent; + } + + i_stream_skip(input, size); + } + + if ( ret < 0 ) { + if ( input->eof ) { + script_client_disconnect(sclient, FALSE); + return; + } + script_client_fail(sclient, SCRIPT_CLIENT_ERROR_IO); + } + } +} + +int script_client_script_connected +(struct script_client *sclient) +{ + int ret = 1; + + sclient->start_time = ioloop_time; + if (sclient->to != NULL) + timeout_remove(&sclient->to); + if ( sclient->set->input_idle_timeout_secs != 0 ) { + sclient->to = timeout_add(sclient->set->input_idle_timeout_secs*1000, + script_client_timeout, sclient); + } + + /* run output */ + if ( sclient->script_output != NULL && + (ret=script_client_script_output(sclient)) == 0 ) { + if ( sclient->script_output != NULL ) { + o_stream_set_flush_callback + (sclient->script_output, script_client_script_output, sclient); + } + } + + return ret; +} + +void script_client_init +(struct script_client *sclient, pool_t pool, const char *path, + const char *const *args, const struct script_client_settings *set) +{ + sclient->pool = pool; + sclient->path = p_strdup(pool, path); + if ( args != NULL ) + sclient->args = p_strarray_dup(pool, args); + sclient->set = set; + sclient->debug = set->debug; + sclient->fd_in = -1; + sclient->fd_out = -1; +} + +void script_client_set_input +(struct script_client *sclient, struct istream *input) +{ + if ( sclient->input ) + i_stream_unref(&sclient->input); + if ( input != NULL ) + i_stream_ref(input); + sclient->input = input; +} + +void script_client_set_output +(struct script_client *sclient, struct ostream *output) +{ + if ( sclient->output ) + o_stream_unref(&sclient->output); + if ( output != NULL ) + o_stream_ref(output); + sclient->output = output; +} + +void script_client_set_env +(struct script_client *sclient, const char *name, const char *value) +{ + const char *env; + + if ( !array_is_created(&sclient->envs) ) + p_array_init(&sclient->envs, sclient->pool, 16); + + env = p_strdup_printf(sclient->pool, "%s=%s", name, value); + array_append(&sclient->envs, &env, 1); +} + +void script_client_init_streams(struct script_client *sclient) +{ + if ( sclient->fd_out >= 0 ) { + sclient->script_output = + o_stream_create_fd(sclient->fd_out, MAX_OUTBUF_SIZE, FALSE); + } + if ( sclient->fd_in >= 0 ) { + sclient->script_input = + i_stream_create_fd(sclient->fd_in, (size_t)-1, FALSE); + sclient->io = io_add + (sclient->fd_in, IO_READ, script_client_script_input, sclient); + } +} + +void script_client_destroy(struct script_client **_sclient) +{ + struct script_client *sclient = *_sclient; + + script_client_disconnect(sclient, TRUE); + + if ( sclient->input != NULL ) + i_stream_unref(&sclient->input); + if ( sclient->output != NULL ) + o_stream_unref(&sclient->output); + if ( sclient->io != NULL ) + io_remove(&sclient->io); + if ( sclient->ioloop != NULL ) + io_loop_destroy(&sclient->ioloop); + + pool_unref(&sclient->pool); + *_sclient = NULL; +} + +int script_client_run(struct script_client *sclient) +{ + int ret; + + sclient->ioloop = io_loop_create(); + + if ( script_client_connect(sclient) >= 0 ) { + /* run output */ + ret = 1; + if ( sclient->script_output != NULL && + (ret=o_stream_flush(sclient->script_output)) == 0 ) { + o_stream_set_flush_callback + (sclient->script_output, script_client_script_output, sclient); + } + + /* run i/o event loop */ + if ( ret < 0 ) { + sclient->error = SCRIPT_CLIENT_ERROR_IO; + } else if ( sclient->io != NULL || ret == 0 ) { + io_loop_run(sclient->ioloop); + } + + /* finished */ + script_client_disconnect(sclient, FALSE); + } + + io_loop_destroy(&sclient->ioloop); + + if ( sclient->error != SCRIPT_CLIENT_ERROR_NONE ) + return -1; + + return sclient->exit_code; +} + diff --git a/src/plugins/sieve-extprograms/script-client.h b/src/plugins/sieve-extprograms/script-client.h new file mode 100644 index 0000000..d642d4e --- /dev/null +++ b/src/plugins/sieve-extprograms/script-client.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SCRIPT_CLIENT_H +#define __SCRIPT_CLIENT_H + +struct script_client; + +struct script_client_settings { + unsigned int client_connect_timeout_msecs; + unsigned int input_idle_timeout_secs; + bool debug; +}; + +struct script_client *script_client_local_create + (const char *bin_path, const char *const *args, + const struct script_client_settings *set); +struct script_client *script_client_remote_create + (const char *socket_path, const char *const *args, + const struct script_client_settings *set, bool noreply); + +void script_client_destroy(struct script_client **_sclient); + +void script_client_set_input + (struct script_client *sclient, struct istream *input); +void script_client_set_output + (struct script_client *sclient, struct ostream *output); + +void script_client_set_env + (struct script_client *sclient, const char *name, const char *value); + +int script_client_run(struct script_client *sclient); + +#endif + diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.c b/src/plugins/sieve-extprograms/sieve-extprograms-common.c new file mode 100644 index 0000000..59f39f1 --- /dev/null +++ b/src/plugins/sieve-extprograms/sieve-extprograms-common.c @@ -0,0 +1,594 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "str.h" +#include "strfuncs.h" +#include "str-sanitize.h" +#include "unichar.h" +#include "array.h" +#include "eacces-error.h" +#include "istream.h" +#include "istream-crlf.h" +#include "istream-header-filter.h" +#include "ostream.h" +#include "mail-user.h" +#include "mail-storage.h" + +#include +#include +#include +#include +#include + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-runtime.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" +#include "sieve-ext-variables.h" + +#include "script-client.h" +#include "sieve-extprograms-common.h" + +/* + * Limits + */ + +#define SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN 128 +#define SIEVE_EXTPROGRAMS_MAX_PROGRAM_ARG_LEN 1024 + +#define SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS 10 +#define SIEVE_EXTPROGRAMS_CONNECT_TIMEOUT_MSECS 5 + +/* + * Pipe Extension Context + */ + +struct sieve_extprograms_config *sieve_extprograms_config_init +(const struct sieve_extension *ext) +{ + struct sieve_instance *svinst = ext->svinst; + struct sieve_extprograms_config *ext_config; + const char *extname = sieve_extension_name(ext); + const char *bin_dir, *socket_dir; + sieve_number_t execute_timeout = + SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS; + + extname = strrchr(extname, '.'); + i_assert(extname != NULL); + extname++; + + bin_dir = sieve_setting_get + (svinst, t_strdup_printf("sieve_%s_bin_dir", extname)); + socket_dir = sieve_setting_get + (svinst, t_strdup_printf("sieve_%s_socket_dir", extname)); + + ext_config = i_new(struct sieve_extprograms_config, 1); + + if ( bin_dir == NULL && socket_dir == NULL ) { + if ( svinst->debug ) { + sieve_sys_debug(svinst, "%s extension: " + "no bin or socket directory specified; extension is unconfigured " + "(both sieve_%s_bin_dir and sieve_%s_socket_dir are not set)", + sieve_extension_name(ext), extname, extname); + } + } else { + ext_config->bin_dir = i_strdup(bin_dir); + ext_config->socket_dir = i_strdup(socket_dir); + } + + if (sieve_setting_get_duration_value + (svinst, t_strdup_printf("sieve_%s_exec_timeout", extname), + &execute_timeout)) { + ext_config->execute_timeout = execute_timeout; + } + + if ( sieve_extension_is(ext, pipe_extension) ) + ext_config->copy_ext = sieve_ext_copy_get_extension(ext->svinst); + if ( sieve_extension_is(ext, execute_extension) ) + ext_config->var_ext = sieve_ext_variables_get_extension(ext->svinst); + return ext_config; +} + +void sieve_extprograms_config_deinit +(struct sieve_extprograms_config **ext_config) +{ + if ( *ext_config == NULL ) + return; + + i_free((*ext_config)->bin_dir); + i_free((*ext_config)->socket_dir); + i_free((*ext_config)); + + *ext_config = NULL; +} + +/* + * Program name and arguments + */ + +bool sieve_extprogram_name_is_valid(string_t *name) +{ + ARRAY_TYPE(unichars) uni_name; + unsigned int count, i; + const unichar_t *name_chars; + size_t namelen = str_len(name); + + /* Check minimum length */ + if ( namelen == 0 ) + return FALSE; + + /* Check worst-case maximum length */ + if ( namelen > SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN * 4 ) + return FALSE; + + /* Intialize array for unicode characters */ + t_array_init(&uni_name, namelen * 4); + + /* Convert UTF-8 to UCS4/UTF-32 */ + if ( uni_utf8_to_ucs4_n(str_data(name), namelen, &uni_name) < 0 ) + return FALSE; + name_chars = array_get(&uni_name, &count); + + /* Check true maximum length */ + if ( count > SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN ) + return FALSE; + + /* Scan name for invalid characters + * FIXME: compliance with Net-Unicode Definition (Section 2 of + * RFC 5198) is not checked fully and no normalization + * is performed. + */ + for ( i = 0; i < count; i++ ) { + + /* 0000-001F; [CONTROL CHARACTERS] */ + if ( name_chars[i] <= 0x001f ) + return FALSE; + + /* 002F; SLASH */ + if ( name_chars[i] == 0x002f ) + return FALSE; + + /* 007F; DELETE */ + if ( name_chars[i] == 0x007f ) + return FALSE; + + /* 0080-009F; [CONTROL CHARACTERS] */ + if ( name_chars[i] >= 0x0080 && name_chars[i] <= 0x009f ) + return FALSE; + + /* 00FF */ + if ( name_chars[i] == 0x00ff ) + return FALSE; + + /* 2028; LINE SEPARATOR */ + /* 2029; PARAGRAPH SEPARATOR */ + if ( name_chars[i] == 0x2028 || name_chars[i] == 0x2029 ) + return FALSE; + } + + return TRUE; +} + +bool sieve_extprogram_arg_is_valid(string_t *arg) +{ + const unsigned char *chars; + unsigned int i; + + /* Check maximum length */ + if ( str_len(arg) > SIEVE_EXTPROGRAMS_MAX_PROGRAM_ARG_LEN ) + return FALSE; + + /* Check invalid characters */ + chars = str_data(arg); + for ( i = 0; i < str_len(arg); i++ ) { + /* 0010; CR */ + if ( chars[i] == 0x0D ) + return FALSE; + + /* 0010; LF */ + if ( chars[i] == 0x0A ) + return FALSE; + } + + return TRUE; +} + +/* + * Command validation + */ + +struct _arg_validate_context { + struct sieve_validator *valdtr; + struct sieve_command *cmd; +}; + +static int _arg_validate +(void *context, struct sieve_ast_argument *item) +{ + struct _arg_validate_context *actx = (struct _arg_validate_context *) context; + + if ( sieve_argument_is_string_literal(item) ) { + string_t *arg = sieve_ast_argument_str(item); + + if ( !sieve_extprogram_arg_is_valid(arg) ) { + sieve_argument_validate_error(actx->valdtr, item, + "%s %s: specified external program argument `%s' is invalid", + sieve_command_identifier(actx->cmd), sieve_command_type_name(actx->cmd), + str_sanitize(str_c(arg), 128)); + + return FALSE; + } + } + + return TRUE; +} + +bool sieve_extprogram_command_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + struct sieve_ast_argument *stritem; + struct _arg_validate_context actx; + string_t *program_name; + + if ( arg == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the %s %s expects at least one positional argument, but none was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + /* argument */ + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "program-name", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* Variables are not allowed */ + if ( !sieve_argument_is_string_literal(arg) ) { + sieve_argument_validate_error(valdtr, arg, + "the %s %s requires a constant string " + "for its program-name argument", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + /* Check program name */ + program_name = sieve_ast_argument_str(arg); + if ( !sieve_extprogram_name_is_valid(program_name) ) { + sieve_argument_validate_error(valdtr, arg, + "%s %s: invalid program name '%s'", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + str_sanitize(str_c(program_name), 80)); + return FALSE; + } + + /* Optional argument */ + + arg = sieve_ast_argument_next(arg); + if ( arg == NULL ) + return TRUE; + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "arguments", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* Check arguments */ + actx.valdtr = valdtr; + actx.cmd = cmd; + stritem = arg; + if ( sieve_ast_stringlist_map + (&stritem, (void *)&actx, _arg_validate) <= 0 ) { + return FALSE; + } + + if ( sieve_ast_argument_next(arg) != NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the %s %s expects at most two positional arguments, but more were found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + return TRUE; +} + +/* + * Common command operands + */ + +int sieve_extprogram_command_read_operands +(const struct sieve_runtime_env *renv, sieve_size_t *address, + string_t **pname_r, struct sieve_stringlist **args_list_r) +{ + string_t *arg; + int ret; + + /* + * Read fixed operands + */ + + if ( (ret=sieve_opr_string_read + (renv, address, "program-name", pname_r)) <= 0 ) + return ret; + + if ( (ret=sieve_opr_stringlist_read_ex + (renv, address, "arguments", TRUE, args_list_r)) <= 0 ) + return ret; + + /* + * Check operands + */ + + arg = NULL; + while ( *args_list_r != NULL && + (ret=sieve_stringlist_next_item(*args_list_r, &arg)) > 0 ) { + if ( !sieve_extprogram_arg_is_valid(arg) ) { + sieve_runtime_error(renv, NULL, + "specified :args item `%s' is invalid", + str_sanitize(str_c(arg), 128)); + return SIEVE_EXEC_FAILURE; + } + } + + if ( ret < 0 ) { + sieve_runtime_trace_error(renv, "invalid args-list item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* + * Running external programs + */ + +struct sieve_extprogram { + struct sieve_instance *svinst; + struct script_client_settings set; + struct script_client *script_client; +}; + +void sieve_extprogram_exec_error +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) +{ + char str[256]; + struct tm *tm; + const char *timestamp; + + tm = localtime(&ioloop_time); + + timestamp = + ( strftime(str, sizeof(str), " [%Y-%m-%d %H:%M:%S]", tm) > 0 ? str : "" ); + + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_error(ehandler, location, + "%s: refer to server log for more information.%s", + t_strdup_vprintf(fmt, args), timestamp); + } T_END; + + va_end(args); +} + +/* API */ + +struct sieve_extprogram *sieve_extprogram_create +(const struct sieve_extension *ext, const struct sieve_script_env *senv, + const struct sieve_message_data *msgdata, const char *action, + const char *program_name, const char * const *args, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = ext->svinst; + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *) ext->context; + struct sieve_extprogram *sprog; + const char *path = NULL; + struct stat st; + bool fork = FALSE; + int ret; + + if ( svinst->debug ) { + sieve_sys_debug(svinst, "action %s: " + "running program: %s", action, program_name); + } + + if ( ext_config == NULL ) { + sieve_sys_error(svinst, "action %s: " + "failed to execute program `%s': " + "vnd.dovecot.%s extension is unconfigured", action, program_name, action); + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + + /* Try socket first */ + if ( ext_config->socket_dir != NULL ) { + path = t_strconcat(senv->user->set->base_dir, "/", + ext_config->socket_dir, "/", program_name, NULL); + if ( (ret=stat(path, &st)) < 0 ) { + switch ( errno ) { + case ENOENT: + if ( svinst->debug ) { + sieve_sys_debug(svinst, "action %s: " + "socket path `%s' for program `%s' not found", + action, path, program_name); + } + break; + case EACCES: + sieve_sys_error(svinst, "action %s: " + "failed to stat socket: %s", action, eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERM; + return NULL; + default: + sieve_sys_error(svinst, "action %s: " + "failed to stat socket `%s': %m", action, path); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + path = NULL; + } else if ( !S_ISSOCK(st.st_mode) ) { + sieve_sys_error(svinst, "action %s: " + "socket path `%s' for program `%s' is not a socket", + action, path, program_name); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + } + + /* Try executable next */ + if ( path == NULL && ext_config->bin_dir != NULL ) { + fork = TRUE; + path = t_strconcat(ext_config->bin_dir, "/", program_name, NULL); + if ( (ret=stat(path, &st)) < 0 ) { + switch ( errno ) { + case ENOENT: + if ( svinst->debug ) { + sieve_sys_debug(svinst, "action %s: " + "executable path `%s' for program `%s' not found", + action, path, program_name); + } + sieve_sys_error(svinst, "action %s: program `%s' not found", + action, program_name); + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + sieve_sys_error(svinst, "action %s: " + "failed to stat program: %s", action, eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERM; + break; + default: + sieve_sys_error(svinst, "action %s: " + "failed to stat program `%s': %m", action, path); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + break; + } + + return NULL; + } else if ( !S_ISREG(st.st_mode) ) { + sieve_sys_error(svinst, "action %s: " + "executable `%s' for program `%s' is not a regular file", + action, path, program_name); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } else if ( (st.st_mode & S_IWOTH) != 0 ) { + sieve_sys_error(svinst, "action %s: " + "executable `%s' for program `%s' is world-writable", + action, path, program_name); + *error_r = SIEVE_ERROR_NO_PERM; + return NULL; + } + } + + /* None found ? */ + if ( path == NULL ) { + sieve_sys_error(svinst, "action %s: " + "program `%s' not found", action, program_name); + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + + sprog = i_new(struct sieve_extprogram, 1); + sprog->svinst = ext->svinst; + + sprog->set.client_connect_timeout_msecs = + SIEVE_EXTPROGRAMS_CONNECT_TIMEOUT_MSECS; + sprog->set.input_idle_timeout_secs = ext_config->execute_timeout; + sprog->set.debug = svinst->debug; + + if ( fork ) { + sprog->script_client = + script_client_local_create(path, args, &sprog->set); + } else { + sprog->script_client = + script_client_remote_create(path, args, &sprog->set, FALSE); + } + + if ( svinst->username != NULL ) + script_client_set_env(sprog->script_client, "USER", svinst->username); + if ( svinst->home_dir != NULL ) + script_client_set_env(sprog->script_client, "HOME", svinst->home_dir); + if ( svinst->hostname != NULL ) + script_client_set_env(sprog->script_client, "HOST", svinst->hostname); + if ( msgdata->return_path != NULL ) { + script_client_set_env + (sprog->script_client, "SENDER", msgdata->return_path); + } + if ( msgdata->final_envelope_to != NULL ) { + script_client_set_env + (sprog->script_client, "RECIPIENT", msgdata->final_envelope_to); + } + if ( msgdata->orig_envelope_to != NULL ) { + script_client_set_env + (sprog->script_client, "ORIG_RECIPIENT", msgdata->orig_envelope_to); + } + + return sprog; +} + +void sieve_extprogram_destroy(struct sieve_extprogram **_sprog) +{ + struct sieve_extprogram *sprog = *_sprog; + + script_client_destroy(&sprog->script_client); + i_free(sprog); + *_sprog = NULL; +} + +/* I/0 */ + +void sieve_extprogram_set_output +(struct sieve_extprogram *sprog, struct ostream *output) +{ + script_client_set_output(sprog->script_client, output); +} + +void sieve_extprogram_set_input +(struct sieve_extprogram *sprog, struct istream *input) +{ + script_client_set_input(sprog->script_client, input); +} + +int sieve_extprogram_set_input_mail +(struct sieve_extprogram *sprog, struct mail *mail) +{ + struct istream *input; + + if (mail_get_stream(mail, NULL, NULL, &input) < 0) + return -1; + + /* Make sure the message contains CRLF consistently */ + input = i_stream_create_crlf(input); + + script_client_set_input(sprog->script_client, input); + i_stream_unref(&input); + + return 1; +} + +int sieve_extprogram_run(struct sieve_extprogram *sprog) +{ + return script_client_run(sprog->script_client); +} + diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.h b/src/plugins/sieve-extprograms/sieve-extprograms-common.h new file mode 100644 index 0000000..c8bfdff --- /dev/null +++ b/src/plugins/sieve-extprograms/sieve-extprograms-common.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SIEVE_EXTPROGRAMS_COMMON_H +#define __SIEVE_EXTPROGRAMS_COMMON_H + +#include "sieve-common.h" + +/* + * Extension configuration + */ + +struct sieve_extprograms_config { + const struct sieve_extension *copy_ext; + const struct sieve_extension *var_ext; + + char *socket_dir; + char *bin_dir; + + unsigned int execute_timeout; +}; + +struct sieve_extprograms_config *sieve_extprograms_config_init + (const struct sieve_extension *ext); +void sieve_extprograms_config_deinit + (struct sieve_extprograms_config **ext_config); + +/* + * Extensions + */ + +extern const struct sieve_extension_def pipe_extension; +extern const struct sieve_extension_def filter_extension; +extern const struct sieve_extension_def execute_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_pipe; +extern const struct sieve_command_def cmd_filter; +extern const struct sieve_command_def cmd_execute; + +/* + * Operations + */ + +extern const struct sieve_operation_def cmd_pipe_operation; +extern const struct sieve_operation_def cmd_filter_operation; +extern const struct sieve_operation_def cmd_execute_operation; + +/* + * Program name and arguments + */ + +bool sieve_extprogram_arg_is_valid(string_t *arg); +bool sieve_extprogram_name_is_valid(string_t *name); + +/* + * Command validation + */ + +bool sieve_extprogram_command_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +/* + * Common command operands + */ + +int sieve_extprogram_command_read_operands + (const struct sieve_runtime_env *renv, sieve_size_t *address, + string_t **pname_r, struct sieve_stringlist **args_list_r); + +/* + * Running external programs + */ + +void sieve_extprogram_exec_error + (struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) ATTR_FORMAT(3, 4); + +struct sieve_extprogram *sieve_extprogram_create + (const struct sieve_extension *ext, const struct sieve_script_env *senv, + const struct sieve_message_data *msgdata, const char *action, + const char *program_name, const char * const *args, + enum sieve_error *error_r); +void sieve_extprogram_destroy(struct sieve_extprogram **_sprog); + +void sieve_extprogram_set_output + (struct sieve_extprogram *sprog, struct ostream *output); +void sieve_extprogram_set_input + (struct sieve_extprogram *sprog, struct istream *input); +int sieve_extprogram_set_input_mail + (struct sieve_extprogram *sprog, struct mail *mail); + +int sieve_extprogram_run(struct sieve_extprogram *sprog); + +#endif /* __SIEVE_EXTPROGRAMS_COMMON_H */ diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c b/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c new file mode 100644 index 0000000..8031271 --- /dev/null +++ b/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-extensions.h" + +#include "sieve-extprograms-common.h" +#include "sieve-extprograms-plugin.h" + +/* + * Sieve plugin interface + */ + +struct _plugin_context { + const struct sieve_extension *ext_pipe; + const struct sieve_extension *ext_filter; + const struct sieve_extension *ext_execute; +}; + +void sieve_extprograms_plugin_load +(struct sieve_instance *svinst, void **context) +{ + struct _plugin_context *pctx = i_new(struct _plugin_context, 1); + + pctx->ext_pipe = sieve_extension_register + (svinst, &pipe_extension, FALSE); + pctx->ext_filter = sieve_extension_register + (svinst, &filter_extension, FALSE); + pctx->ext_execute = sieve_extension_register + (svinst, &execute_extension, FALSE); + + if ( svinst->debug ) { + sieve_sys_debug(svinst, "Sieve Extprograms plugin for %s version %s loaded", + PIGEONHOLE_NAME, PIGEONHOLE_VERSION); + } + + *context = (void *)pctx; +} + +void sieve_extprograms_plugin_unload +(struct sieve_instance *svinst ATTR_UNUSED, void *context) +{ + struct _plugin_context *pctx = (struct _plugin_context *)context; + + sieve_extension_unregister(pctx->ext_pipe); + sieve_extension_unregister(pctx->ext_filter); + sieve_extension_unregister(pctx->ext_execute); + + i_free(pctx); +} + +/* + * Module interface + */ + +void sieve_extprograms_plugin_init(void) +{ + /* Nothing */ +} + +void sieve_extprograms_plugin_deinit(void) +{ + /* Nothing */ +} diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h b/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h new file mode 100644 index 0000000..fee16e0 --- /dev/null +++ b/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __SIEVE_EXTPROGRAMS_PLUGIN_H +#define __SIEVE_EXTPROGRAMS_PLUGIN_H + +/* + * Plugin interface + */ + +void sieve_extprograms_plugin_load + (struct sieve_instance *svinst, void **context); +void sieve_extprograms_plugin_unload + (struct sieve_instance *svinst, void *context); + +/* + * Module interface + */ + +void sieve_extprograms_plugin_init(void); +void sieve_extprograms_plugin_deinit(void); + +#endif /* __SIEVE_EXTPROGRAMS_PLUGIN_H */ diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c index 0c593bd..3c78153 100644 --- a/src/sieve-tools/sieve-test.c +++ b/src/sieve-tools/sieve-test.c @@ -58,21 +58,24 @@ static void print_help(void) static void *sieve_smtp_open (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, - const char *return_path, FILE **file_r) + const char *return_path, struct ostream **output_r) { i_info("sending message from <%s> to <%s>:", ( return_path == NULL ? "" : return_path ), destination); printf("\nSTART MESSAGE:\n"); - *file_r = stdout; + *output_r = o_stream_create_fd(STDOUT_FILENO, (size_t)-1, FALSE); - return NULL; + return (void*)*output_r; } static bool sieve_smtp_close (const struct sieve_script_env *senv ATTR_UNUSED, void *handle ATTR_UNUSED) { + struct ostream *output = (struct ostream *)handle; + printf("END MESSAGE\n\n"); + o_stream_unref(&output); return TRUE; } diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c index bf5e12c..4754d5e 100644 --- a/src/testsuite/ext-testsuite.c +++ b/src/testsuite/ext-testsuite.c @@ -106,13 +106,11 @@ static bool ext_testsuite_binary_load /* Extension object */ const struct sieve_extension_def testsuite_extension = { - "vnd.dovecot.testsuite", - NULL, NULL, - ext_testsuite_validator_load, - ext_testsuite_generator_load, - ext_testsuite_interpreter_load, - ext_testsuite_binary_load, - NULL, NULL, + .name = "vnd.dovecot.testsuite", + .validator_load = ext_testsuite_validator_load, + .generator_load = ext_testsuite_generator_load, + .interpreter_load = ext_testsuite_interpreter_load, + .binary_load = ext_testsuite_binary_load, SIEVE_EXT_DEFINE_OPERATIONS(testsuite_operations), SIEVE_EXT_DEFINE_OPERANDS(testsuite_operands) }; diff --git a/src/testsuite/testsuite-log.c b/src/testsuite/testsuite-log.c index 4e7e57b..5a788fa 100644 --- a/src/testsuite/testsuite-log.c +++ b/src/testsuite/testsuite-log.c @@ -31,9 +31,9 @@ struct _testsuite_log_message { }; static pool_t _testsuite_logmsg_pool = NULL; -ARRAY_DEFINE(_testsuite_log_errors, struct _testsuite_log_message); -ARRAY_DEFINE(_testsuite_log_warnings, struct _testsuite_log_message); -ARRAY_DEFINE(_testsuite_log_messages, struct _testsuite_log_message); +ARRAY(struct _testsuite_log_message) _testsuite_log_errors; +ARRAY(struct _testsuite_log_message) _testsuite_log_warnings; +ARRAY(struct _testsuite_log_message) _testsuite_log_messages; static inline void ATTR_FORMAT(3, 0) _testsuite_stdout_vlog (const char *prefix, const char *location, const char *fmt, diff --git a/src/testsuite/testsuite-settings.c b/src/testsuite/testsuite-settings.c index 50e3eb7..c448dcc 100644 --- a/src/testsuite/testsuite-settings.c +++ b/src/testsuite/testsuite-settings.c @@ -16,15 +16,14 @@ struct testsuite_setting { char *value; }; -static struct hash_table *settings; +static HASH_TABLE(const char *, struct testsuite_setting *) settings; static const char *testsuite_setting_get (void *context, const char *identifier); void testsuite_settings_init(void) { - settings = hash_table_create - (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + hash_table_create(&settings, default_pool, 0, str_hash, strcmp); sieve_tool_set_setting_callback(sieve_tool, testsuite_setting_get, NULL); } @@ -33,12 +32,10 @@ void testsuite_settings_deinit(void) { struct hash_iterate_context *itx = hash_table_iterate_init(settings); - void *key; - void *value; - - while ( hash_table_iterate(itx, &key, &value) ) { - struct testsuite_setting *setting = (struct testsuite_setting *) value; + const char *key; + struct testsuite_setting *setting; + while ( hash_table_iterate(itx, settings, &key, &setting) ) { i_free(setting->identifier); i_free(setting->value); i_free(setting); @@ -52,7 +49,7 @@ void testsuite_settings_deinit(void) static const char *testsuite_setting_get (void *context ATTR_UNUSED, const char *identifier) { - struct testsuite_setting *setting = (struct testsuite_setting *) + struct testsuite_setting *setting = hash_table_lookup(settings, identifier); if ( setting == NULL ) { @@ -64,7 +61,7 @@ static const char *testsuite_setting_get void testsuite_setting_set(const char *identifier, const char *value) { - struct testsuite_setting *setting = (struct testsuite_setting *) + struct testsuite_setting *setting = hash_table_lookup(settings, identifier); if ( setting != NULL ) { @@ -75,13 +72,13 @@ void testsuite_setting_set(const char *identifier, const char *value) setting->identifier = i_strdup(identifier); setting->value = i_strdup(value); - hash_table_insert(settings, (void *) identifier, (void *) setting); + hash_table_insert(settings, identifier, setting); } } void testsuite_setting_unset(const char *identifier) { - struct testsuite_setting *setting = (struct testsuite_setting *) + struct testsuite_setting *setting = hash_table_lookup(settings, identifier); if ( setting != NULL ) { diff --git a/src/testsuite/testsuite-smtp.c b/src/testsuite/testsuite-smtp.c index 7745dc6..01f72ca 100644 --- a/src/testsuite/testsuite-smtp.c +++ b/src/testsuite/testsuite-smtp.c @@ -3,6 +3,7 @@ #include "lib.h" #include "array.h" +#include "ostream.h" #include "unlink-directory.h" #include "sieve-common.h" @@ -24,7 +25,7 @@ struct testsuite_smtp_message { static pool_t testsuite_smtp_pool; static const char *testsuite_smtp_tmp; -static ARRAY_DEFINE(testsuite_smtp_messages, struct testsuite_smtp_message); +static ARRAY(struct testsuite_smtp_message) testsuite_smtp_messages; /* * Initialize @@ -68,16 +69,17 @@ void testsuite_smtp_reset(void) struct testsuite_smtp { const char *tmp_path; - FILE *mfile; + struct ostream *output; }; void *testsuite_smtp_open (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, - const char *return_path, FILE **file_r) + const char *return_path, struct ostream **output_r) { struct testsuite_smtp_message smtp_msg; struct testsuite_smtp *smtp; unsigned int smtp_count = array_count(&testsuite_smtp_messages); + int fd; smtp_msg.file = p_strdup_printf(testsuite_smtp_pool, "%s/%d.eml", testsuite_smtp_tmp, smtp_count); @@ -89,12 +91,14 @@ void *testsuite_smtp_open smtp = t_new(struct testsuite_smtp, 1); smtp->tmp_path = smtp_msg.file; - smtp->mfile = fopen(smtp->tmp_path, "w"); - if ( smtp->mfile == NULL ) - i_fatal("failed to open tmp file for SMTP simulation."); + if ( (fd=open(smtp->tmp_path, O_WRONLY | O_CREAT, 0600)) < 0 ) { + i_fatal("failed create tmp file for SMTP simulation: open(%s) failed: %m", + smtp->tmp_path); + } - *file_r = smtp->mfile; + smtp->output = o_stream_create_fd(fd, (size_t)-1, TRUE); + *output_r = smtp->output; return (void *) smtp; } @@ -104,8 +108,7 @@ bool testsuite_smtp_close { struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; - fclose(smtp->mfile); - + o_stream_unref(&smtp->output); return TRUE; } diff --git a/src/testsuite/testsuite-smtp.h b/src/testsuite/testsuite-smtp.h index 4894203..573b198 100644 --- a/src/testsuite/testsuite-smtp.h +++ b/src/testsuite/testsuite-smtp.h @@ -13,8 +13,8 @@ void testsuite_smtp_reset(void); */ void *testsuite_smtp_open - (const struct sieve_script_env *senv, const char *destination, - const char *return_path, FILE **file_r); + (const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, + const char *return_path, struct ostream **output_r); bool testsuite_smtp_close (const struct sieve_script_env *senv, void *handle); diff --git a/src/testsuite/testsuite-variables.c b/src/testsuite/testsuite-variables.c index b80700a..46ac927 100644 --- a/src/testsuite/testsuite-variables.c +++ b/src/testsuite/testsuite-variables.c @@ -77,7 +77,7 @@ bool testsuite_varnamespace_validate name_element = array_idx(var_name, 1); if ( name_element->num_variable >= 0 ) { sieve_argument_validate_error(valdtr, arg, - "testsuite: invalid variable name within testsuite namespace 'testsuite.%d': " + "testsuite: invalid variable name within testsuite namespace 'tst.%d': " "encountered numeric variable name", name_element->num_variable); return FALSE; } diff --git a/tests/extensions/environment/basic.svtest b/tests/extensions/environment/basic.svtest index 82f8981..bb0beb4 100644 --- a/tests/extensions/environment/basic.svtest +++ b/tests/extensions/environment/basic.svtest @@ -24,8 +24,10 @@ test "Name" { } } -test "Host" { - if not environment "host" "host.example.com" { - test_fail "wrong testsuite hostname"; +test "Location" { + if not environment "location" "MS" { + test_fail "wrong testsuite environment location"; } } + + diff --git a/tests/extensions/environment/rfc.svtest b/tests/extensions/environment/rfc.svtest index 70e38a6..c3177ae 100644 --- a/tests/extensions/environment/rfc.svtest +++ b/tests/extensions/environment/rfc.svtest @@ -22,7 +22,7 @@ test "Count" { test_fail "count should not match unknown environment item"; } - if not environment :count "eq" "host" "1" { + if not environment :count "eq" "location" "1" { test_fail "count of non-empty environment should be 1"; } } diff --git a/tests/extensions/include/execute/optional.sieve b/tests/extensions/include/execute/optional.sieve new file mode 100644 index 0000000..a6ad479 --- /dev/null +++ b/tests/extensions/include/execute/optional.sieve @@ -0,0 +1,5 @@ +require "include"; + +include :optional "optional-1"; +include :optional "optional-2"; +include :optional "optional-3"; diff --git a/tests/extensions/include/included/optional-1.sieve b/tests/extensions/include/included/optional-1.sieve new file mode 100644 index 0000000..288d141 --- /dev/null +++ b/tests/extensions/include/included/optional-1.sieve @@ -0,0 +1,9 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} ONE"; + +return; + diff --git a/tests/extensions/include/included/optional-2.sieve b/tests/extensions/include/included/optional-2.sieve new file mode 100644 index 0000000..11920f5 --- /dev/null +++ b/tests/extensions/include/included/optional-2.sieve @@ -0,0 +1,9 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} TWO"; + +keep; + diff --git a/tests/extensions/include/optional.svtest b/tests/extensions/include/optional.svtest new file mode 100644 index 0000000..345f830 --- /dev/null +++ b/tests/extensions/include/optional.svtest @@ -0,0 +1,40 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; + +global "result"; +set "result" ""; + +test "Included Optional" { + include :optional "optional-1"; + include :optional "optional-2"; + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value: ${result}"; + } + + # missing + include :optional "optional-3"; + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value after missing script: ${result}"; + } +} + + +test "Included Optional - Binary" { + if not test_script_compile "execute/optional.sieve" { + test_fail "failed to compile sieve script"; + } + + test_binary_save "optional"; + test_binary_load "optional"; + + if not test_script_run { + test_fail "failed to execute sieve script"; + } + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value: ${result}"; + } +} diff --git a/tests/plugins/extprograms/bin/addheader b/tests/plugins/extprograms/bin/addheader new file mode 100755 index 0000000..8f9805a --- /dev/null +++ b/tests/plugins/extprograms/bin/addheader @@ -0,0 +1,6 @@ +#!/bin/sh + +echo "$1: $2" +cat + +exit 0 diff --git a/tests/plugins/extprograms/bin/cat b/tests/plugins/extprograms/bin/cat new file mode 100755 index 0000000..02b9858 --- /dev/null +++ b/tests/plugins/extprograms/bin/cat @@ -0,0 +1,3 @@ +#!/bin/sh + +cat diff --git a/tests/plugins/extprograms/bin/env b/tests/plugins/extprograms/bin/env new file mode 100755 index 0000000..a7b81ac --- /dev/null +++ b/tests/plugins/extprograms/bin/env @@ -0,0 +1,3 @@ +#!/bin/sh + +eval echo -n "\${$1}" diff --git a/tests/plugins/extprograms/bin/frame b/tests/plugins/extprograms/bin/frame new file mode 100755 index 0000000..225005e --- /dev/null +++ b/tests/plugins/extprograms/bin/frame @@ -0,0 +1,7 @@ +#!/bin/sh + +echo -n "FRAMED $1{ " +cat +echo -n " }" + +exit 0 diff --git a/tests/plugins/extprograms/bin/modify b/tests/plugins/extprograms/bin/modify new file mode 100755 index 0000000..ce87014 --- /dev/null +++ b/tests/plugins/extprograms/bin/modify @@ -0,0 +1,8 @@ +#!/bin/sh + +echo "X-Frop: Extra header" +cat +echo +echo "Extra body content!" + +exit 0 diff --git a/tests/plugins/extprograms/bin/program b/tests/plugins/extprograms/bin/program new file mode 100755 index 0000000..4b5edbf --- /dev/null +++ b/tests/plugins/extprograms/bin/program @@ -0,0 +1,5 @@ +#!/bin/sh + +cat > /dev/null + +exit 0 diff --git a/tests/plugins/extprograms/bin/replace b/tests/plugins/extprograms/bin/replace new file mode 100755 index 0000000..b010f06 --- /dev/null +++ b/tests/plugins/extprograms/bin/replace @@ -0,0 +1,12 @@ +#!/bin/sh + +cat > /dev/null + +echo "From: hatseflat@example.com" +echo "To: frutsel@example.org" +echo "Subject: replacement message" +echo +echo "Replaced!" + + +exit 0 diff --git a/tests/plugins/extprograms/bin/sleep10 b/tests/plugins/extprograms/bin/sleep10 new file mode 100755 index 0000000..8c1b96d --- /dev/null +++ b/tests/plugins/extprograms/bin/sleep10 @@ -0,0 +1,3 @@ +#!/bin/sh + +sleep 10 diff --git a/tests/plugins/extprograms/bin/sleep2 b/tests/plugins/extprograms/bin/sleep2 new file mode 100755 index 0000000..a814acd --- /dev/null +++ b/tests/plugins/extprograms/bin/sleep2 @@ -0,0 +1,3 @@ +#!/bin/sh + +sleep 2 diff --git a/tests/plugins/extprograms/bin/stderr b/tests/plugins/extprograms/bin/stderr new file mode 100755 index 0000000..1dfd9bc --- /dev/null +++ b/tests/plugins/extprograms/bin/stderr @@ -0,0 +1,20 @@ +#!/bin/sh + +echo "========================================" 1>&2 +echo "Test shell script successfully executed!" 1>&2 +echo 1>&2 +echo "Arguments: $1 $2" 1>&2 +echo 1>&2 +echo "Environment:" 1>&2 +env 1>&2 +echo 1>&2 +echo "Message:" 1>&2 +cat 1>&2 +echo "========================================" 1>&2 +echo 1>&2 + +echo "Subject: frop!" +echo "From: stephan@rename-it.nl" +echo "To: tss@example.com" +echo +echo "Frop!" diff --git a/tests/plugins/extprograms/errors.svtest b/tests/plugins/extprograms/errors.svtest new file mode 100644 index 0000000..148f4da --- /dev/null +++ b/tests/plugins/extprograms/errors.svtest @@ -0,0 +1,32 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid program names + */ + +test "Invalid Program Names" { + if test_script_compile "errors/programname.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Invalid arguments + */ + +test "Invalid Arguments" { + if test_script_compile "errors/arguments.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} diff --git a/tests/plugins/extprograms/errors/arguments.sieve b/tests/plugins/extprograms/errors/arguments.sieve new file mode 100644 index 0000000..04f0aa0 --- /dev/null +++ b/tests/plugins/extprograms/errors/arguments.sieve @@ -0,0 +1,5 @@ +require "vnd.dovecot.pipe"; + +pipe :args "aaaa + aaaa" "frop"; + diff --git a/tests/plugins/extprograms/errors/programname.sieve b/tests/plugins/extprograms/errors/programname.sieve new file mode 100644 index 0000000..1d2d19c --- /dev/null +++ b/tests/plugins/extprograms/errors/programname.sieve @@ -0,0 +1,25 @@ +require "variables"; +require "encoded-character"; +require "vnd.dovecot.pipe"; + +# Slash +pipe "../frop"; + +# More slashes +pipe "../../james/sieve/vacation"; + +# 0000-001F; [CONTROL CHARACTERS] +pipe "idiotic${unicode: 001a}"; + +# 007F; DELETE +pipe "idiotic${unicode: 007f}"; + +# 0080-009F; [CONTROL CHARACTERS] +pipe "idiotic${unicode: 0085}"; + +# 2028; LINE SEPARATOR +pipe "idiotic${unicode: 2028}"; + +# 2029; PARAGRAPH SEPARATOR +pipe "idiotic${unicode: 2029}"; + diff --git a/tests/plugins/extprograms/execute/command.svtest b/tests/plugins/extprograms/execute/command.svtest new file mode 100644 index 0000000..92c1fd1 --- /dev/null +++ b/tests/plugins/extprograms/execute/command.svtest @@ -0,0 +1,27 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.execute"; +require "variables"; + +test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.execute"; + +test "Basic" { + execute "program"; +} + +test "Input message" { + execute :pipe "program"; +} + +test "Input string" { + execute :input "DATA" "program"; +} + +test "Input variable" { + set "DATA" "DATA"; + execute :input "${DATA}" "program"; +} + +test "Output variable" { + execute :output "DATA" "program"; +} diff --git a/tests/plugins/extprograms/execute/errors.svtest b/tests/plugins/extprograms/execute/errors.svtest new file mode 100644 index 0000000..b08e77c --- /dev/null +++ b/tests/plugins/extprograms/execute/errors.svtest @@ -0,0 +1,35 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.execute"; + +/* + * Command syntax + */ + +test "Command syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "13" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Variables + */ + +test "Variables" { + if test_script_compile "errors/variables.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} diff --git a/tests/plugins/extprograms/execute/errors/syntax.sieve b/tests/plugins/extprograms/execute/errors/syntax.sieve new file mode 100644 index 0000000..bd10ee5 --- /dev/null +++ b/tests/plugins/extprograms/execute/errors/syntax.sieve @@ -0,0 +1,38 @@ +require "vnd.dovecot.execute"; + +# 1: error: no arguments +execute; + +# 2: error: numeric argument +execute 1; + +# 3: error: tag argument +execute :frop; + +# 4: error: numeric second argument +execute "sdfd" 1; + +# 5: error: stringlist first argument +execute ["sdfd","werwe"] "sdfs"; + +# 6: error: too many arguments +execute "sdfs" "sdfd" "werwe"; + +# 7: error: inappropriate :copy argument +execute :copy "234234" ["324234", "23423"]; + +# 8: error: invalid :input argument; missing parameter +execute :input "frop"; + +# 9: error: invalid :input argument; invalid parameter +execute :input 1 "frop"; + +# 10: error: invalid :input argument; invalid parameter +execute :input ["23423","21342"] "frop"; + +# 11: error: invalid :input argument; invalid parameter +execute :input :frop "frop"; + +# 12: error: :output not allowed without variables extension +execute :output "${frop}" "frop"; + diff --git a/tests/plugins/extprograms/execute/errors/variables.sieve b/tests/plugins/extprograms/execute/errors/variables.sieve new file mode 100644 index 0000000..3d0b3e7 --- /dev/null +++ b/tests/plugins/extprograms/execute/errors/variables.sieve @@ -0,0 +1,7 @@ +require "vnd.dovecot.execute"; +require "variables"; + +# 1: invalid variable name +execute :output "wqwe-aeqwe" "frop"; + + diff --git a/tests/plugins/extprograms/execute/execute.svtest b/tests/plugins/extprograms/execute/execute.svtest new file mode 100644 index 0000000..bd2d2d3 --- /dev/null +++ b/tests/plugins/extprograms/execute/execute.svtest @@ -0,0 +1,104 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.execute"; +require "vnd.dovecot.debug"; +require "variables"; +require "relational"; +require "environment"; + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.execute"; +test_result_reset; + +test "Execute - bare" { + execute "program"; +} + +test_result_reset; +test "Execute - i/o" { + execute :input "FROP" :output "out" "frame"; + + if not string "${out}" "FRAMED { FROP }" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - i/o and arguments" { + execute :input "FROP" :output "out" "frame" ["FRIEP "]; + + if not string "${out}" "FRAMED FRIEP { FROP }" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - pipe" { + execute :pipe :output "msg" "cat"; + + if not string :contains "${msg}" "Subject: Frop!" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - env" { + test_set "envelope.from" "stephan@sub.example.com"; + test_set "envelope.to" "stephan@sub.example.net"; + test_set "envelope.orig_to" "all@sub.example.net"; + + execute :output "out" "env" "SENDER"; + if not string :is "${out}" "stephan@sub.example.com" { + test_fail "wrong SENDER env returned: '${out}'"; + } + + execute :output "out" "env" "RECIPIENT"; + if not string :is "${out}" "stephan@sub.example.net" { + test_fail "wrong RECIPIENT env returned: '${out}'"; + } + + execute :output "out" "env" "ORIG_RECIPIENT"; + if not string :is "${out}" "all@sub.example.net" { + test_fail "wrong ORIG_RECIPIENT env returned: '${out}'"; + } + + execute :output "out" "env" "HOST"; + if not environment :is "host" "${out}" { + test_fail "wrong HOST env returned: '${out}'"; + } + + execute :output "out" "env" "HOME"; + if string :count "eq" "${out}" "0" { + test_fail "empty HOME env returned"; + } + + execute :output "out" "env" "USER"; + if string :count "eq" "${out}" "0" { + test_fail "empty USER env returned"; + } +} + +test_result_reset; +test "Execute - used as test" { + if execute :pipe :output "msg" "dog" { + test_fail "execute action indicated success with invalid program"; + } + + if not execute :pipe :output "msg" "cat" { + test_fail "execute action indicated failure with valid program"; + } + + if not string :contains "${msg}" "Subject: Frop!" { + test_fail "wrong string returned: ${out}"; + } +} + + diff --git a/tests/plugins/extprograms/filter/command.svtest b/tests/plugins/extprograms/filter/command.svtest new file mode 100644 index 0000000..50f949a --- /dev/null +++ b/tests/plugins/extprograms/filter/command.svtest @@ -0,0 +1,10 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.filter"; +require "variables"; + +test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.filter"; + +test "Basic" { + filter "program"; +} diff --git a/tests/plugins/extprograms/filter/errors.svtest b/tests/plugins/extprograms/filter/errors.svtest new file mode 100644 index 0000000..3461c01 --- /dev/null +++ b/tests/plugins/extprograms/filter/errors.svtest @@ -0,0 +1,21 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Command syntax + */ + +test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.filter"; + +test "Command syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} diff --git a/tests/plugins/extprograms/filter/errors/syntax.sieve b/tests/plugins/extprograms/filter/errors/syntax.sieve new file mode 100644 index 0000000..00a3a23 --- /dev/null +++ b/tests/plugins/extprograms/filter/errors/syntax.sieve @@ -0,0 +1,22 @@ +require "vnd.dovecot.filter"; + +# 1: error: no arguments +filter; + +# 2: error: numeric argument +filter 1; + +# 3: error: tag argument +filter :frop; + +# 4: error: numeric second argument +filter "sdfd" 1; + +# 5: error: stringlist first argument +filter ["sdfd","werwe"] "sdfs"; + +# 6: error: too many arguments +filter "sdfd" "werwe" "sdfs"; + +# 7: error: inappropriate :copy argument +filter :try :copy "234234" ["324234", "23423"]; diff --git a/tests/plugins/extprograms/filter/execute.svtest b/tests/plugins/extprograms/filter/execute.svtest new file mode 100644 index 0000000..a952c55 --- /dev/null +++ b/tests/plugins/extprograms/filter/execute.svtest @@ -0,0 +1,180 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.filter"; +require "vnd.dovecot.debug"; +require "variables"; +require "editheader"; +require "body"; +require "fileinto"; +require "mailbox"; + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.filter"; +test_result_reset; + +test_result_reset; +test "Replace" { + if header :contains "subject" "replacement" { + test_fail "message already replaced"; + } + + filter "replace"; + + if not header :contains "subject" "replacement" { + test_fail "message not replaced"; + } +} + +test_result_reset; +test "Used as test" { + if filter "nonsense" { + test_fail "filter action indicated success with invalid program"; + } + + if not filter "replace" { + test_fail "filter action indicated failure with valid program"; + } + + if not header :contains "subject" "replacement" { + test_fail "message not replaced; filter not actually executed"; + } +} + +test_result_reset; +test "Modify" { + if anyof ( + body :contains "extra", + exists "x-frop") { + test_fail "message already modified"; + } + + if not header "subject" "Frop!" { + test_fail "message is wrong"; + } + + filter "modify"; + + if not header "subject" "Frop!" { + test_fail "message replaced erroneously"; + } + + if not header :contains "x-frop" "extra" { + test_fail "message header not modified"; + } + + if not body :contains "Extra" { + test_fail "message body not modified"; + } +} + +test_result_reset; +test "Editheader" { + if anyof ( exists "X-A", exists "X-B", exists "X-C", exists "X-D", + exists "X-E") { + test_fail "message already modified"; + } + + addheader "X-A" "1"; + if not header "X-A" "1" { + test_fail "X-A header missing"; + } + + fileinto :create "A"; + + filter "addheader" ["X-B", "2"]; + if not header "X-B" "2" { + test_fail "X-B header missing"; + } + + fileinto :create "B"; + + addheader "X-C" "3"; + if not header "X-C" "3" { + test_fail "X-C header missing"; + } + + fileinto :create "C"; + + filter "addheader" ["X-D", "4"]; + if not header "X-D" "4" { + test_fail "X-D header missing"; + } + + fileinto :create "D"; + + addheader "X-E" "5"; + if not header "X-E" "5" { + test_fail "X-E header missing"; + } + + fileinto :create "E"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + test_message :folder "A" 0; + + if not header "X-A" "1" { + test_fail "X-A header missing"; + } + if anyof ( + header "X-B" "2", header "X-C" "3", + header "X-D" "4", header "X-E" "5") { + test_fail "X-B, X-C, X-D or X-E header found"; + } + + test_message :folder "B" 0; + + if not header "X-B" "2" { + test_fail "X-B header missing"; + } + if anyof ( + header "X-C" "3", header "X-D" "4", header "X-E" "5") { + test_fail "X-C, X-D or X-E header found"; + } + + test_message :folder "C" 0; + + if not header "X-C" "3" { + test_fail "X-C header missing"; + } + if anyof (header "X-D" "4", header "X-E" "5") { + test_fail "X-D or X-E header found"; + } + + test_message :folder "D" 0; + + if not header "X-D" "4" { + test_fail "X-D header missing"; + } + if anyof (header "X-E" "5") { + test_fail "X-E header found"; + } + + test_message :folder "E" 0; + + if not header "X-A" "1" { + test_fail "X-A header missing in final message"; + } + if not header "X-B" "2" { + test_fail "X-B header missing in final message"; + } + if not header "X-C" "3" { + test_fail "X-C header missing in final message"; + } + if not header "X-D" "4" { + test_fail "X-D header missing in final message"; + } + if not header "X-E" "5" { + test_fail "X-E header missing in final message"; + } +} diff --git a/tests/plugins/extprograms/pipe/command.svtest b/tests/plugins/extprograms/pipe/command.svtest new file mode 100644 index 0000000..dabd970 --- /dev/null +++ b/tests/plugins/extprograms/pipe/command.svtest @@ -0,0 +1,10 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.pipe"; + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.pipe"; + +test "Basic" { + pipe "program"; +} + diff --git a/tests/plugins/extprograms/pipe/errors.svtest b/tests/plugins/extprograms/pipe/errors.svtest new file mode 100644 index 0000000..4987fbc --- /dev/null +++ b/tests/plugins/extprograms/pipe/errors.svtest @@ -0,0 +1,58 @@ +require "vnd.dovecot.testsuite"; +require "variables"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Command syntax + */ + +test "Command syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + +/* Timeout */ + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_set "sieve_pipe_exec_timeout" "1s"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Timeout" { + if not test_script_compile "errors/timeout.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execute failed"; + } + + if test_result_execute { + test_fail "pipe should have timed out"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :contains "failed to pipe" { + test_fail "wrong error reported"; + } + +} diff --git a/tests/plugins/extprograms/pipe/errors/syntax.sieve b/tests/plugins/extprograms/pipe/errors/syntax.sieve new file mode 100644 index 0000000..64d5310 --- /dev/null +++ b/tests/plugins/extprograms/pipe/errors/syntax.sieve @@ -0,0 +1,22 @@ +require "vnd.dovecot.pipe"; + +# 1: error: no arguments +pipe; + +# 2: error: numeric argument +pipe 1; + +# 3: error: tag argument +pipe :frop; + +# 4: error: numeric second argument +pipe "sdfd" 1; + +# 5: error: stringlist first argument +pipe ["sdfd","werwe"] "sdfs"; + +# 6: error: too many arguments +pipe "sdfd" "werwe" "sdfs"; + +# 7: error: inappropriate :copy argument +pipe :try :copy "234234" ["324234", "23423"]; diff --git a/tests/plugins/extprograms/pipe/errors/timeout.sieve b/tests/plugins/extprograms/pipe/errors/timeout.sieve new file mode 100644 index 0000000..7a940c8 --- /dev/null +++ b/tests/plugins/extprograms/pipe/errors/timeout.sieve @@ -0,0 +1,3 @@ +require "vnd.dovecot.pipe"; + +pipe "sleep10"; diff --git a/tests/plugins/extprograms/pipe/execute.svtest b/tests/plugins/extprograms/pipe/execute.svtest new file mode 100644 index 0000000..34b6798 --- /dev/null +++ b/tests/plugins/extprograms/pipe/execute.svtest @@ -0,0 +1,56 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.pipe"; +require "vnd.dovecot.debug"; +require "variables"; + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +/* Basic pipe */ + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Pipe" { + pipe "stderr" ["ONE", "TWO"]; + + if not test_result_execute { + test_fail "failed to pipe message to script"; + } +} + +/* Timeout */ + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_set "sieve_pipe_exec_timeout" "3s"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Timeout 3s" { + pipe "sleep2"; + + if not test_result_execute { + test_fail "failed to pipe message to script"; + } +} + +test_result_reset; +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_set "sieve_pipe_exec_timeout" "0"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Timeout infinite" { + pipe "sleep2"; + + if not test_result_execute { + test_fail "failed to pipe message to script"; + } +} -- 1.7.10.2 From 82a5e2a99344ab443619894ca4c6858a8de311cb Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Sun, 2 Jun 2013 16:08:22 +0200 Subject: [PATCH] Finished support for temporary failures. This change is bigger than I would have liked, so this needs to be tested more. diff --git a/src/lib-sieve/cmd-discard.c b/src/lib-sieve/cmd-discard.c index cf4c926..8afa7b2 100644 --- a/src/lib-sieve/cmd-discard.c +++ b/src/lib-sieve/cmd-discard.c @@ -58,7 +58,7 @@ const struct sieve_operation_def cmd_discard_operation = { static void act_discard_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_discard_commit +static int act_discard_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -128,7 +128,7 @@ static void act_discard_print *keep = FALSE; } -static bool act_discard_commit +static int act_discard_commit (const struct sieve_action *action ATTR_UNUSED, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep) @@ -138,6 +138,6 @@ static bool act_discard_commit "(discard action)"); *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index f1785b9..d3a2735 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -87,7 +87,7 @@ static int act_redirect_check_duplicate static void act_redirect_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_redirect_commit +static int act_redirect_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -307,7 +307,7 @@ static void act_redirect_print *keep = FALSE; } -static bool act_redirect_send +static int act_redirect_send (const struct sieve_action_exec_env *aenv, struct mail *mail, struct act_redirect_context *ctx) { @@ -326,11 +326,11 @@ static bool act_redirect_send if ( !sieve_smtp_available(senv) ) { sieve_result_global_warning (aenv, "redirect action has no means to send mail."); - return TRUE; + return SIEVE_EXEC_FAILURE; } if (mail_get_stream(mail, NULL, NULL, &input) < 0) - return FALSE; + return SIEVE_EXEC_TEMP_FAILURE; /* Open SMTP transport */ smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &output); @@ -359,13 +359,13 @@ static bool act_redirect_send "failed to redirect message to <%s> " "(refer to server log for more information)", str_sanitize(ctx->to_address, 80)); - return FALSE; + return SIEVE_EXEC_FAILURE; } - return TRUE; + return SIEVE_EXEC_OK; } -static bool act_redirect_commit +static int act_redirect_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep) @@ -378,6 +378,7 @@ static bool act_redirect_commit const struct sieve_script_env *senv = aenv->scriptenv; const char *orig_recipient = sieve_message_get_orig_recipient(aenv->msgctx); const char *dupeid; + int ret; /* Prevent mail loops if possible */ dupeid = msgdata->id == NULL ? NULL : t_strdup_printf @@ -388,12 +389,12 @@ static bool act_redirect_commit sieve_result_global_log(aenv, "discarded duplicate forward to <%s>", str_sanitize(ctx->to_address, 128)); *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } } /* Try to forward the message */ - if ( act_redirect_send(aenv, mail, ctx) ) { + if ( (ret=act_redirect_send(aenv, mail, ctx)) == SIEVE_EXEC_OK) { /* Mark this message id as forwarded to the specified destination */ if (dupeid != NULL) { @@ -410,10 +411,10 @@ static bool act_redirect_commit /* Cancel implicit keep */ *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } - return FALSE; + return ret; } diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c index 4532677..025e4de 100644 --- a/src/lib-sieve/ext-reject.c +++ b/src/lib-sieve/ext-reject.c @@ -181,7 +181,7 @@ int act_reject_check_conflict static void act_reject_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_reject_commit +static int act_reject_commit (const struct sieve_action *action ATTR_UNUSED, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -379,7 +379,7 @@ static void act_reject_print *keep = FALSE; } -static bool act_reject_commit +static int act_reject_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep) { @@ -391,7 +391,7 @@ static bool act_reject_commit if ( recipient == NULL ) { sieve_result_global_warning(aenv, "reject action aborted: envelope recipient is <>"); - return TRUE; + return SIEVE_EXEC_OK; } if ( rj_ctx->reason == NULL ) { @@ -399,14 +399,14 @@ static bool act_reject_commit "not sending reject message (would cause second response to sender)"); *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } if ( sender == NULL ) { sieve_result_global_log(aenv, "not sending reject message to <>"); *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } if ( sieve_action_reject_mail(aenv, sender, recipient, rj_ctx->reason) ) { @@ -415,10 +415,10 @@ static bool act_reject_commit ( rj_ctx->ereject ? "ereject" : "reject" )); *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } - return FALSE; + return SIEVE_EXEC_FAILURE; } diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c index 488fc18..993f952 100644 --- a/src/lib-sieve/plugins/enotify/cmd-notify.c +++ b/src/lib-sieve/plugins/enotify/cmd-notify.c @@ -136,7 +136,7 @@ static int act_notify_check_duplicate static void act_notify_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_notify_commit +static int act_notify_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -566,7 +566,7 @@ static void act_notify_print /* Result execution */ -static bool act_notify_commit +static int act_notify_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED) { @@ -593,7 +593,7 @@ static bool act_notify_commit sieve_error_handler_unref(&nenv.ehandler); } - return result; + return ( result ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE ); } diff --git a/src/lib-sieve/plugins/imap4flags/tag-flags.c b/src/lib-sieve/plugins/imap4flags/tag-flags.c index 001f69c..6c64c11 100644 --- a/src/lib-sieve/plugins/imap4flags/tag-flags.c +++ b/src/lib-sieve/plugins/imap4flags/tag-flags.c @@ -70,7 +70,7 @@ static int seff_flags_merge static void seff_flags_print (const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool seff_flags_pre_execute +static int seff_flags_pre_execute (const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void **context, void *tr_context); @@ -383,7 +383,7 @@ static void seff_flags_print /* Result execution */ -static bool seff_flags_pre_execute +static int seff_flags_pre_execute (const struct sieve_side_effect *seffect, const struct sieve_action *action ATTR_UNUSED, const struct sieve_action_exec_env *aenv, void **context, void *tr_context) @@ -402,7 +402,7 @@ static bool seff_flags_pre_execute sieve_act_store_add_flags(aenv, tr_context, keywords, ctx->flags); - return TRUE; + return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c index 9e3dbfd..f1745b0 100644 --- a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c +++ b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c @@ -41,7 +41,7 @@ const struct sieve_argument_def mailbox_create_tag = { static void seff_mailbox_create_print (const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool seff_mailbox_create_pre_execute +static int seff_mailbox_create_pre_execute (const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void **se_context, void *tr_context); @@ -113,7 +113,7 @@ static void seff_mailbox_create_print sieve_result_seffect_printf(rpenv, "create mailbox if it does not exist"); } -static bool seff_mailbox_create_pre_execute +static int seff_mailbox_create_pre_execute (const struct sieve_side_effect *seffect ATTR_UNUSED, const struct sieve_action *action ATTR_UNUSED, const struct sieve_action_exec_env *aenv ATTR_UNUSED, @@ -126,17 +126,22 @@ static bool seff_mailbox_create_pre_execute /* Check whether creation is necessary */ if ( trans->box == NULL || trans->disabled ) - return TRUE; + return SIEVE_EXEC_OK; /* Check whether creation has a chance of working */ - if ( trans->error_code != MAIL_ERROR_NONE && - trans->error_code != MAIL_ERROR_NOTFOUND ) - return FALSE; + switch ( trans->error_code ) { + case MAIL_ERROR_NONE: + case MAIL_ERROR_NOTFOUND: + break; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + return SIEVE_EXEC_FAILURE; + } trans->error = NULL; trans->error_code = MAIL_ERROR_NONE; - *storage = mailbox_get_storage(trans->box); /* Create mailbox */ @@ -144,7 +149,8 @@ static bool seff_mailbox_create_pre_execute (void)mail_storage_get_last_error(*storage, &error); if ( error != MAIL_ERROR_EXISTS ) { sieve_act_store_get_storage_error(aenv, trans); - return FALSE; + return ( trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE ); } } @@ -159,10 +165,11 @@ static bool seff_mailbox_create_pre_execute if ( mailbox_open(trans->box) < 0 ) { /* Failed definitively */ sieve_act_store_get_storage_error(aenv, trans); - return FALSE; + return ( trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE ); } - return TRUE; + return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c index 8496623..d77d8bd 100644 --- a/src/lib-sieve/plugins/notify/cmd-notify.c +++ b/src/lib-sieve/plugins/notify/cmd-notify.c @@ -146,7 +146,7 @@ static int act_notify_check_duplicate static void act_notify_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_notify_commit +static int act_notify_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -789,7 +789,7 @@ static bool act_notify_send return TRUE; } -static bool act_notify_commit +static int act_notify_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED) { @@ -820,7 +820,7 @@ static bool act_notify_commit result = act_notify_send(aenv, act); } T_END; - return result; + return ( result ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE ); } diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c index e7196d1..098525a 100644 --- a/src/lib-sieve/plugins/vacation/cmd-vacation.c +++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c @@ -190,7 +190,7 @@ int act_vacation_check_conflict static void act_vacation_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_vacation_commit +static int act_vacation_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -1018,7 +1018,7 @@ static void act_vacation_hash md5_final(&ctx, hash_r); } -static bool act_vacation_commit +static int act_vacation_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED) { @@ -1043,14 +1043,14 @@ static bool act_vacation_commit if ( recipient == NULL ) { sieve_result_global_warning (aenv, "vacation action aborted: envelope recipient is <>"); - return TRUE; + return SIEVE_EXEC_OK; } /* Is the return path unset ? */ if ( sender == NULL ) { sieve_result_global_log(aenv, "discarded vacation reply to <>"); - return TRUE; + return SIEVE_EXEC_OK; } /* Are we perhaps trying to respond to ourselves ? @@ -1059,7 +1059,7 @@ static bool act_vacation_commit sieve_result_global_log(aenv, "discarded vacation reply to own address <%s>", str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } /* Are we perhaps trying to respond to one of our alternative :addresses? @@ -1073,7 +1073,7 @@ static bool act_vacation_commit "discarded vacation reply to own address <%s> " "(as specified using :addresses argument)", str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } alt_address++; } @@ -1088,7 +1088,7 @@ static bool act_vacation_commit sieve_result_global_log(aenv, "discarded duplicate vacation response to <%s>", str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } } @@ -1101,7 +1101,7 @@ static bool act_vacation_commit sieve_result_global_log(aenv, "discarding vacation response to mailinglist recipient <%s>", str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } hdsp++; } @@ -1116,7 +1116,7 @@ static bool act_vacation_commit sieve_result_global_log(aenv, "discarding vacation response to auto-submitted message from <%s>", str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } hdsp++; } @@ -1133,7 +1133,7 @@ static bool act_vacation_commit sieve_result_global_log(aenv, "discarding vacation response to precedence=%s message from <%s>", *hdsp, str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } hdsp++; } @@ -1144,7 +1144,7 @@ static bool act_vacation_commit sieve_result_global_log(aenv, "not sending vacation response to system address <%s>", str_sanitize(sender, 128)); - return TRUE; + return SIEVE_EXEC_OK; } /* Fetch original recipient if necessary */ @@ -1213,7 +1213,7 @@ static bool act_vacation_commit str_sanitize(recipient, 128), original_recipient, (ctx->addresses == NULL ? " no" : "")); - return TRUE; + return SIEVE_EXEC_OK; } } @@ -1243,7 +1243,7 @@ static bool act_vacation_commit } } - return TRUE; + return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c index 836baaf..fb10e3f 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c +++ b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c @@ -77,7 +77,7 @@ struct act_duplicate_mark_data { static void act_duplicate_mark_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_duplicate_mark_commit +static int act_duplicate_mark_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -106,7 +106,7 @@ static void act_duplicate_mark_print } } -static bool act_duplicate_mark_commit +static int act_duplicate_mark_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED) @@ -121,7 +121,7 @@ static bool act_duplicate_mark_commit sieve_action_duplicate_mark (senv, data->hash, sizeof(data->hash), ioloop_time + data->period); - return TRUE; + return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index e39db49..7ae4ca2 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -190,13 +190,13 @@ static void act_store_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_store_start +static int act_store_start (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void **tr_context); -static bool act_store_execute +static int act_store_execute (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context); -static bool act_store_commit +static int act_store_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); static void act_store_rollback @@ -283,16 +283,6 @@ void sieve_act_store_add_flags trans->flags_altered = TRUE; } -void sieve_act_store_get_storage_error -(const struct sieve_action_exec_env *aenv, struct act_store_transaction *trans) -{ - pool_t pool = sieve_result_pool(aenv->result); - - trans->error = p_strdup(pool, - mail_storage_get_last_error(mailbox_get_storage(trans->box), - &trans->error_code)); -} - /* Equality */ static bool act_store_equals @@ -353,6 +343,17 @@ static void act_store_print /* Action implementation */ +void sieve_act_store_get_storage_error +(const struct sieve_action_exec_env *aenv, + struct act_store_transaction *trans) +{ + pool_t pool = sieve_result_pool(aenv->result); + + trans->error = p_strdup(pool, + mail_storage_get_last_error(mailbox_get_storage(trans->box), + &trans->error_code)); +} + static bool act_store_mailbox_open (const struct sieve_action_exec_env *aenv, const char *mailbox, struct mailbox **box_r, enum mail_error *error_code_r, const char **error_r) @@ -384,7 +385,7 @@ static bool act_store_mailbox_open return TRUE; } -static bool act_store_start +static int act_store_start (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void **tr_context) { @@ -434,8 +435,17 @@ static bool act_store_start *tr_context = (void *)trans; - return ( trans->error_code == MAIL_ERROR_NONE || - trans->error_code == MAIL_ERROR_NOTFOUND ); + switch ( trans->error_code ) { + case MAIL_ERROR_NONE: + case MAIL_ERROR_NOTFOUND: + return SIEVE_EXEC_OK; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + break; + } + + return SIEVE_EXEC_FAILURE; } static struct mail_keywords *act_store_keywords_create @@ -461,7 +471,7 @@ static struct mail_keywords *act_store_keywords_create return box_keywords; } -static bool act_store_execute +static int act_store_execute (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context) { @@ -472,17 +482,27 @@ static bool act_store_execute struct mail *real_mail = mail_get_real_mail(mail); struct mail_save_context *save_ctx; struct mail_keywords *keywords = NULL; - bool result = TRUE; + int status = SIEVE_EXEC_OK; /* Verify transaction */ - if ( trans == NULL ) return FALSE; + if ( trans == NULL ) return SIEVE_EXEC_FAILURE; /* Check whether we need to do anything */ - if ( trans->disabled ) return TRUE; + if ( trans->disabled ) return SIEVE_EXEC_OK; /* Exit early if mailbox is not available */ - if ( trans->box == NULL || trans->error_code != MAIL_ERROR_NONE ) - return FALSE; + if ( trans->box == NULL ) + return SIEVE_EXEC_FAILURE; + + /* Exit early if transaction already failed */ + switch ( trans->error_code ) { + case MAIL_ERROR_NONE: + break; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + return SIEVE_EXEC_FAILURE; + } /* If the message originates from the target mailbox, only update the flags * and keywords (if not read-only) @@ -504,7 +524,7 @@ static bool act_store_execute mail_update_flags(mail, MODIFY_REPLACE, trans->flags); } - return TRUE; + return SIEVE_EXEC_OK; /* If the message is modified, only store it in the source mailbox when it is * not opened read-only. Mail structs of modified messages have their own @@ -516,7 +536,7 @@ static bool act_store_execute && ( mailbox_backends_equal(trans->box, aenv->msgdata->mail->box) ) ) { trans->redundant = TRUE; - return TRUE; + return SIEVE_EXEC_OK; } /* Mark attempt to store in default mailbox */ @@ -551,7 +571,8 @@ static bool act_store_execute if ( mailbox_copy(&save_ctx, mail) < 0 ) { sieve_act_store_get_storage_error(aenv, trans); - result = FALSE; + status = ( trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE ); } /* Deallocate keywords */ @@ -559,7 +580,7 @@ static bool act_store_execute mailbox_keywords_unref(&keywords); } - return result; + return status; } static void act_store_log_status @@ -597,13 +618,11 @@ static void act_store_log_status const char *errstr; enum mail_error error_code; - if ( trans->error != NULL ) { - errstr = trans->error; - error_code = trans->error_code; - } else { - errstr = mail_storage_get_last_error - (mailbox_get_storage(trans->box), &error_code); - } + if ( trans->error == NULL ) + sieve_act_store_get_storage_error(aenv, trans); + + errstr = trans->error; + error_code = trans->error_code; if ( error_code == MAIL_ERROR_NOTFOUND || error_code == MAIL_ERROR_PARAMS ) { @@ -629,7 +648,7 @@ static void act_store_log_status } } -static bool act_store_commit +static int act_store_commit (const struct sieve_action *action ATTR_UNUSED, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep) { @@ -638,7 +657,7 @@ static bool act_store_commit bool status = TRUE; /* Verify transaction */ - if ( trans == NULL ) return FALSE; + if ( trans == NULL ) return SIEVE_EXEC_FAILURE; /* Check whether we need to do anything */ if ( trans->disabled ) { @@ -646,14 +665,14 @@ static bool act_store_commit *keep = FALSE; if ( trans->box != NULL ) mailbox_free(&trans->box); - return TRUE; + return SIEVE_EXEC_OK; } else if ( trans->redundant ) { act_store_log_status(trans, aenv, FALSE, status); aenv->exec_status->keep_original = TRUE; aenv->exec_status->message_saved = TRUE; if ( trans->box != NULL ) mailbox_free(&trans->box); - return TRUE; + return SIEVE_EXEC_OK; } /* Mark attempt to use storage. Can only get here when all previous actions @@ -682,7 +701,11 @@ static bool act_store_commit if ( trans->box != NULL ) mailbox_free(&trans->box); - return status; + if (status) + return SIEVE_EXEC_OK; + + return ( trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE ); } static void act_store_rollback diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h index f932228..3b76274 100644 --- a/src/lib-sieve/sieve-actions.h +++ b/src/lib-sieve/sieve-actions.h @@ -69,13 +69,13 @@ struct sieve_action_def { /* Result execution */ - bool (*start) + int (*start) (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void **tr_context); - bool (*execute) + int (*execute) (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context); - bool (*commit) + int (*commit) (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); void (*rollback) @@ -138,11 +138,11 @@ struct sieve_side_effect_def { /* Result execution */ - bool (*pre_execute) + int (*pre_execute) (const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void **context, void *tr_context); - bool (*post_execute) + int (*post_execute) (const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context); void (*post_commit) diff --git a/src/lib-sieve/sieve-binary-file.c b/src/lib-sieve/sieve-binary-file.c index 0f68b37..555c776 100644 --- a/src/lib-sieve/sieve-binary-file.c +++ b/src/lib-sieve/sieve-binary-file.c @@ -301,13 +301,13 @@ int sieve_binary_save "binary save: failed to create temporary file: %s", eacces_error_get_creating("open", str_c(temp_path))); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; } else { sieve_sys_error(sbin->svinst, "binary save: failed to create temporary file: open(%s) failed: %m", str_c(temp_path)); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; } return -1; } @@ -318,7 +318,7 @@ int sieve_binary_save if ( !_sieve_binary_save(sbin, stream) ) { result = -1; if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; } o_stream_destroy(&stream); @@ -335,12 +335,12 @@ int sieve_binary_save sieve_sys_error(sbin->svinst, "binary save: failed to save binary: %s", eacces_error_get_creating("rename", path)); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; } else { sieve_sys_error(sbin->svinst, "binary save: failed to save binary: " "rename(%s, %s) failed: %m", str_c(temp_path), path); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; } result = -1; } @@ -386,13 +386,13 @@ bool sieve_binary_file_open sieve_sys_error(svinst, "binary open: failed to open: %s", eacces_error_get("open", path)); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; break; default: sieve_sys_error(svinst, "binary open: failed to open: " "open(%s) failed: %m", path); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; break; } return FALSE; diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c index a67dc4e..0198f37 100644 --- a/src/lib-sieve/sieve-result.c +++ b/src/lib-sieve/sieve-result.c @@ -93,6 +93,8 @@ struct sieve_result { HASH_TABLE(const struct sieve_action_def *, struct sieve_result_action_context *) action_contexts; + + unsigned int executed:1; }; struct sieve_result *sieve_result_create @@ -925,11 +927,11 @@ static void _sieve_result_prepare_execution(struct sieve_result *result) } } -static bool _sieve_result_implicit_keep +static int _sieve_result_implicit_keep (struct sieve_result *result, bool rollback) { struct sieve_result_action *rac; - bool success = TRUE; + int status = SIEVE_EXEC_OK; struct sieve_result_side_effect *rsef, *rsef_first = NULL; void *tr_context = NULL; struct sieve_action act_keep; @@ -943,7 +945,7 @@ static bool _sieve_result_implicit_keep } /* If keep is a non-action, return right away */ - if ( act_keep.def == NULL ) return TRUE; + if ( act_keep.def == NULL ) return SIEVE_EXEC_OK; /* Scan for execution of keep-equal actions */ rac = result->first_action; @@ -952,7 +954,7 @@ static bool _sieve_result_implicit_keep act_keep.def->equals (result->action_env.scriptenv, NULL, &rac->action) && rac->action.executed ) - return TRUE; + return SIEVE_EXEC_OK; rac = rac->next; } @@ -970,42 +972,42 @@ static bool _sieve_result_implicit_keep /* Start keep action */ if ( act_keep.def->start != NULL ) - success = act_keep.def->start + status = act_keep.def->start (&act_keep, &result->action_env, &tr_context); /* Execute keep action */ - if ( success ) { + if ( status == SIEVE_EXEC_OK ) { rsef = rsef_first; - while ( success && rsef != NULL ) { + while ( status == SIEVE_EXEC_OK && rsef != NULL ) { struct sieve_side_effect *sef = &rsef->seffect; if ( sef->def->pre_execute != NULL ) - success = success && sef->def->pre_execute + status = sef->def->pre_execute (sef, &act_keep, &result->action_env, &sef->context, tr_context); rsef = rsef->next; } - if ( act_keep.def->execute != NULL ) - success = success && act_keep.def->execute + if ( status == SIEVE_EXEC_OK && act_keep.def->execute != NULL ) + status = act_keep.def->execute (&act_keep, &result->action_env, tr_context); rsef = rsef_first; - while ( success && rsef != NULL ) { + while ( status == SIEVE_EXEC_OK && rsef != NULL ) { struct sieve_side_effect *sef = &rsef->seffect; if ( sef->def->post_execute != NULL ) - success = success && sef->def->post_execute + status = sef->def->post_execute (sef, &act_keep, &result->action_env, tr_context); rsef = rsef->next; } } /* Finish keep action */ - if ( success ) { + if ( status == SIEVE_EXEC_OK ) { bool dummy = TRUE; if ( act_keep.def->commit != NULL ) - success = act_keep.def->commit + status = act_keep.def->commit (&act_keep, &result->action_env, tr_context, &dummy); rsef = rsef_first; @@ -1019,18 +1021,18 @@ static bool _sieve_result_implicit_keep rsef = rsef->next; } - return success; + return status; } /* Failed, rollback */ if ( act_keep.def->rollback != NULL ) - act_keep.def->rollback - (&act_keep, &result->action_env, tr_context, success); + act_keep.def->rollback(&act_keep, + &result->action_env, tr_context, ( status == SIEVE_EXEC_OK )); - return FALSE; + return status; } -bool sieve_result_implicit_keep +int sieve_result_implicit_keep (struct sieve_result *result) { _sieve_result_prepare_execution(result); @@ -1055,13 +1057,20 @@ void sieve_result_mark_executed(struct sieve_result *result) } } + +bool sieve_result_executed(struct sieve_result *result) +{ + return result->executed; +} + int sieve_result_execute (struct sieve_result *result, bool *keep) { - bool implicit_keep = TRUE; - bool success = TRUE, commit_ok; + bool implicit_keep = TRUE, executed = result->executed; + int status = SIEVE_EXEC_OK, commit_status; struct sieve_result_action *rac, *first_action; struct sieve_result_action *last_attempted; + int ret; if ( keep != NULL ) *keep = FALSE; @@ -1080,7 +1089,7 @@ int sieve_result_execute */ rac = first_action; - while ( success && rac != NULL ) { + while ( status == SIEVE_EXEC_OK && rac != NULL ) { struct sieve_action *act = &rac->action; /* Skip non-actions (inactive keep) and executed ones */ @@ -1090,9 +1099,9 @@ int sieve_result_execute } if ( act->def->start != NULL ) { - rac->success = act->def->start + status = act->def->start (act, &result->action_env, &rac->tr_context); - success = success && rac->success; + rac->success = ( status == SIEVE_EXEC_OK ); } rac = rac->next; @@ -1104,7 +1113,7 @@ int sieve_result_execute last_attempted = rac; rac = first_action; - while ( success && rac != NULL ) { + while ( status == SIEVE_EXEC_OK && rac != NULL ) { struct sieve_action *act = &rac->action; struct sieve_result_side_effect *rsef; struct sieve_side_effect *sef; @@ -1117,27 +1126,28 @@ int sieve_result_execute /* Execute pre-execute event of side effects */ rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; - while ( success && rsef != NULL ) { + while ( status == SIEVE_EXEC_OK && rsef != NULL ) { sef = &rsef->seffect; if ( sef->def != NULL && sef->def->pre_execute != NULL ) - success = success & sef->def->pre_execute + status = sef->def->pre_execute (sef, act, &result->action_env, &sef->context, rac->tr_context); rsef = rsef->next; } /* Execute the action itself */ - if ( success && act->def != NULL && act->def->execute != NULL ) { - rac->success = act->def->execute + if ( status == SIEVE_EXEC_OK && act->def != NULL && + act->def->execute != NULL ) { + status = act->def->execute (act, &result->action_env, rac->tr_context); - success = success && rac->success; + rac->success = ( status == SIEVE_EXEC_OK ); } /* Execute post-execute event of side effects */ rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; - while ( success && rsef != NULL ) { + while ( status == SIEVE_EXEC_OK && rsef != NULL ) { sef = &rsef->seffect; if ( sef->def != NULL && sef->def->post_execute != NULL ) - success = success && sef->def->post_execute + status = sef->def->post_execute (sef, act, &result->action_env, rac->tr_context); rsef = rsef->next; } @@ -1149,15 +1159,16 @@ int sieve_result_execute * Transaction commit/rollback */ - commit_ok = success; + commit_status = status; rac = first_action; while ( rac != NULL && rac != last_attempted ) { struct sieve_action *act = &rac->action; struct sieve_result_side_effect *rsef; struct sieve_side_effect *sef; - if ( success ) { + if ( status == SIEVE_EXEC_OK ) { bool impl_keep = TRUE; + int cstatus = SIEVE_EXEC_OK; if ( rac->keep && keep != NULL ) *keep = TRUE; @@ -1168,19 +1179,35 @@ int sieve_result_execute } if ( act->def->commit != NULL ) { - act->executed = act->def->commit + cstatus = act->def->commit (act, &result->action_env, rac->tr_context, &impl_keep); - commit_ok = act->executed && commit_ok; + if ( cstatus == SIEVE_EXEC_OK ) { + act->executed = TRUE; + result->executed = TRUE; + executed = TRUE; + } else { + /* This is bad; try to salvage as much as possible */ + if (commit_status == SIEVE_EXEC_OK) { + commit_status = cstatus; + if (!executed) { + /* We haven't executed anything yet; continue as rollback */ + status = cstatus; + } + } + impl_keep = TRUE; + } } - /* Execute post_commit event of side effects */ - rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; - while ( rsef != NULL ) { - sef = &rsef->seffect; - if ( sef->def->post_commit != NULL ) - sef->def->post_commit - (sef, act, &result->action_env, rac->tr_context, &impl_keep); - rsef = rsef->next; + if ( cstatus == SIEVE_EXEC_OK ) { + /* Execute post_commit event of side effects */ + rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; + while ( rsef != NULL ) { + sef = &rsef->seffect; + if ( sef->def->post_commit != NULL ) + sef->def->post_commit + (sef, act, &result->action_env, rac->tr_context, &impl_keep); + rsef = rsef->next; + } } implicit_keep = implicit_keep && impl_keep; @@ -1191,9 +1218,10 @@ int sieve_result_execute continue; } - if ( act->def->rollback != NULL ) + if ( act->def->rollback != NULL ) { act->def->rollback (act, &result->action_env, rac->tr_context, rac->success); + } /* Rollback side effects */ rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; @@ -1217,19 +1245,28 @@ int sieve_result_execute * though. */ + if ( !executed && commit_status == SIEVE_EXEC_TEMP_FAILURE ) + return commit_status; + /* Execute implicit keep if the transaction failed or when the implicit keep * was not canceled during transaction. */ - if ( !commit_ok || implicit_keep ) { - if ( !_sieve_result_implicit_keep(result, !commit_ok) ) + if ( commit_status != SIEVE_EXEC_OK || implicit_keep ) { + switch ((ret=_sieve_result_implicit_keep + (result, ( commit_status != SIEVE_EXEC_OK ))) ) { + case SIEVE_EXEC_OK: + break; + case SIEVE_EXEC_TEMP_FAILURE: + if (!executed) + return ret; + default: return SIEVE_EXEC_KEEP_FAILED; + } - return ( commit_ok ? - SIEVE_EXEC_OK /* Success */ : - SIEVE_EXEC_FAILURE /* Implicit keep executed */ ); + return commit_status; } - /* Unconditional success */ + /* success */ return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h index 804bdc6..f52f132 100644 --- a/src/lib-sieve/sieve-result.h +++ b/src/lib-sieve/sieve-result.h @@ -141,12 +141,14 @@ void sieve_result_set_failure_action * Result execution */ -bool sieve_result_implicit_keep(struct sieve_result *result); +int sieve_result_implicit_keep(struct sieve_result *result); void sieve_result_mark_executed(struct sieve_result *result); int sieve_result_execute(struct sieve_result *result, bool *keep); +bool sieve_result_executed(struct sieve_result *result); + /* * Result evaluation */ diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c index 6260528..1435866 100644 --- a/src/lib-sieve/sieve-script-dict.c +++ b/src/lib-sieve/sieve-script-dict.c @@ -80,7 +80,7 @@ static int sieve_dict_script_open } else { sieve_critical(svinst, ehandler, NULL, "failed to open sieve script", "sieve dict backend: invalid option `%s'", option); - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } @@ -96,7 +96,7 @@ static int sieve_dict_script_open 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } username = svinst->username; @@ -106,7 +106,7 @@ static int sieve_dict_script_open 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } @@ -122,7 +122,7 @@ static int sieve_dict_script_open sieve_critical(svinst, ehandler, name, "failed to open sieve script", "sieve dict backend: failed to initialize dict with data `%s' " "for user `%s': %s", data, username, error); - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } @@ -135,7 +135,7 @@ static int sieve_dict_script_open 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; } else { if ( svinst->debug ) { sieve_sys_debug(svinst, "sieve dict backend: " @@ -193,7 +193,7 @@ static int sieve_dict_script_get_stream "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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c index 18df9fb..eee6f50 100644 --- a/src/lib-sieve/sieve-script-file.c +++ b/src/lib-sieve/sieve-script-file.c @@ -72,12 +72,12 @@ static void sieve_file_script_handle_error 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; + *error_r = SIEVE_ERROR_NO_PERMISSION; 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; break; } } @@ -133,7 +133,7 @@ static int sieve_file_script_open sieve_critical(svinst, ehandler, NULL, "failed to open sieve script", "sieve file backend: invalid option `%s'", option); - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } @@ -153,7 +153,7 @@ static int sieve_file_script_open "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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; success = FALSE; } } @@ -165,7 +165,7 @@ static int sieve_file_script_open 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; success = FALSE; } else { /* Extend path with filename */ @@ -211,7 +211,7 @@ static int sieve_file_script_open 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; success = FALSE; } } @@ -269,7 +269,7 @@ static int sieve_file_script_get_stream 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; result = NULL; } else { /* Re-check the file type just to be sure */ @@ -277,7 +277,7 @@ static int sieve_file_script_get_stream 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; + *error_r = SIEVE_ERROR_TEMP_FAILURE; result = NULL; } else { result = i_stream_create_fd(fd, SIEVE_FILE_READ_BLOCK_SIZE, TRUE); diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index ced71c9..1986948 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -234,7 +234,7 @@ struct sieve_script *sieve_script_create if ( script_class == NULL ) { if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return NULL; } @@ -265,7 +265,7 @@ int sieve_script_open "failed to access sieve script", "failed to parse script location: %s", parse_error); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h index 375a0f7..33f2f27 100644 --- a/src/lib-sieve/sieve-types.h +++ b/src/lib-sieve/sieve-types.h @@ -83,13 +83,13 @@ enum sieve_error { SIEVE_ERROR_NONE = 0, /* Temporary internal error */ - SIEVE_ERROR_TEMP_FAIL, + SIEVE_ERROR_TEMP_FAILURE, /* It's not possible to do the wanted operation */ SIEVE_ERROR_NOT_POSSIBLE, /* Invalid parameters (eg. script name not valid) */ SIEVE_ERROR_BAD_PARAMS, /* No permission to do the request */ - SIEVE_ERROR_NO_PERM, + SIEVE_ERROR_NO_PERMISSION, /* Out of disk space */ SIEVE_ERROR_NO_SPACE, /* Out of disk space */ @@ -235,10 +235,11 @@ struct sieve_exec_status { */ enum sieve_execution_exitcode { - SIEVE_EXEC_OK = 1, - SIEVE_EXEC_FAILURE = 0, - SIEVE_EXEC_BIN_CORRUPT = -1, - SIEVE_EXEC_KEEP_FAILED = -2 + SIEVE_EXEC_OK = 1, + SIEVE_EXEC_FAILURE = 0, + SIEVE_EXEC_TEMP_FAILURE = -1, + SIEVE_EXEC_BIN_CORRUPT = -2, + SIEVE_EXEC_KEEP_FAILED = -3 }; #endif /* __SIEVE_TYPES_H */ diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index c7bfd0a..f3ae31f 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -385,7 +385,6 @@ struct sieve_binary *sieve_open_script } else { sbin = sieve_compile_script(script, ehandler, flags, error_r); - /* Save the binary if compile was successful */ if ( sbin != NULL ) { if ( svinst->debug ) sieve_sys_debug(svinst, "script `%s' from %s successfully compiled", @@ -535,12 +534,17 @@ int sieve_execute if ( ret > 0 ) { /* Execute result */ ret = sieve_result_execute(result, keep); - } else if ( ret == 0 ) { + } else if ( ret == SIEVE_EXEC_FAILURE ) { /* Perform implicit keep if script failed with a normal runtime error */ - if ( !sieve_result_implicit_keep(result) ) { - ret = SIEVE_EXEC_KEEP_FAILED; - } else { + switch ( sieve_result_implicit_keep(result) ) { + case SIEVE_EXEC_OK: if ( keep != NULL ) *keep = TRUE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + ret = SIEVE_EXEC_TEMP_FAILURE; + break; + default: + ret = SIEVE_EXEC_KEEP_FAILED; } } @@ -674,6 +678,39 @@ int sieve_multiscript_status(struct sieve_multiscript *mscript) return mscript->status; } +int sieve_multiscript_tempfail(struct sieve_multiscript **mscript, + struct sieve_error_handler *ehandler) +{ + struct sieve_result *result = (*mscript)->result; + int ret = (*mscript)->status; + + if ( ehandler != NULL ) + sieve_result_set_error_handler(result, ehandler); + + if ( (*mscript)->active ) { + ret = SIEVE_EXEC_TEMP_FAILURE; + + if ( !(*mscript)->teststream && sieve_result_executed(result) ) { + /* Part of the result is already executed, need to fall back to + * to implicit keep (FIXME) + */ + switch ( sieve_result_implicit_keep(result) ) { + case SIEVE_EXEC_OK: + ret = SIEVE_EXEC_FAILURE; + break; + default: + ret = SIEVE_EXEC_KEEP_FAILED; + } + } + } + + /* Cleanup */ + sieve_result_unref(&result); + *mscript = NULL; + + return ret; +} + int sieve_multiscript_finish(struct sieve_multiscript **mscript, struct sieve_error_handler *ehandler, bool *keep) { @@ -681,7 +718,7 @@ int sieve_multiscript_finish(struct sieve_multiscript **mscript, int ret = (*mscript)->status; if ( ehandler != NULL ) - sieve_result_set_error_handler((*mscript)->result, ehandler); + sieve_result_set_error_handler(result, ehandler); if ( (*mscript)->active ) { ret = SIEVE_EXEC_FAILURE; @@ -689,10 +726,19 @@ int sieve_multiscript_finish(struct sieve_multiscript **mscript, if ( (*mscript)->teststream ) { (*mscript)->keep = TRUE; } else { - if ( !sieve_result_implicit_keep((*mscript)->result) ) - ret = SIEVE_EXEC_KEEP_FAILED; - else + switch ( sieve_result_implicit_keep(result) ) { + case SIEVE_EXEC_OK: (*mscript)->keep = TRUE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + if (!sieve_result_executed(result)) { + ret = SIEVE_EXEC_TEMP_FAILURE; + break; + } + /* fall through */ + default: + ret = SIEVE_EXEC_KEEP_FAILED; + } } } @@ -760,7 +806,7 @@ struct sieve_directory *sieve_directory_open "sieve dir path %s is relative to home directory, " "but home directory is not available.", path); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return NULL; } } @@ -776,13 +822,13 @@ struct sieve_directory *sieve_directory_open sieve_sys_error(svinst, "failed to open sieve dir: %s", eacces_error_get("stat", path)); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; break; default: sieve_sys_error(svinst, "failed to open sieve dir: stat(%s) failed: %m", path); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; break; } return NULL; @@ -801,13 +847,13 @@ struct sieve_directory *sieve_directory_open sieve_sys_error(svinst, "failed to open sieve dir: %s", eacces_error_get("opendir", path)); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; break; default: sieve_sys_error(svinst, "failed to open sieve dir: opendir(%s) failed: " "%m", path); if ( error_r != NULL ) - *error_r = SIEVE_ERROR_TEMP_FAIL; + *error_r = SIEVE_ERROR_TEMP_FAILURE; break; } return NULL; diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index b756c1d..b2b2601 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -191,6 +191,8 @@ bool sieve_multiscript_run int sieve_multiscript_status(struct sieve_multiscript *mscript); +int sieve_multiscript_tempfail(struct sieve_multiscript **mscript, + struct sieve_error_handler *ehandler); int sieve_multiscript_finish (struct sieve_multiscript **mscript, struct sieve_error_handler *ehandler, bool *keep); diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c index eb6a4a4..fb1d65c 100644 --- a/src/lib-sievestorage/sieve-storage.c +++ b/src/lib-sievestorage/sieve-storage.c @@ -596,7 +596,7 @@ static void ATTR_FORMAT(4, 0) sieve_storage_verror if (fmt != NULL) { storage->error = i_strdup_vprintf(fmt, args); } - storage->error_code = SIEVE_ERROR_TEMP_FAIL; + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; } void sieve_storage_clear_error(struct sieve_storage *storage) @@ -631,7 +631,7 @@ void sieve_storage_set_critical sieve_storage_clear_error(storage); if (fmt != NULL) { i_free(storage->error); - storage->error_code = SIEVE_ERROR_TEMP_FAIL; + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { struct tm *tm; diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c index de97723..2e9b961 100644 --- a/src/managesieve/managesieve-client.c +++ b/src/managesieve/managesieve-client.c @@ -382,7 +382,7 @@ void client_send_storage_error error = sieve_storage_get_last_error(storage, &error_code); switch ( error_code ) { - case SIEVE_ERROR_TEMP_FAIL: + case SIEVE_ERROR_TEMP_FAILURE: client_send_noresp(client, "TRYLATER", error); break; diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index 77a1850..bf1ea93 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -128,6 +128,7 @@ struct lda_sieve_run_context { struct sieve_instance *svinst; struct mail_deliver_context *mdctx; + const char *home_dir; struct sieve_script **scripts; unsigned int script_count; @@ -183,18 +184,18 @@ static const char *lda_sieve_get_default_location 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_error_handler *ehandler, ARRAY_TYPE(sieve_scripts) *scripts, + enum sieve_error *error_r) { 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; // FIXME: make this a generic iteration API - if ( (sdir=sieve_directory_open(svinst, location, &error)) == NULL ) - return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); + if ( (sdir=sieve_directory_open(svinst, location, error_r)) == NULL ) + return ( *error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); t_array_init(&script_files, 16); @@ -218,19 +219,23 @@ static int lda_sieve_multiscript_get_scripts files = array_get(&script_files, &count); for ( i = 0; i < count; i++ ) { struct sieve_script *script = sieve_script_create_open - (svinst, files[i], NULL, ehandler, &error); + (svinst, files[i], NULL, ehandler, error_r); if ( script == NULL ) { - switch ( error ) { + switch ( *error_r ) { case SIEVE_ERROR_NOT_FOUND: /* Shouldn't normally happen, but the script could have disappeared */ - sieve_sys_warning - (svinst, "%s script %s doesn't exist", label, files[i]); + sieve_sys_warning(svinst, + "%s script %s doesn't exist", label, files[i]); break; - + case SIEVE_ERROR_TEMP_FAILURE: + sieve_sys_error(svinst, + "failed to access %s script %s (temporary failure)", + label, files[i]); + return -1; default: - sieve_sys_error - (svinst, "failed to access %s script %s", label, files[i]); + sieve_sys_error(svinst, + "failed to access %s script %s", label, files[i]); break; } @@ -240,6 +245,7 @@ static int lda_sieve_multiscript_get_scripts array_append(scripts, &script, 1); } + *error_r = SIEVE_ERROR_NONE; return 1; } @@ -251,7 +257,7 @@ static void lda_sieve_binary_save /* Save binary when compiled */ if ( sieve_save(sbin, FALSE, &error) < 0 && - error == SIEVE_ERROR_NO_PERM && script != srctx->user_script ) { + error == SIEVE_ERROR_NO_PERMISSION && script != srctx->user_script ) { /* Cannot save binary for global script */ sieve_sys_error(srctx->svinst, @@ -264,105 +270,76 @@ static void lda_sieve_binary_save static struct sieve_binary *lda_sieve_open (struct lda_sieve_run_context *srctx, struct sieve_script *script, - enum sieve_compile_flags cpflags, enum sieve_error *error_r) + enum sieve_compile_flags cpflags, bool recompile, enum sieve_error *error_r) { struct sieve_instance *svinst = srctx->svinst; struct sieve_error_handler *ehandler; struct sieve_binary *sbin; bool debug = srctx->mdctx->dest_user->mail_debug; + const char *compile_name = "compile"; + + if ( recompile ) { + /* Warn */ + sieve_sys_warning(svinst, + "encountered corrupt binary: re-compiling script %s", + sieve_script_location(script)); + compile_name = "re-compile"; + } else if ( debug ) { + sieve_sys_debug(svinst, + "loading script %s", sieve_script_location(script)); + } if ( script == srctx->user_script ) ehandler = srctx->user_ehandler; else ehandler = srctx->master_ehandler; - if ( debug ) - sieve_sys_debug(svinst, "loading script %s", sieve_script_location(script)); - sieve_error_handler_reset(ehandler); + if ( recompile ) + sbin = sieve_compile_script(script, ehandler, cpflags, error_r); + else + sbin = sieve_open_script(script, ehandler, cpflags, error_r); + /* Load or compile the sieve script */ - if ( (sbin=sieve_open_script(script, ehandler, cpflags, error_r)) == NULL ) { + if ( sbin == NULL ) { switch ( *error_r ) { /* Script not found */ case SIEVE_ERROR_NOT_FOUND: if ( debug ) { - sieve_sys_debug(svinst, "script file %s is missing", - sieve_script_location(script)); + sieve_sys_debug(svinst, "script file %s is missing for %s", + sieve_script_location(script), compile_name); } break; + /* Temporary failure */ + case SIEVE_ERROR_TEMP_FAILURE: + sieve_sys_error(svinst, + "failed to open script %s for %s (temporary failure)", + sieve_script_location(script), compile_name); + break; /* Compile failed */ case SIEVE_ERROR_NOT_VALID: if (script == srctx->user_script && srctx->userlog != NULL ) { - sieve_sys_info(svinst, "failed to compile script %s " + sieve_sys_info(svinst, "failed to %s script %s " "(view user logfile %s for more information)", - sieve_script_location(script), srctx->userlog); + compile_name, sieve_script_location(script), srctx->userlog); break; } - /* Fall through */ + sieve_sys_error(svinst, "failed to %s script %s", + compile_name, sieve_script_location(script)); + break; /* Something else */ default: - sieve_sys_error(svinst, "failed to open script %s", - sieve_script_location(script)); - break; - } - - return NULL; - } - - lda_sieve_binary_save(srctx, sbin, script); - return sbin; -} - -static struct sieve_binary *lda_sieve_recompile -(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; - struct sieve_error_handler *ehandler; - struct sieve_binary *sbin; - bool debug = srctx->mdctx->dest_user->mail_debug; - - /* Warn */ - - sieve_sys_warning(svinst, - "encountered corrupt binary: re-compiling script %s", - sieve_script_location(script)); - - /* Recompile */ - - if ( script == srctx->user_script ) - ehandler = srctx->user_ehandler; - else - ehandler = srctx->master_ehandler; - - if ( (sbin=sieve_compile_script(script, ehandler, cpflags, error_r)) - == NULL ) { - - switch ( *error_r ) { - case SIEVE_ERROR_NOT_FOUND: - if ( debug ) { - sieve_sys_debug(svinst, "script file %s is missing for re-compile", - sieve_script_location(script)); - } + sieve_sys_error(svinst, "failed to open script %s for %s", + sieve_script_location(script), compile_name); break; - case SIEVE_ERROR_NOT_VALID: - if ( script == srctx->user_script && srctx->userlog != NULL ) { - sieve_sys_info(svinst, - "failed to re-compile script %s " - "(view user logfile %s for more information)", - sieve_script_location(script), srctx->userlog); - break; - } - /* Fall through */ - default: - sieve_sys_error(svinst, "failed to open script %s for re-compile", - sieve_script_location(script)); } return NULL; } + if (!recompile) + lda_sieve_binary_save(srctx, sbin, script); return sbin; } @@ -370,16 +347,16 @@ static int lda_sieve_handle_exec_status (struct lda_sieve_run_context *srctx, struct sieve_script *script, int status) { struct sieve_instance *svinst = srctx->svinst; + struct mail_deliver_context *mdctx = srctx->mdctx; struct sieve_exec_status *estatus = srctx->scriptenv->exec_status; const char *userlog_notice = ""; sieve_sys_error_func_t error_func, user_error_func; + enum mail_error mail_error = MAIL_ERROR_NONE; int ret; error_func = user_error_func = sieve_sys_error; if ( estatus != NULL && estatus->last_storage != NULL ) { - enum mail_error mail_error; - mail_storage_get_last_error(estatus->last_storage, &mail_error); /* Don't bother administrator too much with benign errors */ @@ -391,7 +368,7 @@ static int lda_sieve_handle_exec_status if ( script == srctx->user_script && srctx->userlog != NULL ) { userlog_notice = t_strdup_printf - (" (user logfile %s should reveal additional details)", srctx->userlog); + (" (user logfile %s may reveal additional details)", srctx->userlog); user_error_func = sieve_sys_info; } @@ -402,6 +379,16 @@ static int lda_sieve_handle_exec_status sieve_script_location(script), userlog_notice); ret = 1; break; + case SIEVE_EXEC_TEMP_FAILURE: + error_func(svinst, + "execution of script %s was aborted due to temporary failure%s", + sieve_script_location(script), userlog_notice); + if ( mail_error != MAIL_ERROR_TEMP && mdctx->tempfail_error == NULL ) { + mdctx->tempfail_error = + "Execution of Sieve filters was aborted due to temporary failure"; + } + ret = -1; + break; case SIEVE_EXEC_BIN_CORRUPT: sieve_sys_error(svinst, "!!BUG!!: binary compiled from %s is still corrupt; " @@ -411,7 +398,7 @@ static int lda_sieve_handle_exec_status break; case SIEVE_EXEC_KEEP_FAILED: error_func(svinst, - "script %s failed with unsuccessful implicit keep%s", + "execution of script %s failed with unsuccessful implicit keep%s", sieve_script_location(script), userlog_notice); ret = -1; break; @@ -447,8 +434,18 @@ static int lda_sieve_singlescript_execute /* Open the script */ - if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) - return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); + sbin = lda_sieve_open(srctx, script, cpflags, FALSE, &error); + if ( sbin == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + return 0; + case SIEVE_ERROR_TEMP_FAILURE: + return lda_sieve_handle_exec_status + (srctx, script, SIEVE_EXEC_TEMP_FAILURE); + default: + return -1; + } + } /* Execute */ @@ -467,8 +464,18 @@ static int lda_sieve_singlescript_execute /* Recompile */ - if ( (sbin=lda_sieve_recompile(srctx, script, cpflags, &error)) == NULL ) - return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 ); + sbin = lda_sieve_open(srctx, script, cpflags, TRUE, &error); + if ( sbin == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + return 0; + case SIEVE_ERROR_TEMP_FAILURE: + return lda_sieve_handle_exec_status + (srctx, script, SIEVE_EXEC_TEMP_FAILURE); + default: + return -1; + } + } /* Execute again */ @@ -502,9 +509,9 @@ static int lda_sieve_multiscript_execute bool debug = srctx->mdctx->dest_user->mail_debug; struct sieve_script *last_script = NULL; bool user_script = FALSE, more = TRUE, compile_error = FALSE; - unsigned int i; - int ret = 1; enum sieve_error error; + unsigned int i; + int ret; /* Start execution */ @@ -539,7 +546,8 @@ static int lda_sieve_multiscript_execute sieve_script_location(script)); } - if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) { + sbin = lda_sieve_open(srctx, script, cpflags, FALSE, &error); + if ( sbin == NULL ) { compile_error = TRUE; break; } @@ -562,9 +570,11 @@ static int lda_sieve_multiscript_execute /* Recompile */ - sbin=lda_sieve_recompile(srctx, script, cpflags, &error); - if ( sbin == NULL ) + sbin = lda_sieve_open(srctx, script, cpflags, TRUE, &error); + if ( sbin == NULL ) { + compile_error = TRUE; break; + } /* Execute again */ @@ -583,138 +593,129 @@ static int lda_sieve_multiscript_execute } /* Finish execution */ - - ret = sieve_multiscript_finish(&mscript, ehandler, NULL); + if ( compile_error && error == SIEVE_ERROR_TEMP_FAILURE ) + ret = sieve_multiscript_tempfail(&mscript, ehandler); + else + ret = sieve_multiscript_finish(&mscript, ehandler, NULL); /* Don't log additional messages about compile failure */ if ( compile_error && ret == SIEVE_EXEC_FAILURE ) { sieve_sys_info(svinst, - "aborted script execution sequence with successful implicit keep"); + "aborted script execution sequence with successful implicit keep"); return 1; } return lda_sieve_handle_exec_status(srctx, last_script, ret); } -static int lda_sieve_deliver_mail -(struct mail_deliver_context *mdctx, struct mail_storage **storage_r) +static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) { - 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; - struct sieve_error_handler *master_ehandler; + struct mail_deliver_context *mdctx = srctx->mdctx; + struct sieve_instance *svinst = srctx->svinst; + struct sieve_error_handler *master_ehandler = srctx->master_ehandler; const char *user_location, *default_location, *sieve_before, *sieve_after; const char *setting_name; + enum sieve_error error; ARRAY_TYPE(sieve_scripts) script_sequence; - unsigned int after_index; + struct sieve_script *const *scripts; bool debug = mdctx->dest_user->mail_debug; - enum sieve_error error; - int ret = 0; - - /* Initialize Sieve engine */ - - 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; - svenv.location = SIEVE_ENV_LOCATION_MDA; - svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING; - - svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug); - - /* Initialize master error handler */ - - master_ehandler = sieve_master_ehandler_create(svinst, mdctx->session_id, 0); - sieve_system_ehandler_set(master_ehandler); - - sieve_error_handler_accept_infolog(master_ehandler, TRUE); - sieve_error_handler_accept_debuglog(master_ehandler, debug); - - *storage_r = NULL; - - /* Find scripts and run them */ - - T_BEGIN { - struct sieve_script *const *scripts; - unsigned int count, i; - - /* Initialize run context */ - - memset(&srctx, 0, sizeof(srctx)); - srctx.svinst = svinst; - srctx.mdctx = mdctx; - srctx.master_ehandler = master_ehandler; + unsigned int after_index, count, i; + int ret = 1; - /* Find the personal script to execute */ + /* Find the personal script to execute */ - user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user); - if ( user_location != NULL ) { - srctx.user_script = sieve_script_create_open_as - (svinst, user_location, "main script", master_ehandler, &error); + user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user); + if ( user_location != NULL ) { + srctx->user_script = sieve_script_create_open_as + (svinst, user_location, "main script", master_ehandler, &error); - 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; + if ( srctx->user_script == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + if ( debug ) { + sieve_sys_debug(svinst, "user's script %s doesn't exist " + "(trying default script location instead)", user_location); } - } else { - srctx.main_script = srctx.user_script; + break; + case SIEVE_ERROR_TEMP_FAILURE: + sieve_sys_error(svinst, + "failed to access user's Sieve script %s (temporary failure)", + user_location); + ret = -1; + break; + default: + sieve_sys_error(svinst, + "failed to access user's Sieve script %s " + "(trying default script location instead)", + user_location); + break; } + } else { + srctx->main_script = srctx->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_open_as - (svinst, default_location, "main script", master_ehandler, &error); + if ( ret >= 0 && srctx->user_script == NULL ) { + default_location = lda_sieve_get_default_location(mdctx->dest_user); + if ( default_location != NULL ) { + srctx->main_script = sieve_script_create_open_as + (svinst, default_location, "main script", master_ehandler, &error); - if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND && - debug ) { - sieve_sys_debug(svinst, "default user script %s doesn't exist", + if ( srctx->main_script == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + if ( debug ) { + sieve_sys_debug(svinst, "default user script %s doesn't exist", + default_location); + } + break; + case SIEVE_ERROR_TEMP_FAILURE: + sieve_sys_error(svinst, + "failed to access default user script %s (temporary failure)", default_location); + ret = -1; + break; + default: + sieve_sys_error(svinst, "failed to access default user script %s", + default_location); + break; } - } else { - sieve_sys_debug(svinst, "no default script configured for user"); } + } else { + sieve_sys_debug(svinst, "no default script configured for user"); } + } - if ( debug && srctx.main_script == NULL ) { - sieve_sys_debug(svinst, - "user has no valid location for a personal script"); - } - - /* Compose script array */ + if ( debug && ret >= 0 && srctx->main_script == NULL ) { + sieve_sys_debug(svinst, + "user has no valid location for a personal script"); + } - t_array_init(&script_sequence, 16); + /* Compose script array */ + t_array_init(&script_sequence, 16); + + /* before */ + if ( ret >= 0 ) { i = 2; setting_name = "sieve_before"; sieve_before = mail_user_plugin_getenv(mdctx->dest_user, setting_name); - while ( sieve_before != NULL && *sieve_before != '\0' ) { - if ( lda_sieve_multiscript_get_scripts - (svinst, setting_name, sieve_before, master_ehandler, - &script_sequence) == 0 && debug ) { + while ( ret >= 0 && sieve_before != NULL && *sieve_before != '\0' ) { + ret = lda_sieve_multiscript_get_scripts(svinst, setting_name, + sieve_before, master_ehandler, &script_sequence, &error); + if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) { + ret = -1; + break; + } else if (ret == 0 && debug ) { sieve_sys_debug(svinst, "%s location not found: %s", setting_name, sieve_before); } + ret = 0; setting_name = t_strdup_printf("sieve_before%u", i++); sieve_before = mail_user_plugin_getenv(mdctx->dest_user, setting_name); } - if ( debug ) { + if ( ret >= 0 && debug ) { scripts = array_get(&script_sequence, &count); for ( i = 0; i < count; i ++ ) { sieve_sys_debug(svinst, @@ -722,156 +723,229 @@ static int lda_sieve_deliver_mail i+1, sieve_script_location(scripts[i])); } } + } - if ( srctx.main_script != NULL ) { - array_append(&script_sequence, &srctx.main_script, 1); + /* main */ + 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 ( ret >= 0 && debug ) { + sieve_sys_debug(svinst, + "using the following location for user's Sieve script: %s", + sieve_script_location(srctx->main_script)); } + } - after_index = array_count(&script_sequence); + after_index = array_count(&script_sequence); + /* after */ + if ( ret >= 0 ) { i = 2; setting_name = "sieve_after"; sieve_after = mail_user_plugin_getenv(mdctx->dest_user, setting_name); while ( sieve_after != NULL && *sieve_after != '\0' ) { - if ( lda_sieve_multiscript_get_scripts(svinst, setting_name, - sieve_after, master_ehandler, &script_sequence) == 0 && debug ) { + ret = lda_sieve_multiscript_get_scripts(svinst, setting_name, + sieve_after, master_ehandler, &script_sequence, &error); + if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) { + ret = -1; + break; + } else if (ret == 0 && debug ) { sieve_sys_debug(svinst, "%s location not found: %s", setting_name, sieve_after); } + ret = 0; setting_name = t_strdup_printf("sieve_after%u", i++); sieve_after = mail_user_plugin_getenv(mdctx->dest_user, setting_name); } - if ( debug ) { + if ( ret >= 0 && 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])); } } + } - srctx.scripts = - array_get_modifiable(&script_sequence, &srctx.script_count); + if (ret < 0) { + mdctx->tempfail_error = + "Temporarily unable to access necessary Sieve scripts"; + } + srctx->scripts = + array_get_modifiable(&script_sequence, &srctx->script_count); + return ret; +} - /* Check whether there are any scripts to execute at all */ +static int lda_sieve_execute +(struct lda_sieve_run_context *srctx, struct mail_storage **storage_r) +{ + struct mail_deliver_context *mdctx = srctx->mdctx; + struct sieve_instance *svinst = srctx->svinst; + struct sieve_message_data msgdata; + struct sieve_script_env scriptenv; + struct sieve_exec_status estatus; + bool debug = mdctx->dest_user->mail_debug; + int ret; - if ( srctx.script_count == 0 ) { - if ( debug ) { - sieve_sys_debug(svinst, - "no scripts to execute: reverting to default delivery."); - } + /* Check whether there are any scripts to execute at all */ - /* 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 { - /* 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); + 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. + */ + ret = 0; + } else { + /* 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 ( srctx->home_dir != NULL ) { + log_path = t_strconcat + (srctx->home_dir, "/.dovecot.sieve.log", NULL); } + } else { + /* Use script file as a basic (legacy behavior) */ + log_path = t_strconcat(path, ".log", NULL); } - - if ( log_path != NULL ) { - srctx.userlog = log_path; - srctx.user_ehandler = sieve_logfile_ehandler_create - (svinst, srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS); + } else if ( srctx->home_dir != NULL ) { + /* Expand home dir if necessary */ + if ( log_path[0] == '~' ) { + log_path = home_expand_tilde(log_path, srctx->home_dir); + } else if ( log_path[0] != '/' ) { + log_path = t_strconcat(srctx->home_dir, "/", log_path, NULL); } } - /* Collect necessary message data */ + if ( log_path != NULL ) { + srctx->userlog = log_path; + srctx->user_ehandler = sieve_logfile_ehandler_create + (svinst, srctx->userlog, LDA_SIEVE_MAX_USER_ERRORS); + } + } - memset(&msgdata, 0, sizeof(msgdata)); + /* Collect necessary message data */ - 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); + memset(&msgdata, 0, sizeof(msgdata)); - srctx.msgdata = &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); - /* Compose script execution environment */ + srctx->msgdata = &msgdata; - memset(&scriptenv, 0, sizeof(scriptenv)); - memset(&estatus, 0, sizeof(estatus)); + /* Compose script execution environment */ - 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; + memset(&scriptenv, 0, sizeof(scriptenv)); + memset(&estatus, 0, sizeof(estatus)); - srctx.scriptenv = &scriptenv; + 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; - /* Execute script(s) */ + srctx->scriptenv = &scriptenv; - if ( srctx.script_count == 1 ) - ret = lda_sieve_singlescript_execute(&srctx); - else - ret = lda_sieve_multiscript_execute(&srctx); + /* Execute script(s) */ - /* Record status */ + if ( srctx->script_count == 1 ) + ret = lda_sieve_singlescript_execute(srctx); + else + ret = lda_sieve_multiscript_execute(srctx); - mdctx->tried_default_save = estatus.tried_default_save; - *storage_r = estatus.last_storage; + /* Record status */ - /* Clean up user error handlers */ + mdctx->tried_default_save = estatus.tried_default_save; + *storage_r = estatus.last_storage; + } - if ( srctx.user_ehandler != NULL ) - sieve_error_handler_unref(&srctx.user_ehandler); - } + return ret; +} - /* Cleanup scripts */ - for ( i = 0; i < srctx.script_count; i++ ) { - sieve_script_unref(&srctx.scripts[i]); - } +static int lda_sieve_deliver_mail +(struct mail_deliver_context *mdctx, struct mail_storage **storage_r) +{ + struct lda_sieve_run_context srctx; + bool debug = mdctx->dest_user->mail_debug; + struct sieve_environment svenv; + unsigned int i; + int ret = 0; + + /* Initialize run context */ + + memset(&srctx, 0, sizeof(srctx)); + srctx.mdctx = mdctx; + (void)mail_user_get_home(mdctx->dest_user, &srctx.home_dir); + + /* Initialize Sieve engine */ + + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = mdctx->dest_user->username; + svenv.home_dir = srctx.home_dir; + svenv.hostname = mdctx->set->hostname; + svenv.base_dir = mdctx->dest_user->set->base_dir; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + svenv.location = SIEVE_ENV_LOCATION_MDA; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING; + + srctx.svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug); + + /* Initialize master error handler */ + srctx.master_ehandler = + sieve_master_ehandler_create(srctx.svinst, mdctx->session_id, 0); + sieve_system_ehandler_set(srctx.master_ehandler); + + sieve_error_handler_accept_infolog(srctx.master_ehandler, TRUE); + sieve_error_handler_accept_debuglog(srctx.master_ehandler, debug); + + *storage_r = NULL; + + /* Find Sieve scripts and run them */ + + T_BEGIN { + if (lda_sieve_find_scripts(&srctx) < 0) + ret = -1; + else + ret = lda_sieve_execute(&srctx, storage_r); } T_END; - sieve_deinit(&svinst); - sieve_error_handler_unref(&master_ehandler); + /* Clean up */ + + for ( i = 0; i < srctx.script_count; i++ ) + sieve_script_unref(&srctx.scripts[i]); + if ( srctx.user_ehandler != NULL ) + sieve_error_handler_unref(&srctx.user_ehandler); + sieve_error_handler_unref(&srctx.master_ehandler); + sieve_deinit(&srctx.svinst); + return ret; } diff --git a/src/plugins/sieve-extprograms/cmd-pipe.c b/src/plugins/sieve-extprograms/cmd-pipe.c index 94f05ce..fbbdbc8 100644 --- a/src/plugins/sieve-extprograms/cmd-pipe.c +++ b/src/plugins/sieve-extprograms/cmd-pipe.c @@ -92,7 +92,7 @@ static int act_pipe_check_duplicate static void act_pipe_print (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); -static bool act_pipe_commit +static int act_pipe_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); @@ -328,7 +328,7 @@ static void act_pipe_print /* Result execution */ -static bool act_pipe_commit +static int act_pipe_commit (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep) { @@ -354,6 +354,9 @@ static bool act_pipe_commit if ( ret > 0 ) { sieve_result_global_log(aenv, "pipe action: " "piped message to program `%s'", str_sanitize(act->program_name, 128)); + + /* Indicate that message was successfully 'forwarded' */ + aenv->exec_status->message_forwarded = TRUE; } else { if ( ret < 0 ) { if ( error == SIEVE_ERROR_NOT_FOUND ) { @@ -371,13 +374,13 @@ static bool act_pipe_commit str_sanitize(act->program_name, 80)); } - if ( act->try ) return TRUE; + if ( act->try ) return SIEVE_EXEC_OK; - return FALSE; + return SIEVE_EXEC_FAILURE; } *keep = FALSE; - return TRUE; + return SIEVE_EXEC_OK; } diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.c b/src/plugins/sieve-extprograms/sieve-extprograms-common.c index 59f39f1..283bd2a 100644 --- a/src/plugins/sieve-extprograms/sieve-extprograms-common.c +++ b/src/plugins/sieve-extprograms/sieve-extprograms-common.c @@ -439,12 +439,12 @@ struct sieve_extprogram *sieve_extprogram_create case EACCES: sieve_sys_error(svinst, "action %s: " "failed to stat socket: %s", action, eacces_error_get("stat", path)); - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; return NULL; default: sieve_sys_error(svinst, "action %s: " "failed to stat socket `%s': %m", action, path); - *error_r = SIEVE_ERROR_NOT_POSSIBLE; + *error_r = SIEVE_ERROR_TEMP_FAILURE; return NULL; } path = NULL; @@ -476,12 +476,12 @@ struct sieve_extprogram *sieve_extprogram_create case EACCES: sieve_sys_error(svinst, "action %s: " "failed to stat program: %s", action, eacces_error_get("stat", path)); - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; break; default: sieve_sys_error(svinst, "action %s: " "failed to stat program `%s': %m", action, path); - *error_r = SIEVE_ERROR_NOT_POSSIBLE; + *error_r = SIEVE_ERROR_TEMP_FAILURE; break; } @@ -496,7 +496,7 @@ struct sieve_extprogram *sieve_extprogram_create sieve_sys_error(svinst, "action %s: " "executable `%s' for program `%s' is world-writable", action, path, program_name); - *error_r = SIEVE_ERROR_NO_PERM; + *error_r = SIEVE_ERROR_NO_PERMISSION; return NULL; } } diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c index af3554f..d054806 100644 --- a/src/sieve-tools/sieve-filter.c +++ b/src/sieve-tools/sieve-filter.c @@ -218,15 +218,12 @@ static int filter_message sieve_error(ehandler, NULL, "sieve script binary is corrupt"); return -1; case SIEVE_EXEC_FAILURE: + case SIEVE_EXEC_TEMP_FAILURE: case SIEVE_EXEC_KEEP_FAILED: sieve_error(ehandler, NULL, "sieve script execution failed for this message; " "message left in source mailbox"); return 0; - default: - sieve_error(ehandler, NULL, - "sieve execution result: unrecognized return value?!"); - return -1; } return 1; diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c index 3c78153..63e62e3 100644 --- a/src/sieve-tools/sieve-test.c +++ b/src/sieve-tools/sieve-test.c @@ -371,12 +371,12 @@ int main(int argc, char **argv) i_info("final result: failed; resolved with successful implicit keep"); exit_status = EXIT_FAILURE; break; - case SIEVE_EXEC_KEEP_FAILED: - i_info("final result: utter failure"); + case SIEVE_EXEC_TEMP_FAILURE: + i_info("final result: temporary failure"); exit_status = EXIT_FAILURE; break; - default: - i_info("final result: unrecognized return value?!"); + case SIEVE_EXEC_KEEP_FAILED: + i_info("final result: utter failure"); exit_status = EXIT_FAILURE; break; } diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c index 722ad2a..ab2911a 100644 --- a/src/testsuite/testsuite.c +++ b/src/testsuite/testsuite.c @@ -197,13 +197,12 @@ int main(int argc, char **argv) break; case SIEVE_EXEC_FAILURE: case SIEVE_EXEC_KEEP_FAILED: + case SIEVE_EXEC_TEMP_FAILURE: testsuite_testcase_fail("test script execution aborted due to error"); break; case SIEVE_EXEC_BIN_CORRUPT: testsuite_testcase_fail("compiled test script binary is corrupt"); break; - default: - testsuite_testcase_fail("unknown execution exit code"); } sieve_close(&sbin); -- 1.7.10.2 From ffdb6f4134c012632f1faa8567f03ff580152a6c Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Tue, 18 Jun 2013 08:11:37 +0200 Subject: [PATCH] Don't access ns->storage. Patch by Timo Sirainen. diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c index d054806..7b892ab 100644 --- a/src/sieve-tools/sieve-filter.c +++ b/src/sieve-tools/sieve-filter.c @@ -505,7 +505,7 @@ int main(int argc, char **argv) src_box = mailbox_alloc(ns->list, src_mailbox, open_flags); if ( mailbox_open(src_box) < 0 ) { i_fatal("Couldn't open source mailbox '%s': %s", - src_mailbox, mail_storage_get_last_error(ns->storage, &error)); + src_mailbox, mailbox_get_last_error(src_box, &error)); } /* Open move box if necessary */ @@ -520,7 +520,7 @@ int main(int argc, char **argv) move_box = mailbox_alloc(ns->list, move_mailbox, open_flags); if ( mailbox_open(move_box) < 0 ) { i_fatal("Couldn't open mailbox '%s': %s", - move_mailbox, mail_storage_get_last_error(ns->storage, &error)); + move_mailbox, mailbox_get_last_error(move_box, &error)); } if ( mailbox_backends_equal(src_box, move_box) ) { -- 1.7.10.2 From c6f7816c2d7b93c38549a8220a4c429780560077 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Tue, 18 Jun 2013 08:24:40 +0200 Subject: [PATCH] Fixed line endings in X-Sieve headers added by redirect command. Modified patch by Andriy Syrovenko. diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index d3a2735..51bea21 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -344,10 +344,12 @@ static int act_redirect_send string_t *hdr = t_str_new(256); /* Prepend sieve headers (should not affect signatures) */ - rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); - if ( recipient != NULL ) - rfc2822_header_write(hdr, "X-Sieve-Redirected-From", recipient); - o_stream_send(output, str_data(hdr), str_len(hdr)); + rfc2822_header_append(hdr, "X-Sieve", SIEVE_IMPLEMENTATION, FALSE, NULL); + if ( recipient != NULL ) { + rfc2822_header_append + (hdr, "X-Sieve-Redirected-From", recipient, FALSE, NULL); + } + o_stream_send(output, str_data(hdr), str_len(hdr)); } T_END; o_stream_send_istream(output, input); -- 1.7.10.2 From 21a70095b779fb84c134c06530d3b4b2bda2e333 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Tue, 18 Jun 2013 08:34:26 +0200 Subject: [PATCH] lib-sievestorage: Removed PATH_MAX limitation for active symlink. Fixes issue for GNU/Hurd. diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c index 55f6756..5945f21 100644 --- a/src/lib-sievestorage/sieve-storage-script.c +++ b/src/lib-sievestorage/sieve-storage-script.c @@ -2,6 +2,7 @@ */ #include "lib.h" +#include "abspath.h" #include "mempool.h" #include "hostpid.h" #include "ioloop.h" @@ -118,14 +119,13 @@ static struct sieve_script *sieve_storage_script_init_from_file static int sieve_storage_read_active_link (struct sieve_storage *storage, const char **link_r) { - char linkbuf[PATH_MAX]; - int ret; - - *link_r = NULL; + int ret; - ret = readlink(storage->active_path, linkbuf, sizeof(linkbuf)); + ret = t_readlink(storage->active_path, link_r); if ( ret < 0 ) { + *link_r = NULL; + if ( errno == EINVAL ) { /* Our symlink is no symlink. Report 'no active script'. * Activating a script will automatically resolve this, so @@ -152,7 +152,6 @@ static int sieve_storage_read_active_link } /* ret is now assured to be valid, i.e. > 0 */ - *link_r = t_strndup(linkbuf, ret); return 1; } -- 1.7.10.2