about summary refs log tree commit diff
path: root/ophiculus/ophiculus.py
diff options
context:
space:
mode:
Diffstat (limited to 'ophiculus/ophiculus.py')
-rwxr-xr-xophiculus/ophiculus.py60
1 files changed, 60 insertions, 0 deletions
diff --git a/ophiculus/ophiculus.py b/ophiculus/ophiculus.py
new file mode 100755
index 0000000..af145e8
--- /dev/null
+++ b/ophiculus/ophiculus.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+import socket
+import ssl
+import sys
+import urllib.parse
+
+# read target url from arguments
+arg = sys.argv[1]
+if '//' not in arg:
+    arg = '//' + arg
+
+url = urllib.parse.urlparse(arg, scheme='gemini')
+if url.scheme != 'gemini':
+    sys.exit('Unable to handle scheme')
+
+port = url.port if url.port is not None else 1965
+context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+context.options |= OP_NO_TLSv1 | OP_NO_TLSV1_1 | OP_NO_TLSv1_2
+# ignore certs for now, TODO actually handle cert TOFU
+context.check_hostname = False
+context.verify_mode = ssl.CERT_NONE 
+
+# TODO AF_INET6 support
+with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+    with context.wrap_socket(sock, server_hostname=url.hostname) as ssock:
+        # connect to server [TLS handshake occurs after connect() by default]
+        ip = socket.gethostbyname(url.hostname)
+        ssock.connect((ip, port))
+        ssock.sendall(urlparse.urlunsplit(url) + '\r\n')
+
+        with ssock.makefile() as io:
+            header = io.readline()
+            print(header)
+            status, meta = header.split(' ', 1)
+            switch = status[0]
+            if switch == '1':
+                query = urllib.parse.quote(input('server requesting input: ' + meta))
+                # TODO retry request
+            else if switch == '2':
+                # meta is mime type
+                # TODO handle text/gemini i guess?
+                if meta.startswith('text/'):
+                    body = io.read()
+                    for line in body.spiltlines():
+                        print(line)
+                else:
+                    # TODO download file instead
+                    sys.exit('didn\'t get a text file')
+            else if switch == '3':
+                print('temporary redirect to: ' + meta)
+                # TODO prompt to follow
+            else if switch == '4':
+                print('temporary failure: ' + meta)
+            else if switch == '5':
+                print('permanent failure: ' + meta)
+            else if switch == '6':
+                print('client certificate required: ' + meta)
+                # TODO prompt to retry
+            else:
+                print('unknown response: ' + meta)