diff --git a/mailsystem/dovecot.nix b/mailsystem/dovecot.nix index 460e0ef..4f17464 100644 --- a/mailsystem/dovecot.nix +++ b/mailsystem/dovecot.nix @@ -92,6 +92,14 @@ with (import ./common.nix {inherit config;}); let chgrp "${cfg.vmailGroupName}" ${cfg.mailDirectory} chmod 02770 ${cfg.mailDirectory} ''; + + junkMailboxes = builtins.attrNames (lib.filterAttrs (n: v: v ? "specialUse" && v.specialUse == "Junk") dovecot2Cfg.mailboxes); + junkMailboxNumber = builtins.length junkMailboxes; + # The assertion guarantees that there is exactly one Junk mailbox. + junkMailboxName = + if junkMailboxNumber == 1 + then builtins.elemAt junkMailboxes 0 + else ""; in { options.mailsystem.dovecot.dhparamSize = lib.mkOption { type = lib.types.int; @@ -101,12 +109,18 @@ in { config = lib.mkIf cfg.enable { assertions = - lib.mapAttrsToList (user: value: [ + [ { + assertion = junkMailboxNumber == 1; + message = "mailnix requires exactly one dovecot mailbox with the 'special use' flag to 'Junk' (${builtins.toString junkMailboxNumber} have been found)"; + } + ] + ++ lib.mapAttrsToList ( + user: value: { assertion = value.hashedPasswordFile != null; message = "A file containing the hashed password for user ${user} needs to be set."; } - ]) + ) cfg.accounts; services.dovecot2 = { @@ -136,6 +150,44 @@ in { sieve = "file:~/sieve;active=~/.dovecot.sieve"; }; + sieve = { + extensions = [ + "fileinto" + "mailbox" + ]; + + scripts.after = builtins.toFile "spam.sieve" '' + require "fileinto"; + require "mailbox"; + + if header :is "X-Spam" "Yes" { + fileinto :create "${junkMailboxName}"; + stop; + } + ''; + + pipeBins = map lib.getExe [ + (pkgs.writeShellScriptBin "learn-ham.sh" + "exec ${pkgs.rspamd}/bin/rspamc -h ${rspamdControllerSocket} learn_ham") + (pkgs.writeShellScriptBin "learn-spam.sh" + "exec ${pkgs.rspamd}/bin/rspamc -h ${rspamdControllerSocket} learn_spam") + ]; + }; + + imapsieve.mailbox = [ + { + name = junkMailboxName; + causes = ["COPY" "APPEND"]; + before = ./dovecot/report-spam.sieve; + } + { + name = "*"; + from = junkMailboxName; + causes = ["COPY"]; + before = ./dovecot/report-ham.sieve; + } + ]; + # TODO: move configuration to default.nix? mailboxes = { Drafts = { diff --git a/mailsystem/dovecot/report-ham.sieve b/mailsystem/dovecot/report-ham.sieve new file mode 100644 index 0000000..6217a90 --- /dev/null +++ b/mailsystem/dovecot/report-ham.sieve @@ -0,0 +1,15 @@ +require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; + +if environment :matches "imap.mailbox" "*" { + set "mailbox" "${1}"; +} + +if string "${mailbox}" "Trash" { + stop; +} + +if environment :matches "imap.user" "*" { + set "username" "${1}"; +} + +pipe :copy "learn-ham.sh" [ "${username}" ]; diff --git a/mailsystem/dovecot/report-spam.sieve b/mailsystem/dovecot/report-spam.sieve new file mode 100644 index 0000000..9d4c74b --- /dev/null +++ b/mailsystem/dovecot/report-spam.sieve @@ -0,0 +1,7 @@ +require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; + +if environment :matches "imap.user" "*" { + set "username" "${1}"; +} + +pipe :copy "learn-spam.sh" [ "${username}" ];