Gem2Browser/server.py

184 lines
8.8 KiB
Python
Raw Normal View History

2025-01-12 21:36:58 -08:00
#!/usr/bin/python3
2025-01-12 22:31:01 -08:00
import asyncio, traceback, socket, ssl
2025-01-13 20:28:54 -08:00
from urllib.parse import urlparse, quote
2025-01-13 16:27:15 -08:00
from flask import Flask, request, redirect, send_file, Response
2025-01-12 21:36:58 -08:00
from hypercorn.config import Config
from hypercorn.asyncio import serve
app = Flask(__name__)
@app.route("/")
def root():
2025-01-13 21:31:01 -08:00
return send_file("home.html")
2025-01-13 15:53:40 -08:00
@app.route("/external.png")
def external():
return send_file("external.png")
@app.route("/cross-server.png")
2025-01-13 15:55:48 -08:00
def crosserver():
return send_file("cross-server.png")
@app.route("/style.css")
2025-01-13 16:00:57 -08:00
def style():
2025-01-13 16:13:58 -08:00
return send_file("style.css")
2025-01-12 21:51:59 -08:00
@app.route("/gem")
def relay():
url = request.args.get('gemini')
2025-01-13 20:27:09 -08:00
queries = request.args.get('query')
2025-01-12 21:51:59 -08:00
if url == None:
return redirect("/")
2025-01-12 22:00:24 -08:00
code = "<h1>Something went wrong...</h1>\n"
2025-01-12 21:36:58 -08:00
title = "Something went wrong..."
2025-01-13 22:11:26 -08:00
print("GET " + "gemini://" + url + ("?" + queries if queries != None else ""))
2025-01-12 21:36:58 -08:00
try:
2025-01-12 22:31:01 -08:00
gsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2025-01-13 20:27:09 -08:00
fulladdr = "gemini://" + url + ("?" + queries if queries != None else "")
2025-01-12 22:39:23 -08:00
gemsocket = ssl._create_unverified_context().wrap_socket(gsocket, server_hostname=urlparse(fulladdr).hostname)
2025-01-12 22:37:07 -08:00
gemsocket.connect((urlparse(fulladdr).hostname, 1965))
2025-01-13 20:27:09 -08:00
gemsocket.send(bytes(fulladdr + "\r\n", "UTF-8"))
2025-01-12 22:31:01 -08:00
received = ""
2025-01-13 21:52:44 -08:00
mimetype = ""
gemraw = bytearray()
num = 0
2025-01-13 21:57:21 -08:00
ok = False
2025-01-13 21:58:28 -08:00
binary = False
2025-01-14 15:24:05 -08:00
found = False
2025-01-12 22:31:01 -08:00
while True:
2025-01-14 15:24:05 -08:00
gemresponse = gemsocket.recv(1)
2025-01-14 15:24:42 -08:00
gemraw.append(gemresponse)
2025-01-13 16:37:23 -08:00
try:
2025-01-13 21:53:30 -08:00
if num == 0:
2025-01-14 15:12:16 -08:00
print(gemresponse)
2025-01-13 21:52:44 -08:00
try:
2025-01-14 15:24:05 -08:00
if "\r" in bytes(gemraw).decode() and not found:
found = True
mimetype = bytes(gemraw).decode()[:-1].split(" ")[1]
ok = bytes(gemraw).decode()[:-1].split(" ")[0][0] == 2
print(mimetype)
2025-01-13 21:52:44 -08:00
except:
pass
2025-01-13 21:58:28 -08:00
try:
if gemresponse.decode() != "":
received += gemresponse.decode()
else:
break
except:
binary = True
2025-01-13 16:37:23 -08:00
except:
2025-01-13 16:39:33 -08:00
return f'<!DOCTYPE html>\n<html><head><meta charset="UTF-8"><link rel="stylesheet" href="/style.css"><title>Something went wrong...</title></head><body><h1>Something went wrong...</h1><p>The file that the Gemini server sent couldn\'t be decoded by Gem2Browser.</p></body></html>'
2025-01-14 15:19:47 -08:00
print(num)
2025-01-14 15:19:21 -08:00
if num == 1:
print(mimetype)
for i in bytes(gemraw):
2025-01-14 15:17:05 -08:00
try:
2025-01-14 15:19:21 -08:00
if i.decode() != "\r":
mimetyp += i.decode()
2025-01-14 15:17:05 -08:00
else:
2025-01-14 15:19:21 -08:00
break
2025-01-14 15:17:05 -08:00
except:
2025-01-14 15:19:21 -08:00
print(traceback.format_exc())
break
gr2 = bytearray()
if not mimetype in ["text/gemini", ""]:
frline = True
for i in gemraw:
try:
if frline and i.decode() != "\n":
pass
else:
frline = False
except:
print(traceback.format_exc())
pass
2025-01-13 21:58:28 -08:00
if binary:
2025-01-13 22:00:07 -08:00
return Response(bytes(gemraw), mimetype="text/gemini" if mimetype=="" else mimetype)
2025-01-12 22:31:01 -08:00
received = received.replace("\r", "")
firstline = True
redirected = False
gemtext = True
code = ""
2025-01-13 20:12:31 -08:00
escaped = False
2025-01-12 22:31:01 -08:00
for i in received.split("\n"):
2025-01-13 18:35:32 -08:00
i = i.replace("<", "&lt;")
2025-01-12 22:31:01 -08:00
if firstline:
if i.split(" ")[0][0] == "3":
2025-01-13 22:04:59 -08:00
if i.split(" ")[1][0] == "/":
2025-01-13 22:05:33 -08:00
return redirect("/gem?gemini=" + urlparse(fulladdr).hostname + quote(i.split(" ")[1], safe=''))
2025-01-13 20:28:54 -08:00
return redirect("/gem?gemini=" + quote(i.split(" ")[1][9:], safe=''))
2025-01-13 20:27:09 -08:00
elif i.split(" ")[0][0] == "1":
2025-01-13 20:32:43 -08:00
return f'<!DOCTYPE html>\n<html><head><meta charset="UTF-8"><link rel="stylesheet" href="/style.css"><title>Input required</title></head><body><h1>Input required</h1><p>The specified Gemini server wants more data: <pre>{i}</pre></p><form action="/gem"><input hidden class="input" value="{url}" type="text" name="gemini"><input class="input" type="text" name="query"><br><input type="submit" class="go" value="Go!"><br><br></form></body></html>'
2025-01-12 22:31:01 -08:00
elif i.split(" ")[0][0] != "2":
2025-01-13 16:19:28 -08:00
return f'<!DOCTYPE html>\n<html><head><meta charset="UTF-8"><link rel="stylesheet" href="/style.css"><title>Something went wrong...</title></head><body><h1>Something went wrong...</h1><p>The specified Gemini server returned a status of: {i}</p></body></html>'
2025-01-12 22:31:01 -08:00
else:
firstline = False
2025-01-13 16:28:55 -08:00
if i.split(" ")[1].split(";")[0] != "text/gemini":
2025-01-13 16:28:25 -08:00
print("Unrecognised type: " + i.split(" ")[1])
2025-01-13 16:27:15 -08:00
return Response(" ".join(received.split("\n")[1:]), mimetype=i.split(" ")[1])
2025-01-12 22:31:01 -08:00
else:
2025-01-13 20:11:59 -08:00
if escaped:
if i[0:3] == "```":
code += "</pre>\n"
escaped = False
2025-01-13 15:53:40 -08:00
else:
2025-01-13 20:11:59 -08:00
code += i + "\n"
2025-01-12 22:31:01 -08:00
else:
2025-01-13 20:11:59 -08:00
if i[0:2] == "# ":
if title == "Something went wrong...":
title = i[2:]
temp = i[2:]
code += f"<h1>{temp}</h1>\n"
elif i[0:3] == "## ":
temp = i[3:]
code += f"<h2>{temp}</h2>\n"
elif i[0:3] == "```":
code += "<pre>\n"
escaped = True
elif i[0:4] == "### ":
temp = i[4:]
code += f"<h3>{temp}</h3>\n"
elif i[0:2] == "* ":
temp = i[2:]
code += f"<ul><li>{temp}</li></ul>\n"
elif i[0:2] == "=>":
temp = " ".join(i[2:].strip().replace(" ", " ").split(" "))
goto = temp.split(" ")[0]
prse = urlparse(goto)
extra = ""
extracomment = goto
if prse.netloc == "" and prse.scheme == "":
isdir = url[len(url) - 1] == "/"
isabs = goto[0] == "/" or goto[0:2] == "//"
dubleslash = goto[0:2] == "//"
if isabs:
2025-01-13 20:15:40 -08:00
tempurl = urlparse(fulladdr).hostname + (goto[1:] if dubleslash else goto)
2025-01-13 20:11:59 -08:00
elif isdir:
tempurl = url + goto
else:
tempurl = "/".join(url.split("/")[:-1]) + "/" + goto
2025-01-13 22:09:09 -08:00
tempurl = quote(tempurl, safe='')
2025-01-13 20:11:59 -08:00
goto = f"/gem?gemini={tempurl}"
elif prse.scheme != "gemini":
extra = "<img height=\"19\" src=\"/external.png\">"
extracomment = f"This link points to an address that isn't gemini ({prse.scheme})"
else:
if prse.hostname != urlparse(fulladdr).hostname:
extra = "<img height=\"19\" src=\"/cross-server.png\">"
extracomment = f"This link points to an address that isn't from the server you're currently connecting to ({prse.hostname})"
goto = goto.replace("gemini://", "")
2025-01-13 22:09:09 -08:00
goto = quote(goto, safe='')
2025-01-13 20:11:59 -08:00
goto = f"/gem?gemini={goto}"
if temp.split(" ") == 1:
comment = goto
else:
comment = " ".join(temp.split(" ")[1:])
code += f"<p title=\"{extracomment}\"><a href=\"{goto}\">{comment} {extra}</a></p>\n"
elif i[0:2] == "> ":
code += f"<p class='greentext'>{i}</p>\n"
else:
code += f"<p>{i}</p>\n"
2025-01-12 22:31:01 -08:00
if title == "Something went wrong...":
title = "gemini://" + url
2025-01-12 21:36:58 -08:00
except:
2025-01-13 16:24:58 -08:00
code += "<pre>" + traceback.format_exc() + "</pre>"
2025-01-13 15:55:48 -08:00
return f'<!DOCTYPE html>\n<html><head><meta charset="UTF-8"><link rel="stylesheet" href="/style.css"><title>{title}</title></head><body>{code}</body></html>'
2025-01-12 21:36:58 -08:00
# Run the Hypercorn ASGI server
2025-01-12 21:49:36 -08:00
conf = Config()
conf.bind = "0.0.0.0:2009"
asyncio.run(serve(app, conf))