#!/usr/bin/env python3
"""A stand-in for OpenSSH's ssh, for tests/test_link.py: prints what `ssh -v` prints at
each step of a connection, and plays a ControlMaster. What each HostName does comes
from $FAKESSH_HOSTS (JSON: host -> "ok", "wrong" (a different host key), "denied" or
"slow" (hangs after connecting);
every call is appended to $FAKESSH_LOG as a JSON line. POSIX only."""
import json
import os
import signal
import sys
import time

args = sys.argv[1:]
with open(os.environ["FAKESSH_LOG"], "a") as f:
    f.write(json.dumps(args) + "\n")
hosts = json.loads(os.environ.get("FAKESSH_HOSTS", "{}"))
opts = {}
i = 0
while i < len(args) and args[i].startswith("-"):
    if args[i] in ("-o", "-O", "-p", "-l"):
        key = args[i]
        val = args[i + 1]
        if key == "-o":
            k, _, v = val.partition("=")
            opts[k.lower()] = v
        else:
            opts[key] = val
        i += 2
    else:
        opts[args[i]] = True
        i += 1
alias = args[i] if i < len(args) else ""
host = opts.get("hostname", alias).replace("%%", "%")
marker = os.path.join(os.environ["FAKESSH_DIR"], "master-" + host.replace("/", "_"))
say = lambda s: (sys.stderr.write(s + "\n"), sys.stderr.flush())

if "-G" in opts:
    print(f"hostname {alias}\nport 22\nuser tester")
    sys.exit(0)
if opts.get("-O") == "check":
    sys.exit(0 if os.path.exists(marker) else 255)
if opts.get("-O") == "exit":
    if os.path.exists(marker):
        os.unlink(marker)
    sys.exit(0)

what = hosts.get(host)
if what is None:
    say(f"ssh: Could not resolve hostname {host}: nodename nor servname provided, or not known")
    sys.exit(255)
say(f"debug1: Connecting to {host} [127.0.0.1] port {opts.get('port', 22)}.")
say("debug1: Connection established.")
if what == "slow":
    time.sleep(30)
say(f"debug1: Authenticating to {host}:22 as '{opts.get('user', 'tester')}'")
say("debug1: Server host key: ssh-ed25519 SHA256:fakefakefakefakefakefakefakefakefakefakefak")
if what == "wrong":
    say("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
    say("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @")
    say("Host key verification failed.")
    sys.exit(255)
say(f"debug1: Host '{opts.get('hostkeyalias', host)}' is known and matches the ED25519 host key.")
say("debug1: Next authentication method: publickey")
if what == "denied":
    say(f"tester@{host}: Permission denied (publickey).")
    sys.exit(255)
say(f'Authenticated to {host} ([127.0.0.1]:22) using "publickey".')
if opts.get("controlmaster") == "yes":
    open(marker, "w").close()
    def bye(*_):
        if os.path.exists(marker):
            os.unlink(marker)
        sys.exit(0)
    signal.signal(signal.SIGTERM, bye)
    while True:
        time.sleep(0.2)
        if not os.path.exists(marker):
            sys.exit(0)
sys.exit(0)
