diff --git a/Dockerfile b/Dockerfile
index 874ff2b..459c293 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,6 +2,6 @@ FROM python:3.13
WORKDIR /syscheck_receiver
COPY . /syscheck_receiver
RUN pip install --no-cache-dir -r requirements.txt
-RUN mkdir -p /data
+RUN mkdir -p /data/reports
EXPOSE 6969/tcp
ENTRYPOINT ["python", "app.py"]
diff --git a/app.py b/app.py
index 70a8bfa..7bc21cf 100644
--- a/app.py
+++ b/app.py
@@ -7,16 +7,18 @@ import random
import sqlite3
import time
import html
+import zipfile
from argparse import ArgumentParser
from flask import Flask, request, render_template, send_file
from datetime import datetime
+from io import BytesIO
# stuff
with open(f"{os.getcwd()}/config.json", "r") as f:
config = json.load(f)
-parser = ArgumentParser(prog="syscheck_receiver", description="Server that saves SysCheck report uploads.")
-parser.add_argument("-d", "--debug", dest="debug", action='store_true', help="Runs with Flask debug server instead of Waitress. DO NOT USE IN PRODUCTION!!!!")
+parser = ArgumentParser()
+parser.add_argument("-d", "--debug", dest="debug")
args = parser.parse_args()
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
@@ -30,17 +32,6 @@ def get_console_id(txt):
return "0"
-def return_error(message: string, code: int):
- jsonstring = {"message": message, "code": code, "error": True}
- resp = flask.Response(json.dumps(jsonstring))
- resp.headers["Content-Type"] = "application/json"
- return resp
-
-def no_cf_chunking(txt):
- resp = flask.Response(txt)
- resp.headers["X-Content-Length"] = len(txt.encode("utf-8")) # Cloudflares Proxy strips "Content-Length" when chunking, so we just re-add it manually.
- return resp
-
# docker
if config["docker"]:
report_dir = "/data/reports"
@@ -65,32 +56,26 @@ def index():
uploadIndex=""
for upload in uploads:
upload_time = datetime.fromtimestamp(upload[1])
- uploadIndex+="ID: {} -- Uploaded at {}!
".format(upload[0], upload[0], upload_time)
+ uploadIndex+="ID: {} -- Uploaded at {}!
".format(config["domain"], upload[0], upload[0], upload_time)
- return render_template("index.html", uploadIndex=uploadIndex, report_count=report_count[0][0], svr_ver=config["version"]), 200
+ return render_template("index.html", uploadIndex=uploadIndex, report_count=report_count[0][0]), 200
-@app.route("/syscheck_receiver.php", methods=["POST"])
+@app.route("/syscheck_send.php", methods=["POST"]) # SysCheck2.1.0b.19
+@app.route("/syscheck_receiver.php", methods=["POST"]) # literally anything else
def syscheck_report():
form_data = request.form.to_dict(flat=False)
report_txt = form_data["syscheck"][0]
- console_id = get_console_id(report_txt)[:-4]
-
- # check if console id: is present
+ console_id = get_console_id(report_txt)
if console_id == "0":
- return no_cf_chunking("ERROR: Not a valid sysCheck!"), 200
- # check if syscheck isn't too small or too large
- if len(report_txt.encode("utf-8")) > 6144:
- return no_cf_chunking("ERROR: Report is too large! Max is 6KB."), 200
- elif len(report_txt.encode("utf-8")) < 1330:
- return no_cf_chunking("ERROR: Report is too small! Min is 1.3KB."), 200
-
+ return "ERROR: Not a valid sysCheck!", 200
+ console_id_censor = "Console ID: "+console_id[:-4]+"***"
timestamp = int(time.time())
report_id = id_generator(6, 'AaBbCcDdFfeEgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWXxYyZz1234567890')
- if form_data["password"][0] == config["upload_password"]:
+ if form_data["password"][0] in config["upload_passwords"]:
try:
with open(f"{report_dir}/{report_id}.csv", "a+") as report:
- report.write(report_txt)
+ report.write(report_txt.replace(f"Console ID: {console_id}", "Console ID: {}".format(console_id_censor)))
db = sqlite3.connect(db_dir)
cursor = db.cursor()
@@ -98,26 +83,49 @@ def syscheck_report():
db.commit()
db.close()
- return no_cf_chunking(f"Success! Report ID: {report_id}"), 200
- except Exception as ex:
- print(ex)
- return no_cf_chunking("ERROR: Failed to save SysCheck report!"), 200
+ return f"Success! Report ID: {report_id}", 200
+ except:
+ return "ERROR: Failed to save SysCheck report!", 200
+
else:
- return no_cf_chunking("ERROR: Unauthorized"), 200
+ return "ERROR: Unauthorized", 200
-@app.route("/download_csv", methods=["GET"], defaults={'_route': 'direct'})
-@app.route("/view_report", methods=["GET"], defaults={'_route': 'template'})
-def view_report(_route):
+@app.route("/view_report", methods=["GET"])
+def view_report():
report_id = request.args.get("id")
if os.path.isfile(f"{report_dir}/{report_id}.csv"):
- if _route == "template":
- with open(f"{report_dir}/{report_id}.csv", "r") as report:
- return render_template("view_report.html", report_id=report_id, report_content=html.escape(report.read()), svr_ver=config["version"]), 200
- else:
- return send_file(f"{report_dir}/{report_id}.csv", as_attachment=True, download_name="report.csv")
+ with open(f"{report_dir}/{report_id}.csv", "r") as report:
+ return render_template("view_report.html", report_id=report_id, report_content=html.escape(report.read())), 200
else:
return "Report does not exist.", 404
+@app.route("/syscheck2", methods=["GET"])
+def syscheck():
+ if len("http://syscheck.softwii.de/syscheck_receiver.php") < len(config["replace_str"]):
+ return "Replacement host has to be exactly 48 characters; Specified URL is too long!", 400
+ elif len("http://syscheck.softwii.de/syscheck_receiver.php") > len(config["replace_str"]):
+ return "Replacement host has to be exactly 48 characters; Specified URL is too short!", 400
+
+ dol = BytesIO()
+ zip = BytesIO()
+
+ # hex edit boot.dol
+ dol2 = open(f"{os.getcwd()}/static/syscheck2/boot.dol", "rb")
+ dol.write(dol2.read().replace("http://syscheck.softwii.de/syscheck_receiver.php".encode("utf-8"), config["replace_str"].encode("utf-8")))
+ dol.seek(0)
+ dol2.close()
+
+ zf = zipfile.ZipFile(zip, "w", zipfile.ZIP_DEFLATED, False)
+ zf.writestr("apps/syscheck2/boot.dol", dol.read())
+ dol.close()
+ zf.write(f"{os.getcwd()}/static/syscheck2/icon.png", "apps/syscheck2/icon.png")
+ zf.write(f"{os.getcwd()}/static/syscheck2/meta.xml", "apps/syscheck2/meta.xml")
+ zf.close()
+ zip.seek(0)
+
+ # send zipfile
+ return send_file(zip, mimetype="application/zip", as_attachment=True, download_name="syscheck2.1.0.b19v2.zip"), 200
+
# handle errors
@app.errorhandler(400)
@app.errorhandler(404)
@@ -125,20 +133,32 @@ def view_report(_route):
@app.errorhandler(502)
def errorhandler(e):
if e.code == 400:
- return return_error("Bad request", 400), 400
+ jsonstring = {"message": "Bad Request.", "code": 400, "error": True}
+ resp = flask.Response(json.dumps(jsonstring))
+ resp.headers["Content-Type"] = "application/json"
+ return resp, 400
elif e.code == 404:
- return return_error("Not found", 404), 404
+ jsonstring = {"message": "Not found.", "code": 404, "error": True}
+ resp = flask.Response(json.dumps(jsonstring))
+ resp.headers["Content-Type"] = "application/json"
+ return resp, 404
elif e.code == 405:
- return return_error("Method not allowed", 405), 405
+ jsonstring = {"message": "Method not allowed.", "code": 405, "error": True}
+ resp = flask.Response(json.dumps(jsonstring))
+ resp.headers["Content-Type"] = "application/json"
+ return resp, 405
elif e.code == 502:
- return return_error("Bad gateway", 502), 502
+ jsonstring = {"message": "Bad Gateway.", "code": 502, "error": True}
+ resp = flask.Response(json.dumps(jsonstring))
+ resp.headers["Content-Type"] = "application/json"
+ return resp, 502
# run server
if __name__ == "__main__":
from waitress import serve
- print(f"syscheck_receiver v{config['version']}")
+ print("syscheck_receiver v1.0.3")
# check if sqlite db exist if not make one
if not os.path.isfile(db_dir):
@@ -155,11 +175,9 @@ if __name__ == "__main__":
if config["docker"]:
print("Docker is TRUE")
- if not os.path.isdir(report_dir):
- os.mkdir(report_dir)
# start server
- if args.debug:
+ if args.debug == "1":
print("Debug mode: on")
app.run(host=config["ip"], port=config["port"], debug=True)
else:
diff --git a/config.json b/config.json
index b7695db..68a133d 100644
--- a/config.json
+++ b/config.json
@@ -1,7 +1,11 @@
{
"ip": "0.0.0.0",
+ "domain": "syscheck.crafterpika.cc",
"port": "6969",
- "version": "1.1.2",
- "upload_password": "B277eNGp789a",
+ "upload_passwords": [
+ "d2wRuTEObSAN",
+ "B277eNGp789a"
+ ],
+ "replace_str": "http://syscheck.crafterpika.cc/syscheck_send.php",
"docker": false
}
diff --git a/reports/example.csv b/reports/example.csv
deleted file mode 100644
index 81855b8..0000000
--- a/reports/example.csv
+++ /dev/null
@@ -1,52 +0,0 @@
-sysCheck v2.1.0b19 by Double_A and R2-D2199, Nano
-...runs on IOS58 (rev 32033).
-
-Region: PAL
-System Menu 4.3E (v4610)
-Priiloader installed
-Drive date: 2012.06.29
-Homebrew Channel 1.1.2 running on IOS58
-HomebrewFilter ist nicht installiert
-Console ID: 23620***
-Boot2 v4
-Found 45 titles.
-Found 34 IOS on this console. 2 of them are stub.
-
-IOS3 (rev 65280): Stub
-IOS4 (rev 65280): Stub
-IOS9 (rev 26891): No Patches
-IOS12 (rev 26383): No Patches
-IOS13 (rev 26889): No Patches
-IOS14 (rev 26889): No Patches
-IOS15 (rev 26889): No Patches
-IOS17 (rev 26889): No Patches
-IOS21 (rev 26896): No Patches
-IOS22 (rev 27151): No Patches
-IOS28 (rev 27664): No Patches
-IOS31 (rev 29465): No Patches
-IOS33 (rev 29465): No Patches
-IOS34 (rev 29465): No Patches
-IOS35 (rev 29465): No Patches
-IOS36 (rev 29465): No Patches
-IOS37 (rev 31520): No Patches
-IOS38 (rev 29981): No Patches
-IOS41 (rev 29464): No Patches
-IOS43 (rev 29464): No Patches
-IOS45 (rev 29464): No Patches
-IOS46 (rev 29464): No Patches
-IOS48 (rev 29981): No Patches
-IOS53 (rev 31520): No Patches
-IOS55 (rev 31520): No Patches
-IOS56 (rev 31519): No Patches
-IOS57 (rev 31776): No Patches
-IOS58 (rev 32033): USB 2.0
-IOS59 (rev 32802): No Patches
-IOS61 (rev 31519): No Patches
-IOS62 (rev 31264): No Patches
-IOS80 (rev 32801): No Patches
-IOS236[36] (rev 65535, Info: rev 29465): Trucha Bug, ES Identify, NAND Access
-IOS249[57] (rev 21001, Info: d2xl-v1beta2): Trucha Bug, NAND Access, USB 2.0
-BC v6
-MIOS v10
-
-Report generated on 2024/10/13.
diff --git a/static/favicon.ico b/static/favicon.ico
deleted file mode 100644
index 1caa3d8..0000000
Binary files a/static/favicon.ico and /dev/null differ
diff --git a/static/syscheck2/boot.dol b/static/syscheck2/boot.dol
new file mode 100644
index 0000000..d79248e
Binary files /dev/null and b/static/syscheck2/boot.dol differ
diff --git a/static/syscheck2/icon.png b/static/syscheck2/icon.png
new file mode 100644
index 0000000..4c67b66
Binary files /dev/null and b/static/syscheck2/icon.png differ
diff --git a/static/syscheck2/meta.xml b/static/syscheck2/meta.xml
new file mode 100644
index 0000000..4051067
--- /dev/null
+++ b/static/syscheck2/meta.xml
@@ -0,0 +1,190 @@
+
+
Enter you're SysCheck report ID here:
SysCheck server written by CrafterPika! Source Code
-SysCheck by XFlak & Co
-syscheck_receiver v{{ svr_ver }}
-© CrafterPika
+ SysCheck server written by CrafterPika! Source CodeSysCheck server written by CrafterPika! Source Code
-SysCheck by XFlak & Co
-syscheck_receiver v{{ svr_ver }}
-© CrafterPika