diff --git a/modules/accounts/email.nix b/modules/accounts/email.nix index 6bde6da0b..71ecd3020 100644 --- a/modules/accounts/email.nix +++ b/modules/accounts/email.nix @@ -388,6 +388,7 @@ in mailAccountOpts (import ../programs/alot-accounts.nix pkgs) (import ../programs/astroid-accounts.nix) + (import ../programs/getmail-accounts.nix) (import ../programs/mbsync-accounts.nix) (import ../programs/msmtp-accounts.nix) (import ../programs/notmuch-accounts.nix) diff --git a/modules/modules.nix b/modules/modules.nix index 6900ed67d..1fffb5947 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -51,6 +51,7 @@ let (loadModule ./programs/firefox.nix { }) (loadModule ./programs/fish.nix { }) (loadModule ./programs/fzf.nix { }) + (loadModule ./programs/getmail.nix { }) (loadModule ./programs/git.nix { }) (loadModule ./programs/gnome-terminal.nix { }) (loadModule ./programs/go.nix { }) @@ -99,7 +100,7 @@ let (loadModule ./services/gnome-keyring.nix { }) (loadModule ./services/gpg-agent.nix { }) (loadModule ./services/imapnotify.nix { condition = hostPlatform.isLinux; }) - (loadModule ./services/getmail.nix { }) + (loadModule ./services/getmail.nix { condition = hostPlatform.isLinux; }) (loadModule ./services/kbfs.nix { }) (loadModule ./services/kdeconnect.nix { }) (loadModule ./services/keepassx.nix { }) diff --git a/modules/programs/getmail-accounts.nix b/modules/programs/getmail-accounts.nix new file mode 100644 index 000000000..32e1312dc --- /dev/null +++ b/modules/programs/getmail-accounts.nix @@ -0,0 +1,49 @@ +{ config, lib, ... }: + +with lib; + +{ + options.getmail = { + enable = mkEnableOption "the getmail mail retriever for this account"; + + destinationCommand = mkOption { + type = types.nullOr types.str; + default = null; + example = "\${pkgs.maildrop}/bin/maildrop"; + description = '' + Specify a command delivering the incoming mail to your maildir. + ''; + }; + + mailboxes = mkOption { + type = types.nonEmptyListOf types.str; + default = []; + example = ["INBOX" "INBOX.spam"]; + description = '' + A non-empty list of mailboxes. To download all mail you can + use the ALL mailbox. + ''; + }; + + delete = mkOption { + type = types.bool; + default = false; + description = '' + Enable if you want to delete read messages from the server. Most + users should either enable delete or disable + readAll. + ''; + }; + + readAll = mkOption { + type = types.bool; + default = true; + description = '' + Enable if you want to fetch all, even the read messages from the + server. Most users should either enable delete or + disable readAll. + ''; + }; + + }; +} diff --git a/modules/programs/getmail.nix b/modules/programs/getmail.nix new file mode 100644 index 000000000..8c1ac5e02 --- /dev/null +++ b/modules/programs/getmail.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + accounts = filter (a: a.getmail.enable) + (attrValues config.accounts.email.accounts); + + renderAccountConfig = account: with account; + let + passCmd = concatMapStringsSep ", " (x: "'${x}'") passwordCommand; + renderedMailboxes = concatMapStringsSep ", " (x: "'${x}'") getmail.mailboxes; + retrieverType = if imap.tls.enable + then "SimpleIMAPSSLRetriever" + else "SimpleIMAPRetriever"; + destination = if getmail.destinationCommand != null + then + { + destinationType = "MDA_external"; + destinationPath = getmail.destinationCommand; + } + else + { + destinationType = "Maildir"; + destinationPath = "${maildir.absPath}/"; + }; + renderGetmailBoolean = v: if v then "true" else "false"; + in '' + # Generated by Home-Manager. + [retriever] + type = ${retrieverType} + server = ${imap.host} + username = ${userName} + password_command = (${passCmd}) + mailboxes = ( ${renderedMailboxes} ) + + [destination] + type = ${destination.destinationType} + path = ${destination.destinationPath} + + [options] + delete = ${renderGetmailBoolean getmail.delete} + read_all = ${renderGetmailBoolean getmail.readAll} + ''; + getmailEnabled = length (filter (a: a.getmail.enable) accounts) > 0; + # Watch out! This is used by the getmail.service too! + renderConfigFilepath = a: ".getmail/getmail${if a.primary then "rc" else a.name}"; +in + + { + config = mkIf getmailEnabled { + home.file = map (a: + { target = renderConfigFilepath a; + text = renderAccountConfig a; + }) accounts; + + }; + } diff --git a/modules/services/getmail.nix b/modules/services/getmail.nix index 74c9cf6b6..46d4c1752 100644 --- a/modules/services/getmail.nix +++ b/modules/services/getmail.nix @@ -4,188 +4,43 @@ with lib; let - cfg = config.programs.getmail; + cfg = config.services.getmail; - retrieverModule = types.submodule ({config,...}: { - options = { - type = mkOption { - type = types.enum [ - "SimplePOP3Retriever" - "SimplePOP3SSLRetriever" - "SimpleIMAPRetriever" - "SimpleIMAPSSLRetriever" - ]; - default = "SimpleIMAPSSLRetriever"; - description = "Type of the retriever."; - }; - - server = mkOption { - type = types.string; - default = ""; - description = "The remote server."; - }; - - username = mkOption { - type = types.string; - default = ""; - description = "The server username."; - }; - - password = mkOption { - type = types.nullOr types.string; - default = null; - description = '' - The server password. Note that the passwords are stored clear in the - nix store, so it is recommended to not use this field, but instead - either leave empty or use passwordCommand instead. - ''; - }; - - passwordCommand = mkOption { - type = types.nullOr (types.listOf types.string); - default = null; - example = ["${pkgs.gnupg}/bin/gpg" "--decrypt" "file.gpg"]; - description = '' - The server password. With this the password is retrieved with the - given command. The list value is given escaped to the implementation. - ''; - }; - - mailboxes = mkOption { - type = types.listOf types.string; - default = []; - description = "A list of mailboxes"; - }; - }; - }); - - destinationModule = types.submodule ({config,...}: { - options = { - type = mkOption { - type = types.enum [ - "MDA_external" - "Maildir" - ]; - default = "Maildir"; - description = "Destination type."; - }; - - path = mkOption { - type = types.string; - default = "$HOME/Mail"; - example = "${pkgs.procmail}/bin/procmail"; - description = '' - The destination path. For Maildir it's the file - path and for MDA_external it's the destination - application. - ''; - }; - }; - }); - - optionsModule = types.submodule ({config,...}: { - options = { - delete = mkOption { - type = types.bool; - default = false; - description = '' - Enable if you want to delete read messages from the server. Most - users should either enable delete or disable - readAll. - ''; - }; - - readAll = mkOption { - type = types.bool; - default = true; - description = '' - Enable if you want to fetch all, even the read messages from the - server. Most users should either enable delete or - disable readAll. - ''; - }; - }; - }); + accounts = filter (a: a.getmail.enable) + (attrValues config.accounts.email.accounts); + # Note: The getmail service does not expect a path, but just the filename! + renderConfigFilepath = a: if a.primary then "getmailrc" else "getmail${a.name}"; + configFiles = concatMapStringsSep " " (a: " --rcfile ${renderConfigFilepath a}") accounts; in - { options = { - programs.getmail = { - enable = mkEnableOption "Enable getmail"; - - retriever = mkOption { - type = retrieverModule; - default = {}; - description = "The server section."; - }; - - destination = mkOption { - type = destinationModule; - default = {}; - description = "The destination section."; - }; - - options = mkOption { - type = optionsModule; - default = {}; - description = "The options section."; - }; + services.getmail = { + enable = mkEnableOption "the getmail systemd service to automatically retrieve mail"; frequency = mkOption { - type = types.string; - default = "*:0/15"; + type = types.str; + default = "*:0/5"; example = "hourly"; description = '' The refresh frequency. Check man systemd.time for - more information on the syntax. + more information on the syntax. If you use a gpg-agent in + combination with the passwordCommand, keep the poll + frequency below the cache-ttl value (as set by the + default) to avoid pinentry asking + permanently for a password. ''; }; }; }; config = mkIf cfg.enable { - home.file.".getmail/getmailrc".text = - let - quoted = x: "\"${escape ["\""] x}\""; - - passwordCommand = concatStringsSep ", " (map quoted cfg.retriever.passwordCommand); - - password = if cfg.retriever.passwordCommand != null - then "password_command = (${passwordCommand})" - else optionalString (cfg.retriever.password != null) "password = \"${quoted cfg.retriever.password}\""; - mailboxInner = concatStringsSep ", " ( - map quoted cfg.retriever.mailboxes); - - mailboxes = "(${mailboxInner})"; - - in - - '' - [retriever] - type = ${cfg.retriever.type} - server = ${cfg.retriever.server} - username = ${cfg.retriever.username} - ${password} - mailboxes = ${mailboxes} - - [destination] - type = ${cfg.destination.type} - path = ${cfg.destination.path} - - [options] - delete = ${toString cfg.options.delete} - read_all = ${toString cfg.options.readAll} - ''; - systemd.user.services.getmail = { Unit = { Description = "getmail email fetcher"; - PartOf = ["network-online.target"]; }; Service = { - Type = "simple"; - ExecStart = "${pkgs.getmail}/bin/getmail"; + ExecStart = "${pkgs.getmail}/bin/getmail ${configFiles}"; }; }; @@ -201,5 +56,6 @@ in WantedBy = [ "timers.target" ]; }; }; + }; } diff --git a/tests/default.nix b/tests/default.nix index 8009b90c9..9fcd1d9f8 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -25,6 +25,7 @@ import nmt { git-with-most-options = ./modules/programs/git.nix; git-with-str-extra-config = ./modules/programs/git-with-str-extra-config.nix; mbsync = ./modules/programs/mbsync.nix; + getmail = ./modules/programs/getmail.nix; texlive-minimal = ./modules/programs/texlive-minimal.nix; xresources = ./modules/xresources.nix; } diff --git a/tests/modules/programs/getmail-expected.conf b/tests/modules/programs/getmail-expected.conf new file mode 100644 index 000000000..da54e7092 --- /dev/null +++ b/tests/modules/programs/getmail-expected.conf @@ -0,0 +1,15 @@ +# Generated by Home-Manager. +[retriever] +type = SimpleIMAPSSLRetriever +server = imap.example.com +username = home.manager +password_command = ('password-command') +mailboxes = ( 'INBOX', 'Sent', 'Work' ) + +[destination] +type = MDA_external +path = /bin/maildrop + +[options] +delete = false +read_all = true diff --git a/tests/modules/programs/getmail.nix b/tests/modules/programs/getmail.nix new file mode 100644 index 000000000..12806c256 --- /dev/null +++ b/tests/modules/programs/getmail.nix @@ -0,0 +1,26 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + imports = [ ../accounts/email-test-accounts.nix ]; + + config = { + home.username = "hm-user"; + home.homeDirectory = "/home/hm-user"; + + accounts.email.accounts = { + "hm@example.com".getmail = { + enable = true; + mailboxes = ["INBOX" "Sent" "Work"]; + destinationCommand = "/bin/maildrop"; + delete = false; + }; + }; + + nmt.script = '' + assertFileExists home-files/.getmail/getmailhm@example.com + assertFileContent home-files/.getmail/getmailhm@example.com ${./getmail-expected.conf} + ''; + }; +}