{ pkgs, lib, config, ... }:
let
  inherit (builtins) attrNames concatStringsSep readFile toPath;
  inherit (builtins.extraBuiltins) pass;
  inherit (lib) types;
  inherit (pkgs.lib) loadFile unlines unwords unlinesAttrs;
  inherit (config) networking users;
  inherit (config.services) postfix dovecot2 openldap;
in
{
imports = [
  postfix/sourcephile.fr.nix
  postfix/autogeree.net.nix
];
options = {
  services.postfix = {
    tls_server_sni_maps = lib.mkOption {
      type = types.attrsOf (types.listOf types.path);
      default = {};
      apply = m: pkgs.writeText "sni" (lib.concatStringsSep "\n" (lib.mapAttrsToList (domain: x509: ''
        ${domain} ${unwords x509}
      '') m));
    };
  };
};
config = {
users.groups.acme.members = [ postfix.user ];
systemd.services.postfix = {
  after = ["openldap.service"];
  preStart = ''
    install -m 400 -o root -g root ${postfix.tls_server_sni_maps} /run/keys/postfix-sni
    ${pkgs.postfix}/bin/postmap -F hash:/run/keys/postfix-sni
  '';
};
services.postfix = {
  enable = true;
  networksStyle = "host";
  hostname ="${networking.hostName}.${networking.domain}";
  domain = networking.domain;
  origin = "$myhostname";
  destination = [
    "localhost"
    "localhost.localdomain"
    "$myhostname"
  ];
  postmasterAlias = "root";
  rootAlias = "root@${networking.domain}";
  sslCert = loadFile (../../../sec/openssl + "/${networking.domain}/cert.self-signed.pem");
  sslKey = "/run/keys/${networking.domain}.key.pem";
  networks = [
    "127.0.0.0/8"
    "[::1]/128"
  ];
  setSendmail = true;
  # Parse the extension in email address, eg. contact+extension@
  recipientDelimiter = "+";
  config = {
    debug_peer_level = "4";
    debug_peer_list = [
      #"chomsky.autogeree.net"
      #"localhost"
      #"mail.sourcephile.fr"
    ];

    #
    # Sending to the world
    #
    # Appending .domain is the MUA's job
    append_dot_mydomain = false;
    smtp_body_checks = "";
    #smtp_cname_overrides_servername = false;
    smtp_connect_timeout = "60s";
    #smtp_header_checks = "regexp:/var/lib/postfix/smtp_header_checks";
    smtp_mime_header_checks = "";
    smtp_nested_header_checks = "";
    smtp_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
    #smtp_tls_fingerprint_digest = "sha1";
    smtp_tls_loglevel = "1";
    #smtp_tls_note_starttls_offer = true;
    #smtp_tls_policy_maps = "hash:/var/lib/postfix/conf/tls_policy";
    # Only allow TLSv* protocols
    smtp_tls_protocols = [ "!SSLv2" "!SSLv3" ];
    #smtp_tls_scert_verifydepth = "5";
    #smtp_tls_secure_cert_match = [ "nexthop" "dot-nexthop" ];
    smtp_tls_security_level = "may";
    smtp_tls_session_cache_database = "btree:$data_directory/smtp_tls_session_cache";
    #smtp_tls_session_cache_timeout = "3600s";
    #smtp_tls_verify_cert_match = "hostname";

    #
    # Receiving from the world
    #
    message_size_limit = "20480000";
    maximal_queue_lifetime = "5d";
    default_extra_recipient_limit = "5000";
    line_length_limit = "2048";
    duplicate_filter_limit = "5000";
    # Stops mail from poorly written software
    strict_rfc821_envelopes = true;
    mime_header_checks = [];
    milter_header_checks = [];
    nested_header_checks = [];
    body_checks = [];
    content_filter = "";
    permit_mx_backup_networks = [];
    propagate_unmatched_extensions = [ "canonical" "virtual" "alias" ];
    #masquerade_classes = [ "envelope_sender" "header_sender" "header_recipient" ];
    #masquerade_domains = "";
    #masquerade_exceptions = "root";
    queue_minfree = "0";
    # Stops some techniques used to harvest email addresses
    disable_vrfy_command = true;
    enable_long_queue_ids = false;
    # Useful to test restrictions
    smtpd_authorized_xclient_hosts = "127.0.0.1";
    smtpd_banner = "$myhostname ESMTP $mail_name (NixOS)";
    smtpd_client_connection_count_limit = "50";
    smtpd_client_connection_rate_limit = "0";
    smtpd_client_event_limit_exceptions = "$mynetworks";
    smtpd_client_message_rate_limit = "0";
    smtpd_client_new_tls_session_rate_limit = "0";
    smtpd_client_port_logging = false;
    smtpd_client_recipient_rate_limit = "0";
    # Ban 5 sec on error
    smtpd_error_sleep_time = "5";
    # Needed to enforce reject_unknown_helo_hostname
    smtpd_helo_required = true;
    smtpd_helo_restrictions = [
      "reject_invalid_helo_hostname"
      "reject_non_fqdn_helo_hostname"
      # Don't talk to mail systems that don't know their own hostname.
      "reject_unknown_helo_hostname"
      "permit"
    ];
    smtpd_client_restrictions = [
    ];
    # Set in postfix/*.nix and used in submissions/smptd
    # with reject_sender_login_mismatch
    smtpd_sender_login_maps = [];
    smtpd_sender_restrictions = [
      "reject_non_fqdn_sender"
      "permit"
    ];
    smtpd_reject_unlisted_recipient = true;
    # Check the RCPT TO, before smtpd_recipient_restrictions
    # Restrictions based on what is allowed or not,
    # these are applied before smtpd_recipient_restrictions
    smtpd_relay_restrictions = [
      "permit_mynetworks"
      # Check the recipient's address in virtual_mailbox_domains and virtual_mailbox_maps
      "permit_auth_destination"
      # The world is only authorized to use our relay for the above destinations.
      "reject"
    ];
    # Restrictions based on what is working or not
    smtpd_recipient_restrictions = [
      # Reject if the domain is not fully qualified
      "reject_non_fqdn_recipient"
      # Reject if the domain is not working, even before bothering to check the address
      "reject_unknown_recipient_domain"
      # Reject if the address is not working
      # WARNING: verify(8) has a cache, dumpable if verify(8) is stopped, with:
      # postmap -s btree:/var/lib/postfix/data/verify_cache
      "reject_unverified_recipient"
      "permit"
    ];
    # Trust the verify database
    unverified_recipient_reject_code = "550";
    smtpd_data_restrictions = [
      # Force the smtpd's client to wait OK before sending
      "reject_unauth_pipelining"
      "permit"
    ];
    smtpd_end_of_data_restrictions = [
      # Enforce mail volume quota via policy service callouts.
      #check_policy_service unix:private/policy
    ];
    #smtpd_milters = "";
    smtpd_peername_lookup = true;
    smtpd_recipient_limit = "5000";
    smtpd_recipient_overshoot_limit = "5000";
    #smtpd_restriction_classes = "";
    #smtpd_sasl_auth_enable = true;
    #smtpd_sasl_path = "private/auth";
    #smtpd_sasl_security_options = "noanonymous";
    #smtpd_sasl_type = "dovecot";
    smtpd_starttls_timeout = "300s";
    #smtpd_tls_always_issue_session_ids = true;
    #smtpd_tls_CApath = "/etc/postfix/x509/ca/";
    smtpd_tls_ask_ccert = false;
    #smtpd_tls_ccert_verifydepth = "5";
    smtpd_tls_ciphers = "high";
    smtpd_tls_eecdh_grade = "auto";
    # Disable weak ciphers as reported by https://ssl-tools.net
    # https://serverfault.com/questions/744168/how-to-disable-rc4-on-postfix
    smtpd_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
    smtpd_tls_fingerprint_digest = "sha512";
    # Log only a summary message on TLS handshake completion
    smtpd_tls_loglevel = "1";
    smtpd_tls_mandatory_ciphers = "high";
    smtpd_tls_mandatory_protocols = [ "!SSLv2" "!SSLv3" ];
    # Only allow TLSv*
    smtpd_tls_protocols = [ "!SSLv2" "!SSLv3" ];
    #smtpd_tls_received_header = false;
    smtpd_tls_req_ccert = false;
    # Postfix 2.3 and later
    # encrypt
    #  Mandatory TLS encryption: announce STARTTLS support to SMTP clients, and require that clients use TLS
    #  encryption. According to [1720]RFC 2487 this MUST NOT be applied in case of a publicly-referenced
    #  SMTP server. Instead, this option should be used only on dedicated servers.
    smtpd_tls_security_level = "may";
    smtpd_tls_session_cache_database = "btree:$data_directory/smtpd_tls_session_cache";
    #smtpd_tls_session_cache_timeout = "3600s";
    #smtpd_tls_chain_files =

    relayhost = [];
    #relay_clientcerts = hash:/var/lib/postfix/conf/relay_clientcerts
    # This is where to put backup MX domains
    relay_domains = [];
    relay_recipient_maps = [];

    # Use a non blocking source of randomness
    tls_random_source = "dev:/dev/urandom";
    # Map each domain to a specific X.509 certificate
    tls_server_sni_maps = "hash:/run/keys/postfix-sni";

    # Only explicitely aliased accounts have a mail, not all the passwd
    local_recipient_maps = "$alias_maps";
    # Note that the local transport rewrites the envelope recipient
    # according to the alias_maps, and thus the aliasing is transparent
    # to the nexthop (eg. dovecot)
    #local_transport = local:$myhostname
    # No console bell on new mail
    biff = false;
    forward_path = [
      /*
      "$home/.forward''${recipient_delimiter}''${extension}"
      "$home/.forward"
      */
    ];

    # Filled by the postfix/*.nix
    virtual_mailbox_domains = [];
    # Completed by the postfix/*.nix
    virtual_mailbox_maps = [
      "hash:/etc/postfix/virtual"
    ];
    virtual_transport = "lmtp:unix:private/dovecot-lmtp";

    # There is no fallback
    fallback_transport = "";
  };
  virtualMapType = "hash";
  masterConfig =
    let
      mkVal = value:
        if lib.isList value
        then concatStringsSep "," value
        else
          if value == true then "yes"
          else if value == false then "no"
          else toString value;
      mkKeyVal = opt: val: [ "-o" (opt + "=" + mkVal val) ];
      mkArgs = args: lib.concatLists (lib.mapAttrsToList mkKeyVal args);
    in {
    pickup = {
      args = mkArgs {
        cleanup_service_name = "submissions-header-cleanup";
      };
    };
    # Implicit TLS on port 465
    # https://tools.ietf.org/html/rfc8314#section-3.3
    submissions = {
      type = "inet";
      private = false;
      command = "smtpd";
      args = mkArgs {
        syslog_name = "postfix/submissions";
        # Implicit TLS, not STARTTLS
        smtpd_tls_wrappermode = true;
        smtpd_tls_mandatory_protocols = [
          "TLSv1.3"
          # K-9 Mail 5.600 still requires this..
          "TLSv1.2"
        ];
        milter_macro_daemon_name = "ORIGINATING";
        smtpd_relay_restrictions = [
          # SASL authorizes to send to the world
          "permit_sasl_authenticated"
          "reject"
        ];
        smtpd_sasl_auth_enable = true;
        smtpd_sasl_type = "dovecot";
        smtpd_sasl_path = "private/auth";
        smtpd_sasl_local_domain = "";
        # Offer SASL authentication only after a TLS-encrypted session has been established
        smtpd_tls_auth_only = true;
        smtpd_sasl_tls_security_options = [ "noanonymous" ];
        # Do not put SASL logins in mail headers
        smtpd_sasl_authenticated_header = false;
        # Who cares about (old) Outlook
        broken_sasl_auth_clients = false;
        smtpd_sender_restrictions = [
          "reject_non_fqdn_sender"
          # Check that the SASL user is using only its own
          # mail addresses on the envelope, as indicated in smtpd_sender_login_maps
          "reject_sender_login_mismatch"
          "permit"
        ];
        # No X.509 certificates for users, for now
        smtpd_tls_ask_ccert = false;
        smtpd_tls_ccert_verifydepth = 0;
        smtpd_tls_loglevel = 1;
        smtpd_tls_req_ccert = false;
        cleanup_service_name = "submissions-header-cleanup";
      };
    };
    submissions-header-cleanup = {
      type = "unix";
      private = false;
      maxproc = 0;
      command = "cleanup";
      args = mkArgs {
        header_checks = "pcre:" + pkgs.writeText "submission_header_cleanup_rules" ''
          # Removes sensitive headers from mails handed in via the submission or smtps port.
          # See https://thomas-leister.de/mailserver-debian-stretch/
          # Uses "pcre" style regex.

          /^Received:/         IGNORE
          /^User-Agent:/       IGNORE
          /^X-Enigmail:/       IGNORE
          /^X-Mailer:/         IGNORE
          /^X-Originating-IP:/ IGNORE
        '';
      };
    };
  };
  extraMasterConf = ''
    #spfcheck    unix  -        n       n       -        0        spawn
    #  user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
    # -o smtpd_sender_restrictions=reject_sender_login_mismatch
    # -o smtpd_sender_login_maps=hash:/etc/postfix/vaccounts
    # -o cleanup_service_name=submissions-header-cleanup
    #spfcheck  unix  -       n       n       -       0       spawn
    #  user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl
    #uucp      unix  -       n       n       -       -       pipe
    #  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
    #smtp      inet  n       -       -       -       -       smtpd
    #  -o cleanup_service_name=pre-cleanup
    #  -o content_filter=amavis:[127.0.0.1]:10024
    #  -o smtpd_sender_restrictions=reject_unauth_pipelining,reject_non_fqdn_sender,permit
    #  -o receive_override_options=no_address_mappings
    #amavis    unix  -       -       n        -      2       lmtp
    #  -o lmtp_data_done_timeout=1200
    #  -o lmtp_send_xforward_command=yes
    #  -o lmtp_tls_note_starttls_offer=no
    #127.0.0.1:10025 inet n  -       n       -       -       smtpd
    #  -o content_filter=
    #  -o local_header_rewrite_clients=
    #  -o local_recipient_maps=
    #  -o mynetworks=127.0.0.0/8
    #  -o receive_override_options=no_header_body_checks,no_milters,no_unknown_recipient_checks
    #  -o relay_recipient_maps=
    #  -o smtpd_client_connection_count_limit=0
    #  -o smtpd_client_connection_rate_limit=0
    #  -o smtpd_client_restrictions=permit_mynetworks,reject
    #  -o smtpd_data_restrictions=reject_unauth_pipelining
    #  -o smtpd_delay_reject=no
    #  -o smtpd_end_of_data_restrictions=
    #  -o smtpd_error_sleep_time=0
    #  -o smtpd_hard_error_limit=1000
    #  -o smtpd_helo_restrictions=
    #  -o smtpd_milters=
    #  -o smtpd_recipient_restrictions=permit_mynetworks,reject
    #  -o smtpd_restriction_classes=
    #  -o smtpd_sender_restrictions=
    #  -o smtpd_soft_error_limit=1001
    #  -o strict_rfc821_envelopes=yes
    #submission inet n       -       -       -       -       smtpd
    #  -o cleanup_service_name=pre-cleanup
    #  -o content_filter=amavis:[127.0.0.1]:10024
    #  -o milter_macro_daemon_name=ORIGINATING
    #  -o receive_override_options=no_address_mappings
    #  -o smtpd_sender_restrictions=permit_tls_clientcerts,reject
    #  -o smtpd_tls_ask_ccert=yes
    #  -o smtpd_tls_auth_only=yes
    #  -o smtpd_tls_ccert_verifydepth=2
    #  -o smtpd_tls_loglevel=1
    #  -o smtpd_tls_req_ccert=yes
    #  -o smtpd_tls_security_level=encrypt
    #smtps     inet  n       -       -       -       -       smtpd
    #  -o milter_macro_daemon_name=ORIGINATING
    #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
    #  -o smtpd_sasl_auth_enable=yes
    #  -o smtpd_tls_ask_ccert=yes
    #  -o smtpd_tls_auth_only=yes
    #  -o smtpd_tls_ccert_verifydepth=0
    #  -o smtpd_tls_loglevel=1
    #  -o smtpd_tls_req_ccert=no
    #  -o smtpd_tls_security_level=encrypt
    #  -o smtpd_tls_wrappermode=yes
    #pickup    fifo  n       -       -       60      1       pickup
    #  -o cleanup_service_name=pre-cleanup
    #  -o content_filter=amavis:[127.0.0.1]:10024
    #pre-cleanup unix n      -       -       -       0       cleanup
    #  -o virtual_alias_maps=
    #cleanup   unix  n       -       -       -       0       cleanup
    #  -o mime_header_checks=
    #  -o nested_header_checks=
    #  -o body_checks=
    #  -o header_checks=
    #-- SYMPA begin
    #sympa unix - n n - - pipe
    #  flags=R user=sympa argv=/usr/lib/sympa/bin/queue ''${recipient}
    #sympabounce unix - n n - - pipe
    #  flags=R user=sympa argv=/usr/lib/sympa/bin/bouncequeue ''${recipient}
    #-- SYMPA end
  '';
   #noclue    unix  -       n       n       -       -       pipe
   #  flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}
};
};
}