118 lines
6.2 KiB
Python
Executable File
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('&', '&').replace('<','<').replace('>','>').replace('"', '"').replace('\'', ''')
|
|
|
|
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()
|
|
|