pygem/pygem.py

63 lines
2.4 KiB
Python

from server import TLS_ThreadingTCPServer
import socketserver
import configparser
from urllib.parse import urlparse
import os.path
import mimetypes
mimetypes.init()
mimetypes.add_type("text/gemini", ".gmi")
def is_safe_path(basedir, path, follow_symlinks=True):
# resolves symbolic links
if follow_symlinks:
return os.path.realpath(path).startswith(basedir)
return os.path.abspath(path).startswith(basedir)
config = configparser.ConfigParser()
config.read("pygem.ini")
class GeminiHandler(socketserver.StreamRequestHandler):
def handle(self):
self.url = urlparse(self.rfile.readline().strip())
if self.url.netloc == b"" and self.url.scheme == b"":
self.url = urlparse("gemini://%s" % self.rfile.readline().strip())
# thorough check of the URL
if self.url.scheme != b"gemini" and self.url.scheme != b"":
self.wfile.write(("59 URL scheme is not Gemini %s\r\n" % str(self.url)).encode("utf-8"))
return
if self.url.netloc.decode("utf-8") != config["gemini"]["domain"] and config["gemini"]["allowProxy"] == "no":
self.wfile.write("53 \r\n".encode("utf-8"))
return
elif self.url.netloc.decode("utf-8") != config["gemini"]["domain"]:
self.wfile.write("50 Proxying is not implemented yet.\r\n".encode("utf-8"))
return
# Actual serving
file_path = config["server"]["root"] + "/" + self.url.path.decode("utf-8")
if not is_safe_path(config["server"]["root"], file_path):
self.wfile.write("50 No.\r\n".encode("utf-8"))
return
if os.path.isdir(file_path):
file_path = file_path + "/index.gmi"
if not os.path.exists(file_path):
self.wfile.write("40 File not found.\r\n".encode("utf-8"))
return
with open(file_path) as f:
self.wfile.write(("20 %s\r\n" % mimetypes.guess_type(file_path)[0]).encode("utf-8"))
self.wfile.write(f.read().encode("utf-8"))
server = TLS_ThreadingTCPServer((config['listen']['host'], int(config['listen']['port'])),
GeminiHandler,
config['listen']['cert'],
config['listen']['key'])
try:
print("Starting server...")
server.serve_forever()
finally:
print("Server shutting down...")
server.shutdown()
server.server_close()