#!/usr/bin/python3
import sys
import os
import glob
import shutil
import socket
import tempfile
import subprocess
import syslog

import mini_buildd.cli


#: 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 = """
Steps to install 'uploads via SSH' (uses 'mini-buildd-uploader' as user name).

As user 'root'::

  adduser --disabled-password mini-buildd-uploader
  # OPTIONAL: Allows 'auth log' with the fingerprint
  adduser mini-buildd-uploader adm

As mini-buildd-uploader::

  gpg --gen-key    # Be sure to only have one secret key in the keyring; we will use the first found.
  mkdir -m700 .ssh
  edit ~/.dput.cf    # Put the dput.cf target of your mini-buildd instance here; First target found will be used.

As admin user at the mini-buildd instance (web app)::

  Generate a django pseudo user "ssh-uploads".
  In the new user's "Uploader" profile:
    Add the mini-buildd-uploader's GPG public key to the django users "Uploader" profile.
    Add the repos to access ("may upload to").
    PCA (prepare, check, activate) the new uploader profile.

To authorize a SSH Key, as user mini-buildd-uploader, add a line like this::

  command="/usr/sbin/mini-buildd-ssh-uploader-command" ssh-rsa AA...

per ssh user key.

As SSH uploader::

  Run 'ssh mini-buildd-uploader@the.mini-buildd.host'. This
  (will fail but) gives you a hint how to configure your
  '.dput.cf'
  Patch up your .dput.cf, then you should be able to upload like
  normal via dput with the new target.
"""

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


def log(*args):
    print(*args, file=sys.stderr)


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


def get_dput_target():
    return mini_buildd.cli.DputCf().first_target()


class CLI(mini_buildd.cli.CLI):
    def __init__(self):
        self.dputcf = mini_buildd.cli.DputCf()
        super().__init__("mini-buildd-ssh-uploader-command", DESCRIPTION, epilog=EPILOG)

    def runcli(self):
        retval = 0
        try:
            # Prepare incoming dir and tmp dir for this upload
            incoming = os.path.expanduser("~") + "/incoming/"
            try:
                os.makedirs(incoming)
            except BaseException:
                pass
            tmpdir = tempfile.mkdtemp(dir=incoming)
            log(f"I: Accepting incoming dir: {incoming}")
            log(f"I: Using upload tmp dir: {tmpdir}")

            # Build up secure command to use from original command
            original_command = os.environ.get("SSH_original_command", "").split()
            log("I: Original command: ", original_command)
            command = []
            allowed_items = ["scp", "-p", "-d", "-t"]
            it = iter(original_command)
            for n in it:
                if n in allowed_items:
                    if n == "-t":
                        target = next(it)
                        if target == "--":  # Older dput (squeeze) seem to add a "--" between -t and the directory option, we should skip that
                            target = next(it)
                        if target != incoming:
                            raise Exception("Seems you got the incoming dir wrong.")
                        command.append("-t")
                        command.append(tmpdir)
                    else:
                        command.append(n)
                else:
                    raise Exception(f"Option not allowed: {n}.")

            # Transfer files
            log("I: Uploading files via: ", command)
            subprocess.check_call(command)
            log(f"I: Upload successful to: {tmpdir}")

            # Compute changes file
            changes = glob.glob(tmpdir + "/*.changes")
            log(f"I: Found changes: {changes}")
            if len(changes) != 1:
                raise Exception(f"{len(changes)} changes files uploaded (only upload exactly one).")

            # Re-sign changes file with our GPG key
            sign_command = f"debsign --re-sign -k{get_key_id()} {changes[0]}"
            log(f"I: sign_command: {sign_command}")
            log(subprocess.check_output(sign_command, shell=True))

            # Upload to the actual mini-buildd
            dput_command = f"dput {get_dput_target()} {changes[0]}"
            log(f"I: dput_command: {dput_command}")
            log(subprocess.check_output(dput_command, shell=True))

            # Try to do the auth log
            mini_buildd.cli.auth_log("SUCCESS")

        except BaseException as e:
            mini_buildd.cli.auth_log("FAILED")

            log(f"""\

*ERROR*: {e}

Please only use 'dput' on me, and check that your target in
'~/.dput.cf' looks like this:
---
[{get_dput_target() + '-ssh-upload'}]
method   = scp
login    = {os.getenv('USER')}
fqdn     = {socket.getfqdn()}
incoming = {incoming}
---
    """)
            retval = 1
        finally:
            shutil.rmtree(tmpdir, ignore_errors=True)

        sys.exit(retval)


CLI().run()
