diff --git a/modules/programs/aerc-accounts.nix b/modules/programs/aerc-accounts.nix
index e4c3fa7f0..1d4f4053a 100644
--- a/modules/programs/aerc-accounts.nix
+++ b/modules/programs/aerc-accounts.nix
@@ -4,7 +4,6 @@ with lib;
let
mapAttrNames = f: attr:
- with builtins;
listToAttrs (attrValues (mapAttrs (k: v: {
name = f k;
value = v;
@@ -59,6 +58,7 @@ in {
See aerc-config(5).
'';
};
+
extraBinds = mkOption {
type = confSections;
default = { };
@@ -70,6 +70,7 @@ in {
See aerc-config5.
'';
};
+
extraConfig = mkOption {
type = confSections;
default = { };
@@ -110,14 +111,18 @@ in {
};
});
};
+
mkAccount = name: account:
let
nullOrMap = f: v: if v == null then v else f v;
+
optPort = port: if port != null then ":${toString port}" else "";
+
optAttr = k: v:
if v != null && v != [ ] && v != "" then { ${k} = v; } else { };
+
optPwCmd = k: p:
- optAttr "${k}-cred-cmd" (nullOrMap (builtins.concatStringsSep " ") p);
+ optAttr "${k}-cred-cmd" (nullOrMap (concatStringsSep " ") p);
useOauth = auth: builtins.elem auth [ "oauthbearer" "xoauth2" ];
@@ -133,6 +138,7 @@ in {
source =
"maildir://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}";
};
+
imap = { userName, imap, passwordCommand, aerc, ... }@cfg:
let
loginMethod' =
@@ -147,11 +153,14 @@ in {
if imap.tls.useStartTls then "imap" else "imaps${loginMethod'}"
else
"imap+insecure";
+
port' = optPort imap.port;
+
in {
source =
"${protocol}://${userName}@${imap.host}${port'}${oauthParams'}";
} // optPwCmd "source" passwordCommand;
+
smtp = { userName, smtp, passwordCommand, ... }@cfg:
let
loginMethod' =
@@ -166,25 +175,32 @@ in {
"smtps${loginMethod'}"
else
"smtp${loginMethod'}";
+
port' = optPort smtp.port;
+
smtp-starttls =
if smtp.tls.enable && smtp.tls.useStartTls then "yes" else null;
+
in {
outgoing =
"${protocol}://${userName}@${smtp.host}${port'}${oauthParams'}";
} // optPwCmd "outgoing" passwordCommand
// optAttr "smtp-starttls" smtp-starttls;
+
msmtp = cfg: {
outgoing = "msmtpq --read-envelope-from --read-recipients";
};
+
};
+
basicCfg = account:
{
from = "${account.realName} <${account.address}>";
} // (optAttr "copy-to" account.folders.sent)
// (optAttr "default" account.folders.inbox)
// (optAttr "postpone" account.folders.drafts)
- // (optAttr "aliases" account.aliases) // account.aerc.extraAccounts;
+ // (optAttr "aliases" account.aliases);
+
sourceCfg = account:
if account.mbsync.enable || account.offlineimap.enable then
mkConfig.maildir account
@@ -192,6 +208,7 @@ in {
mkConfig.imap account
else
{ };
+
outgoingCfg = account:
if account.msmtp.enable then
mkConfig.msmtp account
@@ -199,9 +216,13 @@ in {
mkConfig.smtp account
else
{ };
- in (basicCfg account) // (sourceCfg account) // (outgoingCfg account);
+
+ in (basicCfg account) // (sourceCfg account) // (outgoingCfg account)
+ // account.aerc.extraAccounts;
+
mkAccountConfig = name: account:
mapAttrNames (addAccountName name) account.aerc.extraConfig;
+
mkAccountBinds = name: account:
mapAttrNames (addAccountName name) account.aerc.extraBinds;
}
diff --git a/modules/programs/aerc.nix b/modules/programs/aerc.nix
index 95ca984f3..c3f690509 100644
--- a/modules/programs/aerc.nix
+++ b/modules/programs/aerc.nix
@@ -3,24 +3,32 @@
with lib;
let
cfg = config.programs.aerc;
+
primitive = with types;
((type: either type (listOf type)) (nullOr (oneOf [ str int bool float ])))
// {
description =
"values (null, bool, int, string of float) or a list of values, that will be joined with a comma";
};
+
confSection = types.attrsOf primitive;
+
confSections = types.attrsOf confSection;
+
sectionsOrLines = types.either types.lines confSections;
+
accounts = import ./aerc-accounts.nix {
inherit config pkgs lib confSection confSections;
};
+
aerc-accounts =
attrsets.filterAttrs (_: v: v.aerc.enable) config.accounts.email.accounts;
+
in {
meta.maintainers = with lib.hm.maintainers; [ lukasngl ];
options.accounts.email.accounts = accounts.type;
+
options.programs.aerc = {
enable = mkEnableOption "aerc";
@@ -70,6 +78,7 @@ in {
See aerc-stylesets(7).
'';
};
+
templates = mkOption {
type = with types; attrsOf lines;
default = { };
@@ -84,16 +93,14 @@ in {
};
config = let
- joinCfg = cfgs:
- with builtins;
- concatStringsSep "\n" (filter (v: v != "") cfgs);
+ joinCfg = cfgs: concatStringsSep "\n" (filter (v: v != "") cfgs);
+
toINI = conf: # quirk: global section is prepended w/o section heading
let
global = conf.global or { };
local = removeAttrs conf [ "global" ];
optNewLine = if global != { } && local != { } then "\n" else "";
mkValueString = v:
- with builtins;
if isList v then # join with comma
concatStringsSep "," (map (generators.mkValueStringDefault { }) v)
else
@@ -104,64 +111,94 @@ in {
(generators.toKeyValue { inherit mkKeyValue; } global)
(generators.toINI { inherit mkKeyValue; } local)
];
- mkINI = conf: if builtins.isString conf then conf else toINI conf;
+
+ mkINI = conf: if isString conf then conf else toINI conf;
+
mkStyleset = attrsets.mapAttrs' (k: v:
- let value = if builtins.isString v then v else toINI { global = v; };
+ let value = if isString v then v else toINI { global = v; };
in {
name = "aerc/stylesets/${k}";
value.text = joinCfg [ header value ];
});
+
mkTemplates = attrsets.mapAttrs' (k: v: {
name = "aerc/templates/${k}";
value.text = v;
});
- accountsExtraAccounts = builtins.mapAttrs accounts.mkAccount aerc-accounts;
- accountsExtraConfig =
- builtins.mapAttrs accounts.mkAccountConfig aerc-accounts;
- accountsExtraBinds =
- builtins.mapAttrs accounts.mkAccountBinds aerc-accounts;
- joinContextual = contextual:
- with builtins;
- joinCfg (map mkINI (attrValues contextual));
+
+ primaryAccount = attrsets.filterAttrs (_: v: v.primary) aerc-accounts;
+ otherAccounts = attrsets.filterAttrs (_: v: !v.primary) aerc-accounts;
+
+ primaryAccountAccounts = mapAttrs accounts.mkAccount primaryAccount;
+
+ accountsExtraAccounts = mapAttrs accounts.mkAccount otherAccounts;
+
+ accountsExtraConfig = mapAttrs accounts.mkAccountConfig aerc-accounts;
+
+ accountsExtraBinds = mapAttrs accounts.mkAccountBinds aerc-accounts;
+
+ joinContextual = contextual: joinCfg (map mkINI (attrValues contextual));
+
+ isRecursivelyEmpty = x:
+ if isAttrs x then
+ all (x: x == { } || isRecursivelyEmpty x) (attrValues x)
+ else
+ false;
+
+ genAccountsConf = ((cfg.extraAccounts != "" && cfg.extraAccounts != { })
+ || !(isRecursivelyEmpty accountsExtraAccounts)
+ || !(isRecursivelyEmpty primaryAccountAccounts));
+
+ genAercConf = ((cfg.extraConfig != "" && cfg.extraConfig != { })
+ || !(isRecursivelyEmpty accountsExtraConfig));
+
+ genBindsConf = ((cfg.extraBinds != "" && cfg.extraBinds != { })
+ || !(isRecursivelyEmpty accountsExtraBinds));
+
header = ''
# Generated by Home Manager.
'';
+
in mkIf cfg.enable {
- warnings = if ((cfg.extraAccounts != "" && cfg.extraAccounts != { })
- || accountsExtraAccounts != { })
+ warnings = if genAccountsConf
&& (cfg.extraConfig.general.unsafe-accounts-conf or false) == false then [''
aerc: An email account was configured, but `extraConfig.general.unsafe-accounts-conf` is set to false or unset.
- This will prevent aerc from starting, see `unsafe-accounts-conf` in aerc-config(5) for details.
- Consider setting the option `extraConfig.general.unsafe-accounts-conf` to true.
+ This will prevent aerc from starting, see `unsafe-accounts-conf` in the man page aerc-config(5), which states:
+ > By default, the file permissions of accounts.conf must be restrictive and only allow reading by the file owner (0600).
+ > Set this option to true to ignore this permission check. Use this with care as it may expose your credentials.
+ These file permissions are not possible with home-manger, since the generated file is stored in the nix-store with read-only access for all users (0444).
+ If `passwordCommand` is properly set, no credentials will be stored in the nix store.
+ Therefore, consider setting the option `extraConfig.general.unsafe-accounts-conf` to true.
''] else
[ ];
+
home.packages = [ cfg.package ];
+
xdg.configFile = {
- "aerc/accounts.conf" = mkIf
- ((cfg.extraAccounts != "" && cfg.extraAccounts != { })
- || accountsExtraAccounts != { }) {
- text = joinCfg [
- header
- (mkINI cfg.extraAccounts)
- (mkINI accountsExtraAccounts)
- ];
- };
- "aerc/aerc.conf" =
- mkIf (cfg.extraConfig != "" && cfg.extraConfig != { }) {
- text = joinCfg [
- header
- (mkINI cfg.extraConfig)
- (joinContextual accountsExtraConfig)
- ];
- };
- "aerc/binds.conf" = mkIf ((cfg.extraBinds != "" && cfg.extraBinds != { })
- || accountsExtraBinds != { }) {
- text = joinCfg [
- header
- (mkINI cfg.extraBinds)
- (joinContextual accountsExtraBinds)
- ];
- };
+ "aerc/accounts.conf" = mkIf genAccountsConf {
+ text = joinCfg [
+ header
+ (mkINI cfg.extraAccounts)
+ (mkINI primaryAccountAccounts)
+ (mkINI accountsExtraAccounts)
+ ];
+ };
+
+ "aerc/aerc.conf" = mkIf genAercConf {
+ text = joinCfg [
+ header
+ (mkINI cfg.extraConfig)
+ (joinContextual accountsExtraConfig)
+ ];
+ };
+
+ "aerc/binds.conf" = mkIf genBindsConf {
+ text = joinCfg [
+ header
+ (mkINI cfg.extraBinds)
+ (joinContextual accountsExtraBinds)
+ ];
+ };
} // (mkStyleset cfg.stylesets) // (mkTemplates cfg.templates);
};
}
diff --git a/tests/modules/programs/aerc/extraAccounts.expected b/tests/modules/programs/aerc/extraAccounts.expected
index 9377384fc..a53e6e007 100644
--- a/tests/modules/programs/aerc/extraAccounts.expected
+++ b/tests/modules/programs/aerc/extraAccounts.expected
@@ -8,6 +8,10 @@ source = maildir:///dev/null
[Test2]
pgp-key-id = 42
+[primary]
+from = Foo Bar
+source = imap://foobar@imap.host.invalid:1337
+
[a_imap-nopasscmd-tls-starttls-folders]
copy-to = aercSent
default = aercInbox
@@ -74,3 +78,9 @@ outgoing = smtps+login://foobar@smtp.host.invalid:42
[o_msmtp]
from = Foo Bar
outgoing = msmtpq --read-envelope-from --read-recipients
+
+[p_overwrite_defaults]
+from = test
+outgoing = imap+plain://intentionallyWrong:PaSsWorD@smtp.host.invalid:1337
+postpone = dRaFts
+source = smtp+plain://intentionallyWrong:PaSsWorD@smtp.host.invalid:1337
diff --git a/tests/modules/programs/aerc/settings.nix b/tests/modules/programs/aerc/settings.nix
index 1f3c25a94..fa385655a 100644
--- a/tests/modules/programs/aerc/settings.nix
+++ b/tests/modules/programs/aerc/settings.nix
@@ -102,7 +102,7 @@ with lib;
};
};
in {
- a_imap-nopasscmd-tls-starttls-folders = basics // {
+ primary = basics // {
primary = true;
imap = {
host = "imap.host.invalid";
@@ -110,6 +110,14 @@ with lib;
tls.enable = true;
tls.useStartTls = true;
};
+ };
+ a_imap-nopasscmd-tls-starttls-folders = basics // {
+ imap = {
+ host = "imap.host.invalid";
+ port = 1337;
+ tls.enable = true;
+ tls.useStartTls = true;
+ };
folders = {
drafts = "aercDrafts";
inbox = "aercInbox";
@@ -224,6 +232,21 @@ with lib;
};
};
o_msmtp = basics // { msmtp = { enable = true; }; };
+ p_overwrite_defaults = basics // {
+ smtp.host = "should.be.overwritten.invalid";
+ imap.host = "should.be.overwritten.invalid";
+ aerc = {
+ enable = true;
+ extraAccounts = {
+ from = "test ";
+ outgoing =
+ "imap+plain://intentionallyWrong:PaSsWorD@smtp.host.invalid:1337";
+ source =
+ "smtp+plain://intentionallyWrong:PaSsWorD@smtp.host.invalid:1337";
+ postpone = "dRaFts";
+ };
+ };
+ };
};
};
}