diff --git a/modules/accounts/email.nix b/modules/accounts/email.nix new file mode 100644 index 00000000..6846cadb --- /dev/null +++ b/modules/accounts/email.nix @@ -0,0 +1,323 @@ +{ config, lib, ... }: + +with lib; + +let + + cfg = config.accounts.email; + + tlsModule = types.submodule { + options = { + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable TLS/SSL. + ''; + }; + + useStartTls = mkOption { + type = types.bool; + default = false; + description = '' + Whether to use STARTTLS. + ''; + }; + + certificatesFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to file containing certificate authorities that should + be used to validate the connection authenticity. If + null then the system default is used. + Note, if set then the system default may still be accepted. + ''; + }; + }; + }; + + imapModule = types.submodule { + options = { + host = mkOption { + type = types.str; + example = "imap.example.org"; + description = '' + Hostname of IMAP server. + ''; + }; + + port = mkOption { + type = types.nullOr types.ints.positive; + default = null; + example = 993; + description = '' + The port on which the IMAP server listens. If + null then the default port is used. + ''; + }; + + tls = mkOption { + type = tlsModule; + default = {}; + description = '' + Configuration for secure connections. + ''; + }; + }; + }; + + smtpModule = types.submodule { + options = { + host = mkOption { + type = types.str; + example = "smtp.example.org"; + description = '' + Hostname of SMTP server. + ''; + }; + + port = mkOption { + type = types.nullOr types.ints.positive; + default = null; + example = 465; + description = '' + The port on which the SMTP server listens. If + null then the default port is used. + ''; + }; + + tls = mkOption { + type = tlsModule; + default = {}; + description = '' + Configuration for secure connections. + ''; + }; + }; + }; + + maildirModule = types.submodule ({ config, ... }: { + options = { + path = mkOption { + type = types.str; + description = '' + Path to maildir directory where mail for this account is + stored. This is relative to the base maildir path. + ''; + }; + + absPath = mkOption { + type = types.path; + readOnly = true; + internal = true; + default = "${cfg.maildirBasePath}/${config.path}"; + description = '' + A convenience option whose value is the absolute path of + this maildir. + ''; + }; + }; + }); + + # gpgModule = types.submodule { + # }; + + mailAccount = types.submodule ({ name, config, ... }: { + options = { + name = mkOption { + type = types.str; + readOnly = true; + description = '' + Unique identifier of the account. This is set to the + attribute name of the account configuration. + ''; + }; + + primary = mkOption { + type = types.bool; + default = false; + description = '' + Whether this is the primary account. Only one account may be + set as primary. + ''; + }; + + flavor = mkOption { + type = types.enum [ "plain" "runbox.com" ]; + default = "plain"; + description = '' + Some email providers have peculiar behavior that require + special treatment. This option is therefore intended to + indicate the nature of the provider. + + When this indicates a specific provider then, for example, + the IMAP and SMTP server configuration may be set + automatically. + ''; + }; + + address = mkOption { + type = types.strMatching ".*@.*"; + example = "jane.doe@example.org"; + description = "The email address of this account."; + }; + + realName = mkOption { + type = types.str; + example = "Jane Doe"; + description = "Name displayed when sending mails."; + }; + + userName = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The server username of this account. This will be used as + the SMTP and IMAP user name. + ''; + }; + + passwordCommand = mkOption { + type = types.nullOr (types.either types.str (types.listOf types.str)); + default = null; + apply = p: if isString p then splitString " " p else p; + example = "secret-tool lookup email me@example.org"; + description = '' + A command, which when run writes the account password on + standard output. + ''; + }; + + folders = mkOption { + type = types.submodule { + options = { + inbox = mkOption { + type = types.str; + default = "Inbox"; + description = '' + Relative path of the inbox mail. + ''; + }; + + sent = mkOption { + type = types.nullOr types.str; + default = "Sent"; + description = '' + Relative path of the sent mail folder. + ''; + }; + + drafts = mkOption { + type = types.str; + default = "Drafts"; + description = '' + Relative path of the drafts mail folder. + ''; + }; + + trash = mkOption { + type = types.str; + default = "Trash"; + description = '' + Relative path of the deleted mail folder. + ''; + }; + }; + }; + default = {}; + description = '' + Standard email folders. + ''; + }; + + imap = mkOption { + type = types.nullOr imapModule; + default = null; + description = '' + The IMAP configuration to use for this account. + ''; + }; + + smtp = mkOption { + type = types.nullOr smtpModule; + default = null; + description = '' + The SMTP configuration to use for this account. + ''; + }; + + maildir = mkOption { + type = types.nullOr maildirModule; + defaultText = { path = "\${name}"; }; + description = '' + Maildir configuration for this account. + ''; + }; + }; + + config = mkMerge [ + { + name = name; + maildir = mkOptionDefault { path = "${name}"; }; + } + + (mkIf (config.flavor == "runbox.com") { + imap = { + host = "mail.runbox.com"; + }; + + smtp = { + host = "mail.runbox.com"; + }; + }) + ]; + }); + +in + +{ + options.accounts.email = { + maildirBasePath = mkOption { + type = types.str; + default = "${config.home.homeDirectory}/Maildir"; + defaultText = "$HOME/Maildir"; + apply = p: + if hasPrefix "/" p + then p + else "${config.home.homeDirectory}/${p}"; + description = '' + The base directory for account maildir directories. May be a + relative path, in which case it is relative the home + directory. + ''; + }; + + accounts = mkOption { + type = types.attrsOf mailAccount; + default = {}; + description = "List of email accounts."; + }; + }; + + config = mkIf (cfg.accounts != {}) { + assertions = [ + ( + let + primaries = + catAttrs "name" + (filter (a: a.primary) + (attrValues cfg.accounts)); + in + { + assertion = length primaries == 1; + message = + "Must have exactly one primary mail account but found " + + toString (length primaries) + + optionalString (length primaries > 1) + (", namely " + concatStringsSep ", " primaries); + } + ) + ]; + }; +} diff --git a/modules/misc/news.nix b/modules/misc/news.nix index 5704cf26..99c2de5c 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -672,6 +672,24 @@ in ''; } + { + time = "2018-07-01T14:33:15+00:00"; + message = '' + A new module is available: 'accounts.email'. + + As the name suggests, this new module offers a number of + options for configuring email accounts. This, for example, + includes the email address and owner's real name but also + server settings for IMAP and SMTP. + + The intent is to have a central location for account + specific configuration that other modules can use. + + Note, this module is still somewhat experimental and its + structure should not be seen as final. Feedback is greatly + appreciated, both positive and negative. + ''; + } ]; }; } diff --git a/modules/modules.nix b/modules/modules.nix index 61943748..6229ec4b 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -13,6 +13,7 @@ with lib; let modules = [ + ./accounts/email.nix ./files.nix ./home-environment.nix ./manual.nix