summaryrefslogtreecommitdiff
path: root/gemini.py
diff options
context:
space:
mode:
authorMatt Singleton <matt@xcolour.net>2020-09-06 13:52:37 -0500
committerMatt Singleton <matt@xcolour.net>2020-09-06 13:52:37 -0500
commit146891ea5bf477ba711e9b0935825f2f6db7aaa7 (patch)
treeba98733c091ec5441dfa0e978dc7079d86d897c4 /gemini.py
parentdc40aacd148ecc7bf3b3050a47512803b8353409 (diff)
pull gemini code into its own module
Diffstat (limited to 'gemini.py')
-rw-r--r--gemini.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/gemini.py b/gemini.py
new file mode 100644
index 0000000..c0d09c1
--- /dev/null
+++ b/gemini.py
@@ -0,0 +1,112 @@
+import socket
+import ssl
+
+def htmlescape(text):
+ return text.replace('<', '&lt;').replace('>', '&gt;')
+
+def gem2html(gem):
+ html = []
+ state = 'text'
+ blanklines = 0
+ for line in gem.split('\n'):
+ if line.startswith('```'):
+ if state == 'pre':
+ newstate = 'text'
+ blanklines = 0
+ else:
+ newstate = 'pre'
+ elif state == 'pre':
+ newstate = 'pre'
+ elif line.startswith('=>'):
+ newstate = 'links'
+ elif line.startswith('* '):
+ newstate = 'list'
+ else:
+ newstate = 'text'
+
+ if state != 'pre':
+ if len(line.strip()) == 0:
+ blanklines += 1
+ if blanklines > 1:
+ html.append('<br/>')
+ continue
+ blanklines = 0
+
+ if state != newstate:
+ if state in ('links', 'list'):
+ html.append('</ul>')
+ elif state == 'pre':
+ html.append('</pre>')
+
+ if newstate in ('links', 'list'):
+ html.append('<ul>')
+ elif newstate == 'pre':
+ html.append('<pre>')
+ state = newstate
+
+ if line.startswith('```'):
+ pass
+ elif state == 'links':
+ tokens = line.split(None, 2)
+ if len(tokens) == 3:
+ _, url, text = tokens
+ html.append('<li><a href="{url}">{text}</a></li>'.format(url=url, text=text))
+ else:
+ _, url = tokens
+ html.append('<li><a href="{url}">{url}</a></li>'.format(url=url))
+ elif state == 'list':
+ html.append('<li>{}</li>'.format(line[2:]))
+ elif state == 'pre':
+ html.append(line)
+ else:
+ if line.startswith('###'):
+ html.append('<h3>{}</h3>'.format(line[3:].lstrip()))
+ elif line.startswith('##'):
+ html.append('<h2>{}</h2>'.format(line[2:].lstrip()))
+ elif line.startswith('#'):
+ html.append('<h1>{}</h1>'.format(line[1:].lstrip()))
+ else:
+ html.append('<p>{}</p>'.format(htmlescape(line)))
+ return '\n'.join(html)
+
+def absolute_url(base, url):
+ """
+ modifies `url` in place
+ """
+ if not url.scheme():
+ url.setScheme(base.scheme())
+ if not url.host():
+ url.setHost(base.host())
+ if not url.path().startswith('/'):
+ url.setPath(base.path().rsplit('/', 1)[0] + '/' + url.path())
+ if url.port() == -1:
+ url.setPort(1965)
+ return url
+
+def get(url):
+ if len(url.path()) == 0:
+ url.setPath('/')
+ return {
+ 'status': '32',
+ 'meta': url.toDisplayString(),
+ }
+ context = ssl.create_default_context()
+ context.check_hostname = False
+ context.verify_mode = ssl.CERT_NONE
+ with socket.create_connection((url.host(), url.port())) as sock:
+ with context.wrap_socket(sock, server_hostname=url.host()) as ssock:
+ ssock.sendall('gemini://{}{}\r\n'.format(url.host(), url.path()).encode('utf8'))
+ fp = ssock.makefile(mode='rb')
+ header = fp.readline(1027)
+ status, meta = header.decode('utf8').split(None, 1)
+ if status[0] != '2':
+ return {
+ 'status': status,
+ 'meta': meta.strip(),
+ }
+ body = fp.read()
+ return {
+ 'status': status,
+ 'meta': meta.strip(),
+ 'body': body.decode('utf8'),
+ }