{ config, pkgs, ... }:

let
  luaPam = pkgs.callPackage ./custom/luaPam.nix {};
  luaPosix = pkgs.callPackage ./custom/luaPosix.nix {};
  luaSha2 = pkgs.callPackage ./custom/luaSha2.nix {};
  prosodyAuth = pkgs.callPackage ./custom/prosody-auth.nix {};
  prosodyVirtHost = name: {
    enabled = true;
    domain = name;
    ssl = {
      key = "/var/lib/acme/yggdrasil.li/key.pem";
      cert = "/var/lib/acme/yggdrasil.li/fullchain.pem";
    };
  };
in rec {
  imports =
    [
      ./ymir-hw.nix
      ./custom/zsh.nix
      ./users.nix
      ./custom/tinc/def.nix
      ./custom/ymir-nginx.nix
      ./custom/uucp.nix
    ];

  boot.loader.grub = {
    enable = true;
    version = 2;
    device = "/dev/vda";
  };

  boot.kernel.sysctl = {
    "net.ipv4.tcp_keepalive_time" = 60;
    "net.ipv4.tcp_keepalive_intvl" = 10;
    "net.ipv4.tcp_keepalive_probes" = 6;
  };

  nixpkgs.config.packageOverrides = pkgs:
    rec {
      prosody = pkgs.callPackage ./customized/prosody.nix ({
          inherit (pkgs.lua51Packages) luasocket luasec luaexpat luafilesystem luabitop luaevent luazlib;
          lua5 = pkgs.lua5_1;
          communityModules = ["mod_carbons" "mod_reload_modules"];
          extraModules = [prosodyAuth];
          extraLibs = [luaPam luaPosix luaSha2];
        });
      uwsgi = pkgs.callPackage ./customized/uwsgi.nix {
        extraPlugins = [
	  { name = "cgi";
	    interpreter = pkgs.python3;
	    path = "plugins/cgi";
	    deps = [ pkgs.python3 ];
	    install = ''
              ${pkgs.python3.executable} -m compileall $out/${pkgs.python3.sitePackages}/
              ${pkgs.python3.executable} -O -m compileall $out/${pkgs.python3.sitePackages}/
	    '';
	  }
	];
	plugins = [];
      };
      cgit = pkgs.stdenv.lib.overrideDerivation pkgs.cgit (oldAttrs : {
        buildInputs = oldAttrs.buildInputs ++ [
	  pkgs.perl
	  pkgs.python3
	  pkgs.makeWrapper
	];
	postInstall = let
          pythonEnv = pkgs.python3.buildEnv.override { extraLibs = with pkgs.python3Packages; [ pygments markdown ]; };
        in ''
          wrapProgram $out/lib/cgit/filters/syntax-highlighting.py --prefix PYTHONPATH ':' ${pythonEnv}/lib/*/site-packages
          tmpFile=$(mktemp)
          chmod +x $tmpFile
          echo "#!${pythonEnv}/bin/python3" >$tmpFile
          tail -n +2 $out/lib/cgit/filters/html-converters/md2html >>$tmpFile
          mv -v $tmpFile $out/lib/cgit/filters/html-converters/md2html
          wrapProgram $out/lib/cgit/filters/html-converters/md2html --prefix PYTHONPATH ':' ${pythonEnv}/lib/*/site-packages
          wrapProgram $out/lib/cgit/filters/html-converters/man2html --prefix PATH ':' ${pkgs.groff}/bin
        '';
      });
      push2bin = pkgs.writeScriptBin "push2bin" ''
        #!${pkgs.zsh}/bin/zsh

        PATH=${pkgs.coreutils}/bin:${pkgs.gawk}/bin

        baseDir=/srv/www/files
        baseUrl="https://f.141.li"

        tmpFile=$(mktemp "''${baseDir}/.upload.XXXXXXXXXX")

        function zshexit() { [[ -n "''${tmpFile}" && -e "''${tmpFile}" ]] && rm -f "''${tmpFile}" }

        prefix=$(tee "''${tmpFile}" | sha512sum | awk '{ print $1; }' | head -c 10)
        prefix=''${prefix:l}
        filename="$1"
 
        [[ -z "''${prefix}" || -z "''${filename}" ]] && exit 2
        [[ ! -f "''${tmpFile}" || $(stat -c '%s' "''${tmpFile}") == "0" ]] && exit 3
 
        mkdir -p "''${baseDir}/''${prefix}"
        mv "''${tmpFile}" "''${baseDir}/''${prefix}/''${filename}"
 
        chmod 755 "''${baseDir}/''${prefix}"
        chmod 644 "''${baseDir}/''${prefix}/''${filename}"
 
        printf "%s/%s/%s" "''${baseUrl}" "''${prefix}" "''${filename}"
      '';
    };

  environment.systemPackages = with pkgs; [
    git
    mosh
    rsync
    tmux
    zsh
  ];

  networking = {
    hostName = "ymir";
    hostId = "1c5c994e";
    firewall = {
      enable = true;
      allowPing = true;
      allowedTCPPorts = [ 22 # ssh
                          25 # smtp
                          143 # imap
                          993 # imaps
                          5222 # xmpp.s2c
                          5269 # xmpp.s2s
                          655 # tinc.yggdrasil
                          656 # tinc.laeradhr
                          80 # http
                          443 # https
                          9418 # git
                        ];
      allowedUDPPortRanges = [ { from = 60000; to = 61000; } # mosh
                             ];
    };
    enableIPv6 = true;
    defaultGateway6 = "fe80::1";
    interfaces."enp0s3" = {
      ipv6Address = "2a03:4000:6:d004::";
      ipv6PrefixLength = 64;
    };
  };

  users.extraUsers.root = let
    template = (import users/gkleen.nix);
    in {
        inherit (template) shell;
        openssh.authorizedKeys.keyFiles = template.openssh.authorizedKeys.keyFiles;
      };

  services.ntp = {
    enable = false;
  };

  nix.binaryCaches = [ "https://cache.nixos.org/"
                     ];
  nix.binaryCachePublicKeys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
                              ];

  # List services that you want to enable:

  services.openssh = {
    enable = true;
    passwordAuthentication = false;
    extraConfig = ''
      AllowGroups ssh
    '';
  };
  users.groups."ssh" = {
    members = ["gitolite" "uucp" "root"];
  };

  services.fcron = {
    enable = true;
    systab = ''
      %weekly,erroronlymail  * *   nix-collect-garbage --delete-older-than '7d'
    '';
  };

  users.groups."ssl" = {
    members = [ "prosody"
                "nginx"
                "postfix"
              ];
  };

  services.chrony = {
    enable = true;
  };

  services.prosody = {
    enable = true;
    admins = [
               "gkleen@xmpp.li"
               "gkleen@praseodym.org"
               "gkleen@141.li"
               "gkleen@yggdrasil.li"
             ];
    allowRegistration = false;
    extraModules = [ "private"
                     "auth_custom"
                     "carbons"
                     "reload_modules"
                   ];
    extraConfig = ''
      reload_modules = { "group", "tls" }
      authentication="custom"
      custom_alias_file="/etc/prosody/aliases"
      custom_alias_secret_file="/etc/prosody/alias_secret"

      ssl = {
        capath = "/etc/ssl/certs/ca-certificates.crt"
      }
      s2s_secure_auth = true

      Component "alias.xmpp.li"
        Include "/etc/prosody/alias.xmpp.li.cfg.lua"
    '';

    virtualHosts = builtins.listToAttrs (map (name: { inherit name; value = prosodyVirtHost name; })
      ["xmpp.li" "yggdrasil.li" "praseodym.org" "141.li"]);
  };
  security.pam.services."xmpp".text = ''
    auth requisite  pam_succeed_if.so user ingroup xmpp
    auth required   pam_unix.so audit
  '';
  users.groups."shadow" = {
    members = [ "prosody"
              ];
  };
  users.groups."xmpp" = {};
  system.activationScripts."shadow-perms" = ''
    chown root:shadow /etc/shadow
    chmod 0640 /etc/shadow
  '';

  services.customTinc.networks = ((import ./custom/tinc/yggdrasil.nix) {
    inherit (pkgs) stdenv nettools openresolv;
    name = "ymir";
    connect = false;
    ipConf = {
      ip4 = [ { address = "10.141.5.1"; prefixLength = 16; } ];
    };
    })
    // ((import ./custom/tinc/laeradhr.nix) {
     inherit (pkgs) stdenv nettools openresolv;
    name = "ymir";
    connect = false;
    ipConf = {
      ip4 = [ { address = "10.142.0.3"; prefixLength = 16; } ];
    };
    });

  users.extraUsers."nginx".extraGroups = ["uwsgi"];

  services.uwsgi = {
    enable = true;
    plugins = ["python3" "cgi"];
    instance = {
      type = "normal";
      processes = 1;
      threads = 8;
      chdir = "${pkgs.cgit}/cgit";
      cgi = "${pkgs.cgit}/cgit/cgit.cgi";
      socket = "/tmp/cgit.sock";
      chmod-socket = "660";
      chown-socket = "uwsgi:nginx";
    };
  };
  
  users.extraUsers."uwsgi".extraGroups = ["git"];

  environment.etc."cgitrc" = {
    enable = true;
    text = ''
      robots=noindex, nofollow
      virtual-root=/
      enable-git-config=1
      remove-suffix=1

      root-title=git.yggdrasil.li
      root-desc=

      enable-http-clone=1

      enable-commit-graph=1
      snapshots=tar tar.gz tar.bz2 tar.xz zip
      side-by-side-diffs=1

      source-filter=${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py
      about-filter=${pkgs.cgit}/lib/cgit/filters/about-formatting.sh

      readme=:README.md
      readme=:README.txt
      readme=:README
      readme=:readme.md
      readme=:readme.txt
      readme=:readme

      clone-prefix=git://git.yggdrasil.li http://git.yggdrasil.li

      strict-export=git-daemon-export-ok
      project-list=/srv/git/projects.list
      section-from-path=2
      scan-path=/srv/git/repositories
    '';
  };

  services.gitolite = {
    enable = true;
    adminPubkey = builtins.readFile (builtins.head (import ./users/gkleen.nix).openssh.authorizedKeys.keyFiles);
    dataDir = "/srv/git";
  };
  users.extraUsers."gitolite" = {
    group = "git";
  };

  services.gitDaemon = {
    enable = true;
    basePath = services.gitolite.dataDir + "/repositories";
  };

  services.postfix = {
    enable = true;
    hostname = "ymir.yggdrasil.li";
    recipientDelimiter = "+";
    setSendmail = true;
    rootAlias = "gkleen";
    extraAliases = ''
      uucp: root
    '';
    destination = ["yggdrasil.li" "ymir.yggdrasil.li" "praseodym.org" "ymir.praseodym.org" "141.li" "ymir.141.li" "xmpp.li" "ymir.xmpp.li" "dirty-haskell.org" "explainuxul.de" "www.explainuxul.de" "lmu.li" "www.lmu.li" "localhost.yggdrasil.li" "localhost"];
    sslCert = "/var/lib/acme/yggdrasil.li/fullchain.pem";
    sslKey = "/var/lib/acme/yggdrasil.li/key.pem";
    extraConfig = ''
      #the dh params
      smtpd_tls_dh1024_param_file = /etc/ssl/dhparam.pem
      smtpd_tls_dh512_param_file = /etc/ssl/dhparam.pem
      #enable ECDH
      smtpd_tls_eecdh_grade = strong
      #enabled SSL protocols, don't allow SSLv2 and SSLv3
      smtpd_tls_protocols= !SSLv2, !SSLv3
      smtpd_tls_mandatory_protocols= !SSLv2, !SSLv3
      #allowed ciphers for smtpd_tls_security_level=encrypt
      smtpd_tls_mandatory_ciphers = high
      #allowed ciphers for smtpd_tls_security_level=may
      #smtpd_tls_ciphers = high
      #enforce the server cipher preference
      tls_preempt_cipherlist = yes
      #disable following ciphers for smtpd_tls_security_level=encrypt
      smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5 , DES, ADH, RC4, PSD, SRP, 3DES, eNULL
      #disable following ciphers for smtpd_tls_security_level=may
      #smtpd_tls_exclude_ciphers = aNULL, MD5 , DES, ADH, RC4, PSD, SRP, 3DES, eNULL
      #enable TLS logging to see the ciphers for inbound connections
      smtpd_tls_loglevel = 1
      #enable TLS logging to see the ciphers for outbound connections
      smtp_tls_loglevel = 1

      transport_maps = regexp:${pkgs.writeText "transport" ''
        /^gkleen[@\+]/ uucp:isaac
      ''}

      luser_relay = gkleen+''${local}
      
      # 1 GiB
      message_size_limit = 1073741824
      # 10 GiB
      mailbox_size_limit = 10737418240

      mailbox_command = ${pkgs.dovecot}/libexec/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT"

      smtpd_sasl_type = dovecot
      smtpd_sasl_path = auth

      smtpd_sasl_auth_enable = yes
      smtpd_sasl_security_options = noanonymous, noplaintext
      smtpd_sasl_tls_security_options = noanonymous
      smtpd_tls_auth_only = yes

      smtpd_delay_reject = yes
      smtpd_helo_required = yes
      smtpd_helo_restrictions =
        permit_mynetworks,
        reject_non_fqdn_helo_hostname,
        reject_invalid_helo_hostname,
        permit

      smtpd_recipient_restrictions =
        reject_unauth_pipelining,
        reject_non_fqdn_recipient,
        reject_unknown_recipient_domain,
        check_recipient_access hash:/srv/mail/recipient_access,
        check_policy_service unix:policy,
        permit_mynetworks,
        permit_sasl_authenticated,
        reject_unauth_destination

      smtpd_relay_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
        reject_unauth_destination

      alias_maps = hash:/etc/postfix/aliases texthash:/srv/mail/spm
    '';
    extraMasterConf = ''
      uucp unix - n n - - pipe flags=Fqhu user=uucp argv=/var/setuid-wrappers/uux -z -a$sender - $nexthop!rmail ($recipient)
    '';
    networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"];
  };

  services.dovecot2 = {
    enable = true;
    enableImap = true;
    enableLmtp = false;
    enablePop3 = false;
    enablePAM = true;
    sslServerCert = "/var/lib/acme/yggdrasil.li/fullchain.pem";
    sslServerKey = "/var/lib/acme/yggdrasil.li/key.pem";
    mailLocation = "maildir:~/mail:LAYOUT=index:UTF-8";
    extraConfig = ''
      mail_plugins = $mail_plugins quota
      mailbox_list_index = yes
      postmaster_address = postmaster@yggdrasil.li

      service auth {
        unix_listener /var/lib/postfix/queue/auth {
          mode = 0660
          user = postfix
          group = postfix
        }
      }

      namespace {
        separator = /
        inbox = yes
      }

      plugin {
        quota = maildir:User quota
        quota_rule = *:storage=1GB
        quota_rule2 = Trash:storage=+10%%
        quota_status_overquota = "552 5.2.2 Mailbox is full"
        quota_status_success = DUNNO
        quota_status_nouser = DUNNO
      }

      service quota-status {
        executable = quota-status -p postfix
        unix_listener /var/lib/postfix/queue/policy {
          mode = 0660
          user = postfix
          group = postfix
          # You can choose any port you want
        }
        client_limit = 1
      }
    '';
  };
  security.pam.services.dovecot2.text = ''
    auth requisite  pam_succeed_if.so user ingroup mail
    auth required   pam_unix.so audit
    account sufficient pam_unix.so
  '';
  users.groups."mail" = {};

  security.acme = {
    certs = {
      "yggdrasil.li" = {
        allowKeysForGroup = true;
        group = "ssl";
        webroot = "/srv/www/acme/yggdrasil.li";
        email = "phikeebaogobaegh@141.li";
        extraDomains = builtins.listToAttrs (builtins.map (name: { inherit name; value = "/srv/www/acme/${name}"; })
          ["dirty-haskell.org" "www.dirty-haskell.org" "files.141.li" "f.141.li" "ymir.141.li" "141.li" "www.141.li" "ymir.xmpp.li" "xmpp.li" "www.xmpp.li" "files.yggdrasil.li" "f.yggdrasil.li" "ymir.yggdrasil.li" "git.yggdrasil.li" "www.yggdrasil.li" "yggdrasil.li" "files.praseodym.org" "f.praseodym.org" "ymir.praseodym.org" "praseodym.org" "www.praseodym.org"]);
        postRun = ''
          systemctl reload nginx.service
          prosodyctl reload
        '';
      };
    };
  };

  services.uucp = {
    enable = true;
    nodeName = "ymir";
    remoteNodes = ["isaac"]; # legacy name for odin
    sshUser = {
      openssh.authorizedKeys.keys = [ ''no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command="/var/setuid-wrappers/uucico" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgtDHA7oDIaRwggGGznNaKZF68rFTziqefSCn1t9ZKe uucp@odin''
                                    ];
    };
    sshConfig = ''
      Host isaac
        Hostname odin.asgard.yggdrasil
        IdentityFile ~/.ssh/odin
    '';
    commandPath = ["${pkgs.rmail}/bin" "${pkgs.push2bin}/bin"];
    defaultCommands = ["rmail" "push2bin"];
  };

  services.atd = {
    enable = true;
  };

  users.groups."filebin" = {
    members = ["gkleen" "uucp"];
  };
}