#!/usr/bin/python3
# pylint: disable=invalid-name,inconsistent-quotes
"""Configure script for Uyuni proxy httpd container."""

import logging
import os
import subprocess
import re
import yaml
import socket
import sys

from pathlib import Path
from typing import Tuple

config_path = "/etc/uyuni/"


def get_ips(fqdn: str) -> Tuple[str, str]:
    addrinfo = socket.getaddrinfo(fqdn, None)
    ipv4s = set(
        map(lambda r: r[4][0], filter(lambda f: f[0] == socket.AF_INET, addrinfo))
    )
    ipv6s = set(
        map(lambda r: r[4][0], filter(lambda f: f[0] == socket.AF_INET6, addrinfo))
    )
    ipv4, ipv6 = "", ""

    if len(ipv4s) == 0 and len(ipv6s) == 0:
        logging.critical("Cannot determine proxy IPv4 nor IPv6 from FQDN %s", fqdn)
        sys.exit(1)

    try:
        ipv4 = ipv4s.pop()
        if len(ipv4s) > 0:
            logging.warning(
                "Cannot determine unique IPv4 address for the proxy. TFTP sync may not work. Using IPv4 %s",
                ipv4,
            )
    except KeyError:
        logging.warning(
            "No IPv4 address detected for proxy. If this is single stack IPv6 setup this warning can be ignored"
        )

    try:
        ipv6 = ipv6s.pop()
        if len(ipv6s) > 0:
            logging.debug("Multiple IPv6 addresses resolved, using IPv6 %s", ipv6)
    except KeyError:
        logging.warning(
            "No IPv6 address detected for proxy. If this is single stack IPv4 setup this warning can be ignored"
        )

    logging.debug("Detected ips '%s', '%s' for fqdn %s", ipv4, ipv6, fqdn)
    return (ipv4, ipv6)


def insert_under_line(file_path, line_to_match, line_to_insert):
    # add 4 leading spaces and a new line in the end
    line_to_insert = line_to_insert.rjust(len(line_to_insert) + 4) + "\n"

    with open(file_path, "r", encoding="utf-8") as f:
        contents = f.readlines()

    index = -1
    for ind, line in enumerate(contents):
        if line_to_match in line:
            index = ind + 1

    contents.insert(index, line_to_insert)

    with open(file_path, "w", encoding="utf-8") as f:
        contents = "".join(contents)
        f.write(contents)


# read from files
with open(config_path + "config.yaml", encoding="utf-8") as source:
    config = yaml.safe_load(source)

    # log_level is the value for rhn.conf and should be a positive integer
    log_level = logging.WARNING if config.get("log_level") == 1 else logging.DEBUG
    logging.getLogger().setLevel(log_level)

