{ zentralwerk, config, hostRegistry, pkgs, lib, ... }:

let
  domain = "jabber.c3d2.de";

  inherit (pkgs.jabber-secrets) coturnUser coturnPassword;
  prosodyFirewall = pkgs.writeText "antispam.pfw" ''
    %ZONE spam: creep.im, default.rs, sj.ms, anonym.im, xmpp.jp, safetyjabber.com, im.hot-chilli.net, jabb3r.org, draugr.de, laba.im, xmpp.sh, jabber.bitactive.com, 404.city, jabber.cd, jabber.jc-otto.de, jabster.pl, jabber.no, anoxinon.me, ubuntu-jabber.net, anonarchy.im, jabber.freenet.de, exploit.im, 616.pub, omemo.im, rsocks.net, chatwith.xyz, jabber.cz, jabbim.cz, blabber.im, jabber.root.cz, jabb.im, jabber.infos.ru, jabbim.pl, jabbim.com, linuxlovers.at, jabbim.ru, jabber.sk, njs.netlab.cz, jabba.biz, chatterboxtown.us, crime.io, 0nl1ne.at, verdammung.org, im.apinc.org, 0day.la, 0day.im, xabber.de, conversations.im, jabber.de, chinwag.im, jabber.ccc.de, thesecure.biz, shad0w.ru, yourdata.forsale, linux.monster, xmpp.international, paranoid.network, og.im, 4ept.net, darknet.im, ubuntu-jabber.de, deshalbfrei.org, nixnet.services, marxist.club, dw.live, 01337.io, yax.im, sqli.io, breached.im, pwned.life, jabber.fr, chatterboxtown.us, xmpp.xxx, ybgood.de, ejabber.co, jabbers.one

    IN ROSTER?
    PASS.

    LEAVING: spam
    BOUNCE=policy-violation (Your domain has been blacklisted due to spam.)
  '';
