From 7246ef04b6294e7284b21c3bde8f61b77e2b3043 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Sat, 21 Feb 2015 17:28:22 +0100 Subject: [PATCH] doveadm sieve plugin: Implemented commands for managing Sieve scripts. This allows performing ManageSieve actions at command line or through doveadm server. --- .hgignore | 1 + doc/man/Makefile.am | 28 +- doc/man/doveadm-sieve.1.in | 122 +++ doc/man/global-options-formatter.inc | 46 ++ doc/man/global-options.inc | 21 + doc/man/option-A.inc | 27 + doc/man/option-S-socket.inc | 10 + doc/man/option-u-user.inc | 20 + doc/man/pigeonhole.7.in | 16 +- doc/man/reporting-bugs.inc | 2 +- doc/man/sed.sh | 27 +- src/lib-sieve/sieve-script.c | 6 +- src/lib-sieve/sieve-script.h | 2 +- src/managesieve/cmd-deletescript.c | 8 +- src/plugins/doveadm-sieve/Makefile.am | 15 +- .../doveadm-sieve-cmd-activate.c | 136 ++++ .../doveadm-sieve/doveadm-sieve-cmd-delete.c | 115 +++ .../doveadm-sieve/doveadm-sieve-cmd-get.c | 75 ++ .../doveadm-sieve/doveadm-sieve-cmd-list.c | 74 ++ .../doveadm-sieve/doveadm-sieve-cmd-put.c | 180 +++++ .../doveadm-sieve/doveadm-sieve-cmd-rename.c | 77 ++ src/plugins/doveadm-sieve/doveadm-sieve-cmd.c | 181 +++++ src/plugins/doveadm-sieve/doveadm-sieve-cmd.h | 48 ++ .../doveadm-sieve/doveadm-sieve-plugin.c | 720 +---------------- .../doveadm-sieve/doveadm-sieve-plugin.h | 20 + .../doveadm-sieve/doveadm-sieve-sync.c | 727 ++++++++++++++++++ 26 files changed, 1954 insertions(+), 750 deletions(-) create mode 100644 doc/man/doveadm-sieve.1.in create mode 100644 doc/man/global-options-formatter.inc create mode 100644 doc/man/global-options.inc create mode 100644 doc/man/option-A.inc create mode 100644 doc/man/option-S-socket.inc create mode 100644 doc/man/option-u-user.inc create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd.c create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-cmd.h create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-plugin.h create mode 100644 src/plugins/doveadm-sieve/doveadm-sieve-sync.c diff --git a/.hgignore b/.hgignore index 696b3982a..785bb8e0c 100644 --- a/.hgignore +++ b/.hgignore @@ -56,6 +56,7 @@ m4/lt~obsolete.m4 **/.libs **/.deps +doc/man/doveadm-sieve.1 doc/man/sievec.1 doc/man/sieve-dump.1 doc/man/sieve-test.1 diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 1e4091c66..2d2af01aa 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -1,10 +1,13 @@ pkgsysconfdir = $(sysconfdir)/dovecot rundir = ${prefix}/var/run/dovecot +SUFFIXES = .1.in .1 .7.in .7 + dist_man1_MANS = \ sieved.1 nodist_man1_MANS = \ + doveadm-sieve.1 \ sievec.1 \ sieve-dump.1 \ sieve-test.1 \ @@ -14,9 +17,15 @@ nodist_man7_MANS = \ pigeonhole.7 man_includefiles = \ + $(srcdir)/global-options-formatter.inc \ + $(srcdir)/global-options.inc \ + $(srcdir)/option-A.inc \ + $(srcdir)/option-S-socket.inc \ + $(srcdir)/option-u-user.inc \ $(srcdir)/reporting-bugs.inc EXTRA_DIST = \ + doveadm-sieve.1.in \ sievec.1.in \ sieve-dump.1.in \ sieve-test.1.in \ @@ -27,22 +36,11 @@ EXTRA_DIST = \ CLEANFILES = $(nodist_man1_MANS) $(nodist_man7_MANS) -sievec.1: $(srcdir)/sievec.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/sievec.1.in > sievec.1 - -sieve-dump.1: $(srcdir)/sieve-dump.1.in $(man_includefiles) Makefile +.1.in.1: $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/sieve-dump.1.in > sieve-dump.1 - -sieve-filter.1: $(srcdir)/sieve-filter.1.in $(man_includefiles) Makefile + $(pkglibexecdir) < $< > $@ +.7.in.7: $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/sieve-filter.1.in > sieve-filter.1 + $(pkglibexecdir) < $< > $@ -sieve-test.1: $(srcdir)/sieve-test.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/sieve-test.1.in > sieve-test.1 -pigeonhole.7: $(srcdir)/pigeonhole.7.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/pigeonhole.7.in > pigeonhole.7 diff --git a/doc/man/doveadm-sieve.1.in b/doc/man/doveadm-sieve.1.in new file mode 100644 index 000000000..38b30a63a --- /dev/null +++ b/doc/man/doveadm-sieve.1.in @@ -0,0 +1,122 @@ +.\" Copyright (c) 2010-2015 Pigeonhole authors, see the included COPYING file +.TH DOVEADM\-SIEVE 1 "2015-02-21" "Pigeonhole v0.4 for Dovecot v2.2" "Pigeonhole" +.SH NAME +doveadm\-sieve \- Commands related to handling Sieve scripts +.\"------------------------------------------------------------------------ +.SH SYNOPSIS +.BR doveadm " [" \-Dv "] [" \-f +.IR formatter "] " sieve_cmd " [" options "] [" arguments ] +.\"------------------------------------------------------------------------ +.SH DESCRIPTION +.PP +The +.B doveadm sieve +commands are part of the Pigeonhole Project (\fBpigeonhole\fR(7)), which adds +Sieve (RFC 5228) and ManageSieve (RFC 5804) support to the Dovecot secure IMAP +and POP3 server (\fBdovecot\fR(1)). The +.B doveadm sieve +commands can be used to manage Sieve filtering. +.\"------------------------------------------------------------------------ +@INCLUDE:global-options-formatter@ +.\" --- command specific options --- "/. +.PP +Command specific +.IR options : +.\"------------------------------------- +@INCLUDE:option-A@ +.\"------------------------------------- +@INCLUDE:option-S-socket@ +.\"------------------------------------- +@INCLUDE:option-u-user@ +.\"------------------------------------------------------------------------ +.SH ARGUMENTS +.TP +.I scriptname +Is the name of a +.IR Sieve\ script , +as visible to ManageSieve clients. +.\"------------------------------------------------------------------------ +.SH COMMANDS +.SS sieve put +.B doveadm sieve put +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.RB [ \-a ] +.IR scriptname +.PP +This command puts one new Sieve script in the script storage. The script +is read from standard input. If the script compiles successfully, it is stored +under the provided +.IR scriptname\ . +If the +.B \-a +option is present, the Sieve script is subsequently marked as the active script +for execution at delivery. +.\"------------------------------------------------------------------------ +.SS sieve get +.B doveadm sieve get +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.I scriptname +.PP +This command retrieves the Sieve script named +.IR scriptname . +.\"------------------------------------------------------------------------ +.SS sieve delete +.B doveadm sieve delete +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.RB [ \-a ] +.IR scriptname\ ... +.PP +This command deletes one or more Sieve scripts. The deleted script may not be the +active script, unless the +.B \-a +option is present. +.\"------------------------------------------------------------------------ +.SS sieve list +.B doveadm sieve list +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.I scriptname +.PP +Use this command to get an overview of existing Sieve scripts. +.\"------------------------------------------------------------------------ +.SS sieve rename +.B doveadm sieve rename +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.I old_name +.I new_name +.PP +The +.B sieve rename +command is used to rename the Sieve script +.I old_name +to +.IR new_name . +.\"------------------------------------------------------------------------ +.SS sieve activate +.B doveadm sieve activate +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.IR scriptname +.PP +This command marks the Sieve script named +.I scriptname +as the active script for execution at delivery. +.\"------------------------------------------------------------------------ +.SS sieve deactivate +.B doveadm sieve deactivate +[\fB\-A\fP|\fB\-u\fP \fIuser\fP] +[\fB\-S\fP \fIsocket_path\fP] +.I scriptname +.PP +This command deactivates Sieve processing. +.\"------------------------------------------------------------------------ +@INCLUDE:reporting-bugs@ +.\"------------------------------------------------------------------------ +.SH SEE ALSO +.BR doveadm (1) +.BR dovecot\-lda (1), +.BR pigeonhole (7) diff --git a/doc/man/global-options-formatter.inc b/doc/man/global-options-formatter.inc new file mode 100644 index 000000000..cb792e117 --- /dev/null +++ b/doc/man/global-options-formatter.inc @@ -0,0 +1,46 @@ +.SH OPTIONS +Global +.BR doveadm (1) +.IR options : +.TP +.B \-D +Enables verbosity and debug messages. +.TP +.BI \-f\ formatter +Specifies the +.I formatter +for formatting the output. +Supported formatters are: +.RS +.TP +.B flow +prints each line with +.IB key = value +pairs. +.TP +.B pager +prints each +.IR key :\ value +pair on its own line and separates records with form feed character +.RB ( ^L ). +.TP +.B tab +prints a table header followed by tab separated value lines. +.TP +.B table +prints a table header followed by adjusted value lines. +.RE +.TP +.BI \-o\ setting = value +Overrides the configuration +.I setting +from +.I @pkgsysconfdir@/dovecot.conf +and from the userdb with the given +.IR value . +In order to override multiple settings, the +.B \-o +option may be specified multiple times. +.TP +.B \-v +Enables verbosity, including progress counter. diff --git a/doc/man/global-options.inc b/doc/man/global-options.inc new file mode 100644 index 000000000..bf99294d4 --- /dev/null +++ b/doc/man/global-options.inc @@ -0,0 +1,21 @@ +.SH OPTIONS +Global +.BR doveadm (1) +.IR options : +.TP +.B \-D +Enables verbosity and debug messages. +.TP +.BI \-o\ setting = value +Overrides the configuration +.I setting +from +.I @pkgsysconfdir@/dovecot.conf +and from the userdb with the given +.IR value . +In order to override multiple settings, the +.B \-o +option may be specified multiple times. +.TP +.B \-v +Enables verbosity, including progress counter. diff --git a/doc/man/option-A.inc b/doc/man/option-A.inc new file mode 100644 index 000000000..256e93996 --- /dev/null +++ b/doc/man/option-A.inc @@ -0,0 +1,27 @@ +.TP +.B \-A +If the +.B \-A +option is present, the +.I command +will be performed for all users. +Using this option in combination with system users from +.B userdb { driver = passwd } +is not recommended, because it contains also users with a lower UID than +the one configured with the +.I first_valid_uid +setting. +.sp +When the SQL userdb module is used make sure that the +.I iterate_query +setting in +.I @pkgsysconfdir@/dovecot\-sql.conf.ext +matches your database layout. +When using the LDAP userdb module, make sure that the +.IR iterate_attrs " and " iterate_filter +settings in +.I @pkgsysconfdir@/dovecot-ldap.conf.ext +match your LDAP schema. +Otherwise +.BR doveadm (1) +will be unable to iterate over all users. diff --git a/doc/man/option-S-socket.inc b/doc/man/option-S-socket.inc new file mode 100644 index 000000000..2e0e3743d --- /dev/null +++ b/doc/man/option-S-socket.inc @@ -0,0 +1,10 @@ +.TP +.BI \-S\ socket_path +The option\(aqs argument is either an absolute path to a local UNIX domain +socket, or a hostname and port +.RI ( hostname : port ), +in order to connect a remote host via a TCP socket. +.sp +This allows an administrator to execute +.BR doveadm (1) +mail commands through the given socket. diff --git a/doc/man/option-u-user.inc b/doc/man/option-u-user.inc new file mode 100644 index 000000000..e8611452b --- /dev/null +++ b/doc/man/option-u-user.inc @@ -0,0 +1,20 @@ +.TP +.BI \-u\ user/mask +Run the +.I command +only for the given +.IR user . +It\(aqs also possible to use +.RB \(aq * \(aq +and +.RB \(aq ? \(aq +wildcards (e.g. \-u *@example.org). +.br +When neither the +.B \-A +option nor +.BI \-u\ user +was specified, the +.I command +will be executed with the environment of the +currently logged in user. diff --git a/doc/man/pigeonhole.7.in b/doc/man/pigeonhole.7.in index d4a6c90bf..d2df7a02a 100644 --- a/doc/man/pigeonhole.7.in +++ b/doc/man/pigeonhole.7.in @@ -1,5 +1,5 @@ .\" Copyright (c) 2010-2015 Pigeonhole authors, see the included COPYING file -.TH "PIGEONHOLE" 7 "2014-01-01" "Pigeonhole for Dovecot v2.2" "Pigeonhole" +.TH "PIGEONHOLE" 7 "2015-02-21" "Pigeonhole v0.4 for Dovecot v2.2" "Pigeonhole" .\"------------------------------------------------------------------------ .SH NAME pigeonhole \- Overview of the Pigeonhole project\(aqs Sieve support for the @@ -40,18 +40,22 @@ The LDA Sieve plugin for Dovecot\(aqs Local Delivery Agent (LDA) (\fBdovecot\-lda\fR(1)) that facilitates the actual Sieve filtering upon delivery. .IP \(bu -The ManageSieve Service that implements the ManageSieve protocol through which +The ManageSieve service that implements the ManageSieve protocol through which users can remotely manage Sieve scripts on the server. .IP \(bu -Command line tools that provide the means to manually compile, analyse and test -Sieve scripts. +A plugin for Dovecot\(aqs +.BR doveadm (1) +command line tool that adds new the new +.BR doveadm-sieve (1) +commands for management of Sieve filtering. .PP The functionality and configuration of the LDA Sieve plugin and the ManageSieve service is described in detail in the README and INSTALL files contained in the Pigeonhole package and in the Dovecot Wiki <http://wiki2.dovecot.org/Pigeonhole>. .PP -The following command line tools are available: +The following command line tools are available outside of +.BR doveadm : .TP .BR sievec (1) Compiles Sieve scripts into a binary representation for later execution. @@ -78,6 +82,8 @@ LGPLv2.1 license, which is the same license as Dovecot, see .SH "SEE ALSO" .BR dovecot (1), .BR dovecot\-lda (1), +.BR doveadm (1), +.BR doveadm-sieve (1), .BR sieve\-dump (1), .BR sieve\-test (1), .BR sieve\-filter (1), diff --git a/doc/man/reporting-bugs.inc b/doc/man/reporting-bugs.inc index c3288a8ca..1823ca2cd 100644 --- a/doc/man/reporting-bugs.inc +++ b/doc/man/reporting-bugs.inc @@ -2,5 +2,5 @@ Report bugs, including .I doveconf \-n output, to the Dovecot Mailing List <dovecot@dovecot.org>. -Information about reporting Dovecot and Pigeonhole bugs is available at: +Information about reporting bugs is available at: http://dovecot.org/bugreport.html diff --git a/doc/man/sed.sh b/doc/man/sed.sh index e96c38d5f..6da39636e 100644 --- a/doc/man/sed.sh +++ b/doc/man/sed.sh @@ -3,9 +3,32 @@ SRCDIR="${1:-`pwd`}" RUNDIR="${2:-/usr/local/var/run/dovecot}" PKGSYSCONFDIR="${3:-/usr/local/etc/dovecot}" +PKGLIBEXECDIR="${4:-/usr/local/libexec/dovecot}" -sed -e "/^@INCLUDE:reporting-bugs@$/{ +sed -e "/^@INCLUDE:global-options@$/{ + r ${SRCDIR}/global-options.inc + d + }" \ + -e "/^@INCLUDE:global-options-formatter@$/{ + r ${SRCDIR}/global-options-formatter.inc + d + }" \ + -e "/^@INCLUDE:option-A@$/{ + r ${SRCDIR}/option-A.inc + d + }" \ + -e "/^@INCLUDE:option-S-socket@$/{ + r ${SRCDIR}/option-S-socket.inc + d + }" \ + -e "/^@INCLUDE:option-u-user@$/{ + r ${SRCDIR}/option-u-user.inc + d + }" \ + -e "/^@INCLUDE:reporting-bugs@$/{ r ${SRCDIR}/reporting-bugs.inc d - }" | sed -e "s|@pkgsysconfdir@|${PKGSYSCONFDIR}|" -e "s|@rundir@|${RUNDIR}|" + }" | sed -e "s|@pkgsysconfdir@|${PKGSYSCONFDIR}|" \ + -e "s|@rundir@|${RUNDIR}|" \ + -e "s|@pkglibexecdir@|${PKGLIBEXECDIR}|" diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index b360934cf..d412b3dfa 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -483,9 +483,8 @@ int sieve_script_rename return ret; } -int sieve_script_delete(struct sieve_script **_script) +int sieve_script_delete(struct sieve_script *script) { - struct sieve_script *script = *_script; struct sieve_storage *storage = script->storage; int ret = 0; @@ -505,9 +504,6 @@ int sieve_script_delete(struct sieve_script **_script) /* unset INBOX mailbox attribute */ if ( ret >= 0 ) (void)sieve_storage_sync_script_delete(storage, script->name); - - /* Always deinitialize the script object */ - sieve_script_unref(_script); return ret; } diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h index a496ecad1..ec1051d18 100644 --- a/src/lib-sieve/sieve-script.h +++ b/src/lib-sieve/sieve-script.h @@ -95,7 +95,7 @@ int sieve_script_rename (struct sieve_script *script, const char *newname); int sieve_script_is_active(struct sieve_script *script); int sieve_script_activate(struct sieve_script *script, time_t mtime); -int sieve_script_delete(struct sieve_script **script); +int sieve_script_delete(struct sieve_script *script); /* * Properties diff --git a/src/managesieve/cmd-deletescript.c b/src/managesieve/cmd-deletescript.c index 99aba9d53..fc596084b 100644 --- a/src/managesieve/cmd-deletescript.c +++ b/src/managesieve/cmd-deletescript.c @@ -23,19 +23,17 @@ bool cmd_deletescript(struct client_command_context *cmd) script = sieve_storage_open_script (storage, scriptname, NULL); - if (script == NULL) { + if ( script == NULL ) { client_send_storage_error(client, storage); return TRUE; } - if (sieve_script_delete(&script) < 0) { + if ( sieve_script_delete(script) < 0 ) { client_send_storage_error(client, storage); } else { client_send_ok(client, "Deletescript completed."); } - /* Script object is deleted no matter what in - * sieve_script_delete() - */ + sieve_script_unref(&script); return TRUE; } diff --git a/src/plugins/doveadm-sieve/Makefile.am b/src/plugins/doveadm-sieve/Makefile.am index 48f7009d3..3a5c18909 100644 --- a/src/plugins/doveadm-sieve/Makefile.am +++ b/src/plugins/doveadm-sieve/Makefile.am @@ -1,13 +1,26 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib-sieve \ - $(LIBDOVECOT_INCLUDE) + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_STORAGE_INCLUDE) \ + $(LIBDOVECOT_DOVEADM_INCLUDE) doveadm_moduledir = $(dovecot_moduledir)/doveadm lib10_doveadm_sieve_plugin_la_LDFLAGS = -module -avoid-version doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la +commands = \ + doveadm-sieve-cmd-list.c \ + doveadm-sieve-cmd-get.c \ + doveadm-sieve-cmd-put.c \ + doveadm-sieve-cmd-delete.c \ + doveadm-sieve-cmd-activate.c \ + doveadm-sieve-cmd-rename.c + lib10_doveadm_sieve_plugin_la_SOURCES = \ + $(commands) \ + doveadm-sieve-cmd.c \ + doveadm-sieve-sync.c \ doveadm-sieve-plugin.c lib10_doveadm_sieve_plugin_la_LIBADD = \ $(top_builddir)/src/lib-sieve/libdovecot-sieve.la diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c new file mode 100644 index 000000000..139f64a92 --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c @@ -0,0 +1,136 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_activate_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *scriptname; +}; + +static int +cmd_sieve_activate_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_activate_cmd_context *ctx = + (struct doveadm_sieve_activate_cmd_context *)_ctx; + struct sieve_storage *storage = _ctx->storage; + struct sieve_script *script; + enum sieve_error error; + int ret = 0; + + script = sieve_storage_open_script + (storage, ctx->scriptname, NULL); + if ( script == NULL ) { + i_error("Failed to activate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + + if ( sieve_script_is_active(script) <= 0 ) { + /* Script is first being activated; compile it again without the UPLOAD + * flag. + */ + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_ACTIVATED; + struct sieve_binary *sbin; + enum sieve_error error; + + /* Compile */ + ehandler = sieve_master_ehandler_create(ctx->ctx.svinst, NULL, 0); + if ( (sbin=sieve_compile_script + (script, ehandler, cpflags, &error)) == NULL ) { + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } else { + sieve_close(&sbin); + } + sieve_error_handler_unref(&ehandler); + } + + /* Activate only when script is valid (or already active) */ + if ( ret == 0 ) { + /* Refresh activation no matter what; this can also resolve some erroneous + * situations. + */ + ret = sieve_script_activate(script, (time_t)-1); + if ( ret < 0 ) { + i_error("Failed to activate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + + sieve_script_unref(&script); + return ret; +} + +static int cmd_sieve_deactivate_run +(struct doveadm_sieve_cmd_context *_ctx) +{ + struct sieve_storage *storage = _ctx->storage; + enum sieve_error error; + + if (sieve_storage_deactivate(storage, (time_t)-1) < 0) { + i_error("Failed to deactivate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + return 0; +} + +static void cmd_sieve_activate_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_activate_cmd_context *ctx = + (struct doveadm_sieve_activate_cmd_context *)_ctx; + + if (str_array_length(args) != 1) + doveadm_mail_help_name("sieve activate"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->scriptname = p_strdup(ctx->ctx.ctx.pool, args[0]); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_activate_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->ctx.v.init = cmd_sieve_activate_init; + ctx->v.run = cmd_sieve_activate_run; + return &ctx->ctx; +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_deactivate_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->v.run = cmd_sieve_deactivate_run; + return &ctx->ctx; +} + +struct doveadm_mail_cmd doveadm_sieve_cmd_activate = { + cmd_sieve_activate_alloc, "sieve activate", "<scriptname>" +}; + +struct doveadm_mail_cmd doveadm_sieve_cmd_deactivate = { + cmd_sieve_deactivate_alloc, "sieve deactivate", NULL +}; + + diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c new file mode 100644 index 000000000..c6451145d --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c @@ -0,0 +1,115 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_delete_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + ARRAY_TYPE(const_string) scriptnames; + unsigned int ignore_active:1; +}; + +static int +cmd_sieve_delete_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_delete_cmd_context *ctx = + (struct doveadm_sieve_delete_cmd_context *)_ctx; + struct sieve_storage *storage = _ctx->storage; + const ARRAY_TYPE(const_string) *scriptnames = &ctx->scriptnames; + const char *const *namep; + struct sieve_script *script; + enum sieve_error error; + int ret = 0; + + array_foreach(scriptnames, namep) { + const char *scriptname = *namep; + int sret = 0; + + script = sieve_storage_open_script + (storage, scriptname, NULL); + if (script == NULL) { + sret = -1; + } else { + if (sieve_script_delete(script) < 0) { + (void)sieve_storage_get_last_error(storage, &error); + if (!ctx->ignore_active || error != SIEVE_ERROR_ACTIVE || + sieve_storage_deactivate(storage, (time_t)-1) < 0 || + sieve_script_delete(script) < 0) { + sret = -1; + } + } + sieve_script_unref(&script); + } + + if (sret < 0) { + i_error("Failed to delete Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + return ret; +} + +static void cmd_sieve_delete_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_delete_cmd_context *ctx = + (struct doveadm_sieve_delete_cmd_context *)_ctx; + const char *name; + unsigned int i; + + if (args[0] == NULL) + doveadm_mail_help_name("sieve delete"); + doveadm_sieve_cmd_scriptnames_check(args); + + for (i = 0; args[i] != NULL; i++) { + name = p_strdup(ctx->ctx.ctx.pool, args[i]); + array_append(&ctx->scriptnames, &name, 1); + } +} + +static bool +cmd_sieve_delete_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct doveadm_sieve_delete_cmd_context *ctx = + (struct doveadm_sieve_delete_cmd_context *)_ctx; + + switch ( c ) { + case 'a': + ctx->ignore_active = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_delete_alloc(void) +{ + struct doveadm_sieve_delete_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_delete_cmd_context); + ctx->ctx.ctx.getopt_args = "a"; + ctx->ctx.ctx.v.parse_arg = cmd_sieve_delete_parse_arg; + ctx->ctx.ctx.v.init = cmd_sieve_delete_init; + ctx->ctx.v.run = cmd_sieve_delete_run; + p_array_init(&ctx->scriptnames, ctx->ctx.ctx.pool, 16); + return &ctx->ctx.ctx; +} + +struct doveadm_mail_cmd doveadm_sieve_cmd_delete = { + cmd_sieve_delete_alloc, "sieve delete", "[-a] <scriptname> [...]" +}; + diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c new file mode 100644 index 000000000..0167be9f3 --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c @@ -0,0 +1,75 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-print.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_get_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *scriptname; +}; + +static int +cmd_sieve_get_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_get_cmd_context *ctx = + (struct doveadm_sieve_get_cmd_context *)_ctx; + struct sieve_script *script; + struct istream *input; + enum sieve_error error; + + script = sieve_storage_open_script + (_ctx->storage, ctx->scriptname, &error); + if ( script == NULL || sieve_script_get_stream + (script, &input, &error) < 0 ) { + i_error("Failed to open Sieve script: %s", + sieve_storage_get_last_error(_ctx->storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + if (script != NULL) + sieve_script_unref(&script); + return -1; + } + + return doveadm_print_istream(input); +} + +static void cmd_sieve_get_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_get_cmd_context *ctx = + (struct doveadm_sieve_get_cmd_context *)_ctx; + + if ( str_array_length(args) != 1 ) + doveadm_mail_help_name("sieve get"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->scriptname = p_strdup(ctx->ctx.ctx.pool, args[0]); + + doveadm_print_header_simple("sieve script"); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_get_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->ctx.v.init = cmd_sieve_get_init; + ctx->v.run = cmd_sieve_get_run; + doveadm_print_init("pager"); + return &ctx->ctx; +} + +struct doveadm_mail_cmd doveadm_sieve_cmd_get = { + cmd_sieve_get_alloc, "sieve get", "<scriptname>" +}; + diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c new file mode 100644 index 000000000..500f81174 --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-print.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +static int +cmd_sieve_list_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct sieve_storage *storage = _ctx->storage; + struct sieve_storage_list_context *lctx; + enum sieve_error error; + const char *scriptname; + bool active; + + if ( (lctx = sieve_storage_list_init(storage)) + == NULL ) { + i_error("Listing Sieve scripts failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + + while ( (scriptname=sieve_storage_list_next(lctx, &active)) + != NULL ) { + doveadm_print(scriptname); + if ( active ) + doveadm_print("ACTIVE"); + else + doveadm_print(""); + } + + if ( sieve_storage_list_deinit(&lctx) < 0 ) { + i_error("Listing Sieve scripts failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + return 0; +} + +static void cmd_sieve_list_init +(struct doveadm_mail_cmd_context *_ctx ATTR_UNUSED, + const char *const args[] ATTR_UNUSED) +{ + doveadm_print_header("script", "script", + DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); + doveadm_print_header("active", "active", + DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_list_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->ctx.v.init = cmd_sieve_list_init; + ctx->ctx.getopt_args = "s"; + ctx->v.run = cmd_sieve_list_run; + doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); + return &ctx->ctx; +} + +struct doveadm_mail_cmd doveadm_sieve_cmd_list = { + cmd_sieve_list_alloc, "sieve list", NULL +}; + diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c new file mode 100644 index 000000000..284a7ee40 --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c @@ -0,0 +1,180 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "istream.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_put_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *scriptname; + + unsigned int activate:1; +}; + +static int cmd_sieve_put_run +(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_put_cmd_context *ctx = + (struct doveadm_sieve_put_cmd_context *)_ctx; + struct sieve_storage_save_context *save_ctx; + struct sieve_storage *storage = _ctx->storage; + struct istream *input = _ctx->ctx.cmd_input; + enum sieve_error error; + ssize_t ret; + bool save_failed = FALSE; + + save_ctx = sieve_storage_save_init + (storage, ctx->scriptname, input); + if ( save_ctx == NULL ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + + while ( (ret = i_stream_read(input)) > 0 || ret == -2 ) { + if ( sieve_storage_save_continue(save_ctx) < 0 ) { + save_failed = TRUE; + ret = -1; + break; + } + } + i_assert(ret == -1); + + if ( input->stream_errno != 0 ) { + i_error("read(script input) failed: %s", i_stream_get_error(input)); + doveadm_sieve_cmd_failed_error + (&ctx->ctx, SIEVE_ERROR_TEMP_FAILURE); + } else if ( save_failed ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, NULL)); + doveadm_sieve_cmd_failed_storage(&ctx->ctx, storage); + } else if ( sieve_storage_save_finish(save_ctx) < 0 ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, NULL)); + doveadm_sieve_cmd_failed_storage(&ctx->ctx, storage); + } else { + ret = 0; + } + + /* Verify that script compiles */ + if ( ret == 0 ) { + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED; + struct sieve_script *script; + struct sieve_binary *sbin; + + /* Obtain script object for uploaded script */ + script = sieve_storage_save_get_tempscript(save_ctx); + + /* Check result */ + if ( script == NULL ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + + } else { + /* Mark this as an activation when we are replacing the active script */ + if ( ctx->activate || sieve_storage_save_will_activate(save_ctx) ) + cpflags |= SIEVE_COMPILE_FLAG_ACTIVATED; + + /* Compile */ + ehandler = sieve_master_ehandler_create(ctx->ctx.svinst, NULL, 0); + if ( (sbin=sieve_compile_script + (script, ehandler, cpflags, &error)) == NULL ) { + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } else { + sieve_close(&sbin); + + /* Script is valid; commit it to storage */ + ret = sieve_storage_save_commit(&save_ctx); + if (ret < 0) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + sieve_error_handler_unref(&ehandler); + } + } + + if ( save_ctx != NULL ) + sieve_storage_save_cancel(&save_ctx); + + if ( ctx->activate && ret == 0 ) { + struct sieve_script *script = sieve_storage_open_script + (storage, ctx->scriptname, NULL); + if ( script == NULL || + sieve_script_activate(script, (time_t)-1) < 0) { + i_error("Failed to activate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + + i_assert(input->eof); + return ret < 0 ? -1 : 0; +} + +static void cmd_sieve_put_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_put_cmd_context *ctx = + (struct doveadm_sieve_put_cmd_context *)_ctx; + + if ( str_array_length(args) != 1 ) + doveadm_mail_help_name("sieve put"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->scriptname = p_strdup(ctx->ctx.ctx.pool, args[0]); + + doveadm_mail_get_input(_ctx); +} + +static bool +cmd_sieve_put_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct doveadm_sieve_put_cmd_context *ctx = + (struct doveadm_sieve_put_cmd_context *)_ctx; + + switch ( c ) { + case 'a': + ctx->activate = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_put_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->ctx.getopt_args = "a"; + ctx->ctx.v.parse_arg = cmd_sieve_put_parse_arg; + ctx->ctx.v.init = cmd_sieve_put_init; + ctx->v.run = cmd_sieve_put_run; + return &ctx->ctx; +} + +struct doveadm_mail_cmd doveadm_sieve_cmd_put = { + cmd_sieve_put_alloc, "sieve put", "[-a] <scriptname>" +}; + diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c new file mode 100644 index 000000000..c470ed4ae --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c @@ -0,0 +1,77 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_rename_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *oldname, *newname; +}; + +static int +cmd_sieve_rename_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_rename_cmd_context *ctx = + (struct doveadm_sieve_rename_cmd_context *)_ctx; + struct sieve_storage *storage = _ctx->storage; + struct sieve_script *script; + enum sieve_error error; + int ret = 0; + + script = sieve_storage_open_script + (storage, ctx->oldname, NULL); + if ( script == NULL ) { + i_error("Failed to rename Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } else if ( sieve_script_rename(script, ctx->newname) < 0 ) { + i_error("Failed to rename Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + + if ( script != NULL ) + sieve_script_unref(&script); + return ret; +} + +static void cmd_sieve_rename_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_rename_cmd_context *ctx = + (struct doveadm_sieve_rename_cmd_context *)_ctx; + + if ( str_array_length(args) != 2 ) + doveadm_mail_help_name("sieve rename"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->oldname = p_strdup(ctx->ctx.ctx.pool, args[0]); + ctx->newname = p_strdup(ctx->ctx.ctx.pool, args[1]); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_rename_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->ctx.v.init = cmd_sieve_rename_init; + ctx->v.run = cmd_sieve_rename_run; + return &ctx->ctx; +} + +struct doveadm_mail_cmd doveadm_sieve_cmd_rename = { + cmd_sieve_rename_alloc, "sieve rename", "<oldname> <newname>" +}; + diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c new file mode 100644 index 000000000..552533011 --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c @@ -0,0 +1,181 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "unichar.h" +#include "mail-storage.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +void doveadm_sieve_cmd_failed_error +(struct doveadm_sieve_cmd_context *ctx, enum sieve_error error) +{ + struct doveadm_mail_cmd_context *mctx = &ctx->ctx; + int exit_code = 0; + + switch ( error ) { + case SIEVE_ERROR_NONE: + i_unreached(); + case SIEVE_ERROR_TEMP_FAILURE: + exit_code = EX_TEMPFAIL; + break; + case SIEVE_ERROR_NOT_POSSIBLE: + case SIEVE_ERROR_EXISTS: + case SIEVE_ERROR_NOT_VALID: + case SIEVE_ERROR_ACTIVE: + exit_code = DOVEADM_EX_NOTPOSSIBLE; + break; + case SIEVE_ERROR_BAD_PARAMS: + exit_code = EX_USAGE; + break; + case SIEVE_ERROR_NO_PERMISSION: + exit_code = EX_NOPERM; + break; + case SIEVE_ERROR_NO_QUOTA: + exit_code = EX_CANTCREAT; + break; + case SIEVE_ERROR_NOT_FOUND: + exit_code = DOVEADM_EX_NOTFOUND; + break; + default: + i_unreached(); + } + /* tempfail overrides all other exit codes, otherwise use whatever + error happened first */ + if ( mctx->exit_code == 0 || exit_code == EX_TEMPFAIL ) + mctx->exit_code = exit_code; +} + +void doveadm_sieve_cmd_failed_storage +(struct doveadm_sieve_cmd_context *ctx, struct sieve_storage *storage) +{ + enum sieve_error error; + + (void)sieve_storage_get_last_error(storage, &error); + doveadm_sieve_cmd_failed_error(ctx, error); +} + +static const char *doveadm_sieve_cmd_get_setting +(void *context, const char *identifier) +{ + struct doveadm_sieve_cmd_context *ctx = + (struct doveadm_sieve_cmd_context *) context; + + return mail_user_plugin_getenv(ctx->ctx.cur_mail_user, identifier); +} + +static const struct sieve_callbacks sieve_callbacks = { + NULL, + doveadm_sieve_cmd_get_setting +}; + +static bool doveadm_sieve_cmd_parse_arg +(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct doveadm_sieve_cmd_context *ctx = + (struct doveadm_sieve_cmd_context *)_ctx; + + switch (c) { + case 's': // FIXME: our own common options here + ctx->subscriptions = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +void doveadm_sieve_cmd_scriptnames_check(const char *const args[]) +{ + unsigned int i; + + for (i = 0; args[i] != NULL; i++) { + if (!uni_utf8_str_is_valid(args[i])) { + i_fatal_status(EX_DATAERR, + "Sieve script name not valid UTF-8: %s", args[i]); + } + if ( !sieve_script_name_is_valid(args[i]) ) { + i_fatal_status(EX_DATAERR, + "Sieve script name not valid: %s", args[i]); + } + } +} + +static int +doveadm_sieve_cmd_run +(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user) +{ + struct doveadm_sieve_cmd_context *ctx = + (struct doveadm_sieve_cmd_context *)_ctx; + struct sieve_environment svenv; + enum sieve_error error; + int ret; + + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.base_dir = user->set->base_dir; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + + ctx->svinst = sieve_init + (&svenv, &sieve_callbacks, (void *)ctx, user->mail_debug); + + ctx->storage = sieve_storage_create_main + (ctx->svinst, user, SIEVE_STORAGE_FLAG_READWRITE, &error); + if ( ctx->storage == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_FOUND: + i_error("Failed to open Sieve storage: Sieve disabled for user"); + break; + default: + i_error("Failed to open Sieve storage."); + } + doveadm_sieve_cmd_failed_error(ctx, error); + ret = -1; + + } else { + i_assert( ctx->v.run != NULL ); + ret = ctx->v.run(ctx); + sieve_storage_unref(&ctx->storage); + } + + sieve_deinit(&ctx->svinst); + return ret; +} + +struct doveadm_sieve_cmd_context * +doveadm_sieve_cmd_alloc_size(size_t size) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = (struct doveadm_sieve_cmd_context *) + doveadm_mail_cmd_alloc_size(size); + ctx->ctx.getopt_args = "s"; + ctx->ctx.v.parse_arg = doveadm_sieve_cmd_parse_arg; + ctx->ctx.v.run = doveadm_sieve_cmd_run; + return ctx; +} + +static struct doveadm_mail_cmd *doveadm_sieve_commands[] = { + &doveadm_sieve_cmd_list, + &doveadm_sieve_cmd_get, + &doveadm_sieve_cmd_put, + &doveadm_sieve_cmd_delete, + &doveadm_sieve_cmd_activate, + &doveadm_sieve_cmd_deactivate, + &doveadm_sieve_cmd_rename +}; + +void doveadm_sieve_cmds_init(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(doveadm_sieve_commands); i++) + doveadm_mail_register_cmd(doveadm_sieve_commands[i]); +} diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd.h b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.h new file mode 100644 index 000000000..2057077bc --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __DOVEADM_SIEVE_CMD_H +#define __DOVEADM_SIEVE_CMD_H + +struct doveadm_sieve_cmd_context; + +struct doveadm_sieve_cmd_vfuncs { + /* This is the main function which performs all the work for the + command. This is called once per each user. */ + int (*run)(struct doveadm_sieve_cmd_context *ctx); +}; + +struct doveadm_sieve_cmd_context { + struct doveadm_mail_cmd_context ctx; + + struct sieve_instance *svinst; + struct sieve_storage *storage; + + struct doveadm_sieve_cmd_vfuncs v; + + unsigned int subscriptions; // FIXME: remove +}; + +void doveadm_sieve_cmd_failed_error +(struct doveadm_sieve_cmd_context *ctx, enum sieve_error error); +void doveadm_sieve_cmd_failed_storage +(struct doveadm_sieve_cmd_context *ctx, struct sieve_storage *storage); + +#define doveadm_sieve_cmd_alloc(type) \ + (type *)doveadm_sieve_cmd_alloc_size(sizeof(type)) +struct doveadm_sieve_cmd_context * +doveadm_sieve_cmd_alloc_size(size_t size); + +void doveadm_sieve_cmd_scriptnames_check(const char *const args[]); + +extern struct doveadm_mail_cmd doveadm_sieve_cmd_list; +extern struct doveadm_mail_cmd doveadm_sieve_cmd_get; +extern struct doveadm_mail_cmd doveadm_sieve_cmd_put; +extern struct doveadm_mail_cmd doveadm_sieve_cmd_delete; +extern struct doveadm_mail_cmd doveadm_sieve_cmd_activate; +extern struct doveadm_mail_cmd doveadm_sieve_cmd_deactivate; +extern struct doveadm_mail_cmd doveadm_sieve_cmd_rename; + +void doveadm_sieve_cmds_init(void); + +#endif diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c index f80b552fa..a22f21a8c 100644 --- a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c +++ b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c @@ -2,729 +2,19 @@ */ #include "lib.h" -#include "str.h" -#include "ioloop.h" -#include "time-util.h" -#include "istream.h" -#include "istream-concat.h" -#include "mail-storage-private.h" +#include "doveadm-mail.h" #include "sieve.h" -#include "sieve-script.h" -#include "sieve-storage.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_storage_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); +#include "doveadm-sieve-cmd.h" +#include "doveadm-sieve-plugin.h" 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); - - if (suser->sieve_storage != NULL) - sieve_storage_unref(&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_READWRITE | - SIEVE_STORAGE_FLAG_SYNCHRONIZING; - struct mail_user_vfuncs *v = user->vlast; - struct sieve_environment svenv; - - if (suser != NULL) { - *svstorage_r = suser->sieve_storage; - return suser->sieve_storage != NULL ? 1 : 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; - - suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks, - user, user->mail_debug); - suser->sieve_storage = sieve_storage_create_main - (suser->svinst, user, storage_flags, NULL); - - MODULE_CONTEXT_SET(user, sieve_user_module, suser); - *svstorage_r = suser->sieve_storage; - return suser->sieve_storage != NULL ? 1 : 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_open_script(svstorage, scriptname, NULL); - ret = script == NULL ? -1 : sieve_script_delete(&script); - if (ret < 0) { - errstr = sieve_storage_get_last_error(svstorage, &error); - if (error == SIEVE_ERROR_NOT_FOUND) { - /* 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; - - if (scriptname == NULL) { - /* don't affect non-link active script */ - if ((ret=sieve_storage_is_singular(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; - } - i_assert(scriptname[0] == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK); - scriptname++; - - /* activate specified script */ - script = sieve_storage_open_script(svstorage, scriptname, NULL); - ret = script == NULL ? -1 : - sieve_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_is_singular(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 - (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 if (value->value_stream != NULL) { - 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]; - } else { - type = MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT; - } - 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_storage_save_context *save_ctx; - struct istream *input; - const char *scriptname; - int ret; - - if ((ret = mail_sieve_user_init(storage->user, &svstorage)) <= 0) { - if (ret == 0) { - mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, - "Sieve not enabled for user"); - } - 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); - } - - 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; - } - - sieve_storage_save_set_mtime(save_ctx, value->last_change); - - ret = 0; - 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 || ret < 0); - 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); - - if (t->box->storage->user->dsyncing && - type == MAIL_ATTRIBUTE_TYPE_PRIVATE && - strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, - strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) == 0) { - time_t ts = - (value->last_change != 0 ? value->last_change : ioloop_time); - - if (sieve_attribute_set_sieve(t->box->storage, key, value) < 0) - return -1; - - if (user->mail_debug) { - const char *change; - if (value->last_change != 0) { - change = t_strflocaltime - ("(last change: %Y-%m-%d %H:%M:%S)", value->last_change); - } else { - change = t_strflocaltime - ("(time: %Y-%m-%d %H:%M:%S)", ioloop_time); - } - i_debug("doveadm-sieve: Assigned value for key `%s' %s", - key, change); - } - - /* 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) -{ - static 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_is_singular(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_open - (svstorage, NULL)) == NULL) - return 0; - - if ((ret=sieve_attribute_retrieve_script - (storage, svstorage, script, TRUE, value_r, &errstr)) < 0) { - mail_storage_set_critical(storage, - "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 ((ret = mail_sieve_user_init(storage->user, &svstorage)) <= 0) - return ret; - - 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_open_script(svstorage, scriptname, NULL); - 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; - int ret; - - if (user->mail_debug) - i_debug("doveadm-sieve: Iterating Sieve mailbox attributes"); - - if ((ret = mail_sieve_user_init(user, &svstorage)) <= 0) - return ret; - - 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; - - if (siter->sieve_list == NULL) - return NULL; - - /* 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_is_singular(svstorage)) < 0) { - mail_storage_set_critical(siter->iter.box->storage, - "Failed to iterate sieve scripts: %s", - sieve_storage_get_last_error(svstorage, NULL)); - 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); + doveadm_sieve_sync_init(module); + doveadm_sieve_cmds_init(); } void doveadm_sieve_plugin_deinit(void) diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.h b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.h new file mode 100644 index 000000000..954fefb64 --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __DOVEADM_SIEVE_PLUGIN_H +#define __DOVEADM_SIEVE_PLUGIN_H + +/* + * Plugin interface + */ + +void doveadm_sieve_plugin_init(struct module *module); +void doveadm_sieve_plugin_deinit(void); + +/* + * Replication + */ + +void doveadm_sieve_sync_init(struct module *module); + +#endif /* __DOVEADM_SIEVE_PLUGIN_H */ diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-sync.c b/src/plugins/doveadm-sieve/doveadm-sieve-sync.c new file mode 100644 index 000000000..c11f0d3ab --- /dev/null +++ b/src/plugins/doveadm-sieve/doveadm-sieve-sync.c @@ -0,0 +1,727 @@ +/* Copyright (c) 2002-2015 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "ioloop.h" +#include "time-util.h" +#include "istream.h" +#include "istream-concat.h" +#include "mail-storage-private.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-plugin.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_storage_list_context *sieve_list; + string_t *name; + + bool failed; + bool have_active; +}; + +static MODULE_CONTEXT_DEFINE_INIT(sieve_storage_module, + &mail_storage_module_register); +static MODULE_CONTEXT_DEFINE_INIT(sieve_user_module, + &mail_user_module_register); + +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); + + if (suser->sieve_storage != NULL) + sieve_storage_unref(&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_READWRITE | + SIEVE_STORAGE_FLAG_SYNCHRONIZING; + struct mail_user_vfuncs *v = user->vlast; + struct sieve_environment svenv; + + if (suser != NULL) { + *svstorage_r = suser->sieve_storage; + return suser->sieve_storage != NULL ? 1 : 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; + + suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks, + user, user->mail_debug); + suser->sieve_storage = sieve_storage_create_main + (suser->svinst, user, storage_flags, NULL); + + MODULE_CONTEXT_SET(user, sieve_user_module, suser); + *svstorage_r = suser->sieve_storage; + return suser->sieve_storage != NULL ? 1 : 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_open_script(svstorage, scriptname, NULL); + ret = script == NULL ? -1 : sieve_script_delete(&script); + if (ret < 0) { + errstr = sieve_storage_get_last_error(svstorage, &error); + if (error == SIEVE_ERROR_NOT_FOUND) { + /* 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; + + if (scriptname == NULL) { + /* don't affect non-link active script */ + if ((ret=sieve_storage_is_singular(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; + } + i_assert(scriptname[0] == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK); + scriptname++; + + /* activate specified script */ + script = sieve_storage_open_script(svstorage, scriptname, NULL); + ret = script == NULL ? -1 : + sieve_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_is_singular(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 + (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 if (value->value_stream != NULL) { + 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]; + } else { + type = MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT; + } + 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_storage_save_context *save_ctx; + struct istream *input; + const char *scriptname; + int ret; + + if ((ret = mail_sieve_user_init(storage->user, &svstorage)) <= 0) { + if (ret == 0) { + mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, + "Sieve not enabled for user"); + } + 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); + } + + 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; + } + + sieve_storage_save_set_mtime(save_ctx, value->last_change); + + ret = 0; + 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 || ret < 0); + 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); + + if (t->box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE && + strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE, + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) == 0) { + time_t ts = + (value->last_change != 0 ? value->last_change : ioloop_time); + + if (sieve_attribute_set_sieve(t->box->storage, key, value) < 0) + return -1; + + if (user->mail_debug) { + const char *change; + if (value->last_change != 0) { + change = t_strflocaltime + ("(last change: %Y-%m-%d %H:%M:%S)", value->last_change); + } else { + change = t_strflocaltime + ("(time: %Y-%m-%d %H:%M:%S)", ioloop_time); + } + i_debug("doveadm-sieve: Assigned value for key `%s' %s", + key, change); + } + + /* 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) +{ + static 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_is_singular(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_open + (svstorage, NULL)) == NULL) + return 0; + + if ((ret=sieve_attribute_retrieve_script + (storage, svstorage, script, TRUE, value_r, &errstr)) < 0) { + mail_storage_set_critical(storage, + "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 ((ret = mail_sieve_user_init(storage->user, &svstorage)) <= 0) + return ret; + + 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_open_script(svstorage, scriptname, NULL); + 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; + int ret; + + if (user->mail_debug) + i_debug("doveadm-sieve: Iterating Sieve mailbox attributes"); + + if ((ret = mail_sieve_user_init(user, &svstorage)) <= 0) + return ret; + + 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; + + if (siter->sieve_list == NULL) + return NULL; + + /* 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_is_singular(svstorage)) < 0) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + 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_sync_init(struct module *module) +{ + mail_storage_hooks_add_forced + (module, &doveadm_sieve_mail_storage_hooks); +} -- GitLab