Merge pull request #45470 from Infinisil/znc-config

nixos/znc: More flexible module, cleanups
This commit is contained in:
Silvan Mosberger 2018-10-17 03:01:30 +02:00 committed by GitHub
commit e443bbf6fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 583 additions and 432 deletions

View file

@ -122,6 +122,14 @@
the Python 2 or 3 version of the package.
</para>
</listitem>
<listitem>
<para>
Options
<literal>services.znc.confOptions.networks.<replaceable>name</replaceable>.userName</literal> and
<literal>services.znc.confOptions.networks.<replaceable>name</replaceable>.modulePackages</literal>
were removed. They were never used for anything and can therefore safely be removed.
</para>
</listitem>
</itemizedlist>
</section>

View file

@ -635,7 +635,7 @@
./services/networking/zerobin.nix
./services/networking/zeronet.nix
./services/networking/zerotierone.nix
./services/networking/znc.nix
./services/networking/znc/default.nix
./services/printing/cupsd.nix
./services/scheduling/atd.nix
./services/scheduling/chronos.nix

View file

@ -1,431 +0,0 @@
{ config, lib, pkgs, ...}:
with lib;
let
cfg = config.services.znc;
defaultUser = "znc"; # Default user to own process.
# Default user and pass:
# un=znc
# pw=nixospass
defaultUserName = "znc";
defaultPassBlock = "
<Pass password>
Method = sha256
Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
Salt = l5Xryew4g*!oa(ECfX2o
</Pass>
";
modules = pkgs.buildEnv {
name = "znc-modules";
paths = cfg.modulePackages;
};
# Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`.
mkZncConf = confOpts: ''
Version = 1.6.3
${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.modules}
<Listener l>
Port = ${toString confOpts.port}
IPv4 = true
IPv6 = true
SSL = ${boolToString confOpts.useSSL}
${lib.optionalString (confOpts.uriPrefix != null) "URIPrefix = ${confOpts.uriPrefix}"}
</Listener>
<User ${confOpts.userName}>
${confOpts.passBlock}
Admin = true
Nick = ${confOpts.nick}
AltNick = ${confOpts.nick}_
Ident = ${confOpts.nick}
RealName = ${confOpts.nick}
${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.userModules}
${ lib.concatStringsSep "\n" (lib.mapAttrsToList (name: net: ''
<Network ${name}>
${concatMapStrings (m: "LoadModule = ${m}\n") net.modules}
Server = ${net.server} ${lib.optionalString net.useSSL "+"}${toString net.port} ${net.password}
${concatMapStrings (c: "<Chan #${c}>\n</Chan>\n") net.channels}
${lib.optionalString net.hasBitlbeeControlChannel ''
<Chan &bitlbee>
</Chan>
''}
${net.extraConf}
</Network>
'') confOpts.networks) }
</User>
${confOpts.extraZncConf}
'';
zncConfFile = pkgs.writeTextFile {
name = "znc.conf";
text = if cfg.zncConf != ""
then cfg.zncConf
else mkZncConf cfg.confOptions;
};
networkOpts = { ... }: {
options = {
server = mkOption {
type = types.str;
example = "chat.freenode.net";
description = ''
IRC server address.
'';
};
port = mkOption {
type = types.int;
default = 6697;
example = 6697;
description = ''
IRC server port.
'';
};
userName = mkOption {
default = "";
example = "johntron";
type = types.string;
description = ''
A nick identity specific to the IRC server.
'';
};
password = mkOption {
type = types.str;
default = "";
description = ''
IRC server password, such as for a Slack gateway.
'';
};
useSSL = mkOption {
type = types.bool;
default = true;
description = ''
Whether to use SSL to connect to the IRC server.
'';
};
modulePackages = mkOption {
type = types.listOf types.package;
default = [];
example = [ "pkgs.zncModules.push" "pkgs.zncModules.fish" ];
description = ''
External ZNC modules to build.
'';
};
modules = mkOption {
type = types.listOf types.str;
default = [ "simple_away" ];
example = literalExample "[ simple_away sasl ]";
description = ''
ZNC modules to load.
'';
};
channels = mkOption {
type = types.listOf types.str;
default = [];
example = [ "nixos" ];
description = ''
IRC channels to join.
'';
};
hasBitlbeeControlChannel = mkOption {
type = types.bool;
default = false;
description = ''
Whether to add the special Bitlbee operations channel.
'';
};
extraConf = mkOption {
default = "";
type = types.lines;
example = ''
Encoding = ^UTF-8
FloodBurst = 4
FloodRate = 1.00
IRCConnectEnabled = true
Ident = johntron
JoinDelay = 0
Nick = johntron
'';
description = ''
Extra config for the network.
'';
};
};
};
in
{
###### Interface
options = {
services.znc = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Enable a ZNC service for a user.
'';
};
user = mkOption {
default = "znc";
example = "john";
type = types.string;
description = ''
The name of an existing user account to use to own the ZNC server process.
If not specified, a default user will be created to own the process.
'';
};
group = mkOption {
default = "";
example = "users";
type = types.string;
description = ''
Group to own the ZNCserver process.
'';
};
dataDir = mkOption {
default = "/var/lib/znc/";
example = "/home/john/.znc/";
type = types.path;
description = ''
The data directory. Used for configuration files and modules.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for ZNC.
'';
};
zncConf = mkOption {
default = "";
example = "See: http://wiki.znc.in/Configuration";
type = types.lines;
description = ''
Config file as generated with `znc --makeconf` to use for the whole ZNC configuration.
If specified, `confOptions` will be ignored, and this value, as-is, will be used.
If left empty, a conf file with default values will be used.
'';
};
confOptions = {
modules = mkOption {
type = types.listOf types.str;
default = [ "webadmin" "adminlog" ];
example = [ "partyline" "webadmin" "adminlog" "log" ];
description = ''
A list of modules to include in the `znc.conf` file.
'';
};
userModules = mkOption {
type = types.listOf types.str;
default = [ "chansaver" "controlpanel" ];
example = [ "chansaver" "controlpanel" "fish" "push" ];
description = ''
A list of user modules to include in the `znc.conf` file.
'';
};
userName = mkOption {
default = defaultUserName;
example = "johntron";
type = types.string;
description = ''
The user name used to log in to the ZNC web admin interface.
'';
};
networks = mkOption {
default = { };
type = with types; attrsOf (submodule networkOpts);
description = ''
IRC networks to connect the user to.
'';
example = {
"freenode" = {
server = "chat.freenode.net";
port = 6697;
useSSL = true;
modules = [ "simple_away" ];
};
};
};
nick = mkOption {
default = "znc-user";
example = "john";
type = types.string;
description = ''
The IRC nick.
'';
};
passBlock = mkOption {
example = defaultPassBlock;
type = types.string;
description = ''
Generate with `nix-shell -p znc --command "znc --makepass"`.
This is the password used to log in to the ZNC web admin interface.
'';
};
port = mkOption {
default = 5000;
example = 5000;
type = types.int;
description = ''
Specifies the port on which to listen.
'';
};
useSSL = mkOption {
default = true;
type = types.bool;
description = ''
Indicates whether the ZNC server should use SSL when listening on the specified port. A self-signed certificate will be generated.
'';
};
uriPrefix = mkOption {
type = types.nullOr types.str;
default = null;
example = "/znc/";
description = ''
An optional URI prefix for the ZNC web interface. Can be
used to make ZNC available behind a reverse proxy.
'';
};
extraZncConf = mkOption {
default = "";
type = types.lines;
description = ''
Extra config to `znc.conf` file.
'';
};
};
modulePackages = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
description = ''
A list of global znc module packages to add to znc.
'';
};
mutable = mkOption {
default = true;
type = types.bool;
description = ''
Indicates whether to allow the contents of the `dataDir` directory to be changed
by the user at run-time.
If true, modifications to the ZNC configuration after its initial creation are not
overwritten by a NixOS system rebuild.
If false, the ZNC configuration is rebuilt by every system rebuild.
If the user wants to manage the ZNC service using the web admin interface, this value
should be set to true.
'';
};
extraFlags = mkOption {
default = [ ];
example = [ "--debug" ];
type = types.listOf types.str;
description = ''
Extra flags to use when executing znc command.
'';
};
};
};
###### Implementation
config = mkIf cfg.enable {
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.confOptions.port ];
};
systemd.services.znc = {
description = "ZNC Server";
wantedBy = [ "multi-user.target" ];
after = [ "network.service" ];
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Restart = "always";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
};
preStart = ''
${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/configs
# If mutable, regenerate conf file every time.
${optionalString (!cfg.mutable) ''
${pkgs.coreutils}/bin/echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
${pkgs.coreutils}/bin/rm -f ${cfg.dataDir}/configs/znc.conf
''}
# Ensure essential files exist.
if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
${pkgs.coreutils}/bin/echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
${pkgs.coreutils}/bin/cp --no-clobber ${zncConfFile} ${cfg.dataDir}/configs/znc.conf
${pkgs.coreutils}/bin/chmod u+rw ${cfg.dataDir}/configs/znc.conf
${pkgs.coreutils}/bin/chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
fi
if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
${pkgs.coreutils}/bin/echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
fi
# Symlink modules
rm ${cfg.dataDir}/modules || true
ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
'';
script = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${toString cfg.extraFlags}";
};
users.users = optional (cfg.user == defaultUser)
{ name = defaultUser;
description = "ZNC server daemon owner";
group = defaultUser;
uid = config.ids.uids.znc;
home = cfg.dataDir;
createHome = true;
};
users.groups = optional (cfg.user == defaultUser)
{ name = defaultUser;
gid = config.ids.gids.znc;
members = [ defaultUser ];
};
};
}

