diff options
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/main.py | 43 | ||||
| -rw-r--r-- | 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 | 
