From 596d683fc26e18e68f2a7a13520dd2b85674814b Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 10 Jul 2022 21:57:27 +0200 Subject: Factor out common functions into utils --- backend/main.py | 43 +++++++++++++++---------------------------- backend/utils.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 32 deletions(-) diff --git a/backend/main.py b/backend/main.py index 2156d0e..baf8b1d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,24 +4,14 @@ import cgi import os import sqlite3 import mimetypes +import urllib.parse import Admin -from utils import redirect +import utils def playout(filename, content = "text/html"): return ["", [('Content-Type', content if content else "application/octet-stream"), ("X-Accel-Redirect", filename)], None] -def target_filename_internal(alias, filename): - target = alias["path"] + "/" - if filename: - target += filename - if config.user_subdirs: - target = alias["user"] + "/" + target - return target - -def target_filename(alias, file): - return config.fileroot + target_filename_internal(alias, file) - def resolve_alias(alias): resolved = None db = sqlite3.connect(config.database, check_same_thread = False) @@ -38,16 +28,12 @@ def resolve_alias(alias): def listing(alias): listing = {"total": 0, "files": [], "access": alias["access"], "display": alias["display"]} - # TODO make sorting configurable if 'r' in alias["access"]: - directory = target_filename(alias, None) - files = sorted(os.listdir(directory)) - - for filename in files: - if os.path.isfile(directory + filename): - size = os.path.getsize(directory + filename) - listing["files"].append({"name": filename, "size": size}) - listing["total"] += size + directory = utils.target_filename(alias, None) + listing["files"] = utils.dirlisting(directory, True, False) + # Calculate total size + for file in listing["files"]: + listing["total"] += file["size"] return [json.dumps(listing), [('Content-Type','application/json')], "200 OK"] @@ -55,7 +41,7 @@ def upload(alias, post): if 'c' not in alias["access"]: return ["", [('Content-Type','text/html')], "403 No"] if post["file"].filename: - target = target_filename(alias, os.path.basename(post["file"].filename)) + target = utils.target_filename(alias, utils.sanitize_filename(post["file"].filename)) while os.path.isfile(target): target += "_dup" @@ -85,11 +71,11 @@ def route(path, env, post): alias = resolve_alias(path[0]) if not alias: - return redirect(config.homepage) + return utils.redirect(config.homepage) # Redirect if no slash after alias if len(path) == 1: - return redirect(path[0] + "/"); + return utils.redirect(path[0] + "/"); if len(path) > 1 and path[1] == "upload": return upload(alias, post) @@ -98,11 +84,12 @@ def route(path, env, post): return listing(alias) if len(path) > 1 and path[1] == "file": - print("/data/" + target_filename_internal(alias, path[2])) - return playout("/data/" + target_filename_internal(alias, path[2]), mimetypes.guess_type(path[2])[0]) + filename = utils.sanitize_filename(path[2]) + return playout("/data/" + utils.target_filename_internal(alias, filename), mimetypes.guess_type(filename)[0]) if len(path) > 1 and path[1] == "preview" and alias["display"] == "gallery": - return playout("/data/" + target_filename_internal(alias, "preview/" + path[2]), mimetypes.guess_type(path[2])[0]) + filename = utils.sanitize_filename(path[2]) + return playout("/data/" + utils.target_filename_internal(alias, "preview/" + filename), mimetypes.guess_type(filename)[0]) return playout("/interface/listing.htm") @@ -118,7 +105,7 @@ def handle_request(env, response): else: content_length = int(env.get('CONTENT_LENGTH', '0')) post_raw = env["wsgi.input"].read(content_length).decode('utf-8') - post = post_raw + post = urllib.parse.parse_qs(post_raw) except ValueError as e: post = None diff --git a/backend/utils.py b/backend/utils.py index ddb0adc..bab7ebe 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -1,12 +1,59 @@ +import sqlite3 +import os + import config +db = sqlite3.connect(config.database, check_same_thread = False) + def redirect(target): return ["", [('Content-Type','text/html'), ("Location", target)], "302 Redirect"] def ensure_user(name): - # TODO + # Add a user directory if configured + if config.user_subdirs: + try: + os.mkdir(config.fileroot + name) + except FileExistsError: + pass + + # Add the user to the database + db.cursor().execute("INSERT OR IGNORE INTO users (name) VALUES (:user)", {"user": name}) + db.commit() return -def is_user(name): - # TODO - return False +def userdir(name): + rootdir = config.fileroot + if config.user_subdirs: + rootdir += name + "/" + return rootdir + +def sanitize_filename(input): + filename = "".join(filter(lambda c: c not in "<>&", os.path.basename(input))) + # os.path.basename still allows `..` as basename + if not filename or filename.startswith("."): + raise ValueError("Invalid filename supplied: " + filename) + return filename + +def target_filename_internal(alias, filename): + target = alias["path"] + "/" + if filename: + target += filename + if config.user_subdirs: + target = alias["user"] + "/" + target + return target + +def target_filename(alias, file): + return config.fileroot + target_filename_internal(alias, file) + +def dirlisting(path, files, dirs): + listing = [] + entries = sorted(os.listdir(path)) + + for entry in entries: + if dirs and os.path.isdir(path + entry): + listing.append({"name": entry}) + if files and os.path.isfile(path + entry): + size = os.path.getsize(path + entry) + listing.append({"name": entry, "size": size}) + + return listing -- cgit v1.2.3