nix: add git submodule .lib/nixpkgs-commonsoft
[sourcephile-nix.git] / install / logical / friot / dovecot.nix
index 6045bc0415ba3a1df9b0774939628087189948ea..3f809591f8c195f8e63b590585034d1f9869b37c 100644 (file)
@@ -1,7 +1,8 @@
 {pkgs, lib, config, system, ...}:
 let inherit (builtins) toString toFile;
     inherit (lib) types;
-    inherit (pkgs.lib) unlines unlinesAttrs unlinesValues;
+    inherit (pkgs.lib) unlines unlinesAttrs unlinesValues unwords;
+    inherit (config) networking;
     inherit (config.services) dovecot2 postfix x509 openldap;
     when        = x: y: if x == null then "" else y;
     extSep      = postfix.recipientDelimiter;
@@ -17,9 +18,9 @@ let inherit (builtins) toString toFile;
                                         || "0"<=c && c<="9"
                                         || c=="-"
                                         then c else "_");
-    domainGroup = escapeGroup "${config.networking.baseName}";
+    domainGroup = escapeGroup "${networking.domainBase}";
     etc_dovecot = [
-      { target = "dovecot/${config.networking.domain}/dovecot-ldap.conf";
+      { target = "dovecot/${networking.domain}/dovecot-ldap.conf";
         source = pkgs.writeText "dovecot-ldap.conf" ''
           ${lib.optionalString dovecot2.debug ''
             debug_level = 1
@@ -66,7 +67,7 @@ let inherit (builtins) toString toFile;
           #              mailQuota=quota_rule=*:bytes=%$
 
           # doveadm user query
-          iterate_attrs = =user=%{ldap:uid}@${config.networking.domain}
+          iterate_attrs = =user=%{ldap:uid}@${networking.domain}
           iterate_filter = (&(objectClass=posixAccount)(mailEnabled=TRUE))
         '';
       }
@@ -80,6 +81,25 @@ let inherit (builtins) toString toFile;
           all
       '';
     };
+
+    sieve-rspamd-filter =
+      pkgs.stdenv.mkDerivation {
+        name = "sieve-rspamd-filter";
+        nativeBuildInputs = [ pkgs.makeWrapper ];
+        phases = [ "installPhase" ];
+        installPhase = ''
+          mkdir -p $out/bin
+          cat > $out/bin/learn-spam.sh <<EOF
+          #!/bin/sh
+          exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd.sock learn_spam
+          EOF
+          cat > $out/bin/learn-ham.sh <<EOF
+          #!/bin/sh
+          exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd.sock learn_ham
+          EOF
+          chmod +x $out/bin/*.sh
+        '';
+      };
 in
 {
   imports = [
@@ -133,6 +153,8 @@ in
 
         installSieve = ''
           rm -rf "${sieveDir}"
+          install -D -d -m 0755 -o root -g root \
+           "${sieveDir}/bin"
         '' + unlinesAttrs (dir: sieves: ''
           install -D -d -m 0755 -o root -g root \
            ${sieveDir} ${sieveDir}/${dir}.d
@@ -150,9 +172,10 @@ in
           install -D -d -m 1770 \
            -o ${dovecot2.mailUser} \
            -g ${domainGroup} \
-           ${mailDir}/${config.networking.domain} \
-           ${stateDir}/control/${config.networking.domain} \
-           ${stateDir}/index/${config.networking.domain}
+           ${mailDir}/${networking.domain} \
+           ${stateDir}/control/${networking.domain} \
+           ${stateDir}/index/${networking.domain}
+
           # NOTE: do not set the sticky bit (+t)
           #       on acl/<domain>/, to let dovecot
           #       rename acl.db.lock (own by new user)
@@ -160,7 +183,16 @@ in
           install -D -d -m 0770 \
            -o ${dovecot2.mailUser} \
            -g ${domainGroup} \
-           ${stateDir}/acl/${config.networking.domain}
+           ${stateDir}/acl/${networking.domain}
+
+          # NOTE: domainAliases point to the very same mailboxes as domain's.
+          for domainAlias in ${unwords networking.domainAliases}
+           do
+            ln -fns ${networking.domain} ${mailDir}/$domainAlias
+            ln -fns ${networking.domain} ${stateDir}/control/$domainAlias
+            ln -fns ${networking.domain} ${stateDir}/index/$domainAlias
+            ln -fns ${networking.domain} ${stateDir}/acl/$domainAlias
+           done
         '';
       };
     };
@@ -208,6 +240,14 @@ in
               addflag "Junk";
             }
           '';
+          /*
+            require ["fileinto","mailbox"];
+
+            if header :contains "X-Spam" "Yes" {
+             fileinto :create "INBOX.Junk";
+             stop;
+            }
+          */
           extension = ''
             require
              [ "envelope"
@@ -224,12 +264,38 @@ in
               stop;
             }
           '';
+          report-spam = ''
+            require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
+
+            if environment :matches "imap.user" "*" {
+              set "username" "''${1}";
+            }
+
+            pipe :copy "learn-spam.sh" [ "''${username}" ];
+          '';
+          report-ham = ''
+            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}" ];
+          '';
         };
       };
       configFile = toString (pkgs.writeText "dovecot.conf" ''
         passdb {
           driver = ldap
-          args = /etc/dovecot/${config.networking.domain}/dovecot-ldap.conf
+          args = /etc/dovecot/${networking.domain}/dovecot-ldap.conf
           default_fields = userdb_mail_access_groups=${domainGroup}
           override_fields =
         }
@@ -239,7 +305,7 @@ in
         userdb {
           # NOTE: this userdb is only used by lda.
           driver = ldap
-          args = /etc/dovecot/${config.networking.domain}/dovecot-ldap.conf
+          args = /etc/dovecot/${networking.domain}/dovecot-ldap.conf
           default_fields = mail_access_groups=${domainGroup}
           override_fields =
           skip = found
@@ -257,7 +323,7 @@ in
         #}
         mail_home = ${mailDir}/%d/%n
           # NOTE: if needed, may be overrided by userdb_mail
-        mail_location = maildir:${mailDir}/%d/%n/Maildir:LAYOUT=fs:INDEX=${stateDir}/index/%d/%n:CONTROL=${stateDir}/control/%d/%n
+        mail_location = maildir:${mailDir}/%d/%n/Maildir:LAYOUT=fs:INDEX=${stateDir}/index/%d/%n:INDEXPVT=${stateDir}/index/%d/%n:CONTROL=${stateDir}/control/%d/%n
           # NOTE: if needed, may be overrided by userdb_mail
           # NOTE: INDEX and CONTROL are on a partition without quota, as explain in the doc.
           # SEE: http://wiki2.dovecot.org/Quota/FS
@@ -285,6 +351,7 @@ in
         #maildir_copy_with_hardlinks = yes
         namespace inbox {
           # NOTE: here because protocol sieve {namespace inbox{}} does not seem to work.
+          type      = private
           inbox     = yes
           location  =
           list      = yes
@@ -292,14 +359,18 @@ in
           separator = ${dirSep}
         }
         namespace {
-          #list          = children
-          list          = yes
-          location      = maildir:${mailDir}/%d/%n/Maildir:LAYOUT=fs:INDEX=${stateDir}/index/%d/%n/Shared/%n:CONTROL=${stateDir}/control/%d/%n/Shared/%n
-            # FIXME: %d not working
-          prefix        = Partages+%%n+
-          separator     = ${dirSep}
+          type = shared
+          #list = children
+          list = yes
+            # NOTE: always listed in the LIST command.
+          location = maildir:${mailDir}/%%d/%%n/Maildir:LAYOUT=fs:INDEX=${stateDir}/index/%%d/%%n/Shared:INDEXPVT=${stateDir}/index/%d/%n/Shared/%%n:CONTROL=${stateDir}/control/%d/%n/Shared
+            # NOTE: how to access the other users' mailboxes.
+            # NOTE: %var expands to the logged in user's variable, while
+            #       %%var expands to the other users' variables.
+            # NOTE: INDEX and CONTROL are shared, INDEXPVT is not.
+          prefix = Partages+%%n+
+          separator = ${dirSep}
           subscriptions = yes
-          type          = shared
         }
         mail_plugins = $mail_plugins acl quota virtual
         #mail_uid = ${dovecot2.mailUser}
@@ -362,7 +433,25 @@ in
           sieve_spamtest_status_header = X-Spam-Score
           sieve_spamtest_status_type = strlen
           sieve_user_log = /var/log/dovecot/%d/sieve.%n.log
+
+          sieve_plugins = sieve_imapsieve sieve_extprograms
+          sieve_pipe_bin_dir = ${sieve-rspamd-filter}/bin
+          sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
+          # From elsewhere to Spam folder
+          imapsieve_mailbox1_name = Spam
+          imapsieve_mailbox1_causes = COPY
+          imapsieve_mailbox1_before = file:${sieveDir}/global.d/report-spam.sieve
+
+          # From Spam folder to elsewhere
+          imapsieve_mailbox2_name = *
+          imapsieve_mailbox2_from = Spam
+          imapsieve_mailbox2_causes = COPY
+          imapsieve_mailbox2_before = file:${sieveDir}/global.d/report-ham.sieve
         }
+        # If you have Dovecot v2.2.8+ you may get a significant performance improvement with fetch-headers:
+        imapc_features = $imapc_features fetch-headers
+        # Read multiple mails in parallel, improves performance
+        mail_prefetch_count = 20
         service quota-warning {
           executable = script ${
             pkgs.writeScript "quota-warning" ''
@@ -371,7 +460,7 @@ in
               USER=$2
               cat << EOF | ${pkgs.dovecot}/libexec/dovecot/dovecot-lda -d $USER -o
               "plugin/quota=maildir:User quota:noenforcing"
-              From: postmaster@${config.networking.domain}
+              From: postmaster@${networking.domain}
               Subject: [WARNING] your mailbox is now $PERCENT% full.
 
               Please remove some mails to make room for new ones.
@@ -385,7 +474,7 @@ in
         }
         protocol imap {
           #mail_max_userip_connections = 10
-          mail_plugins = $mail_plugins imap_acl imap_quota # antispam
+          mail_plugins = $mail_plugins imap_acl imap_quota imap_sieve # antispam
           namespace inbox {
             inbox = yes
             location =
@@ -395,15 +484,21 @@ in
             }
             mailbox Junk {
               special_use = \Junk
+              auto = subscribe
+              #autoexpunge = 30d
             }
             mailbox Sent {
               special_use = \Sent
+              auto = subscribe
             }
             mailbox "Sent Messages" {
               special_use = \Sent
+              auto = subscribe
             }
             mailbox Trash {
               special_use = \Trash
+              auto = subscribe
+              #autoexpunge = 30d
             }
             prefix =
             separator = ${dirSep}
@@ -411,7 +506,7 @@ in
         }
         protocol lda {
           auth_socket_path = /var/run/dovecot/auth-userdb
-          hostname         = ${config.networking.domain}
+          hostname         = ${networking.domain}
           info_log_path    =
           log_path         =
           mail_plugins     = $mail_plugins sieve
@@ -422,7 +517,7 @@ in
             prefix    =
             separator = ${dirSep}
           }
-          postmaster_address = postmaster${extSep}dovecot${extSep}lda@${config.networking.domain}
+          postmaster_address = postmaster${extSep}dovecot${extSep}lda@${networking.domain}
           syslog_facility = mail
         }
         protocol lmtp {
@@ -435,7 +530,7 @@ in
             prefix    =
             separator = ${dirSep}
           }
-          postmaster_address = postmaster${extSep}dovecot${extSep}lmtp@${config.networking.domain}
+          postmaster_address = postmaster${extSep}dovecot${extSep}lmtp@${networking.domain}
         }
         protocol pop3 {
           #mail_max_userip_connections = 10