This repository has been archived on 2024-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
GeminiScripts/smolcgi.py

165 lines
3.8 KiB
Python

"""Smol CGI utilities."""
from os import environ
from pathlib import Path
from urllib.parse import unquote
def getenv(name):
return environ.get(name, "")
gateway_interface = getenv("GATEWAY_INTERFACE")
remote_addr = getenv("REMOTE_ADDR")
remote_host = getenv("REMOTE_HOST")
request_method = getenv("REQUEST_METHOD")
script_name = getenv("SCRIPT_NAME")
server_name = getenv("SERVER_NAME")
server_port = getenv("SERVER_PORT")
server_protocol = getenv("SERVER_PROTOCOL")
server_software = getenv("SERVER_SOFTWARE")
gemini_document_root = getenv("GEMINI_DOCUMENT_ROOT")
gemini_script_filename = getenv("GEMINI_SCRIPT_FILENAME")
gemini_url = getenv("GEMINI_URL")
gemini_url_path = getenv("GEMINI_URL_PATH")
tls_version = getenv("TLS_VERSION")
tls_cipher = getenv("TLS_CIPHER")
path_info = getenv("PATH_INFO")
query_string = getenv("QUERY_STRING")
auth_type = getenv("AUTH_TYPE")
remote_user = getenv("REMOTE_USER")
tls_client_issuer = getenv("TLS_CLIENT_ISSUER")
tls_client_hash = getenv("TLS_CLIENT_HASH")
tls_client_not_after = getenv("TLS_CLIENT_NOT_AFTER")
tls_client_not_before = getenv("TLS_CLIENT_NOT_BEFORE")
query_string_dec = unquote(query_string)
cgi_vars = [
"gateway_interface", "remote_addr", "remote_host", "request_method",
"script_name", "server_name", "server_port", "server_protocol",
"server_software", "gemini_document_root", "gemini_script_filename",
"gemini_url", "gemini_url_path", "tls_version", "tls_cipher", "path_info",
"query_string", "auth_type", "remote_user", "tls_client_issuer",
"tls_client_hash", "tls_client_not_after", "tls_client_not_before",
"query_string_dec",
]
def get_storage_path():
script_filename = script_name.split("/")[-1]
path = getenv(f"GEMINI_{script_filename.upper()}_STORAGE")
if not path:
path = f"/home/gemini/storage/{script_filename}"
path = Path(path)
if not path.exists():
path.mkdir(parents=True)
return path
def get_user_dir():
user_dir = get_storage_path() / tls_client_hash[7:]
if not user_dir.is_dir():
user_dir.mkdir()
return user_dir
def header(code, meta):
print(f"{code} {meta}", end="\r\n")
def success(mime):
header(20, mime)
def exit_with_header(code, meta):
header(code, meta)
exit()
def require_input(reason=""):
exit_with_header(10, reason)
def require_sensitive_input(reason=""):
exit_with_header(11, reason)
def redirect_temp(url):
exit_with_header(30, url)
def redirect_perm(url):
exit_with_header(31, url)
def temp_error(reason=""):
exit_with_header(40, reason)
def server_unavailable(reason=""):
exit_with_header(41, reason)
def cgi_error(reason=""):
exit_with_header(42, reason)
def proxy_error(reason=""):
exit_with_header(43, reason)
def slow_down(num_seconds):
exit_with_header(44, num_seconds)
def perm_error(reason=""):
exit_with_header(50, reason)
def not_found(reason=""):
exit_with_header(51, reason)
def gone(reason=""):
exit_with_header(52, reason)
def proxy_request_refused(reason=""):
exit_with_header(53, reason)
def bad_request(reason=""):
exit_with_header(59, reason)
def require_client_cert():
if auth_type != "CERTIFICATE":
exit_with_header(60, "You need a certificate to use this app")
def cert_not_authorised(reason=""):
exit_with_header(61, reason)
def require_cool_client(reason=""):
require_client_cert()
with open(get_storage_path() / "cool_hashes", "rt") as f:
cool_hashes = f.read().rstrip().split("\n")
if tls_client_hash not in cool_hashes:
cert_not_authorised(reason)
def cert_not_valid(reason=""):
exit_with_header(62, reason)
def print_env():
globz = globals()
for key in cgi_vars:
print(f"{key} = {repr(globz[key])}")
def link(path, text=""):
print(f"=> {script_name}{path} {text}")