View file

@ -0,0 +1,306 @@
{ config, lib, pkgs, ...}:
with lib;
let
cfg = config.services.znc;
defaultUser = "znc";
modules = pkgs.buildEnv {
name = "znc-modules";
paths = cfg.modulePackages;
};
listenerPorts = concatMap (l: optional (l ? Port) l.Port)
(attrValues (cfg.config.Listener or {}));
# Converts the config option to a string
semanticString = let
sortedAttrs = set: sort (l: r:
if l == "extraConfig" then false # Always put extraConfig last
else if isAttrs set.${l} == isAttrs set.${r} then l < r
else isAttrs set.${r} # Attrsets should be last, makes for a nice config
# This last case occurs when any side (but not both) is an attrset
# The order of these is correct when the attrset is on the right
# which we're just returning
) (attrNames set);
# Specifies an attrset that encodes the value according to its type
encode = name: value: {
null = [];
bool = [ "${name} = ${boolToString value}" ];
int = [ "${name} = ${toString value}" ];
# extraConfig should be inserted verbatim
string = [ (if name == "extraConfig" then value else "${name} = ${value}") ];
# Values like `Foo = [ "bar" "baz" ];` should be transformed into
# Foo=bar
# Foo=baz
list = concatMap (encode name) value;
# Values like `Foo = { bar = { Baz = "baz"; Qux = "qux"; Florps = null; }; };` should be transmed into
# <Foo bar>
# Baz=baz
# Qux=qux
# </Foo>
set = concatMap (subname: [
"<${name} ${subname}>"
] ++ map (line: "\t${line}") (toLines value.${subname}) ++ [
"</${name}>"
]) (filter (v: v != null) (attrNames value));
}.${builtins.typeOf value};
# One level "above" encode, acts upon a set and uses encode on each name,value pair
toLines = set: concatMap (name: encode name set.${name}) (sortedAttrs set);
in
concatStringsSep "\n" (toLines cfg.config);
semanticTypes = with types; rec {
zncAtom = nullOr (either (either int bool) str);
zncAttr = attrsOf (nullOr zncConf);
zncAll = either (either zncAtom (listOf zncAtom)) zncAttr;
zncConf = attrsOf (zncAll // {
# Since this is a recursive type and the description by default contains
# the description of its subtypes, infinite recursion would occur without
# explicitly breaking this cycle
description = "znc values (null, atoms (str, int, bool), list of atoms, or attrsets of znc values)";
});
};
in
{
imports = [ ./options.nix ];
options = {
services.znc = {
enable = mkEnableOption "ZNC";
user = mkOption {
default = "znc";
example = "john";
type = types.str;
description = ''
The name of an existing user account to use to own the ZNC server
process. If not specified, a default user will be created.
'';
};
group = mkOption {
default = defaultUser;
example = "users";
type = types.str;
description = ''
Group to own the ZNC process.
'';
};
dataDir = mkOption {
default = "/var/lib/znc/";
example = "/home/john/.znc/";
type = types.path;
description = ''
The state directory for ZNC. The config and the modules will be linked
to from this directory as well.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for ZNC. Does work with
ports for listeners specified in
<option>services.znc.config.Listener</option>.
'';
};
config = mkOption {
type = semanticTypes.zncConf;
default = {};
example = literalExample ''
{
LoadModule = [ "webadmin" "adminlog" ];
User.paul = {
Admin = true;
Nick = "paul";
AltNick = "paul1";
LoadModule = [ "chansaver" "controlpanel" ];
Network.freenode = {
Server = "chat.freenode.net +6697";
LoadModule = [ "simple_away" ];
Chan = {
"#nixos" = { Detached = false; };
"##linux" = { Disabled = true; };
};
};
Pass.password = {
Method = "sha256";
Hash = "e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93";
Salt = "l5Xryew4g*!oa(ECfX2o";
};
};
}
'';
description = ''
Configuration for ZNC, see
<literal>https://wiki.znc.in/Configuration</literal> for details. The
Nix value declared here will be translated directly to the xml-like
format ZNC expects. This is much more flexible than the legacy options
under <option>services.znc.confOptions.*</option>, but also can't do
any type checking.
</para>
<para>
You can use <command>nix-instantiate --eval --strict '&lt;nixpkgs/nixos&gt;' -A config.services.znc.config</command>
to view the current value. By default it contains a listener for port
5000 with SSL enabled.
</para>
<para>
Nix attributes called <literal>extraConfig</literal> will be inserted
verbatim into the resulting config file.
</para>
<para>
If <option>services.znc.useLegacyConfig</option> is turned on, the
option values in <option>services.znc.confOptions.*</option> will be
gracefully be applied to this option.
</para>
<para>
If you intend to update the configuration through this option, be sure
to enable <option>services.znc.mutable</option>, otherwise none of the
changes here will be applied after the initial deploy.
'';
};
configFile = mkOption {
type = types.path;
example = "~/.znc/configs/znc.conf";
description = ''
Configuration file for ZNC. It is recommended to use the
<option>config</option> option instead.
</para>
<para>
Setting this option will override any auto-generated config file
through the <option>confOptions</option> or <option>config</option>
options.
'';
};
modulePackages = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
description = ''
A list of global znc module packages to add to znc.
'';
};
mutable = mkOption {
default = true; # TODO: Default to true when config is set, make sure to not delete the old config if present
type = types.bool;
description = ''
Indicates whether to allow the contents of the
<literal>dataDir</literal> directory to be changed by the user at
run-time.
</para>
<para>
If enabled, modifications to the ZNC configuration after its initial
creation are not overwritten by a NixOS rebuild. If disabled, the
ZNC configuration is rebuilt on every NixOS rebuild.
</para>
<para>
If the user wants to manage the ZNC service using the web admin
interface, this option should be enabled.
'';
};
extraFlags = mkOption {
default = [ ];
example = [ "--debug" ];
type = types.listOf types.str;
description = ''
Extra arguments to use for executing znc.
'';
};
};
};
###### Implementation
config = mkIf cfg.enable {
services.znc = {
configFile = mkDefault (pkgs.writeText "znc-generated.conf" semanticString);
config = {
Version = (builtins.parseDrvName pkgs.znc.name).version;
Listener.l.Port = mkDefault 5000;
Listener.l.SSL = mkDefault true;
};
};
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall listenerPorts;
systemd.services.znc = {
description = "ZNC Server";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Restart = "always";
ExecStart = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${escapeShellArgs cfg.extraFlags}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
};
preStart = ''
mkdir -p ${cfg.dataDir}/configs
# If mutable, regenerate conf file every time.
${optionalString (!cfg.mutable) ''
echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
rm -f ${cfg.dataDir}/configs/znc.conf
''}
# Ensure essential files exist.
if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf
chmod u+rw ${cfg.dataDir}/configs/znc.conf
chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
fi
if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
fi
# Symlink modules
rm ${cfg.dataDir}/modules || true
ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
'';
};
users.users = optional (cfg.user == defaultUser)
{ name = defaultUser;
description = "ZNC server daemon owner";
group = defaultUser;
uid = config.ids.uids.znc;
home = cfg.dataDir;
createHome = true;
};
users.groups = optional (cfg.user == defaultUser)
{ name = defaultUser;
gid = config.ids.gids.znc;
members = [ defaultUser ];
};
};
}

