#!/usr/bin/env python3

import sys
import socket
import urllib.parse

import OpenSSL

GEMINI_PORT = 1965
INSECURE=True # Too many self-signed certificates
BUFSIZE=4096

if len(sys.argv) <= 1:
    raise Exception("Usage: %s URI ..." % sys.argv[0])

for url in sys.argv[1:]:
    components = urllib.parse.urlparse(url)
    if components.scheme != 'gemini':
        print("Ignoring non-Gemini URL \"%s\"" % url, file=sys.stderr)
        print("")
        continue
    addrinfo_list = socket.getaddrinfo(components.netloc, GEMINI_PORT)
    addrinfo_set = { (addrinfo[4], addrinfo[0]) for addrinfo in addrinfo_list }
    success = False
    for (addr, family) in addrinfo_set:
        sock = socket.socket(family, socket.SOCK_STREAM) 
        context = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
        if INSECURE:
            context.set_verify(OpenSSL.SSL.VERIFY_NONE, lambda *x: True)
        else:
            context.set_default_verify_paths()
            context.set_verify(OpenSSL.SSL.VERIFY_PEER | OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT | \
                               OpenSSL.SSL.VERIFY_CLIENT_ONCE,
                               lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
        session = OpenSSL.SSL.Connection(context, sock)
        session.set_tlsext_host_name(components.netloc.encode()) # Server Name Indication (SNI)
        print("Connecting to %s (%s)..." % (url, addr))
        try:
            session.connect(addr)
            session.do_handshake()
            if not INSECURE:
                pass # TODO check the name is in the certificate
            request = url + '\r\n' 
            session.send(request.encode())
            data = ""
            while True:
                try:
                    response = session.recv(BUFSIZE)
                    data += response.decode()
                except (OpenSSL.SSL.SysCallError, OpenSSL.SSL.ZeroReturnError): # TODO check if SysCallError is really EOF
                    break
            if data[0:2] == "20":
                print(data)
                success = True
            # TODO handle redirections
            else:
                print("Error \"%s\"" % data , file=sys.stderr)
            session.shutdown()
            session.close()
            break
        except (ConnectionRefusedError, TimeoutError) as e: 
            print("%s failed because of \"%s\"" % (addr, e), file=sys.stderr)
            continue # Try another address
    if not success:
        print("No address worked for \"%s\"" % url, file=sys.stderr)
    print("")
