1
0
Fork 0
mirror of https://github.com/nix-community/home-manager synced 2024-12-27 04:09:47 +01:00
home-manager/modules/programs/thunderbird.nix
2024-12-21 23:54:29 +01:00

460 lines
16 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
inherit (pkgs.stdenv.hostPlatform) isDarwin;
cfg = config.programs.thunderbird;
thunderbirdJson = types.attrsOf (pkgs.formats.json { }).type // {
description =
"Thunderbird preference (int, bool, string, and also attrs, list, float as a JSON string)";
};
# The extensions path shared by all profiles.
extensionPath = "extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
moduleName = "programs.thunderbird";
enabledAccounts = attrValues
(filterAttrs (_: a: a.thunderbird.enable) config.accounts.email.accounts);
enabledAccountsWithId =
map (a: a // { id = builtins.hashString "sha256" a.name; }) enabledAccounts;
thunderbirdConfigPath =
if isDarwin then "Library/Thunderbird" else ".thunderbird";
thunderbirdProfilesPath = if isDarwin then
"${thunderbirdConfigPath}/Profiles"
else
thunderbirdConfigPath;
profilesWithId =
imap0 (i: v: v // { id = toString i; }) (attrValues cfg.profiles);
profilesIni = foldl recursiveUpdate {
General = {
StartWithLastProfile = 1;
} // lib.optionalAttrs (cfg.profileVersion != null) {
Version = cfg.profileVersion;
};
} (flip map profilesWithId (profile: {
"Profile${profile.id}" = {
Name = profile.name;
Path = if isDarwin then "Profiles/${profile.name}" else profile.name;
IsRelative = 1;
Default = if profile.isDefault then 1 else 0;
};
}));
toThunderbirdIdentity = account: address:
# For backwards compatibility, the primary address reuses the account ID.
let
id = if address == account.address then
account.id
else
builtins.hashString "sha256" address;
in {
"mail.identity.id_${id}.fullName" = account.realName;
"mail.identity.id_${id}.useremail" = address;
"mail.identity.id_${id}.valid" = true;
"mail.identity.id_${id}.htmlSigText" =
if account.signature.showSignature == "none" then
""
else
account.signature.text;
} // optionalAttrs (account.gpg != null) {
"mail.identity.id_${id}.attachPgpKey" = false;
"mail.identity.id_${id}.autoEncryptDrafts" = true;
"mail.identity.id_${id}.e2etechpref" = 0;
"mail.identity.id_${id}.encryptionpolicy" =
if account.gpg.encryptByDefault then 2 else 0;
"mail.identity.id_${id}.is_gnupg_key_id" = true;
"mail.identity.id_${id}.last_entered_external_gnupg_key_id" =
account.gpg.key;
"mail.identity.id_${id}.openpgp_key_id" = account.gpg.key;
"mail.identity.id_${id}.protectSubject" = true;
"mail.identity.id_${id}.sign_mail" = account.gpg.signByDefault;
} // optionalAttrs (account.smtp != null) {
"mail.identity.id_${id}.smtpServer" = "smtp_${account.id}";
} // account.thunderbird.perIdentitySettings id;
toThunderbirdAccount = account: profile:
let
id = account.id;
addresses = [ account.address ] ++ account.aliases;
in {
"mail.account.account_${id}.identities" = concatStringsSep ","
([ "id_${id}" ]
++ map (address: "id_${builtins.hashString "sha256" address}")
account.aliases);
"mail.account.account_${id}.server" = "server_${id}";
} // optionalAttrs account.primary {
"mail.accountmanager.defaultaccount" = "account_${id}";
} // optionalAttrs (account.imap != null) {
"mail.server.server_${id}.directory" =
"${thunderbirdProfilesPath}/${profile.name}/ImapMail/${id}";
"mail.server.server_${id}.directory-rel" = "[ProfD]ImapMail/${id}";
"mail.server.server_${id}.hostname" = account.imap.host;
"mail.server.server_${id}.login_at_startup" = true;
"mail.server.server_${id}.name" = account.name;
"mail.server.server_${id}.port" =
if (account.imap.port != null) then account.imap.port else 143;
"mail.server.server_${id}.socketType" = if !account.imap.tls.enable then
0
else if account.imap.tls.useStartTls then
2
else
3;
"mail.server.server_${id}.type" = "imap";
"mail.server.server_${id}.userName" = account.userName;
} // optionalAttrs (account.smtp != null) {
"mail.smtpserver.smtp_${id}.authMethod" = 3;
"mail.smtpserver.smtp_${id}.hostname" = account.smtp.host;
"mail.smtpserver.smtp_${id}.port" =
if (account.smtp.port != null) then account.smtp.port else 587;
"mail.smtpserver.smtp_${id}.try_ssl" = if !account.smtp.tls.enable then
0
else if account.smtp.tls.useStartTls then
2
else
3;
"mail.smtpserver.smtp_${id}.username" = account.userName;
} // optionalAttrs (account.smtp != null && account.primary) {
"mail.smtp.defaultserver" = "smtp_${id}";
} // builtins.foldl' (a: b: a // b) { }
(builtins.map (address: toThunderbirdIdentity account address) addresses)
// account.thunderbird.settings id;
mkUserJs = prefs: extraPrefs: ''
// Generated by Home Manager.
${concatStrings (mapAttrsToList (name: value: ''
user_pref("${name}", ${builtins.toJSON value});
'') prefs)}
${extraPrefs}
'';
in {
meta.maintainers = with hm.maintainers; [ d-dervishi jkarlson ];
options = {
programs.thunderbird = {
enable = mkEnableOption "Thunderbird";
package = mkOption {
type = types.package;
default = pkgs.thunderbird;
defaultText = literalExpression "pkgs.thunderbird";
example = literalExpression "pkgs.thunderbird-91";
description = "The Thunderbird package to use.";
};
profileVersion = mkOption {
internal = true;
type = types.nullOr types.ints.unsigned;
default = if isDarwin then null else 2;
description = "profile version, set null for nix-darwin";
};
profiles = mkOption {
type = with types;
attrsOf (submodule ({ config, name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
readOnly = true;
description = "This profile's name.";
};
isDefault = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether this is a default profile. There must be exactly one
default profile.
'';
};
settings = mkOption {
type = thunderbirdJson;
default = { };
example = literalExpression ''
{
"mail.spellcheck.inline" = false;
"mailnews.database.global.views.global.columns" = {
selectCol = {
visible = false;
ordinal = 1;
};
threadCol = {
visible = true;
ordinal = 2;
};
};
}
'';
description = ''
Preferences to add to this profile's
{file}`user.js`.
'';
};
withExternalGnupg = mkOption {
type = types.bool;
default = false;
example = true;
description = "Allow using external GPG keys with GPGME.";
};
userChrome = mkOption {
type = types.lines;
default = "";
description = "Custom Thunderbird user chrome CSS.";
example = ''
/* Hide tab bar in Thunderbird */
#tabs-toolbar {
visibility: collapse !important;
}
'';
};
userContent = mkOption {
type = types.lines;
default = "";
description = "Custom Thunderbird user content CSS.";
example = ''
/* Hide scrollbar on Thunderbird pages */
*{scrollbar-width:none !important}
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Extra preferences to add to {file}`user.js`.
'';
};
search = mkOption {
type = types.submodule (args:
import ./firefox/profiles/search.nix {
inherit (args) config;
inherit lib pkgs;
appName = "Thunderbird";
package = cfg.package;
modulePath =
[ "programs" "thunderbird" "profiles" name "search" ];
profilePath = name;
});
default = { };
description = "Declarative search engine configuration.";
};
extensions = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExpression ''
[
pkgs.some-thunderbird-extension
]
'';
description = ''
List of ${name} add-on packages to install for this profile.
Note that it is necessary to manually enable extensions
inside ${name} after the first installation.
To automatically enable extensions add
`"extensions.autoDisableScopes" = 0;`
to
[{option}`${moduleName}.profiles.<profile>.settings`](#opt-${moduleName}.profiles._name_.settings)
'';
};
};
}));
description = "Attribute set of Thunderbird profiles.";
};
settings = mkOption {
type = thunderbirdJson;
default = { };
example = literalExpression ''
{
"general.useragent.override" = "";
"privacy.donottrackheader.enabled" = true;
}
'';
description = ''
Attribute set of Thunderbird preferences to be added to
all profiles.
'';
};
darwinSetupWarning = mkOption {
type = types.bool;
default = true;
example = false;
visible = isDarwin;
readOnly = !isDarwin;
description = ''
Warn to set environment variables before using this module. Only
relevant on Darwin.
'';
};
};
accounts.email.accounts = mkOption {
type = with types;
attrsOf (submodule {
options.thunderbird = {
enable =
mkEnableOption "the Thunderbird mail client for this account";
profiles = mkOption {
type = with types; listOf str;
default = [ ];
example = literalExpression ''
[ "profile1" "profile2" ]
'';
description = ''
List of Thunderbird profiles for which this account should be
enabled. If this list is empty (the default), this account will
be enabled for all declared profiles.
'';
};
settings = mkOption {
type = with types; functionTo (attrsOf (oneOf [ bool int str ]));
default = _: { };
defaultText = literalExpression "_: { }";
example = literalExpression ''
id: {
"mail.server.server_''${id}.check_new_mail" = false;
};
'';
description = ''
Extra settings to add to this Thunderbird account configuration.
The {var}`id` given as argument is an automatically
generated account identifier.
'';
};
perIdentitySettings = mkOption {
type = with types; functionTo (attrsOf (oneOf [ bool int str ]));
default = _: { };
defaultText = literalExpression "_: { }";
example = literalExpression ''
id: {
"mail.identity.id_''${id}.protectSubject" = false;
"mail.identity.id_''${id}.autoEncryptDrafts" = false;
};
'';
description = ''
Extra settings to add to each identity of this Thunderbird
account configuration. The {var}`id` given as
argument is an automatically generated identifier.
'';
};
};
});
};
};
config = mkIf cfg.enable {
assertions = [
(let defaults = catAttrs "name" (filter (a: a.isDefault) profilesWithId);
in {
assertion = cfg.profiles == { } || length defaults == 1;
message = "Must have exactly one default Thunderbird profile but found "
+ toString (length defaults) + optionalString (length defaults > 1)
(", namely " + concatStringsSep "," defaults);
})
(let
profiles = catAttrs "name" profilesWithId;
selectedProfiles =
concatMap (a: a.thunderbird.profiles) enabledAccounts;
in {
assertion = (intersectLists profiles selectedProfiles)
== selectedProfiles;
message = "Cannot enable an account for a non-declared profile. "
+ "The declared profiles are " + (concatStringsSep "," profiles)
+ ", but the used profiles are "
+ (concatStringsSep "," selectedProfiles);
})
];
warnings = optional (isDarwin && cfg.darwinSetupWarning) ''
Thunderbird packages are not yet supported on Darwin. You can still use
this module to manage your accounts and profiles by setting
'programs.thunderbird.package' to a dummy value, for example using
'pkgs.runCommand'.
'';
home.packages = [ cfg.package ]
++ optional (any (p: p.withExternalGnupg) (attrValues cfg.profiles))
pkgs.gpgme;
home.file = mkMerge ([{
"${thunderbirdConfigPath}/profiles.ini" =
mkIf (cfg.profiles != { }) { text = generators.toINI { } profilesIni; };
}] ++ flip mapAttrsToList cfg.profiles (name: profile: {
"${thunderbirdProfilesPath}/${name}/chrome/userChrome.css" =
mkIf (profile.userChrome != "") { text = profile.userChrome; };
"${thunderbirdProfilesPath}/${name}/chrome/userContent.css" =
mkIf (profile.userContent != "") { text = profile.userContent; };
"${thunderbirdProfilesPath}/${name}/user.js" = let
accounts = filter (a:
a.thunderbird.profiles == [ ]
|| any (p: p == name) a.thunderbird.profiles) enabledAccountsWithId;
smtp = filter (a: a.smtp != null) accounts;
in {
text = mkUserJs (builtins.foldl' (a: b: a // b) { } ([
cfg.settings
(optionalAttrs (length accounts != 0) {
"mail.accountmanager.accounts" =
concatStringsSep "," (map (a: "account_${a.id}") accounts);
})
(optionalAttrs (length smtp != 0) {
"mail.smtpservers" =
concatStringsSep "," (map (a: "smtp_${a.id}") smtp);
})
{ "mail.openpgp.allow_external_gnupg" = profile.withExternalGnupg; }
profile.settings
] ++ (map (a: toThunderbirdAccount a profile) accounts)))
profile.extraConfig;
};
"${thunderbirdProfilesPath}/${name}/search.json.mozlz4" =
mkIf (profile.search.enable) {
enable = profile.search.enable;
force = profile.search.force;
source = profile.search.file;
};
"${thunderbirdProfilesPath}/${name}/extensions" =
mkIf (profile.extensions != [ ]) {
source = let
extensionsEnvPkg = pkgs.buildEnv {
name = "hm-thunderbird-extensions";
paths = profile.extensions;
};
in "${extensionsEnvPkg}/share/mozilla/${extensionPath}";
recursive = true;
force = true;
};
}));
};
}