about summary refs log tree commit diff
path: root/nixos/modules/services/mail
diff options
context:
space:
mode:
authorRaito Bezarius <masterancpp@gmail.com>2024-01-14 21:07:42 +0100
committerRaito Bezarius <masterancpp@gmail.com>2024-01-23 14:04:08 +0100
commitcaf9e51e0f836a14f34acafef3cde2e608f15f33 (patch)
tree0415417ea0820a73a3f13f9711a4ca91154689b4 /nixos/modules/services/mail
parenta95d98f05a5da6cf66bef2584cf7a70b474d4518 (diff)
nixos/mail/dovecot2: re-introduce extra settings and rename `sieveScripts`
https://github.com/NixOS/nixpkgs/pull/275031 introduced structured configuration
for the dovecot2 sieve plugin, by doing so, it broke SNM configuration doing Sieve configurations.

This attempts to fix up the public API to make it possible for SNM to pick up the pieces.
Diffstat (limited to 'nixos/modules/services/mail')
-rw-r--r--nixos/modules/services/mail/dovecot.nix89
1 files changed, 69 insertions, 20 deletions
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 25c7017a1d258..13f3e337cec28 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -4,7 +4,8 @@ let
   inherit (lib) any attrValues concatMapStringsSep concatStrings
     concatStringsSep flatten imap1 isList literalExpression mapAttrsToList
     mkEnableOption mkIf mkOption mkRemovedOptionModule optional optionalAttrs
-    optionalString singleton types;
+    optionalString singleton types mkRenamedOptionModule nameValuePair
+    mapAttrs' listToAttrs;
 
   cfg = config.services.dovecot2;
   dovecotPkg = pkgs.dovecot;
@@ -12,6 +13,30 @@ let
   baseDir = "/run/dovecot2";
   stateDir = "/var/lib/dovecot";
 
+  sieveScriptSettings = mapAttrs' (to: from: nameValuePair "sieve_${to}" "${stateDir}/sieve/${from}") cfg.sieve.scripts;
+  imapSieveMailboxSettings = listToAttrs (flatten (imap1 (idx: el:
+    singleton {
+      name = "imapsieve_mailbox${toString idx}_name";
+      value = el.name;
+    } ++ optional (el.from != null) {
+      name = "imapsieve_mailbox${toString idx}_from";
+      value = el.from;
+    } ++ optional (el.causes != []) {
+      name = "imapsieve_mailbox${toString idx}_causes";
+      value = concatStringsSep "," el.causes;
+    } ++ optional (el.before != null) {
+      name = "imapsieve_mailbox${toString idx}_before";
+      value = "file:${stateDir}/imapsieve/before/${baseNameOf el.before}";
+    } ++ optional (el.after != null) {
+      name = "imapsieve_mailbox${toString idx}_after";
+      value = "file:${stateDir}/imapsieve/after/${baseNameOf el.after}";
+    }
+  ) cfg.imapsieve.mailbox));
+  sievePipeBinScriptDirectory = pkgs.linkFarm "sieve-pipe-bins" (map (el: {
+      name = builtins.unsafeDiscardStringContext (baseNameOf el);
+      path = el;
+  }) cfg.sieve.pipeBins);
+
   dovecotConf = concatStrings [
     ''
       base_dir = ${baseDir}
@@ -78,14 +103,6 @@ let
     )
 
     (
-      optionalString (cfg.sieveScripts != {}) ''
-        plugin {
-          ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieveScripts)}
-        }
-      ''
-    )
-
-    (
       optionalString (cfg.mailboxes != {}) ''
         namespace inbox {
           inbox=yes
@@ -116,11 +133,16 @@ let
       ''
     )
 
+    # General plugin settings:
+    # - sieve is mostly generated here, refer to `pluginSettings` to follow
+    # the control flow.
     ''
       plugin {
+        ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieve.scripts)}
         sieve_plugins = ${concatStringsSep " " cfg.sieve.plugins}
         sieve_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions)}
         sieve_global_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions)}
