#!/usr/bin/python3
import sys
import os
import re
import glob
import subprocess
import fcntl
import contextlib
import syslog

from mini_buildd import util, cli, changes


#: Needed for man page hack in setup.py
DESCRIPTION = "Script to use as a 'command' in an authorized_key file to allow authorization to mini-buildd uploads via SSH"
EPILOG = """
See ``mini-buildd-ssh-setup --help`` for further explanations.
"""

# Use syslog, facility user, to log access and key
syslog.openlog(facility=syslog.LOG_USER)


def log(msg):
    print(msg, file=sys.stderr)
    syslog.syslog(syslog.LOG_NOTICE, msg)


@contextlib.contextmanager
def filelock(lockfile_path):
    with open(lockfile_path, "w", encoding="UTF-8") as lockfile:
        fcntl.lockf(lockfile, fcntl.LOCK_EX)
        yield
        fcntl.lockf(lockfile, fcntl.LOCK_UN)


class CLI(cli.CLI):
    """
    >>> any(re.match(regex_str, "/usr/lib/openssh/sftp-server") for regex_str in CLI.ALLOWED_COMMANDS)  # dput (bookworm and higher)
    True
    >>> any(re.match(regex_str, "scp -p -d -t /home/mini-buildd-uploader/incoming/") for regex_str in CLI.ALLOWED_COMMANDS)  # dput (bullseye and lower)
    True
    >>> any(re.match(regex_str, "scp -p -t /home/mini-buildd-uploader/incoming/pkg_1.2.3-1.dsc") for regex_str in CLI.ALLOWED_COMMANDS)  # dput-ng (paramiko)
    True
    >>> any(re.match(regex_str, "scp -p -d -t /tmp/") for regex_str in CLI.ALLOWED_COMMANDS)
    False
    """
    ALLOWED_COMMANDS = [
        r"/usr/lib/openssh/sftp-server",                        # dput (bookworm and higher)
        r"scp.*\-t[ ]*/home/mini-buildd-uploader/incoming.*",   # dput-ng, dput (bullseye and lower)
    ]

    def __init__(self):
        super().__init__("mini-buildd-ssh-uploader-command", DESCRIPTION, epilog=EPILOG)
        self.incoming = os.path.expanduser("~") + "/incoming/"
        self.lockfile = os.path.expanduser("~") + "/lock"

    def _gpg_key_id(self):
        return subprocess.check_output("gpg --list-secret-keys --with-colons | grep --max-count=1 '^sec' | cut -d: -f5", shell=True).strip().decode(encoding="UTF-8")

    def _upload(self):
        for c_path in glob.glob(self.incoming + "/*.changes"):
            try:
                subprocess.check_output(["debsign", "--re-sign", "-k", self._gpg_key_id(), c_path], stderr=subprocess.STDOUT)
                subprocess.check_output(["dput", "-U", cli.DputCf().first_target(), c_path], stderr=subprocess.STDOUT)
                log(f"{c_path} uploaded from {os.environ.get('SSH_CONNECTION')}")
            except Exception as e:
                log(f"Upload error: {e}")
                if isinstance(e, subprocess.CalledProcessError):
                    for line in e.output.split(b"\n"):
                        log(line.decode(encoding="UTF-8"))
            finally:
                for f in changes.Base(c_path).get_files():
                    f_path = os.path.join(self.incoming, f["name"])
                    util.attempt(os.remove, f_path)
                util.attempt(os.remove, c_path)

    def _runcli(self):
        original_command = os.environ.get("SSH_ORIGINAL_COMMAND")
        if not any(re.match(regex_str, original_command) for regex_str in CLI.ALLOWED_COMMANDS):
            raise Exception(f"Command not allowed: '{original_command}'")  # pylint: disable=broad-exception-raised
        subprocess.check_call(original_command, shell=True)

        self._upload()

    def runcli(self):
        with filelock(self.lockfile):
            self._runcli()


if __name__ == "__main__":
    CLI().run()
