mirror of
https://github.com/nix-community/home-manager
synced 2024-09-12 14:26:30 +02:00
This function takes in a set of groups, which includes their consituent channels and writes the appropriate .mbsyncrc block for the channel. The block that is generated is shown below: Channel groupName1-channelName1 Master :<accountName>-remote:<master-pattern> Slave :<accountName>-local:<slave-pattern> Channel groupName2-channelName2 Master :<accountName>-remote:<master-pattern> Slave :<accountName>-local:<slave-pattern> Each group must have a unique name, no matter which account it is declared under. The same holds true for channels. Using channels with the patterns set up this way allows one to specify which maildir directories are to be synchronized FROM the master TO the slave. In addition, it allows for these maildirs to be remapped, between the master server and the local slave. This is critical, because Gmail has a strange way of storing its mail that makes using mbsync, mu, and mu4e more difficult. There are additional channel parameters that are already present in this codebase from the previous use of group-channel configuration, which will be reused.
215 lines
7.5 KiB
Nix
215 lines
7.5 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
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 = toString tls.certificatesFile;
|
|
};
|
|
|
|
masterSlaveMapping = {
|
|
none = "None";
|
|
imap = "Master";
|
|
maildir = "Slave";
|
|
both = "Both";
|
|
};
|
|
|
|
genSection = header: entries:
|
|
let
|
|
escapeValue = escape [ ''"'' ];
|
|
hasSpace = v: builtins.match ".* .*" v != null;
|
|
genValue = n: v:
|
|
if isList v then
|
|
concatMapStringsSep " " (genValue n) v
|
|
else if isBool v then
|
|
(if v then "yes" else "no")
|
|
else if isInt v then
|
|
toString v
|
|
else if isString v && hasSpace v then
|
|
''"${escapeValue v}"''
|
|
else if isString v then
|
|
v
|
|
else
|
|
let prettyV = lib.generators.toPretty { } v;
|
|
in throw "mbsync: unexpected value for option ${n}: '${prettyV}'";
|
|
in ''
|
|
${header}
|
|
${concatStringsSep "\n"
|
|
(mapAttrsToList (n: v: "${n} ${genValue n v}") entries)}
|
|
'';
|
|
|
|
genAccountConfig = account:
|
|
with account;
|
|
genSection "IMAPAccount ${name}" ({
|
|
Host = imap.host;
|
|
User = userName;
|
|
PassCmd = toString passwordCommand;
|
|
} // genTlsConfig imap.tls
|
|
// optionalAttrs (imap.port != null) { Port = toString imap.port; }
|
|
// mbsync.extraConfig.account) + "\n"
|
|
+ genSection "IMAPStore ${name}-remote"
|
|
({ Account = name; } // mbsync.extraConfig.remote) + "\n"
|
|
+ genSection "MaildirStore ${name}-local" ({
|
|
Path = "${maildir.absPath}/";
|
|
Inbox = "${maildir.absPath}/${folders.inbox}";
|
|
SubFolders = "Verbatim";
|
|
} // optionalAttrs (mbsync.flatten != null) { Flatten = mbsync.flatten; }
|
|
// mbsync.extraConfig.local) + "\n" + 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) + "\n";
|
|
# Given the attr set of groups, return a string of channels to put into each group.
|
|
# Given the attr set of groups, return a string of channels that will direct
|
|
# mail to the proper directories, according to the pattern used in channel's
|
|
# master pattern definition.
|
|
genGroupChannelConfig = storeName: groups:
|
|
let
|
|
# Given the name of the group this channel is part of and the channel
|
|
# itself, generate the string for the desired configuration.
|
|
genChannelString = groupName: channel:
|
|
genSection "Channel ${groupName}-${channel.name}" ({
|
|
Master = ":${storeName}-remote:${channel.masterPattern}";
|
|
Slave = ":${storeName}-local:${channel.slavePattern}";
|
|
}) + "\n";
|
|
# Given the group name, and a attr set of channels within that group,
|
|
# Generate a list of strings for each channels' configuration.
|
|
genChannelStrings = groupName: channels:
|
|
mapAttrsToList (channelName: info: genChannelString groupName info) channels;
|
|
# Given a group, return a string that configures all the channels within
|
|
# the group.
|
|
genGroupsChannels = group: concatStrings
|
|
(genChannelStrings group.name group.channels);
|
|
in
|
|
# Generate all channel configurations for all groups for this account.
|
|
concatStringsSep "\n"
|
|
(mapAttrsToList (name: group: genGroupsChannels group) groups);
|
|
|
|
# Given the attr set of groups, return a string which maps channels to groups
|
|
genAccountGroups = groups:
|
|
let
|
|
# Given the name of the group and the attribute set of channels, make
|
|
# make "Channel <grpName>-<chnName>" for each channel to list os strings
|
|
genChannelStrings = groupName: channels: mapAttrsToList
|
|
(name: info: "Channel ${groupName}-${name}") channels;
|
|
# Take in 1 group, construct the "Group <grpName>" header, and construct
|
|
# each of the channels.
|
|
genGroupChannelString = group:
|
|
[("Group " + group.name)] ++
|
|
(genChannelStrings group.name group.channels);
|
|
# Given set of groups, generates list of strings, where each string is one
|
|
# of the groups and its consituent channels.
|
|
genGroupsStrings = mapAttrsToList (name: info: concatStringsSep "\n"
|
|
(genGroupChannelString groups.${name})) groups;
|
|
in (concatStringsSep "\n\n" genGroupsStrings) # Put all strings together.
|
|
# 2 \n needed in concatStringsSep because last element genGroupsStrings
|
|
# has no \n.
|
|
+ "\n\n"; # Additional spacing after this account's group setup.
|
|
|
|
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 = literalExample "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.
|
|
'';
|
|
};
|
|
};
|
|
|
|
accounts.email.accounts = mkOption {
|
|
type = with types; attrsOf (submodule (import ./mbsync-accounts.nix));
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = let
|
|
checkAccounts = pred: msg:
|
|
let badAccounts = filter pred mbsyncAccounts;
|
|
in {
|
|
assertion = badAccounts == [ ];
|
|
message = "mbsync: ${msg} for accounts: "
|
|
+ concatMapStringsSep ", " (a: a.name) badAccounts;
|
|
};
|
|
in [
|
|
(checkAccounts (a: a.maildir == null) "Missing maildir configuration")
|
|
(checkAccounts (a: a.imap == null) "Missing IMAP configuration")
|
|
(checkAccounts (a: a.passwordCommand == null) "Missing passwordCommand")
|
|
(checkAccounts (a: a.userName == null) "Missing username")
|
|
];
|
|
|
|
home.packages = [ cfg.package ];
|
|
|
|
programs.notmuch.new.ignore = [ ".uidvalidity" ".mbsyncstate" ];
|
|
|
|
home.file.".mbsyncrc".text = let
|
|
accountsConfig = map genAccountConfig mbsyncAccounts;
|
|
groupsConfig = mapAttrsToList genGroupConfig cfg.groups;
|
|
in concatStringsSep "\n" ([''
|
|
# Generated by Home Manager.
|
|
''] ++ optional (cfg.extraConfig != "") cfg.extraConfig ++ accountsConfig
|
|
++ groupsConfig) + "\n";
|
|
|
|
home.activation = mkIf (mbsyncAccounts != [ ]) {
|
|
createMaildir =
|
|
hm.dag.entryBetween [ "linkGeneration" ] [ "writeBoundary" ] ''
|
|
$DRY_RUN_CMD mkdir -m700 -p $VERBOSE_ARG ${
|
|
concatMapStringsSep " " (a: a.maildir.absPath) mbsyncAccounts
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
}
|