#!/usr/bin/env python3 """Smol listing manager.""" import string import subprocess from pathlib import Path import smolcgi as scgi scgi.require_client_cert() STORAGE_PATH = Path("/home/gemini/storage/lists") if not STORAGE_PATH.exists(): STORAGE_PATH.mkdir(parents=True) ALLOWED_LIST_NAME_CHARS = string.ascii_lowercase + string.digits + "_" def show_lists(): user_dir = scgi.get_user_dir() scgi.header(20, "text/gemini") print("# Lists") for child in user_dir.iterdir(): if child.is_file(): scgi.link(f"/{child.name}", f"📄 {child.name}") scgi.link("/_/create", "✨ Create a new list") def show_list(list_name): list_file = scgi.get_user_dir() / list_name try: with open(list_file, "rt") as f: content = f.read() except FileNotFoundError: scgi.not_found() else: scgi.header(20, "text/gemini") print("# " + list_name) print(content) scgi.link(f"/{list_name}/add", "Append item") scgi.link(f"/{list_name}/pop", "Pop first item") scgi.link(f"/{list_name}/clear", "Clear list") scgi.link(f"/{list_name}/delete", "Delete list") def process_action(list_name, action): if list_name == "_": if action == "create": process_creation() else: scgi.not_found() else: if action == "add": process_add(list_name) elif action == "pop": process_pop(list_name) elif action == "clear": process_clear(list_name) elif action == "delete": process_delete(list_name) else: scgi.not_found() def process_creation(): if not (new_list_name := scgi.query_string_dec): meta = "List name (allowed chars: lowercase, digits, underscore)" scgi.require_input(meta) else: for char in new_list_name: if char not in ALLOWED_LIST_NAME_CHARS: scgi.temp_error(f"Illegal character in list name: {char}") list_file = scgi.get_user_dir() / new_list_name list_file.touch(mode=0o660) redirect_to_list(new_list_name) def process_add(list_name): if not (item := scgi.query_string_dec): scgi.require_input("Enter your item") else: if "\n" in item: scgi.temp_error("No new line allowed in item.") line = f"* {item}\n" list_file = scgi.get_user_dir() / list_name try: with open(list_file, "at") as f: f.write(line) except FileNotFoundError: scgi.not_found() else: redirect_to_list(list_name) def process_pop(list_name): list_file = scgi.get_user_dir() / list_name subprocess.run(["sed", "-i", "1d", str(list_file)]) redirect_to_list(list_name) def process_clear(list_name): if scgi.query_string_dec: if scgi.query_string_dec != list_name: redirect_to_list(list_name) list_file = scgi.get_user_dir() / list_name try: with open(list_file, "wt") as f: f.truncate() except OSError: scgi.temp_error() redirect_to_list(list_name) else: scgi.require_input("Type the list name to confirm its clearing.") def process_delete(list_name): if scgi.query_string_dec: if scgi.query_string_dec != list_name: redirect_to_list(list_name) list_file = scgi.get_user_dir() / list_name list_file.unlink() scgi.redirect_temp(scgi.script_name) else: scgi.require_input("Type the list name to confirm its deletion.") def redirect_to_list(list_name): scgi.redirect_temp(scgi.script_name + "/" + list_name) path_components = scgi.path_info.lstrip("/").split("/", maxsplit=1) list_name = path_components[0] action = path_components[1] if len(path_components) > 1 else '' if not list_name: show_lists() elif not action and list_name != "_": show_list(list_name) else: process_action(list_name, action)