From 85bd2c880498a4c1a1aeaeaddd204075896f4295 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 10 Jul 2014 07:40:51 +0000 Subject: [PATCH] use the Dovecot managesieve service to manage sieve scripts This lets roundcube's manageseive plugin do cool things like vacation responses. Also: * Run the spam filtering sieve script out of a global sieve file that we'll place in /etc/dovecot. It is no longer necessary to create per-user sieve files for this. Remove them with a new migration. Remove the code that created them. * Corrects the spam script. Backslashes were double-escaped probably because this script started embedded within the bash script. Not sure how this was working until now. this adapts work by @h8h in #103 --- conf/{dovecot_sieve.txt => sieve-spam.txt} | 2 +- management/mailconfig.py | 11 +----- setup/mail-dovecot.sh | 42 ++++++++++++++++------ setup/migrate.py | 9 +++++ setup/webmail.sh | 2 +- 5 files changed, 44 insertions(+), 22 deletions(-) rename conf/{dovecot_sieve.txt => sieve-spam.txt} (85%) diff --git a/conf/dovecot_sieve.txt b/conf/sieve-spam.txt similarity index 85% rename from conf/dovecot_sieve.txt rename to conf/sieve-spam.txt index 5428adb5..6f364fea 100644 --- a/conf/dovecot_sieve.txt +++ b/conf/sieve-spam.txt @@ -1,7 +1,7 @@ require ["regex", "fileinto", "imap4flags"]; if allof (header :regex "X-Spam-Status" "^Yes") { - setflag "\\\\Seen"; + setflag "\\Seen"; fileinto "Spam"; stop; } diff --git a/management/mailconfig.py b/management/mailconfig.py index d21069a2..653b98f0 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -84,16 +84,6 @@ def add_mail_user(email, pw, env): if "INBOX" not in existing_mboxes: utils.shell('check_call', ["doveadm", "mailbox", "create", "-u", email, "-s", "INBOX"]) if "Spam" not in existing_mboxes: utils.shell('check_call', ["doveadm", "mailbox", "create", "-u", email, "-s", "Spam"]) - # Create the user's sieve script to move spam into the Spam folder, and make it owned by mail. - maildirstat = os.stat(env["STORAGE_ROOT"] + "/mail/mailboxes") - (em_user, em_domain) = email.split("@", 1) - user_mail_dir = env["STORAGE_ROOT"] + ("/mail/mailboxes/%s/%s" % (em_domain, em_user)) - if not os.path.exists(user_mail_dir): - os.makedirs(user_mail_dir) - os.chown(user_mail_dir, maildirstat.st_uid, maildirstat.st_gid) - shutil.copyfile(utils.CONF_DIR + "/dovecot_sieve.txt", user_mail_dir + "/.dovecot.sieve") - os.chown(user_mail_dir + "/.dovecot.sieve", maildirstat.st_uid, maildirstat.st_gid) - # Update things in case any new domains are added. return kick(env, "mail user added") @@ -222,3 +212,4 @@ if __name__ == "__main__": if len(sys.argv) > 1 and sys.argv[1] == "update": from utils import load_environment print(kick(load_environment())) + diff --git a/setup/mail-dovecot.sh b/setup/mail-dovecot.sh index 00403fcb..003a20ba 100755 --- a/setup/mail-dovecot.sh +++ b/setup/mail-dovecot.sh @@ -20,7 +20,8 @@ source /etc/mailinabox.conf # load global vars # Install packages. apt_install \ - dovecot-core dovecot-imapd dovecot-lmtpd dovecot-sqlite sqlite3 dovecot-sieve + dovecot-core dovecot-imapd dovecot-lmtpd dovecot-sqlite sqlite3 \ + dovecot-sieve dovecot-managesieved # The dovecot-imapd dovecot-lmtpd packages automatically enable IMAP and LMTP protocols. @@ -86,18 +87,35 @@ tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \ # SIEVE -# Enable the Dovecot sieve plugin which let's us set a script that automatically moves -# spam into the user's Spam mail filter. (Note: Be careful if we want to use multiple -# plugins later.) -# -# The actual sieve script is copied into user mailboxes at the time the user account -# is created. Our script moves spam into the user's Spam folder. +# Enable the Dovecot sieve plugin which let's users run scripts that process +# mail as it comes in. We'll also set a global script that moves mail marked +# as spam by Spamassassin into the user's Spam folder. sudo sed -i "s/#mail_plugins = .*/mail_plugins = \$mail_plugins sieve/" /etc/dovecot/conf.d/20-lmtp.conf -# PERMISSIONS +cat > /etc/dovecot/conf.d/99-local-sieve.conf << EOF; +plugin { + # The path to our global sieve which handles moving spam to the Spam folder. + sieve_before = /etc/dovecot/sieve-spam.sieve -# Make the place for mailboxes. -mkdir -p $STORAGE_ROOT/mail + # The path to the user's main active script. ManageSieve will create a symbolic + # link here to the actual sieve script. It should not be in the mailbox directory + # (because then it might appear as a folder) and it should not be in the sieve_dir + # (because then I suppose it might appear to the user as one of their scripts). + sieve = $STORAGE_ROOT/mail/sieve/%d/%n.sieve + + # Directory for :personal include scripts for the include extension. This + # is also where the ManageSieve service stores the user's scripts. + sieve_dir = $STORAGE_ROOT/mail/sieve/%d/%n +} +EOF + +# Copy the global sieve script into where we've told Dovecot to look for it. Then +# compile it. Global scripts must be compiled now because Dovecot won't have +# permission later. +cp `pwd`/conf/sieve-spam.txt /etc/dovecot/sieve-spam.sieve +sievec /etc/dovecot/sieve-spam.sieve + +# PERMISSIONS # Ensure configuration files are owned by dovecot and not world readable. chown -R mail:dovecot /etc/dovecot @@ -107,6 +125,10 @@ chmod -R o-rwx /etc/dovecot mkdir -p $STORAGE_ROOT/mail/mailboxes chown -R mail.mail $STORAGE_ROOT/mail/mailboxes +# Same for the sieve scripts. +mkdir -p $STORAGE_ROOT/mail/sieve +chown -R mail.mail $STORAGE_ROOT/mail/sieve + # Allow the IMAP port in the firewall. ufw_allow imaps diff --git a/setup/migrate.py b/setup/migrate.py index 86788ffb..9256634d 100755 --- a/setup/migrate.py +++ b/setup/migrate.py @@ -36,6 +36,15 @@ def migration_1(env): except: pass +def migration_2(env): + # Delete the .dovecot_sieve script everywhere. This was formerly a copy of our spam -> Spam + # script. We now install it as a global script, and we use managesieve, so the old file is + # irrelevant. Also delete the compiled binary form. + for fn in glob.glob(os.path.join(env["STORAGE_ROOT"], 'mail/mailboxes/*/*/.dovecot.sieve')): + os.unlink(fn) + for fn in glob.glob(os.path.join(env["STORAGE_ROOT"], 'mail/mailboxes/*/*/.dovecot.svbin')): + os.unlink(fn) + def get_current_migration(): ver = 0 while True: diff --git a/setup/webmail.sh b/setup/webmail.sh index 2b202625..95b6c835 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -62,7 +62,7 @@ cat - > /usr/local/lib/roundcubemail/config/config.inc.php <