in
{
  microvm.mem = 2048;

  networking = {
    hostName = "jabber";
    firewall.allowedTCPPorts = [
      # Prosody
      5222 5223 5269
      80 5280 443 5281
      # Coturn
      3478 3479
    ];
    firewall.allowedUDPPorts = [
      # Coturn
      3478 3479
    ];
    # TODO: allowedSCTPPorts
  };

  c3d2 = {
    deployment.server = "server10";
    hq.statistics.enable = true;
  };

  services.collectd.plugins.exec = ''
    Exec "${config.services.collectd.user}" "${pkgs.ruby}/bin/ruby" "${./prosody-stats.rb}"
  '';
  systemd.services.collectd.requires = [ "prosody.service" ];

  security.acme.certs."${domain}" = {
    extraDomainNames = [
      "chat.c3d2.de"
      "*.${domain}"
    ];
    # DynDNS method
    dnsProvider = "rfc2136";
    credentialsFile = config.sops.secrets."acme/credentials-file".path;
    # Make keys accessible by putting them in prosody's group
    inherit (config.services.prosody) group;
  };
  
  services.postgresql = {
    enable = true;
    ensureDatabases = [
      "prosody"
    ];
    ensureUsers = [ {
      name = "prosody";
      ensurePermissions = {
        "DATABASE prosody" = "ALL PRIVILEGES";
      };
    } ];
    package = pkgs.postgresql_15;
    upgrade.stopServices = [ "prosody" ];
  };
  #TODO: txt records?
  services.prosody = {
    enable = true;
    allowRegistration = false;
    admins = [ "astro@spaceboyz.net" "0@jabber.c3d2.de" "nek0@c3d2.de" ];
    package = pkgs.prosody.override {
      withCommunityModules = [ "smacks" "csi" "cloud_notify" "extdisco" "firewall" ];
      withExtraLuaPackages = luaPackages: with luaPackages; [
        luadbi-postgresql
      ];
    };

    modules = {
      # HTTP stuff
      bosh = true;
      websocket = true;
      http_files = true;

      admin_telnet = true;
      announce = true;
      mam = true;
      carbons = true;
      proxy65 = false;
    };

    ssl = {
      key = "/var/lib/acme/${domain}/key.pem";
      cert = "/var/lib/acme/${domain}/fullchain.pem";
      # Some TLS hardening we've had on the old setup, probably to
      # defend against downgrading attacks.
      extraOptions.options = [
        "no_sslv2" "no_sslv3"
        "no_ticket" "no_compression"
        "cipher_server_preference"
        "single_dh_use" "single_ecdh_use"
      ];
    };

    c2sRequireEncryption = true;
    s2sRequireEncryption = true; 

    virtualHosts = {
      "${domain}" = {
        enabled = true;
        inherit domain;
      };
      "anon.${domain}" = {
        enabled = true;
        domain = "anon.${domain}";
        extraConfig = ''
          authentication = "anonymous"
        '';
      };
    };
    muc = [ {
      domain = "chat.c3d2.de";
      name = "Group chats";
    } ];

    httpPorts = [ 80 5280 ];
    httpsPorts = [ 443 5281 ];
    uploadHttp = {
      domain = "upload.${domain}";
      uploadFileSizeLimit = "10 * 1024 * 1024";
      userQuota = 512 * 1024 * 1024;
      uploadExpireAfter = "2 * 60 * 60";
    };

    extraConfig = ''
      legacy_ssl_ports = { 5223 }
      legacy_ssl_ssl = {
        key = "/var/lib/acme/${domain}/key.pem",
        certificate = "/var/lib/acme/${domain}/fullchain.pem",
      }
      certificates = "/var/lib/acme"

      storage = "sql"
      sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "" }
      log = { info = "*syslog"; }

      firewall_scripts = { "${prosodyFirewall}" }

      trusted_proxies = { "127.0.0.1", "::1", "${hostRegistry.public-access-proxy.ip4}", "${hostRegistry.public-access-proxy.ip4}", }
      http_default_host = "${domain}"
      http_host = "${domain}"
      http_external_url = "https://${domain}/"
      http_upload_file_size_limit = 10 * 1024 * 1024
      http_upload_expire_after = 60 * 60 * 24 * 7 -- a week in seconds
      cross_domain_bosh = true -- Allow access from scripts on any site with no proxy (requires a modern browser)

      external_services = {
        ["turn.${domain}"] = {
          username = "${coturnUser}";
          password = "${coturnPassword}";
          port = "3478";
          transport = "udp";
          type = "turn";
        };
        ["${zentralwerk.lib.config.site.hosts.upstream4.interfaces.up4-pppoe.upstream.staticIpv4Address}"] = {
          username = "${coturnUser}";
          password = "${coturnPassword}";
          port = "3478";
          transport = "udp";
          type = "turn";
        };
      }

      -- -- File-transfer proxies are an outdated technology
      -- Component "proxy65.${domain}" "proxy65"
      --   proxy65_address = "proxy.${domain}"
      --   proxy65_acl = { "${domain}" }
      --   proxy65_interfaces = { "*", "::" }
      --   proxy65_ports = { 5000 }
    '';
  };
  systemd.services.prosody.serviceConfig = {
    # Allow binding ports <1024
    AmbientCapabilities = "CAP_NET_BIND_SERVICE";

    Restart = "always";
    RestartSec = "3";
  };

  services.coturn = {
    enable = true;
    realm = "turn.${domain}";
    lt-cred-mech = true;
    extraConfig = with zentralwerk.lib.config.site; ''
      external-ip=${hosts.upstream4.interfaces.up4-pppoe.upstream.staticIpv4Address}/${net.serv.hosts4.jabber}

      user=${coturnUser}:${coturnPassword}
    '';
  };

  sops = {
    defaultSopsFile = ./secrets.yaml;
    secrets."acme/credentials-file".owner = "root";
  };

  system.stateVersion = "21.05";
}
