blob: af145e8ede084b0e92e2e1745a0edaad238720b0 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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)
|