2018-06-26 22:45:00 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
|
|
|
|
dag = config.lib.dag;
|
|
|
|
|
|
|
|
cfg = config.programs.mbsync;
|
|
|
|
|
|
|
|
# Accounts for which mbsync is enabled.
|
|
|
|
mbsyncAccounts =
|
|
|
|
filter (a: a.mbsync.enable) (attrValues config.accounts.email.accounts);
|
|
|
|
|
|
|
|
genTlsConfig = tls: {
|
|
|
|
SSLType =
|
|
|
|
if !tls.enable then "None"
|
|
|
|
else if tls.useStartTls then "STARTTLS"
|
|
|
|
else "IMAPS";
|
|
|
|
}
|
|
|
|
//
|
|
|
|
optionalAttrs (tls.enable && tls.certificatesFile != null) {
|
|
|
|
CertificateFile = tls.certificatesFile;
|
|
|
|
};
|
|
|
|
|
|
|
|
masterSlaveMapping = {
|
|
|
|
none = "None";
|
|
|
|
imap = "Master";
|
|
|
|
maildir = "Slave";
|
|
|
|
both = "Both";
|
|
|
|
};
|
|
|
|
|
|
|
|
genSection = header: entries:
|
|
|
|
let
|
|
|
|
escapeValue = escape [ "\"" ];
|
2018-09-02 05:58:54 +00:00
|
|
|
hasSpace = v: builtins.match ".* .*" v != null;
|
2018-06-26 22:45:00 +00:00
|
|
|
genValue = v:
|
|
|
|
if isList v
|
|
|
|
then concatMapStringsSep " " genValue v
|
2018-09-02 05:58:54 +00:00
|
|
|
else if isBool v then (if v then "yes" else "no")
|
|
|
|
else if isInt v then toString v
|
|
|
|
else if hasSpace v then "\"${escapeValue v}\""
|
|
|
|
else v;
|
2018-06-26 22:45:00 +00:00
|
|
|
in
|
|
|
|
''
|
|
|
|
${header}
|
|
|
|
${concatStringsSep "\n"
|
|
|
|
(mapAttrsToList (n: v: "${n} ${genValue v}") entries)}
|
|
|
|
'';
|
|
|
|
|
|
|
|
genAccountConfig = account: with account;
|
|
|
|
if (imap == null || maildir == null)
|
|
|
|
then ""
|
|
|
|
else
|
|
|
|
genSection "IMAPAccount ${name}" (
|
|
|
|
{
|
|
|
|
Host = imap.host;
|
|
|
|
User = userName;
|
|
|
|
PassCmd = toString passwordCommand;
|
|
|
|
}
|
2018-09-02 05:58:54 +00:00
|
|
|
// genTlsConfig imap.tls
|
|
|
|
// optionalAttrs (imap.port != null) { Port = toString imap.port; }
|
2018-10-03 18:35:04 +00:00
|
|
|
// mbsync.extraConfig.account
|
2018-06-26 22:45:00 +00:00
|
|
|
)
|
|
|
|
+ "\n"
|
2018-09-02 05:58:54 +00:00
|
|
|
+ genSection "IMAPStore ${name}-remote" (
|
|
|
|
{
|
|
|
|
Account = name;
|
|
|
|
}
|
|
|
|
// mbsync.extraConfig.remote
|
|
|
|
)
|
2018-06-26 22:45:00 +00:00
|
|
|
+ "\n"
|
|
|
|
+ genSection "MaildirStore ${name}-local" (
|
|
|
|
{
|
|
|
|
Path = "${maildir.absPath}/";
|
|
|
|
Inbox = "${maildir.absPath}/${folders.inbox}";
|
|
|
|
SubFolders = "Verbatim";
|
|
|
|
}
|
2018-09-02 05:58:54 +00:00
|
|
|
// optionalAttrs (mbsync.flatten != null) { Flatten = mbsync.flatten; }
|
|
|
|
// mbsync.extraConfig.local
|
2018-06-26 22:45:00 +00:00
|
|
|
)
|
|
|
|
+ "\n"
|
2018-09-02 05:58:54 +00:00
|
|
|
+ genSection "Channel ${name}" (
|
|
|
|
{
|
|
|
|
Master = ":${name}-remote:";
|
|
|
|
Slave = ":${name}-local:";
|
|
|
|
Patterns = mbsync.patterns;
|
|
|
|
Create = masterSlaveMapping.${mbsync.create};
|
|
|
|
Remove = masterSlaveMapping.${mbsync.remove};
|
|
|
|
Expunge = masterSlaveMapping.${mbsync.expunge};
|
|
|
|
SyncState = "*";
|
|
|
|
}
|
|
|
|
// mbsync.extraConfig.channel
|
|
|
|
)
|
2018-06-26 22:45:00 +00:00
|
|
|
+ "\n";
|
|
|
|
|
|
|
|
genGroupConfig = name: channels:
|
|
|
|
let
|
|
|
|
genGroupChannel = n: boxes: "Channel ${n}:${concatStringsSep "," boxes}";
|
|
|
|
in
|
|
|
|
concatStringsSep "\n" (
|
|
|
|
[ "Group ${name}" ] ++ mapAttrsToList genGroupChannel channels
|
|
|
|
);
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
programs.mbsync = {
|
|
|
|
enable = mkEnableOption "mbsync IMAP4 and Maildir mailbox synchronizer";
|
|
|
|
|
|
|
|
package = mkOption {
|
|
|
|
type = types.package;
|
|
|
|
default = pkgs.isync;
|
|
|
|
defaultText = "pkgs.isync";
|
|
|
|
example = literalExample "pkgs.isync";
|
|
|
|
description = "The package to use for the mbsync binary.";
|
|
|
|
};
|
|
|
|
|
|
|
|
groups = mkOption {
|
|
|
|
type = types.attrsOf (types.attrsOf (types.listOf types.str));
|
|
|
|
default = {};
|
|
|
|
example = literalExample ''
|
|
|
|
{
|
|
|
|
inboxes = {
|
|
|
|
account1 = [ "Inbox" ];
|
|
|
|
account2 = [ "Inbox" ];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
description = ''
|
|
|
|
Definition of groups.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
extraConfig = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = "";
|
|
|
|
description = ''
|
|
|
|
Extra configuration lines to add to the mbsync configuration.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
assertions = [
|
|
|
|
(
|
|
|
|
let
|
|
|
|
badAccounts = filter (a: a.maildir == null) mbsyncAccounts;
|
|
|
|
in
|
|
|
|
{
|
|
|
|
assertion = badAccounts == [];
|
|
|
|
message = "mbsync: Missing maildir configuration for accounts: "
|
|
|
|
+ concatMapStringsSep ", " (a: a.name) badAccounts;
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
(
|
|
|
|
let
|
|
|
|
badAccounts = filter (a: a.imap == null) mbsyncAccounts;
|
|
|
|
in
|
|
|
|
{
|
|
|
|
assertion = badAccounts == [];
|
|
|
|
message = "mbsync: Missing IMAP configuration for accounts: "
|
|
|
|
+ concatMapStringsSep ", " (a: a.name) badAccounts;
|
|
|
|
}
|
|
|
|
)
|
|
|
|
];
|
|
|
|
|
|
|
|
home.packages = [ cfg.package ];
|
|
|
|
|
2018-06-28 21:42:25 +00:00
|
|
|
programs.notmuch.new.ignore = [ ".uidvalidity" ".mbsyncstate" ];
|
|
|
|
|
2018-06-26 22:45:00 +00:00
|
|
|
home.file.".mbsyncrc".text =
|
|
|
|
let
|
|
|
|
accountsConfig = map genAccountConfig mbsyncAccounts;
|
|
|
|
groupsConfig = mapAttrsToList genGroupConfig cfg.groups;
|
|
|
|
in
|
|
|
|
concatStringsSep "\n" (
|
|
|
|
[ "# Generated by Home Manager.\n" ]
|
|
|
|
++ accountsConfig
|
|
|
|
++ groupsConfig
|
|
|
|
++ optional (cfg.extraConfig != "") cfg.extraConfig
|
|
|
|
);
|
|
|
|
|
|
|
|
home.activation.createMaildir =
|
|
|
|
dag.entryBetween [ "linkGeneration" ] [ "writeBoundary" ] ''
|
|
|
|
$DRY_RUN_CMD mkdir -m700 -p $VERBOSE_ARG ${
|
|
|
|
concatMapStringsSep " " (a: a.maildir.absPath) mbsyncAccounts
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
}
|