Archipelago/WebHostLib/check.py

119 lines
5.5 KiB
Python
Raw Normal View History

import os
2020-07-27 02:05:42 +00:00
import zipfile
import base64
from typing import Union, Dict, Set, Tuple
2020-07-27 02:05:42 +00:00
from flask import request, flash, redirect, url_for, render_template
from markupsafe import Markup
2020-07-27 02:05:42 +00:00
from WebHostLib import app
from WebHostLib.upload import allowed_options, allowed_options_extensions, banned_file
2020-08-02 20:11:52 +00:00
from Generate import roll_settings, PlandoOptions
from Utils import parse_yamls
2020-07-27 02:05:42 +00:00
2020-08-02 20:11:52 +00:00
@app.route('/check', methods=['GET', 'POST'])
def check():
2020-07-27 02:05:42 +00:00
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
else:
files = request.files.getlist('file')
options = get_yaml_data(files)
if isinstance(options, str):
2020-08-02 20:11:52 +00:00
flash(options)
2020-07-27 02:05:42 +00:00
else:
2020-10-29 00:43:23 +00:00
results, _ = roll_options(options)
if len(options) > 1:
# offer combined file back
combined_yaml = "\n---\n".join(f"# original filename: {file_name}\n{file_content.decode('utf-8-sig')}"
for file_name, file_content in options.items())
combined_yaml = base64.b64encode(combined_yaml.encode("utf-8-sig")).decode()
else:
combined_yaml = ""
return render_template("checkResult.html",
results=results, combined_yaml=combined_yaml)
2020-07-27 02:05:42 +00:00
return render_template("check.html")
2020-08-02 20:11:52 +00:00
@app.route('/mysterycheck')
def mysterycheck():
return redirect(url_for("check"), 301)
def get_yaml_data(files) -> Union[Dict[str, str], str, Markup]:
2020-08-02 20:11:52 +00:00
options = {}
for uploaded_file in files:
if banned_file(uploaded_file.filename):
return ("Uploaded data contained a rom file, which is likely to contain copyrighted material. "
"Your file was deleted.")
# If the user does not select file, the browser will still submit an empty string without a file name.
elif uploaded_file.filename == "":
return "No selected file."
elif uploaded_file.filename in options:
return f"Conflicting files named {uploaded_file.filename} submitted."
elif uploaded_file and allowed_options(uploaded_file.filename):
if uploaded_file.filename.endswith(".zip"):
if not zipfile.is_zipfile(uploaded_file):
return f"Uploaded file {uploaded_file.filename} is not a valid .zip file and cannot be opened."
uploaded_file.seek(0) # offset from is_zipfile check
with zipfile.ZipFile(uploaded_file, "r") as zfile:
for file in zfile.infolist():
# Remove folder pathing from str (e.g. "__MACOSX/" folder paths from archives created by macOS).
base_filename = os.path.basename(file.filename)
if base_filename.endswith(".archipelago"):
return Markup("Error: Your .zip file contains an .archipelago file. "
'Did you mean to <a href="/uploads">host a game</a>?')
elif base_filename.endswith(".zip"):
return "Nested .zip files inside a .zip are not supported."
elif banned_file(base_filename):
return ("Uploaded data contained a rom file, which is likely to contain copyrighted "
"material. Your file was deleted.")
# Ignore dot-files.
elif not base_filename.startswith(".") and allowed_options(base_filename):
options[file.filename] = zfile.open(file, "r").read()
else:
options[uploaded_file.filename] = uploaded_file.read()
2020-08-02 20:11:52 +00:00
if not options:
return f"Did not find any valid files to process. Accepted formats: {allowed_options_extensions}"
2020-08-02 20:11:52 +00:00
return options
def roll_options(options: Dict[str, Union[dict, str]],
plando_options: Set[str] = frozenset({"bosses", "items", "connections", "texts"})) -> \
Tuple[Dict[str, Union[str, bool]], Dict[str, dict]]:
plando_options = PlandoOptions.from_set(set(plando_options))
2020-08-02 20:11:52 +00:00
results = {}
rolled_results = {}
for filename, text in options.items():
try:
2020-10-29 00:43:23 +00:00
if type(text) is dict:
yaml_datas = (text, )
2020-10-29 00:43:23 +00:00
else:
yaml_datas = tuple(parse_yamls(text))
2020-08-02 20:11:52 +00:00
except Exception as e:
results[filename] = f"Failed to parse YAML data in {filename}: {e}"
else:
try:
if len(yaml_datas) == 1:
rolled_results[filename] = roll_settings(yaml_datas[0],
plando_options=plando_options)
else:
for i, yaml_data in enumerate(yaml_datas):
if yaml_data is not None:
rolled_results[f"{filename}/{i + 1}"] = roll_settings(yaml_data,
plando_options=plando_options)
2020-08-02 20:11:52 +00:00
except Exception as e:
if e.__cause__:
results[filename] = f"Failed to generate options in {filename}: {e} - {e.__cause__}"
else:
results[filename] = f"Failed to generate options in {filename}: {e}"
2020-08-02 20:11:52 +00:00
else:
results[filename] = True
return results, rolled_results