| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02:00
										 |  |  | import zipfile | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  | import base64 | 
					
						
							|  |  |  | from typing import Union, Dict, Set, Tuple | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 01:01:42 +02:00
										 |  |  | from flask import request, flash, redirect, url_for, render_template | 
					
						
							|  |  |  | from markupsafe import Markup | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | from WebHostLib import app | 
					
						
							| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  | from WebHostLib.upload import allowed_options, allowed_options_extensions, banned_file | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  | from Generate import roll_settings, PlandoOptions | 
					
						
							| 
									
										
										
										
											2022-04-12 10:57:29 +02:00
										 |  |  | from Utils import parse_yamls | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 15:17:47 +02:00
										 |  |  | @app.route('/check', methods=['GET', 'POST']) | 
					
						
							|  |  |  | def check(): | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02: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: | 
					
						
							| 
									
										
										
										
											2023-09-11 13:57:14 -07:00
										 |  |  |             files = request.files.getlist('file') | 
					
						
							|  |  |  |             options = get_yaml_data(files) | 
					
						
							| 
									
										
										
										
											2022-12-06 00:40:51 +01:00
										 |  |  |             if isinstance(options, str): | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |                 flash(options) | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2020-10-29 01:43:23 +01:00
										 |  |  |                 results, _ = roll_options(options) | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  |                 if len(options) > 1: | 
					
						
							|  |  |  |                     # offer combined file back | 
					
						
							| 
									
										
										
										
											2024-03-31 03:57:59 +02:00
										 |  |  |                     combined_yaml = "\n---\n".join(f"# original filename: {file_name}\n{file_content.decode('utf-8-sig')}" | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  |                                                  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 04:05:42 +02:00
										 |  |  |     return render_template("check.html") | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 15:17:47 +02:00
										 |  |  | @app.route('/mysterycheck') | 
					
						
							|  |  |  | def mysterycheck(): | 
					
						
							|  |  |  |     return redirect(url_for("check"), 301) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 13:57:14 -07:00
										 |  |  | def get_yaml_data(files) -> Union[Dict[str, str], str, Markup]: | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |     options = {} | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  |     for uploaded_file in files: | 
					
						
							| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  |         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." | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  |         elif uploaded_file.filename in options: | 
					
						
							| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  |             return f"Conflicting files named {uploaded_file.filename} submitted." | 
					
						
							|  |  |  |         elif uploaded_file and allowed_options(uploaded_file.filename): | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  |             if uploaded_file.filename.endswith(".zip"): | 
					
						
							| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  |                 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): | 
					
						
							| 
									
										
										
										
											2023-09-11 13:57:14 -07:00
										 |  |  |                             options[file.filename] = zfile.open(file, "r").read() | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-10-22 02:02:30 +02:00
										 |  |  |                 options[uploaded_file.filename] = uploaded_file.read() | 
					
						
							| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |     if not options: | 
					
						
							| 
									
										
										
										
											2023-12-12 20:12:16 -06:00
										 |  |  |         return f"Did not find any valid files to process. Accepted formats: {allowed_options_extensions}" | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |     return options | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-22 17:20:24 +02:00
										 |  |  | 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]]: | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |     plando_options = PlandoOptions.from_set(set(plando_options)) | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |     results = {} | 
					
						
							|  |  |  |     rolled_results = {} | 
					
						
							|  |  |  |     for filename, text in options.items(): | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-10-29 01:43:23 +01:00
										 |  |  |             if type(text) is dict: | 
					
						
							| 
									
										
										
										
											2022-04-12 10:57:29 +02:00
										 |  |  |                 yaml_datas = (text, ) | 
					
						
							| 
									
										
										
										
											2020-10-29 01:43:23 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2022-04-12 10:57:29 +02:00
										 |  |  |                 yaml_datas = tuple(parse_yamls(text)) | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |         except Exception as e: | 
					
						
							|  |  |  |             results[filename] = f"Failed to parse YAML data in {filename}: {e}" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2022-04-12 10:57:29 +02:00
										 |  |  |                 if len(yaml_datas) == 1: | 
					
						
							|  |  |  |                     rolled_results[filename] = roll_settings(yaml_datas[0], | 
					
						
							| 
									
										
										
										
											2022-05-22 17:20:24 +02:00
										 |  |  |                                                              plando_options=plando_options) | 
					
						
							| 
									
										
										
										
											2022-04-12 10:57:29 +02:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     for i, yaml_data in enumerate(yaml_datas): | 
					
						
							|  |  |  |                         rolled_results[f"{filename}/{i + 1}"] = roll_settings(yaml_data, | 
					
						
							| 
									
										
										
										
											2022-05-22 17:20:24 +02:00
										 |  |  |                                                                               plando_options=plando_options) | 
					
						
							| 
									
										
										
										
											2020-08-02 22:11:52 +02:00
										 |  |  |             except Exception as e: | 
					
						
							| 
									
										
										
										
											2024-04-16 03:26:59 +02:00
										 |  |  |                 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 22:11:52 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 results[filename] = True | 
					
						
							|  |  |  |     return results, rolled_results |