diff --git a/modules/accounts/calendar.nix b/modules/accounts/calendar.nix index 1a8b061cc..94d8d38f3 100644 --- a/modules/accounts/calendar.nix +++ b/modules/accounts/calendar.nix @@ -150,6 +150,7 @@ in types.submodule [ calendarOpts (import ../programs/vdirsyncer/accounts.nix) + (import ../programs/pimsync/accounts.nix) (import ../programs/khal/accounts.nix) (import ../programs/khal/calendar-accounts.nix) ] diff --git a/modules/accounts/contacts.nix b/modules/accounts/contacts.nix index 6e0833618..780f6bb58 100644 --- a/modules/accounts/contacts.nix +++ b/modules/accounts/contacts.nix @@ -141,6 +141,7 @@ in types.submodule [ contactOpts (import ../programs/vdirsyncer/accounts.nix) + (import ../programs/pimsync/accounts.nix) (import ../programs/khal/accounts.nix) (import ../programs/khal/contact-accounts.nix) ] diff --git a/modules/programs/pimsync/accounts.nix b/modules/programs/pimsync/accounts.nix new file mode 100644 index 000000000..ce0abaef1 --- /dev/null +++ b/modules/programs/pimsync/accounts.nix @@ -0,0 +1,41 @@ +{ lib, ... }: + +let + inherit (lib) mkOption; + inherit (lib.hm.types) SCFGDirectives; +in +{ + options.pimsync = { + enable = lib.mkEnableOption "synchronization using pimsync"; + + extraRemoteStorageDirectives = mkOption { + type = SCFGDirectives; + default = [ ]; + description = "Extra directives that should be added under this accounts remote storage directive"; + example = [ + { + name = "interval"; + params = [ 60 ]; + } + ]; + }; + + extraLocalStorageDirectives = mkOption { + type = SCFGDirectives; + default = [ ]; + description = "Extra directives that should be added under this accounts local storage directive"; + }; + + extraPairDirectives = mkOption { + type = SCFGDirectives; + default = [ ]; + description = "Extra directives that should be added under this accounts pair directive"; + example = [ + { + name = "collections"; + params = [ "all" ]; + } + ]; + }; + }; +} diff --git a/modules/programs/pimsync/default.nix b/modules/programs/pimsync/default.nix new file mode 100644 index 000000000..b8edb7df5 --- /dev/null +++ b/modules/programs/pimsync/default.nix @@ -0,0 +1,151 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + options = { + programs.pimsync = { + enable = lib.mkEnableOption "pimsync"; + + package = lib.mkPackageOption pkgs "pimsync" { }; + + settings = lib.mkOption { + description = '' + Settings to be added to pimsync.conf. + See {manpage}`pimsync.conf(5)`. + ''; + type = lib.hm.types.SCFGDirectives; + default = [ + { + name = "status_path"; + params = [ "${config.xdg.dataHome}/pimsync/status" ]; + } + ]; + defaultText = lib.literalExpression '' + [ + { + name = "status_path"; + params = [ "''${config.xdg.dataHome}/pimsync/status" ]; + } + ] + ''; + }; + }; + }; + + config = + let + cfg = config.programs.pimsync; + calendarAccounts = lib.filterAttrs (_: v: v.pimsync.enable) config.accounts.calendar.accounts; + contactAccounts = lib.filterAttrs (_: v: v.pimsync.enable) config.accounts.contact.accounts; + + # Provides a very naïve translation of an (non-nested) attribute set to a SCFGDirective + attrsToDirectives = lib.mapAttrsToList ( + name: value: { + inherit name; + params = lib.toList value; + } + ); + + localStorage = calendar: name: acc: { + name = "storage"; + params = [ "${name}-local" ]; + children = + (attrsToDirectives { + inherit (acc.local) path; + fileext = acc.local.fileExt; + type = if calendar then "vdir/icalendar" else "vdir/vcard"; + }) + ++ acc.pimsync.extraLocalStorageDirectives; + }; + + remoteStorage = calendar: name: acc: { + name = "storage"; + params = [ "${name}-remote" ]; + children = + (attrsToDirectives { + inherit (acc.remote) url; + username = acc.remote.userName; + type = + if !calendar then + "carddav" + else if acc.remote.type == "caldav" then + acc.remote.type + else + "webcal"; + }) + ++ lib.optional (acc.remote.passwordCommand != null) { + name = "password"; + children = lib.singleton { + name = "cmd"; + params = acc.remote.passwordCommand; + }; + } + ++ acc.pimsync.extraRemoteStorageDirectives; + }; + + pair = calendar: name: acc: { + name = "pair"; + params = lib.singleton "${if calendar then "calendar" else "contacts"}-${name}"; + children = + (attrsToDirectives { + storage_a = "${name}-local"; + storage_b = "${name}-remote"; + }) + ++ acc.pimsync.extraPairDirectives; + }; + + multiMapAttrsToList = calendar: attrs: lib.concatMap (f: lib.mapAttrsToList (f calendar) attrs); + + calendarConfig = multiMapAttrsToList true calendarAccounts [ + localStorage + remoteStorage + pair + ]; + + contactConfig = multiMapAttrsToList false contactAccounts [ + localStorage + remoteStorage + pair + ]; + + accountSettings = calendarConfig ++ contactConfig; + in + lib.mkIf cfg.enable { + meta.maintainers = [ lib.maintainers.antonmosich ]; + + assertions = + let + contactRemotes = lib.mapAttrsToList (_: acc: { + assertion = acc.remote.type == "carddav"; + message = "pimsync can only handle contact remotes of type carddav"; + }) contactAccounts; + calendarRemotes = lib.mapAttrsToList (_: acc: { + assertion = acc.remote.type == "caldav" || acc.remote.type == "http"; + message = "pimsync can only handle calendar remotes of types http or caldav"; + }) calendarAccounts; + sharedAsserts = lib.mapAttrsToList ( + _: acc: [ + { + assertion = acc.local.type == "filesystem"; + message = "pimsync only supports type filesystem for local"; + } + ] + ); + in + lib.flatten [ + contactRemotes + calendarRemotes + (sharedAsserts contactAccounts) + (sharedAsserts calendarAccounts) + ]; + + home.packages = [ cfg.package ]; + + xdg.configFile."pimsync/pimsync.conf".text = lib.hm.generators.toSCFG { } ( + accountSettings ++ cfg.settings + ); + }; +}