"""Smol CGI utilities.""" from os import environ from pathlib import Path from urllib.parse import unquote def getenv(name, default=""): return environ.get(name, default) 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: storage_root = getenv("STORAGE_ROOT", "/storage") path = f"{storage_root}/{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}")