View file

@ -0,0 +1,268 @@
{ lib, config, ... }:
with lib;
let
cfg = config.services.znc;
networkOpts = {
options = {
server = mkOption {
type = types.str;
example = "chat.freenode.net";
description = ''
IRC server address.
'';
};
port = mkOption {
type = types.ints.u16;
default = 6697;
description = ''
IRC server port.
'';
};
password = mkOption {
type = types.str;
default = "";
description = ''
IRC server password, such as for a Slack gateway.
'';
};
useSSL = mkOption {
type = types.bool;
default = true;
description = ''
Whether to use SSL to connect to the IRC server.
'';
};
modules = mkOption {
type = types.listOf types.str;
default = [ "simple_away" ];
example = literalExample "[ simple_away sasl ]";
description = ''
ZNC network modules to load.
'';
};
channels = mkOption {
type = types.listOf types.str;
default = [];
example = [ "nixos" ];
description = ''
IRC channels to join.
'';
};
hasBitlbeeControlChannel = mkOption {
type = types.bool;
default = false;
description = ''
Whether to add the special Bitlbee operations channel.
'';
};
extraConf = mkOption {
default = "";
type = types.lines;
example = ''
Encoding = ^UTF-8
FloodBurst = 4
FloodRate = 1.00
IRCConnectEnabled = true
Ident = johntron
JoinDelay = 0
Nick = johntron
'';
description = ''
Extra config for the network. Consider using
<option>services.znc.config</option> instead.
'';
};
};
};
in
{
options = {
services.znc = {
useLegacyConfig = mkOption {
default = true;
type = types.bool;
description = ''
Whether to propagate the legacy options under
<option>services.znc.confOptions.*</option> to the znc config. If this
is turned on, the znc config will contain a user with the default name
"znc", global modules "webadmin" and "adminlog" will be enabled by
default, and more, all controlled through the
<option>services.znc.confOptions.*</option> options.
You can use <command>nix-instantiate --eval --strict '&lt;nixpkgs/nixos&gt;' -A config.services.znc.config</command>
to view the current value of the config.
</para>
<para>
In any case, if you need more flexibility,
<option>services.znc.config</option> can be used to override/add to
all of the legacy options.
'';
};
confOptions = {
modules = mkOption {
type = types.listOf types.str;
default = [ "webadmin" "adminlog" ];
example = [ "partyline" "webadmin" "adminlog" "log" ];
description = ''
A list of modules to include in the `znc.conf` file.
'';
};
userModules = mkOption {
type = types.listOf types.str;
default = [ "chansaver" "controlpanel" ];
example = [ "chansaver" "controlpanel" "fish" "push" ];
description = ''
A list of user modules to include in the `znc.conf` file.
'';
};
userName = mkOption {
default = "znc";
example = "johntron";
type = types.str;
description = ''
The user name used to log in to the ZNC web admin interface.
'';
};
networks = mkOption {
default = { };
type = with types; attrsOf (submodule networkOpts);
description = ''
IRC networks to connect the user to.
'';
example = literalExample ''
{
"freenode" = {
server = "chat.freenode.net";
port = 6697;
useSSL = true;
modules = [ "simple_away" ];
};
};
'';
};
nick = mkOption {
default = "znc-user";
example = "john";
type = types.str;
description = ''
The IRC nick.
'';
};
passBlock = mkOption {
example = literalExample ''
&lt;Pass password&gt;
Method = sha256
Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
Salt = l5Xryew4g*!oa(ECfX2o
&lt;/Pass&gt;
'';
type = types.str;
description = ''
Generate with `nix-shell -p znc --command "znc --makepass"`.
This is the password used to log in to the ZNC web admin interface.
You can also set this through
<option>services.znc.config.User.&lt;username&gt;.Pass.Method</option>
and co.
'';
};
port = mkOption {
default = 5000;
type = types.int;
description = ''
Specifies the port on which to listen.
'';
};
useSSL = mkOption {
default = true;
type = types.bool;
description = ''
Indicates whether the ZNC server should use SSL when listening on
the specified port. A self-signed certificate will be generated.
'';
};
uriPrefix = mkOption {
type = types.nullOr types.str;
default = null;
example = "/znc/";
description = ''
An optional URI prefix for the ZNC web interface. Can be
used to make ZNC available behind a reverse proxy.
'';
};
extraZncConf = mkOption {
default = "";
type = types.lines;
description = ''
Extra config to `znc.conf` file.
'';
};
};
};
};
config = mkIf cfg.useLegacyConfig {
services.znc.config = let
c = cfg.confOptions;
# defaults here should override defaults set in the non-legacy part
mkDefault = mkOverride 900;
in {
LoadModule = mkDefault c.modules;
Listener.l = {
Port = mkDefault c.port;
IPv4 = mkDefault true;
IPv6 = mkDefault true;
SSL = mkDefault c.useSSL;
};
User.${c.userName} = {
Admin = mkDefault true;
Nick = mkDefault c.nick;
AltNick = mkDefault "${c.nick}_";
Ident = mkDefault c.nick;
RealName = mkDefault c.nick;
LoadModule = mkDefault c.userModules;
Network = mapAttrs (name: net: {
LoadModule = mkDefault net.modules;
Server = mkDefault "${net.server} ${optionalString net.useSSL "+"}${toString net.port} ${net.password}";
Chan = optionalAttrs net.hasBitlbeeControlChannel { "&bitlbee" = mkDefault {}; } //
listToAttrs (map (n: nameValuePair "#${n}" (mkDefault {})) net.channels);
extraConfig = if net.extraConf == "" then mkDefault null else net.extraConf;
}) c.networks;
extraConfig = [ c.passBlock ] ++ optional (c.extraZncConf != "") c.extraZncConf;
};
};
};
imports = [
(mkRemovedOptionModule ["services" "znc" "zncConf"] ''
Instead of `services.znc.zncConf = "... foo ...";`, use
`services.znc.configFile = pkgs.writeText "znc.conf" "... foo ...";`.
'')
];
}