{pkgs, lib, config, system, ...}: let inherit (builtins) toString toFile attrNames; inherit (lib) types; inherit (pkgs.lib) unlinesAttrs unlinesValues unwords; inherit (config.services) dovecot2 openldap; inherit (config) networking; stateDir = "/var/lib/dovecot"; mailDir = "${stateDir}/mail"; sieveDir = "${stateDir}/sieve"; authDir = "${stateDir}/auth"; escapeGroup = lib.stringAsChars (c: if "a"<=c && c<="z" || "0"<=c && c<="9" || c=="-" then c else "_"); domainGroup = escapeGroup "${networking.domainBase}"; in { options.services.dovecot2 = { domains = lib.mkOption { default = {}; type = types.attrsOf (types.submodule ({domain, ...}: { #config.domain = lib.mkDefault domain; options = { accounts = lib.mkOption { type = types.attrsOf (types.submodule ({account, ...}: { options = { password = lib.mkOption { type = types.str; example = "{SSHA512}uyjL1KYx4z7HpfNvnKzuVxpMLD2KVueGGBvOcj7AF1EZCTVhT++IIKUVOC4xpZtWdqVD0OVmZqgYr2qpn/3t3Aj4oU0="; description = ''Password. Use: `doveadm pw -s SSHA512 -p "$password"` ''; }; aliases = lib.mkOption { type = with types; listOf types.str; example = [ "abuse@${config.networking.domain}" ]; default = []; description = ''Aliases of this account.''; }; quota = lib.mkOption { type = with types; nullOr types.str; default = null; example = "2G"; description = '' Per user quota rules. Accepted sizes are `xx k/M/G/T` with the obvious meaning. Leave blank for the standard quota `100G`. ''; }; sieves = lib.mkOption { type = with types; attrsOf str; default = { main = '' require ["include"]; #include :personal "roundcube"; include :global "spam"; include :global "list"; include :global "extension"; ''; }; }; groups = lib.mkOption { type = with types; listOf str; default = []; }; }; })); }; }; })); }; debug = lib.mkOption { type = types.bool; default = false; description = '' Whether to enable verbose logging or not in mail related services. ''; }; sieves = { global = lib.mkOption { description = "global scripts."; type = types.attrsOf types.str; default = {}; }; before = lib.mkOption { description = "before scripts."; type = types.attrsOf types.str; default = {}; }; after = lib.mkOption { description = "after scripts."; type = types.attrsOf types.str; default = {}; }; }; }; config = lib.mkIf dovecot2.enable { systemd.services.dovecot2 = { preStart = unlinesValues { installMailDir = '' # SEE: http://wiki2.dovecot.org/SharedMailboxes/Permissions install -D -d -m 0771 \ -o ${dovecot2.mailUser} \ -g ${dovecot2.mailGroup} \ ${mailDir} ''; 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 '' + unlinesAttrs (name: text: '' src=${pkgs.writeText "${name}.sieve" text} dst="${sieveDir}/${dir}.d/${name}.sieve" ln -fns "$src" "$dst" ${pkgs.dovecot_pigeonhole}/bin/sievec "$dst" '') sieves ) dovecot2.sieves; installDomains = lib.optionalString openldap.enable '' # NOTE: make sure nslcd cache is in sync with the LDAP data systemctl restart nslcd '' + '' install -D -d -m 1770 \ -o ${dovecot2.mailUser} \ -g ${domainGroup} \ ${mailDir}/${networking.domain} \ ${stateDir}/control/${networking.domain} \ ${stateDir}/index/${networking.domain} # NOTE: do not set the sticky bit (+t) # on acl//, to let dovecot # rename acl.db.lock (own by new user) # to acl.db (own by old user) install -D -d -m 0770 \ -o ${dovecot2.mailUser} \ -g ${domainGroup} \ ${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 ''; }; }; }; }