wobbly/browser.cgi

118 lines
6.2 KiB
Python
Executable File

#!/var/www/gemini/env/bin/python3
# vim: tabstop=4 shiftwidth=4 expandtab
import gemcall
import gemtextparser
import urllib.parse
import sys
from os import getenv
urllib.parse.uses_relative.append("gemini")
urllib.parse.uses_netloc.append("gemini")
redirect_count = 0
def clean(text):
return text.replace('&', '&amp;').replace('<','&lt;').replace('>','&gt;').replace('"', '&quot;').replace('\'', '&#x27;')
def query(url = None):
global redirect_count
if url:
queryurl = url
elif getenv('QUERY_STRING'):
queryurl = getenv('QUERY_STRING')
else:
print("<h1>What?</h1><p>I'm sorry, but I don't know what you want me to do without a URL.</p>")
sys.exit()
try:
baseurl = queryurl.split('?')[0]
response = gemcall.request(baseurl)
if response.responsecode in [30, 31]:
newtarget = urllib.parse.urljoin(baseurl, response.meta)
if redirect_count < 5:
redirect_count += 1
query(newtarget)
else:
print(f"<h1>RESPONSE: \"{str(response.responsecode)} {response.meta}\"</h1><p>This address redirects to <a href='{newtarget}'>{newtarget}</a>.</p>")
elif response.responsecode == 20 and "text/" in response.meta:
responsetext = response.read(100*1024) # We don't support more than 100kb
if "text/gemini" in response.meta:
parser = gemtextparser.GemtextParser()
preform = False
listmode = False
blockquote = False
for line in parser.parseText(responsetext.decode('utf8', errors='ignore')):
if line.linetype == gemtextparser.LineType.PREFORM:
if not preform:
print("<pre><code>")
preform = True
print(clean(line.text()))
continue
if not line.linetype == gemtextparser.LineType.PREFORM and preform:
print("</code></pre>")
preform = False
if line.linetype == gemtextparser.LineType.LISTITEM:
if not listmode:
print("<ul>")
listmode = True
print(f"<li>{clean(line.text())}</li>")
continue
if not line.linetype == gemtextparser.LineType.LISTITEM and listmode:
print("</ul>")
listmode = False
if line.linetype == gemtextparser.LineType.BLOCKQUOTE:
if not blockquote:
print("<blockquote>")
blockquote = True
print(clean(line.text()))
continue
if not line.linetype == gemtextparser.LineType.BLOCKQUOTE and blockquote:
print("</blockquote>")
blockquote = False
if line.linetype == gemtextparser.LineType.H1 and line.text():
print(f"<h1>{clean(line.text())}</h1>")
continue
if line.linetype == gemtextparser.LineType.H2 and line.text():
print(f"<h2>{clean(line.text())}</h2>")
continue
if line.linetype == gemtextparser.LineType.H3 and line.text():
print(f"<h3>{clean(line.text())}</h3>")
continue
if line.linetype == gemtextparser.LineType.LINK:
linkURL = urllib.parse.urljoin(baseurl, clean(line.linkURL().replace("'","")))
target = "" if linkURL.startswith("gemini://") else " target='_blank'"
if line.text():
print(f"<a href='{linkURL}'{target}>{clean(line.text())}</a><br/>")
else:
print(f"<a href='{linkURL}'{target}>{linkURL}</a><br/>")
continue
if line.linetype == gemtextparser.LineType.PLAIN:
print(f"{clean(line.text())}<br/>")
if listmode:
print("</ul>")
if blockquote:
print("</blockquote>")
if preform:
print("</code></pre>")
else:
print("<pre><code>")
print(clean(responsetext.decode('utf8', errors='ignore')))
print("</code></pre>")
elif response.responsecode == 20:
print(f"<h1>RESPONSE: \"20 Success\", but...</h1><p>The Wobbly browser only supports text responses, and this response is '{clean(response.meta)}'.</p>\n<p>You can visit the url with a real gemini browser via <a href='{clean(baseurl)}'>{clean(baseurl)}</a></p>")
elif response.responsecode in [40, 41, 42, 43, 44, 50, 51, 52, 53, 59]:
print(f"<h1>RESPONSE: \"{str(response.responsecode)} {clean(response.meta)}\"</h1><p>This is an error message from the server. Hopefully it makes sense to you.</p>")
elif response.responsecode in [60, 61, 62]:
print(f"<h1>RESPONSE: \"{str(response.responsecode)} {clean(response.meta)}\"</h1><p>The Wobbly browser does not support client certificates. Please install a more full-featured browser to visit this page.</p>")
elif response.responsecode in [10,11]:
print(f"<h1>RESPONSE: \"{str(response.responsecode)} {clean(response.meta)}\"</h1><p>The status codes 10 and 11 are requests for user input.</p><p>The only means of submitting user input to a server in gemini is via the so-called QUERY_STRING; the part of a URL that follows after a '?'. The Wobbly browser does not support this for privacy/security reasons. Please install a more full-featured browser to be able to submit data.</p>")
response.discard()
except Exception as e:
print(f"<h1>Unknown Error</h1><p>Something went wrong when trying to fetch '{queryurl}'. Could not recover.</p>")
print(f"<p>Exception Message:</p><p>{print(repr(e))}</p>")
print("Content-type: text/html\r\n\r\n")
query()