# modules
import flask
import json
import os
import string
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()
parser.add_argument("-d", "--debug", dest="debug")
args = parser.parse_args()
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def get_console_id(txt):
array = txt.split("\n")
for ln in array:
if ln.startswith("Console ID: "):
return ln.split(":")[1][1:]
return "0"
# docker
if config["docker"]:
report_dir = "/data/reports"
db_dir = "/data/reports.db"
else:
report_dir = f"{os.getcwd()}/reports"
db_dir = f"{os.getcwd()}/reports.db"
# sever code
app = Flask('syscheck_receiver')
@app.route("/")
def index():
# Get data
db = sqlite3.connect(db_dir)
cursor = db.cursor()
uploads = cursor.execute("SELECT * FROM reports ORDER BY ROWID DESC LIMIT 15").fetchall()
report_count = cursor.execute("SELECT COUNT(*) FROM reports").fetchall()
db.close()
# Make HTML Page
uploadIndex=""
for upload in uploads:
upload_time = datetime.fromtimestamp(upload[1])
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]), 200
@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)
if console_id == "0":
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] in config["upload_passwords"]:
try:
with open(f"{report_dir}/{report_id}.csv", "a+") as report:
report.write(report_txt.replace(f"Console ID: {console_id}", "Console ID: {}".format(console_id_censor)))
db = sqlite3.connect(db_dir)
cursor = db.cursor()
cursor.execute("INSERT INTO reports VALUES ('{}', {}, {})".format(report_id, timestamp, console_id))
db.commit()
db.close()
return f"Success! Report ID: {report_id}", 200
except:
return "ERROR: Failed to save SysCheck report!", 200
else:
return "ERROR: Unauthorized", 200
@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"):
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)
@app.errorhandler(405)
@app.errorhandler(502)
def errorhandler(e):
if e.code == 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:
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:
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:
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("syscheck_receiver v1.0.3")
# check if sqlite db exist if not make one
if not os.path.isfile(db_dir):
print("reports.db missing generating new one...")
db = sqlite3.connect(db_dir)
cursor = db.cursor()
cursor.execute("""CREATE TABLE reports (
report_id TEXT,
timestamp INTEGER,
console_id INTEGER
)""")
db.commit()
db.close()
if config["docker"]:
print("Docker is TRUE")
# start server
if args.debug == "1":
print("Debug mode: on")
app.run(host=config["ip"], port=config["port"], debug=True)
else:
print(f"Server is running at http://{config['ip']}:{config['port']}/")
serve(app, host=config["ip"], port=config["port"])