+        ${concatStringsSep "\n" (mapAttrsToList (key: value: "${key} = ${value}") cfg.sieve.extraPluginSettings)}
     ''
     (optionalString (cfg.imapsieve.mailbox != []) ''
       ${
@@ -199,6 +221,7 @@ in
 {
   imports = [
     (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "")
+    (mkRenamedOptionModule [ "services" "dovecot2" "sieveScripts" ] [ "services" "dovecot2" "sieve" "scripts" ])
   ];
 
   options.services.dovecot2 = {
@@ -337,12 +360,6 @@ in
 
     enableDHE = mkEnableOption (lib.mdDoc "ssl_dh and generation of primes for the key exchange") // { default = true; };
 
-    sieveScripts = mkOption {
-      type = types.attrsOf types.path;
-      default = {};
-      description = lib.mdDoc "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc.";
-    };
-
     showPAMFailure = mkEnableOption (lib.mdDoc "showing the PAM failure message on authentication error (useful for OTPW)");
 
     mailboxes = mkOption {
@@ -376,6 +393,26 @@ in
       description = lib.mdDoc "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %.";
     };
 
+
+    pluginSettings = mkOption {
+      # types.str does not coerce from packages, like `sievePipeBinScriptDirectory`.
+      type = types.attrsOf (types.oneOf [ types.str types.package ]);
+      default = {};
+      example = literalExpression ''
+        {
+          sieve = "file:~/sieve;active=~/.dovecot.sieve";
+        }
+      '';
+      description = ''
+        Plugin settings for dovecot in general, e.g. `sieve`, `sieve_default`, etc.
+
+        Some of the other knobs of this module will influence by default the plugin settings, but you
+        can still override any plugin settings.
+
+        If you override a plugin setting, its value is cleared and you have to copy over the defaults.
+      '';
+    };
+
     imapsieve.mailbox = mkOption {
       default = [];
       description = "Configure Sieve filtering rules on IMAP actions";
@@ -462,6 +499,12 @@ in
         type = types.listOf types.str;
       };
 
+      scripts = mkOption {
+        type = types.attrsOf types.path;
+        default = {};
+        description = lib.mdDoc "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc.";
+      };
+
       pipeBins = mkOption {
         default = [];
         example = literalExpression ''
@@ -476,7 +519,6 @@ in
     };
   };
 
-
   config = mkIf cfg.enable {
     security.pam.services.dovecot2 = mkIf cfg.enablePAM {};
 
@@ -501,6 +543,13 @@ in
         ++ optional (cfg.sieve.pipeBins != []) "sieve_extprograms";
 
       sieve.globalExtensions = optional (cfg.sieve.pipeBins != []) "vnd.dovecot.pipe";
+
+      pluginSettings = lib.mapAttrs (n: lib.mkDefault) ({
+        sieve_plugins = concatStringsSep " " cfg.sieve.plugins;
+        sieve_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions);
+        sieve_global_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions);
+        sieve_pipe_bin_dir = sievePipeBinScriptDirectory;
+      } // sieveScriptSettings // imapSieveMailboxSettings);
     };
 
     users.users = {
@@ -556,7 +605,7 @@ in
       # the source file and Dovecot won't try to compile it.
       preStart = ''
         rm -rf ${stateDir}/sieve ${stateDir}/imapsieve
-      '' + optionalString (cfg.sieveScripts != {}) ''
+      '' + optionalString (cfg.sieve.scripts != {}) ''
         mkdir -p ${stateDir}/sieve
         ${concatStringsSep "\n" (
         mapAttrsToList (
@@ -569,7 +618,7 @@ in
             fi
             ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}'
           ''
-        ) cfg.sieveScripts
+        ) cfg.sieve.scripts
       )}
         chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve'
       ''
@@ -615,8 +664,8 @@ in
         message = "dovecot is configured with showPAMFailure while enablePAM is disabled";
       }
       {
-        assertion = cfg.sieveScripts != {} -> (cfg.mailUser != null && cfg.mailGroup != null);
-        message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set";
+        assertion = cfg.sieve.scripts != {} -> (cfg.mailUser != null && cfg.mailGroup != null);
+        message = "dovecot requires mailUser and mailGroup to be set when `sieve.scripts` is set";
       }
     ];