with open(config_path + "httpd.yaml", encoding="utf-8") as httpdSource:
    httpdConfig = yaml.safe_load(httpdSource).get("httpd")

    server_version = config.get("server_version")
    # Only check version for SUSE Multi-Linux Manager, not Uyuni
    matcher = re.fullmatch(r"([0-9]+\.[0-9]+\.)[0-9]+", server_version)
    if matcher:
        major_version = matcher.group(1)
        container_version = subprocess.run(
            ["rpm", "-q", "--queryformat", "%{version}", "spacewalk-proxy-common"],
            stdout=subprocess.PIPE,
            universal_newlines=True,
            check=False,
        ).stdout
        if not container_version.startswith(major_version):
            logging.critical(
                "Proxy container image version (%s) doesn't match server major version (%s)",
                container_version,
                major_version,
            )
            sys.exit(1)

    # store the systemid content
    with open("/etc/sysconfig/rhn/systemid", "w", encoding="utf-8") as file:
        file.write(httpdConfig.get("system_id"))

    # store SSL CA certificate
    with open(
        "/etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT", "w", encoding="utf-8"
    ) as file:
        file.write(config.get("ca_crt"))
    os.symlink(
        "/etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT",
        "/usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT",
    )
    os.system("/usr/sbin/update-ca-certificates")

    # store server certificate files
    with open("/etc/apache2/ssl.crt/server.crt", "w", encoding="utf-8") as file:
        file.write(httpdConfig.get("server_crt"))
    with open("/etc/apache2/ssl.key/server.key", "w", encoding="utf-8") as file:
        file.write(httpdConfig.get("server_key"))

    with open("/etc/apache2/httpd.conf", "r+", encoding="utf-8") as file:
        file_content = file.read()
        # make sure to send logs to stdout/stderr instead to file
        file_content = re.sub(r"ErrorLog .*", "ErrorLog /proc/self/fd/2", file_content)
        # writing back the content
        file.seek(0, 0)
        file.write(file_content)
        file.truncate()

    # resolve needed IP addresses
    server_ipv4, server_ipv6 = get_ips(config["server"])
    proxy_ipv4, proxy_ipv6 = get_ips(config["proxy_fqdn"])

    # Create conf file
    with open("/etc/rhn/rhn.conf", "w", encoding="utf-8") as file:
        file.write(
            f"""# Automatically generated Uyuni Proxy Server configuration file.
        # -------------------------------------------------------------------------
        
        # Debug log level
        debug = {config.get("log_level", 1)}
        
        # Logs redirect
        proxy.broker.log_file = stdout
        proxy.redirect.log_file = stdout

        # SSL CA certificate location
        proxy.ca_chain = /etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT
        
        # Corporate HTTP proxy, format: corp_gateway.example.com:8080
        proxy.http_proxy = 
        
        # Username for that corporate HTTP proxy
        proxy.http_proxy_username = 
        
        # Password for that corporate HTTP proxy
        proxy.http_proxy_password = 
        
        # Location of locally built, custom packages
        proxy.pkg_dir = /var/spool/rhn-proxy
        
        # Hostname of Uyuni, SUSE Multi-Linux Manager Server or another proxy
        proxy.rhn_parent = {config['server']}
        proxy.proxy_fqdn = {config['proxy_fqdn']}
        
        # Destination of all tracebacks, etc.
        traceback_mail = {config['email']}

        # Tftp sync configuration
        tftpsync.server_fqdn = {config['server']}
        tftpsync.server_ip = {server_ipv4}
        tftpsync.server_ip6 = {server_ipv6}
        tftpsync.proxy_ip = {proxy_ipv4}
        tftpsync.proxy_ip6 = {proxy_ipv6}
        tftpsync.proxy_fqdn = {config['proxy_fqdn']}
        tftpsync.tftpboot = /srv/tftpboot"""
        )

    with open(
        "/etc/apache2/conf.d/susemanager-tftpsync-recv.conf", "w", encoding="utf-8"
    ) as file:
        require_ipv4 = ""
        require_ipv6 = ""
        if len(server_ipv4) > 0:
            require_ipv4 = f"Require ip {server_ipv4}"
        if len(server_ipv6) > 0:
            require_ipv6 = f"Require ip {server_ipv6}"
        file.write(
            f"""<Directory "/srv/www/tftpsync">
    <RequireAny>
        {require_ipv4}
        {require_ipv6}
    </RequireAny>
</Directory>

WSGIScriptAlias /tftpsync/add /srv/www/tftpsync/add
WSGIScriptAlias /tftpsync/delete /srv/www/tftpsync/delete"""
        )

    with open("/etc/apache2/conf.d/cobbler-proxy.conf", "w", encoding="utf-8") as file:
        file.write(
            f"""ProxyPass /cobbler_api https://{config['server']}/download/cobbler_api
ProxyPassReverse /cobbler_api https://{config['server']}/download/cobbler_api
RewriteRule ^/cblr/svc/op/ks/(.*)$ /download/$0 [R,L]
RewriteRule ^/cblr/svc/op/autoinstall/(.*)$ /download/$0 [R,L]
ProxyPass /cblr https://{config['server']}/cblr
ProxyPassReverse /cblr https://{config['server']}/cblr
ProxyPass /cobbler https://{config['server']}/cobbler
ProxyPassReverse /cobbler https://{config['server']}/cobbler
        """
        )

    with open(
        "/etc/apache2/conf.d/susemanager-pub.conf", "w", encoding="utf-8"
    ) as file:
        file.write("WSGIScriptAlias /pub /usr/share/rhn/wsgi/xmlrpc.py")

    # redirect API calls to the server
    with open("/etc/apache2/conf.d/smlm-api.conf", "w", encoding="utf-8") as file:
        file.write("WSGIScriptAlias /rhn/manager/api /usr/share/rhn/wsgi/xmlrpc.py")

    with open("/etc/apache2/vhosts.d/ssl.conf", "w", encoding="utf-8") as file:
        file.write(
            f"""
<IfDefine SSL>
<IfDefine !NOSSL>
<VirtualHost _default_:443>

	DocumentRoot "/srv/www/htdocs"
	ServerName {config['proxy_fqdn']}

	ErrorLog /proc/self/fd/2
	TransferLog /proc/self/fd/1
	CustomLog /proc/self/fd/1   ssl_combined

	SSLEngine on
	SSLUseStapling  on

    SSLCertificateFile /etc/apache2/ssl.crt/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl.key/server.key

    SSLProtocol all -SSLv2 -SSLv3
    RewriteEngine on
    RewriteOptions inherit
    SSLProxyEngine on
</VirtualHost>
</IfDefine>
</IfDefine>
"""
        )

    # Adjust logs format in apache httpd:
    # Modify the other configurations so that the var HANDLER_TYPE gets set based on a directory of a script executed
    insert_under_line(
        "/etc/apache2/conf.d/spacewalk-proxy-wsgi.conf",
        "<Directory /usr/share/rhn>",
        'SetEnv HANDLER_TYPE "proxy-broker"',
    )
    insert_under_line(
        "/etc/apache2/conf.d/spacewalk-proxy.conf",
        '<Directory "/srv/www/htdocs/pub/*">',
        'SetEnv HANDLER_TYPE "proxy-html"',
    )
    insert_under_line(
        "/etc/apache2/conf.d/spacewalk-proxy.conf",
        '<Directory "/srv/www/htdocs/docs/*">',
        'SetEnv HANDLER_TYPE "proxy-docs"',
    )

    # redirect /saltboot to the server
    insert_under_line(
        "/etc/apache2/conf.d/spacewalk-proxy-wsgi.conf",
        "WSGIScriptAlias /tftp /usr/share/rhn/wsgi/xmlrpc.py",
        "WSGIScriptAlias /saltboot /usr/share/rhn/wsgi/xmlrpc.py",
    )

    os.system("chown root:www /etc/rhn/rhn.conf")
    os.system("chmod 640 /etc/rhn/rhn.conf")

# Make sure permissions are set as desired
os.system("chown -R wwwrun:www /var/spool/rhn-proxy")
os.system("chmod -R 750 /var/spool/rhn-proxy")
if not os.path.exists("/var/cache/rhn/proxy-auth"):
    os.makedirs("/var/cache/rhn/proxy-auth")
os.system("chown -R wwwrun:root /var/cache/rhn/proxy-auth")
os.system("chown -R wwwrun:root /srv/tftpboot")
os.system("chmod 755 /srv/tftpboot")

# Invalidate (remove) possible old proxy auth cache files, based on sha1
# after migration to sha256 proxy auth cache files.
#
# The old sha1 based cache files are like (filename length = 51):
#    p10000100040c488b45d72291a0da497f5101d47e274c6b63ac
#
# The new sha256 based cache files are like (filename length = 75):
#    p1000010004e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#
for cache_file in Path("/var/cache/rhn/proxy-auth").iterdir():
    if cache_file.is_file() and len(cache_file.name) == 51:
        cache_file.unlink()
