diff options
Diffstat (limited to 'gemini.py')
-rw-r--r-- | gemini.py | 112 |
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('<', '<').replace('>', '>') + +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'), + } |