y'know I probably should have uploaded this to gh sooner

This commit is contained in:
Logan Joy-Koer
2026-06-22 23:53:22 -07:00
commit b55d3306c4
62 changed files with 1002233 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
database.txt
database.txt.bak
__pycache__/
*/videos/*
+4
View File
@@ -0,0 +1,4 @@
In Visual Studio Code, go to File > Open Workspace From File..., then select the file "htmlproject.code-workspace"
Then, in the corner when prompted to install extensions, select "Show Recommendations" and install all 3 Python
extensions, then install one of the browser debug extensions, and edit the file ".vscode/launch.json" to start the
correct browser debug extension.
+999998
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
Googlebot
Bingbot
GPTBot
Claude-Web
ClaudeBot
CCBot
FacebookBot
Applebot-Extended
Applebot
Diffbot
Googlebot-Image
Googlebot-Video
Slurp
DuckDuckBot
Baiduspider
YandexBot
HTTrack
+35
View File
@@ -0,0 +1,35 @@
import os
import json
import shutil
class Database:
def __init__(self, file):
self.database = {}
self.file = file
self.backup = file + ".bak"
if not os.path.exists(file):
open(self.file, "wt").write("{}")
def read(self):
try:
with open(self.file, "rt") as f:
self.database = json.loads(f.read())
except (json.JSONDecodeError, OSError):
print(f"[db] Failed to read {self.file}, attempting fallback to {self.backup}")
try:
with open(self.backup, "rt") as f:
self.database = json.loads(f.read())
print(f"[db] Fallback successful, restoring {self.file} from backup")
shutil.copy2(self.backup, self.file)
except (json.JSONDecodeError, OSError) as e:
print(f"[db] Backup read also failed: {e} — starting with empty database")
self.database = {}
def write(self):
if os.path.exists(self.file):
shutil.copy2(self.file, self.backup)
with open(self.file, "wt") as f:
f.write(json.dumps(self.database))
def database(file):
return Database(file)
+2
View File
@@ -0,0 +1,2 @@
python-magic
urllib3
+386
View File
@@ -0,0 +1,386 @@
from socket import *
import os, sys, random
import math, datetime
import re, magic
import html
import urllib
import urllib.parse
import userManagement
import time
import threading
import calendar
import subprocess
running = True
ALLOWED_METHODS = {"GET", "POST", "HEAD"}
script_directory = os.path.dirname(os.path.abspath(sys.argv[0]))
webroot_real = os.path.realpath(script_directory + "/webroot")
trying = True
while trying:
try:
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', 8123))
serverSocket.listen(128)
trying = False
except Exception as e:
print("Waiting for socket to be free...")
print(e)
time.sleep(1)
recentConnections = []
recentConnectionsExtended = []
recentConnections_lock = threading.Lock()
def get_last_modified(path: str) -> str:
mtime = os.path.getmtime(path)
return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime))
def is_newer_than(path: str, date_str: str) -> bool:
mtime = os.path.getmtime(path)
parsed = time.strptime(date_str, "%a, %d %b %Y %H:%M:%S GMT")
return mtime > calendar.timegm(parsed)
def recv_full(sock, timeout=5):
"""Receive a full HTTP request, not just the first 1024 bytes.
Reads until the end of headers, then reads Content-Length more bytes
for the body if present."""
sock.settimeout(timeout)
data = b""
try:
while b"\r\n\r\n" not in data and b"\n\n" not in data:
chunk = sock.recv(4096)
if not chunk:
break
data += chunk
# Split off headers to check Content-Length
if b"\r\n\r\n" in data:
header_part, body_so_far = data.split(b"\r\n\r\n", 1)
elif b"\n\n" in data:
header_part, body_so_far = data.split(b"\n\n", 1)
else:
return data.decode(errors="replace")
# Check for Content-Length and read remaining body bytes
content_length = 0
for line in header_part.decode(errors="replace").splitlines():
if line.lower().startswith("content-length:"):
try:
content_length = int(line.split(":", 1)[1].strip())
except ValueError:
pass
break
while len(body_so_far) < content_length:
chunk = sock.recv(4096)
if not chunk:
break
body_so_far += chunk
return (header_part + b"\r\n\r\n" + body_so_far).decode(errors="replace")
except TimeoutError:
return data.decode(errors="replace")
blocklist_lock = threading.Lock()
def addtoblocklist(IP):
with blocklist_lock:
with open("/etc/nginx/blocklist.txt", "a") as blocklistfile:
blocklistfile.write(IP + " 1;\n")
subprocess.run(["sudo","systemctl","reload","nginx"])
blocked_agents_lock = threading.Lock()
def load_blocked_agents(path: str) -> set:
try:
with open(path) as f:
return {line.strip().lower() for line in f if line.strip() and not line.startswith("#")}
except FileNotFoundError:
return set()
blocked_agents_file = script_directory + "/blocked_agents.txt"
blocked_agents = load_blocked_agents(blocked_agents_file)
def handle_connection(connectionSocket, addr):
global recentConnections
global recentConnectionsExtended
sourceIP, sourcePort = addr
responseType = "200 OK"
responseHeaders = b""
responsecontent = b""
fullRequestPath = ""
contentType = ""
message = recv_full(connectionSocket)
connectionSocket.settimeout(None)
try:
messages = message.replace('\r\n', '\n').split('\n')
requestheaders = []
for i in range(len(messages)):
if i > 0:
if len(messages[i]) == 0:
break
requestheaders.append(messages[i])
ClientTargetHost = ''
ClientUserAgent = ''
ClientContentLength = ''
ClientAcceptTypes = ''
ClientAcceptLanguage = ''
ClientAcceptEncoding = ''
ClientConnectionType = ''
ClientUseHTTPS = ''
ClientSecFetchDest = ''
ClientSecFetchMode = ''
ClientSecFetchSite = ''
ClientSecFetchUser = ''
XForwardedFor = ''
IfModifiedSince = ''
Cookies = {}
for i in requestheaders:
if i.startswith("Host: "):
ClientTargetHost = i[6:]
elif i.startswith("User-Agent: "):
ClientUserAgent = i[12:]
elif i.startswith("Content-Length: "):
ClientContentLength = i[16:]
elif i.startswith("Accept: "):
ClientAcceptTypes = i[8:]
elif i.startswith("Accept-Language: "):
ClientAcceptLanguage = i[17:]
elif i.startswith("Accept-Encoding: "):
ClientAcceptEncoding = i[17:]
elif i.startswith("Connection: "):
ClientConnectionType = i[12:]
elif i.startswith("Upgrade-Insecure-Requests: "):
ClientUseHTTPS = i[27:]
elif i.startswith("Sec-Fetch-Dest: "):
ClientSecFetchDest = i[16:]
elif i.startswith("Sec-Fetch-Mode: "):
ClientSecFetchMode = i[16:]
elif i.startswith("Sec-Fetch-Site: "):
ClientSecFetchSite = i[16:]
elif i.startswith("Sec-Fetch-User: "):
ClientSecFetchUser = i[16:]
elif i.startswith("X-Forwarded-For: "):
XForwardedFor = i[17:]
elif i.startswith("If-Modified-Since: "):
IfModifiedSince = i[19:]
elif i.startswith("Cookie: "):
# Parse cookie header correctly into a flat dict
cookie_str = i[8:]
for pair in cookie_str.split("; "):
k = pair.split("=", 1)
if len(k) == 2:
Cookies[k[0].strip()] = k[1].strip()
if XForwardedFor == '':
sourceIPvisual = sourceIP
else:
sourceIPvisual = XForwardedFor.strip()
print(sourceIPvisual + ":" + str(sourcePort) + " [" + ClientUserAgent + "] requested \"" + messages[0] + "\"")
if any(sub in messages[0] for sub in [".env","php",".git","cgi","SDK/webLanguage"]):
addtoblocklist(sourceIPvisual)
return
now = datetime.datetime.now()
with recentConnections_lock:
recentConnections.append([sourceIPvisual, now])
recentConnectionsExtended.append([sourceIPvisual, now])
recentConnections = [
entry for entry in recentConnections
if now <= entry[1] + datetime.timedelta(seconds=60)
]
recentConnectionsExtended = [
entry for entry in recentConnectionsExtended
if now <= entry[1] + datetime.timedelta(seconds=200)
]
rateLimitCount = {}
rateLimitCountExtended = {}
for entry in recentConnections:
rateLimitCount[entry[0]] = rateLimitCount.get(entry[0], 0) + 1
for entry in recentConnectionsExtended:
rateLimitCountExtended[entry[0]] = rateLimitCountExtended.get(entry[0], 0) + 1
print(rateLimitCount)
if rateLimitCountExtended[sourceIPvisual] >= 200:
addtoblocklist(sourceIPvisual)
return
elif rateLimitCount[sourceIPvisual] >= 100:
responseType = "429 Too Many Requests (please stop)"
responsecontent = b"429 Too Many Requests.\nPlease stop.\n"
responseHeaders += b"Retry-After: 9999999999999999999 \n"
elif rateLimitCount[sourceIPvisual] >= 50:
responseType = "429 Too Many Requests"
responsecontent = open(script_directory + "/webroot/429.html", "rb").read()
fullRequestPath = script_directory + "/webroot/429.html"
contentType = "text/html"
responseHeaders += b"Retry-After: 60\n"
elif len(messages[0]) < 10:
responseType = "400 Bad Request"
responsecontent = open(script_directory + "/webroot/400.html", "rb").read()
fullRequestPath = script_directory + "/webroot/400.html"
contentType = "text/html"
else:
requestType = messages[0].split()[0]
# Validate HTTP method
if requestType not in ALLOWED_METHODS:
responseType = "405 Method Not Allowed"
responsecontent = b"<h1>405 Method Not Allowed</h1>"
fullRequestPath = ""
contentType = "text/html"
responseHeaders += b"Allow: GET, POST, HEAD\n"
else:
# URL-decode the path before sanitizing to catch encoded traversal sequences
raw_path = (messages[0].split()[1] + "?").split("?")[0]
decoded_path = urllib.parse.unquote(raw_path)
# Normalize and build the candidate path, then realpath-check it
candidate_path = os.path.realpath(script_directory + "/webroot" + decoded_path)
with blocked_agents_lock:
agent_blocked = any(agent in ClientUserAgent.lower() for agent in blocked_agents)
if agent_blocked and decoded_path != "/robots.txt":
responseType = "403 Forbidden"
responsecontent = b"<h1>403 Forbidden</h1>"
contentType = "text/html"
if not candidate_path.startswith(webroot_real + os.sep) and candidate_path != webroot_real:
responseType = "403 Forbidden"
responsecontent = b"<h1>403 Forbidden</h1>"
fullRequestPath = ""
contentType = "text/html"
else:
fullRequestPath = candidate_path
requestPath = decoded_path # exposed to exec'd scripts via script_env
argumentsRaw = (messages[0].split()[1] + "?").split("?")[1]
arguments = argumentsRaw.split("&")
argumentsDict = {}
for i in arguments:
j = i.split("=")
if len(j) == 2:
argumentsDict[j[0]] = j[1]
if responseType == "200 OK":
if os.path.exists(fullRequestPath):
if os.path.isdir(fullRequestPath):
indexFiles = ["/index.html", "/index.py"]
for i in indexFiles:
if os.path.exists(fullRequestPath + i):
fullRequestPath += i
break
else:
fullRequestPath = script_directory + "/webroot/Scripts/listFiles.py"
if not os.path.exists(fullRequestPath):
responseType = "404 Not Found"
fullRequestPath = script_directory + "/webroot/404.html"
plainresponse = False
extensions = [".html", ".json", ".png", ".gif", ".ico", ".jpg", ".css", ".txt", ".ttf", ".mp4"]
contentTypes = ["text/html", "text/json", "image/png", "image/gif", "image/x-icon", "image/jpeg", "text/css", "text/plain", "application/octet-stream", "video/mp4"]
for i in range(len(extensions)):
if fullRequestPath.endswith(extensions[i]):
plainresponse = True
contentType = contentTypes[i]
break
if plainresponse:
with open(fullRequestPath, "rb") as file:
responsecontent = file.read()
elif fullRequestPath.endswith(".py"):
with open(fullRequestPath) as script:
local_vars = locals()
exec(script.read(), globals(), local_vars)
responsecontent = local_vars.get("responsecontent", responsecontent)
responseType = local_vars.get("responseType", responseType)
responseHeaders = local_vars.get("responseHeaders", responseHeaders)
contentType = local_vars.get("contentType", contentType)
else:
responseType = "415 Unsupported Media Type"
contentType = ""
responsecontent = b""
except Exception as e:
print("error: ", e)
responseType = "500 Internal Server Error"
fullRequestPath = script_directory + "/webroot/500.html"
contentType = "text/html"
if os.path.exists(fullRequestPath):
with open(fullRequestPath, "rb") as file:
responsecontent = file.read()
else:
responsecontent = ("<h1 style=\"color: #a00;\">Error 500: Internal Server Error.</h1><br><p>Additionally, While handling that error, we encountered error 404. (page not found!)<br>Error: " + str(e) + "</p>").encode()
print("Serving " + fullRequestPath + " to " + sourceIPvisual + ":" + str(sourcePort))
if contentType != "":
if contentType[:5] == "image":
responseHeaders += ("Last-Modified: " + get_last_modified(fullRequestPath) + "\n").encode()
print(IfModifiedSince)
if IfModifiedSince != '':
try:
if is_newer_than(fullRequestPath, IfModifiedSince):
pass
else:
responsecontent = b''
responseType = "304 Not Modified"
except Exception as e:
print("excepted: ")
print(e)
responseHeaders += ("Content-Length: " + str(len(responsecontent)) + '\n').encode()
responseHeaders += ("Content-Type: " + contentType + "\n").encode()
responseHeaders += b"Server: Xbox 360\n"
response = ("HTTP/1.1 " + responseType + "\n").encode()
response += responseHeaders
response += b"\n"
response += responsecontent
numchunks = math.ceil(len(response) / 1024) if len(response) > 0 else 1
responsechunks = []
for i in range(numchunks):
responsechunks.append(response[(1024 * i):(1024 * i + 1024)])
try:
transferStarted = datetime.datetime.now()
lastSentTime = datetime.datetime.now()
for i in range(len(responsechunks)):
connectionSocket.send(responsechunks[i])
if datetime.datetime.now() >= transferStarted + datetime.timedelta(seconds=10):
if datetime.datetime.now() >= lastSentTime + datetime.timedelta(seconds=0.5):
lastSentTime = datetime.datetime.now()
print("Sending chunk " + str(i + 1) + " of " + str(numchunks) + " to " + sourceIP + ":" + str(sourcePort) + " for file \"" + fullRequestPath + "\"")
except BrokenPipeError:
print("Broken Pipe!")
except ConnectionResetError:
print("Connection Reset!")
except TimeoutError:
print("Send timed out!")
print("Served " + fullRequestPath + " to " + sourceIPvisual + ":" + str(sourcePort))
connectionSocket.close()
while running:
print('Waiting for a connection...')
connectionSocket, addr = serverSocket.accept()
t = threading.Thread(target=handle_connection, args=(connectionSocket, addr))
t.daemon = True
t.start()
print("Server Stopped.")
serverSocket.close()
+211
View File
@@ -0,0 +1,211 @@
import bcrypt
import hashlib
import base64
import os
import json
import secrets
import datetime
import mydatabase
from cryptography.fernet import Fernet
db = mydatabase.database("database.txt")
# In-memory session store: { token: { "username": str, "expires": datetime } }
_sessions = {}
SESSION_LIFETIME_MINUTES = 60
# Load bad passwords list once at import time
_badpasswords = set()
try:
with open("badpasswords.txt") as passlist:
for line in passlist:
word = line.strip()
if word:
_badpasswords.add(word.lower())
except FileNotFoundError:
pass
# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------
def _derive_key(password: str, salt: bytes) -> bytes:
"""Derive a 32-byte key from a password and salt using PBKDF2-HMAC-SHA256.
This is used to encrypt/decrypt per-user data — the server never stores it."""
return hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, 390000)
def _fernet(key: bytes) -> Fernet:
return Fernet(base64.urlsafe_b64encode(key))
def _encrypt(data: str, key: bytes) -> str:
"""Encrypt a JSON string, return a base64 string safe for JSON storage."""
return _fernet(key).encrypt(data.encode("utf-8")).decode("utf-8")
def _decrypt(data: str, key: bytes) -> str:
"""Decrypt a base64 string back to a JSON string. Raises on bad key/data."""
return _fernet(key).decrypt(data.encode("utf-8")).decode("utf-8")
def _purge_expired_sessions():
now = datetime.datetime.utcnow()
expired = [t for t, s in _sessions.items() if s["expires"] <= now]
for t in expired:
del _sessions[t]
# ---------------------------------------------------------------------------
# Public API — all functions return ("success"|"fail", payload)
# ---------------------------------------------------------------------------
def register(username: str, password: str) -> tuple:
"""Create a new account.
Returns ("success", None) or ("fail", reason_string)."""
if not username or not password:
return ("fail", "username and password are required")
if len(username) > 64:
return ("fail", "username too long")
if len(password) < 8:
return ("fail", "password too short")
if password.lower() in _badpasswords:
return ("fail", "password is too common")
db.read()
users = db.database.setdefault("users", {})
if username in users:
return ("fail", "user already exists")
# bcrypt handles its own salt internally
pw_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
# Separate salt for PBKDF2 key derivation (not reusing the bcrypt salt)
kdf_salt = os.urandom(32)
key = _derive_key(password, kdf_salt)
initial_data = _encrypt(json.dumps({}), key)
users[username] = {
"pw_hash": pw_hash,
"kdf_salt": base64.b64encode(kdf_salt).decode("utf-8"),
"user_data": initial_data,
}
db.write()
return ("success", None)
def login(username: str, password: str) -> tuple:
"""Authenticate and return a session token.
Returns ("success", token_string) or ("fail", reason_string)."""
db.read()
user = db.database.get("users", {}).get(username)
if user is None:
# Run a dummy bcrypt check to avoid timing-based username enumeration
bcrypt.checkpw(password.encode("utf-8"), bcrypt.hashpw(b"dummy", bcrypt.gensalt()))
return ("fail", "invalid username or password")
if not bcrypt.checkpw(password.encode("utf-8"), user["pw_hash"].encode("utf-8")):
return ("fail", "invalid username or password")
_purge_expired_sessions()
token = secrets.token_urlsafe(32)
_sessions[token] = {
"username": username,
"expires": datetime.datetime.utcnow() + datetime.timedelta(minutes=SESSION_LIFETIME_MINUTES),
}
return ("success", token)
def logout(token: str) -> tuple:
"""Invalidate a session token.
Returns ("success", None) or ("fail", reason_string)."""
if token in _sessions:
del _sessions[token]
return ("success", None)
return ("fail", "invalid or expired session")
def validate_session(token: str) -> tuple:
"""Check whether a token is valid and not expired.
Returns ("success", username) or ("fail", reason_string)."""
_purge_expired_sessions()
session = _sessions.get(token)
if session is None:
return ("fail", "invalid or expired session")
return ("success", session["username"])
def get_user_data(token: str, password: str) -> tuple:
"""Retrieve the decrypted user data dict for the session owner.
Password is required to derive the decryption key.
Returns ("success", data_dict) or ("fail", reason_string)."""
status, username = validate_session(token)
if status != "success":
return ("fail", username)
db.read()
user = db.database.get("users", {}).get(username)
if user is None:
return ("fail", "user not found")
kdf_salt = base64.b64decode(user["kdf_salt"])
key = _derive_key(password, kdf_salt)
try:
data = json.loads(_decrypt(user["user_data"], key))
except Exception:
return ("fail", "decryption failed — wrong password or corrupted data")
return ("success", data)
def set_user_data(token: str, password: str, data: dict) -> tuple:
"""Encrypt and store a user data dict for the session owner.
Password is required to derive the encryption key.
Returns ("success", None) or ("fail", reason_string)."""
status, username = validate_session(token)
if status != "success":
return ("fail", username)
db.read()
users = db.database.get("users", {})
user = users.get(username)
if user is None:
return ("fail", "user not found")
# Verify password before allowing a write
if not bcrypt.checkpw(password.encode("utf-8"), user["pw_hash"].encode("utf-8")):
return ("fail", "invalid password")
kdf_salt = base64.b64decode(user["kdf_salt"])
key = _derive_key(password, kdf_salt)
user["user_data"] = _encrypt(json.dumps(data), key)
db.write()
return ("success", None)
def delete_account(token: str, password: str) -> tuple:
"""Permanently delete an account and invalidate the session.
Returns ("success", None) or ("fail", reason_string)."""
status, username = validate_session(token)
if status != "success":
return ("fail", username)
db.read()
users = db.database.get("users", {})
user = users.get(username)
if user is None:
return ("fail", "user not found")
if not bcrypt.checkpw(password.encode("utf-8"), user["pw_hash"].encode("utf-8")):
return ("fail", "invalid password")
del users[username]
db.write()
# Invalidate all sessions for this user
to_remove = [t for t, s in _sessions.items() if s["username"] == username]
for t in to_remove:
del _sessions[t]
return ("success", None)
+34
View File
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Bad Request</title>
<style>
body {
background-image: url("/Images/DingusGrass.jpg");
background-color: #0f8340;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
min-height: 100vh;
min-height: 100svh
}
.center {
background-color: white;
width: fit-content;
padding: 5mm;
margin: auto;
}
.dense {
margin: 1mm;
}
</style>
</head>
<body>
<div class="center">
<h1 style="color: #a00;" class="dense">Error 400: Bad Request.</h1>
<p class="dense">
There is an issue with your web request.<br>If possible, please verify all required name/value pairs are present in the query string of the URL.
</p>
</div>
</body>
</html>
+34
View File
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Page Not Found!</title>
<style>
body {
background-image: url("/Images/DingusGrass.jpg");
background-color: #0f8340;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
min-height: 100vh;
min-height: 100svh
}
.center {
background-color: white;
width: fit-content;
padding: 5mm;
margin: auto;
}
.dense {
margin: 1mm;
}
</style>
</head>
<body>
<div class="center">
<h1 style="color: #a00;" class="dense">Error 404: Page not found.</h1>
<p class="dense">
The page you have requested does not exist.<br>Please verify you typed the URL correctly and try again.
</p>
</div>
</body>
</html>
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Too Many Requests</title>
<style>
body {
background-color: #0f8340;
background-position: center;
background-size: cover;
min-height: 100vh;
min-height: 100svh
}
.center {
background-color: white;
width: fit-content;
padding: 5mm;
margin: auto;
}
.dense {
margin: 1mm;
}
</style>
</head>
<body>
<div class="center">
<h1 style="color: #a00;" class="dense">Error 429: Too many Requests</h1>
<p class="dense">
You are sending too many requests. If this is caused by an error in a page, please contact the site admin.
</p>
</div>
</body>
</html>
+34
View File
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Internal Server Error.</title>
<style>
body {
background-image: url("/Images/DingusGrass.jpg");
background-color: #0f8340;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
min-height: 100vh;
min-height: 100svh
}
.center {
background-color: white;
width: fit-content;
padding: 5mm;
margin: auto;
}
.dense {
margin: 1mm;
}
</style>
</head>
<body>
<div class="center">
<h1 style="color: #a00;" class="dense">Error 500: Internal Server Error.</h1>
<p class="dense">
An error has occured on the server.<br>Please try again later. If this continues to occur, please contact the server administrator.
</p>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

+30
View File
@@ -0,0 +1,30 @@
webroot_real = os.path.realpath(script_directory + "/webroot")
folderPath = os.path.realpath(script_directory + "/webroot" + requestPath)
if not folderPath.startswith(webroot_real + os.sep) and folderPath != webroot_real:
responseType = "403 Forbidden"
contentType = "text/html"
responsecontent = b"<h1>403 Forbidden</h1>"
else:
filesList = list(os.scandir(folderPath))
responseType = "200 OK"
contentType = "text/html"
template = open(script_directory + "/webroot/Templates/fileList.html").read()
template = template.replace("<!--dir-->", html.escape(requestPath))
listText = "<li>Folder: <a href=\"..\">..</a></li>"
for i in filesList:
temp = "File: "
funnynameorsomethingidk = i.name
if os.path.isdir(folderPath + "/" + funnynameorsomethingidk):
temp = "Folder: "
funnynameorsomethingidk += "/"
link = requestPath + "/" + funnynameorsomethingidk
link = link.replace("//", "/")
if link.endswith(".py"):
link = "/raw/?p=" + html.escape(link)
else:
link = html.escape(link)
listText += ("<li>" + temp + "<a href='" + link + "'>" + html.escape(i.name) + "</a></li>")
template = template.replace("<!--fileList-->", listText)
responsecontent = template.encode()
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Directory Listing of <!--dir--></h1>
<ul>
<!--fileList-->
</ul>
</body>
</html>
+64
View File
@@ -0,0 +1,64 @@
<div class="navbar no-print">
<span class="nav-button">
<a href="/?<!--args-->p=/">
<div class="nav-content">
<img src="/Images/mudkip.png" alt="Page Icon" style="height:100%; margin: 0px;">
</div>
</a>
</span>
<span class="nav-button">
<a href="/?<!--args-->p=/Projects">
<div class="nav-content">
Projects
</div>
</a>
<div class="dropdown-content">
<a href="/?<!--args-->p=/Projects/wiitop/" class="nav-button">
<div class="nav-content">
Wiitop
</div>
</a>
</div>
</span>
<span class="nav-button">
<a href="/?<!--args-->p=/Contact" >
<div class="nav-content">
Contact
</div>
</a>
</span>
<span class="nav-button">
<a href="/?<!--args-->p=/webring" >
<div class="nav-content">
Webring?
</div>
</a>
</span>
<span class="nav-button" style="float: right; margin-right:0;">
<div class="nav-content">
<label for="theme">Theme: </label>
<select name="theme" id="theme" onchange="changeTheme(this.value)">
<option value="default">Page Default</option>
<option value="Light"<!--themeLight-->>Light</option>
<option value="Dark"<!--themeDark-->>Dark</option>
</select>
</div>
</span>
<!--userbar-->
</div>
<script>
function replaceUrlParam(url, paramName, paramValue) {
if (paramValue == null) {
paramValue = '';
}
var pattern = new RegExp('\\b('+paramName+'=).*?(&|#|$)');
if (url.search(pattern)>=0) {
return url.replace(pattern,'$1' + paramValue + '$2');
}
url = url.replace(/[?#]$/,'');
return url + (url.indexOf('?')>0 ? '&' : '?') + paramName + '=' + paramValue;
}
function changeTheme(theme) {
window.location.href = replaceUrlParam('/?<!--args-->p=<!--path-->', "theme", theme)
}
</script>
+27
View File
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<!--head-->
</head>
<body>
<div class="bgimage"></div>
<div id="grid-container">
<header>
<!--header-->
</header>
<article>
<a id="oldmodelink" style="color: rgba(127,0,255,0);" href="/?v=old&p=<!--path-->">View in Compatibility Mode</a>
<br>
<!--content-->
<br>
</article>
<footer class="gray">
<p style="margin: 0px; display:flex; align-items: center; justify-content: space-between;">
<span style="display: flex; align-items: center; margin-right:10px;">(c) 2026 Logan Joy-Koer (SuperbeeLavaman)
<a style="margin-left: 10px" href="http://wii-linux.org/"><img style="display: block;" src="Images/wl88x31.gif" alt="Wii-Linux NOW!" class=no-print></a></span>
<span style="float: left;">This page last updated: <!--updateddate--></span>
</p>
</footer>
</div>
</body>
</html>
+28
View File
@@ -0,0 +1,28 @@
Navigation<br>
<div>
<a href="/?<!--args-->&p=/">
Home Page
</a>
<br>
<a href="/?<!--args-->&p=/Projects">
Projects
</a>
<br>
<div>
<a href="/?<!--args-->&p=/Projects/wiitop/">
Wiitop
</a>
<br>
</div>
<a href="/?<!--args-->&p=/Contact" >
Contact Page
</a>
<br>
<a href="/?<!--args-->&p=/webring" >
Webring?
</a>
<br>
<a href="/?<!--args-->returnto=<!--path-->&p=/login" >
Login
</a>
</div>
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<!--head-->
</head>
<body>
Using Compatibility Mode.<br>Click <a href="/?v=new&p=<!--path-->">here</a> for modern browsers.
<div id="grid-container">
<header>
<!--header-->
</header>
<article>
<br>
<!--content-->
<br>
</article>
<footer>
<p style="margin: 0px; display:flex; align-items:center;"><span style="margin-right: 10px;">(c) 2026 Logan Joy-Koer (SuperbeeLavaman)</span><a style="display: inline-block;" href="http://wii-linux.org/"><img style="margin: 0px; display: block;" src="Images/wl88x31.gif" alt="Wii-Linux NOW!"></a></p>
</footer>
</div>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
responseType = "200 OK"
contentType = "text/plain"
responsecontent = datetime.datetime.now().strftime("%I:%M:%S%p on %B %d, %Y").encode()
+19
View File
@@ -0,0 +1,19 @@
Title: Contact Me<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: Contact Info<br>
Author: SuperbeeLavaman<br>
Content:
<div class="article-content-block">
<h1>Contact Page</h1>
<p>
This page contains contact information, for if you want to tell me anything or ask me a question.
</p>
<p>
<strong>Phone:</strong> <a href="tel:9714442780">(971) 444-2780</a><br>
<strong>Email:</strong> <a href="mailto:loganlee.jk@outlook.com">loganlee.jk@outlook.com</a><!--, <a href="mailto:superbee@superbeelavaman.onthewifi.com">superbee@superbeelavaman.onthewifi.com</a>--><br>
<strong>Discord:</strong> <a href="https://discordapp.com/users/763802866921242646">superbee_hashtag_3637</a><br>
<strong>Matrix:</strong> <a href="https://matrix.to/#/%40superbeelavaman%3Asuperbeelavaman.duckdns.org">@superbeelavaman:superbeelavaman.duckdns.org</a><br>
<strong>Nintendo Switch friend code:</strong> SW-7690-5791-7118<br>
</p>
</div>
+19
View File
@@ -0,0 +1,19 @@
Title: Projects<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: My recent projects.
Author: SuperbeeLavaman<br>
Content:
<div class="article-content-block">
<h1>Projects</h1>
<p>This page will list recent projects and projects I'm currently working on, if I think to write about them. It will probably not have a lot of information on it.</p>
<ul>
<li>
<a href="/?<!--args-->p=/Projects/wiitop/"><b>Preparation for making the Wiitop</b></a>
<br>As you may have noticed, I have an 88x31 button leading to Wii-Linux.org. One of the projects they have is a laptop that uses a Nintendo Wii
as the main computer portion of the laptop. As you would expect, several modifications are expected to be performed to the Wii that is being
used in the Wiitop.
<br><a href="/?<!--args-->p=/Projects/wiitop/"><b>[Read More]</b></a>
</li>
</ul>
</div>
@@ -0,0 +1,36 @@
Title: The Wiitop<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: I'm working on making the first prototype Wiitop*<br>
Author: SuperbeeLavaman<br>
Content:
<div class="article-content-block">
<h1>Preparation for making the Wiitop</h1>
<p> As you may have noticed, I have an 88x31 button leading to Wii-Linux.org. One of the projects they have is a laptop that uses a Nintendo Wii
as the main computer portion of the laptop. As you would expect, several modifications are expected to be performed to the Wii that is being
used in the Wiitop.
<br>
<br>The Wiitop is specified to be a 14" laptop, with a trimmed Wii inside. The publically available spec is quite out of date from what has been
talked about in the Wii-Linux server, and unfortunately while it specifies an 854x480 screen, those are effectively impossible to get. As
<i>Lily&nbsp;lily&nbsp;lily&nbsp;lily</i> recommended, 720x576@50 letterboxes cleanly into 800x600, which is a slightly more common LCD resolution.
<br>Since I'm likely to be the first person to actually construct a Wiitop, my decisions will likely have a strong effect on how the design is
finalized. A 4:3 aspect ratio makes sense for a device that often displays 4:3 content, and a 4:3 laptop makes certain components easier to
arrange. Since I've never done a Wii trim before, I'll be using the <a href="https://bitbuilt.net/forums/threads/the-definitive-wii-trimming-guide.198/">OMGWTF</a>
trim. For the power supply, I will be designing a custom board that includes an STM32-based CrOS_EC, a BQ25703A charge controller, and various
regulators, which will be controllable from Linux.
<br>The following is written during or after the actions listed above.
<br>
<h2>The Wii to use</h2>
I have an RVL-CPU-40 wii that has gone through a lot.
<br>At one point, it was running with no shell and no heatsink, in a freezer. During that time, it was nicknamed <i>Friizer</i>, and continues to
retain that name.
<br><abbr title="Friizer in the freezer."><a href="/Images/projects/wiitop/friizerfreezer.jpg"><img src="/Images/projects/wiitop/friizerfreezer.jpg" alt="Friizer in the freezer" class="fitinpageplease"></a></abbr>
<br>
<br>Recently, I delidded the CPU and SoC of Friizer. I then decided to prepare for trimming and do research for what I would need to do to make the Wiitop.
<br><abbr title="Friizer has been delidded."><a href="/Images/projects/wiitop/friizerdelid.jpg"><img src="/Images/projects/wiitop/friizerdelid.jpg" alt="Friizer has been delidded." class="fitinpageplease""></a></abbr>
<br><abbr title="Marked for the OMGWTF trim"><a href="/Images/projects/wiitop/friizermarked.jpg"><img src="/Images/projects/wiitop/friizermarked.jpg" alt="Marked for the OMGWTF trim" class="fitinpageplease"></a></abbr>
<br>
<br>[THIS PAGE IS UNDER CONSTRUCTION]
<br><abbr title="construction."><img src=/Images/underconstruction.gif alt="construction." class="fitinpageplease"></abbr>
</p>
</div>
+16
View File
@@ -0,0 +1,16 @@
Title: My Website<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: I've made a website. There's not much content yet.<br>
Author: SuperbeeLavaman<br>
Content:
<div class="article-content-block">
<h1>SuperbeeLavaman's Website</h1>
<p>
Idk what to put on my website but it exists.
<br>
🫜
<br><br>
<p style="font-family: comicsansms;">Comic Sans MS</p>
</p>
</div>
+29
View File
@@ -0,0 +1,29 @@
Title: Login<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: Login.<br>
Author: SuperbeeLavaman<br>
Content:
<div class="article-content-block">
<h1>Login</h1>
<p><!--errortext--></p>
<p>
<form action="/login/">
<table>
<tr>
<td><label for="username">Username: </label></td>
<td><input type="text" name="username" id="username"></td>
</tr>
<tr>
<td><label for="password">Password: </label></td>
<td><input type="password" name="password" id="password"></td>
</tr>
<tr>
<td><!--formargs--></td>
<td><input type="submit" name="submit" id="submit" value="Submit"></td>
</tr>
</table>
</form>
<p>No Account? <a href="/?<!--args-->p=/signup/">Sign Up.</a></p>
</p>
</div>
+75
View File
@@ -0,0 +1,75 @@
Title: Resume<br>
Icon: /favicon.ico<br>
Style: /styles/newDark.css<br>
Description: Logan's Resume<br>
Author: Logan Joy-Koer (SuperbeeLavaman)<br>
Content:
<div class="article-content-block">
<section class="container">
<h1>Logan Joy-Koer</h1>
<h6 style="margin-top: 0px" class="subtitle">
Gervais, OR |
<a href="mailto:loganlee.jk@outlook.com">loganlee.jk@outlook.com</a> |
<a href="tel:9714442780">(971) 444-2780</a> |<br>
<a href="https://www.github.com/superbeelavaman">github.com/superbeelavaman</a> |
<a href="https://superbeelavaman.duckdns.org/?p=/">https://superbeelavaman.duckdns.org</a> |<br>
<a href="https://matrix.to/#/%40superbeelavaman%3Asuperbeelavaman.duckdns.org">@superbeelavaman:superbeelavaman.duckdns.org</a> |
CompTIA Tech+ Certified |
</h6>
</section>
<section class="container">
<h2>Summary</h2>
<p style="margin-top: 0px">Linux enthusiast with hands-on experience in embedded hardware, low-level software, and systems programming. Comfortable across x86, PowerPC/POWER ISA, eZ80, and ARM CPU architectures. Passionate about unusual and older hardware and making things work in environments they weren't intended to.</p>
</section>
<section class="container">
<h2>Projects & Hands-On Experience</h2>
<h3>Xbox 360 PBX Server <span class="subtitle">Personal Lab</span></h3>
<p style="margin-top: 0px"><ul>
<li>Runs Arch Linux (ArchPOWER) on Xbox 360 hardware</li>
<li>Compiled and configured Asterisk 23.2.2 from source for a full home PBX system</li>
<li>Integrated a Cisco VG224 as a SIP/FXS gateway; successfully registered all 24 extensions via chan_pjsip</li>
<li>Set up dnsmasq for PTR resolution and managed routing across two subnets</li>
</ul></p>
<h3>Multiple Workstations <span class="subtitle">Personal Lab</span></h3>
<p style="margin-top: 0px"><ul>
<li>Deployed and Configured several workstations to conveniently perform tasks from several locations</li>
<li>Custom-compiled Linux Kernel builds for nonstandard hardware</li>
<li>Became familiar with several Linux distros including but not limited to Ubuntu, Arch, Adélie, Void, and more</li>
</ul></p>
</section>
<section class="container">
<h2>Prior Work Experience</h2>
<h3>Crew Member/Cook - Domino's Pizza <span class="subtitle">Woodburn, OR | July 2024 - August 2025</span></h3>
<ul style="margin-top: 0px;">
<li>Prepared orders accurately with time pressure during high volume hours</li>
<li>Handled customer service tasks including taking orders and resolving complaints both in person and over the phone</li>
<li>Maintained food safety and cleanliness standards consistently across shifts</li>
</ul>
</section>
<section class="container">
<h2>Skills</h2>
<p style="margin-top: 0px; margin-bottom: 0px;">Operating Systems:</p>
<ul style="margin-top: 0px;">
<li>Arch Linux</li>
<li>Linux Mint</li>
<li>Debian</li>
<li>Ubuntu</li>
<li>Adélie</li>
<li>Void</li>
<li>PostMarketOS</li>
<li>Windows 98/ME</li>
<li>Windows XP</li>
<li>Windows Vista/7</li>
<li>Windows 10</li>
</ul>
<p>Familiar CPU Architectures: x86/AMD64, PowerPC/POWER ISA, ARM, eZ80</p>
<p>Networking: VoIP/SIP (Asterisk, PJSIP), Cisco IOS, VLANs, NAT, dnsmasq, DNS</p>
<p>Hardware: Embedded systems, PCB design (KiCad), Soldering</p>
<p>Tools: Git, Vim, SSH, Nginx, Sledgehammer</p>
<p>Languages: C, Python, Shell scripting [Bash], JavaScript, HTML, CSS</p>
</section>
<section class="container">
<h2>Certifications</h2>
<p style="margin-top: 0px;">CompTIA Tech+ (FC0-U71) - Issued May 22, 2025</p>
</section>
</div>
+33
View File
@@ -0,0 +1,33 @@
Title: Sign Up<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: Sign up.<br>
Author: SuperbeeLavaman<br>
Content:
<div class="article-content-block">
<h1>Sign Up</h1>
<p><!--errortext--></p>
<p>
<form action="/signup/">
<table>
<tr>
<td><label for="username">Username: </label></td>
<td><input type="text" name="username" id="username"></td>
</tr>
<tr>
<td><label for="password">Password: </label></td>
<td><input type="password" name="password" id="password"></td>
</tr>
<tr>
<td><label for="password"> Repeat Password: </label></td>
<td><input type="password" name="password2" id="password2"></td>
</tr>
<tr>
<td><!--formargs--></td>
<td><input type="submit" name="submit" id="submit" value="Submit"></td>
</tr>
</table>
</form>
<p>Already have an account? <a href="/?<!--args-->p=/login/">Sign In.</a></p>
</p>
</div>
+25
View File
@@ -0,0 +1,25 @@
Title: Webring<br>
Icon: /favicon.ico<br>
Style: /styles/newLight.css<br>
Description: This site is part of a Webring. Read more here.<br>
Author: SuperbeeLavaman, PiplupRox Productions<br>
Content:
<div class="article-content-block">
<h1>This site is part of a <a href="https://en.wikipedia.org/wiki/Webring">Webring</a></h1>
<p>
This webring includes the Porypedia Wiki, PiplupRox Productions, and more! Using the top bar here, you can navigate to other pages on this site, and from here you can navigate to other pages in the webring.
<br><br>
I am part of this webring as a result of being one of 3 active community members in the Porygondoliers community, and PiplupRox was adamant about creating a webring
<br>
Blame him if you think this is weird.
<br><br>
<div style="width: min-content;">
<a href="https://pipluprox-productions.neocities.org/webring"><img class="center" src="/Images/webring/porygondolier_community_webring.gif"></a>
<div style="display: flex; justify-content: center;">
<a href="https://pipluprox-productions.neocities.org/webring?action=previous"><img src="/Images/webring/porygondolier_webring_prev.gif"></a>
<a href="https://pipluprox-productions.neocities.org/webring?action=random"><img src="/Images/webring/porygondolier_webring_rand.gif"></a>
<a href="https://pipluprox-productions.neocities.org/webring?action=next"><img src="/Images/webring/porygondolier_webring_next.gif"></a>
</div>
</div>
</p>
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.
+143
View File
@@ -0,0 +1,143 @@
page = ""
args = ""
responseType = "200 OK"
contentType = "text/html"
for i in argumentsDict:
argumentsDict[i] = urllib.parse.unquote(argumentsDict[i])
if "p" in argumentsDict:
path = argumentsDict["p"]
content_real = os.path.realpath(script_directory + "/webroot/content")
filePath = os.path.realpath(script_directory + "/webroot/content/" + path)
if not filePath.startswith(content_real + os.sep) and filePath != content_real:
responseType = "403 Forbidden"
responsecontent = b"<h1>403 Forbidden</h1>"
contentType = "text/html"
else:
if "v" in argumentsDict and argumentsDict["v"] in ("new", "old"):
version = argumentsDict["v"]
else:
version = "new"
if os.path.exists(filePath):
if os.path.isdir(filePath):
if os.path.exists(filePath + "/" + version + "index.html"):
filePath += ("/" + version + "index.html")
else:
filePath += "/index.html"
if version == "new":
page = open(script_directory + "/webroot/Templates/index.html").read()
header = open(script_directory + "/webroot/Templates/header.html").read()
elif version == "old":
page = open(script_directory + "/webroot/Templates/oldindex.html").read()
header = open(script_directory + "/webroot/Templates/oldheader.html").read()
page = page.replace("<!--header-->", header)
# Resolve session cookie and build userbar before other replacements,
# as the logged-out userbar contains <!--args--> and <!--path--> itself
session_token = Cookies.get("session", "")
if session_token:
session_status, session_user = userManagement.validate_session(session_token)
else:
session_status = "fail"
session_user = ""
if session_status == "success":
pfp_base = os.path.realpath(script_directory + "/webroot/Images/profileimages")
pfp_real_gif = os.path.realpath(script_directory + "/webroot/Images/profileimages/" + session_user + ".gif")
pfp_real_jpg = os.path.realpath(script_directory + "/webroot/Images/profileimages/" + session_user + ".jpg")
if pfp_real_gif.startswith(pfp_base + os.sep) and os.path.exists(pfp_real_gif):
pfp_src = "/Images/profileimages/" + html.escape(session_user) + ".gif"
elif pfp_real_jpg.startswith(pfp_base + os.sep) and os.path.exists(pfp_real_jpg):
pfp_src = "/Images/profileimages/" + html.escape(session_user) + ".jpg"
else:
pfp_src = "/Images/default_pfp.gif"
userbar = (
'<span class="nav-button" style="float:right; margin-right:0;">'
'<a href="/logout/">'
'<div class="nav-content">Logout</div>'
'</a></span>'
'<span class="nav-button" style="float:right; margin-right:0;">'
'<div class="nav-content">'
'<img src="' + pfp_src + '" alt="Profile Picture" style="height:100%; vertical-align:middle;">'
' Logged in as ' + html.escape(session_user) +
'</div></span>'
)
else:
userbar = (
'<span class="nav-button" style="float:right; margin-right:0;">'
'<a href="/?<!--args-->returnto=<!--path-->&p=/login/">'
'<div class="nav-content">Login</div>'
'</a></span>'
)
page = page.replace("<!--userbar-->", userbar)
content = open(filePath).read()
title = content.split("Title: ")[1].split("<br>\n")[0]
icon = content.split("Icon: ")[1].split("<br>\n")[0]
style = content.split("Style: ")[1].split("<br>\n")[0]
description = content.split("Description: ")[1].split("<br>\n")[0]
author = content.split("Author: ")[1].split("<br>\n")[0]
content = content.split("Content: \n")[1]
updateddate = get_last_modified(filePath)
if version == "old":
style = "/styles/oldmain.css"
if "theme" in argumentsDict:
theme = argumentsDict["theme"]
if os.path.exists(script_directory + "/webroot/styles/" + version + theme + ".css"):
style = "/styles/" + version + theme + ".css"
args += "theme=" + theme + "&"
page = page.replace("<!--theme" + theme + "-->", " selected")
page = re.sub('<!--theme.*?-->', '', page, flags=re.DOTALL)
head = "<title>" + title + """</title>
<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"""" + icon + """\">
<link rel=\"icon\" type=\"image/x-icon\" href=\"""" + icon + """\">
<link rel=\"stylesheet\" type=\"text/css\" href=\"/styles/newCommon.css\">
<link rel=\"stylesheet\" type=\"text/css\" href=\"""" + html.escape(style) + """\">
<meta charset=\"UTF-8\">
<meta name=\"description\" content=\"""" + description + """\">
<meta name=\"author\" content=\"""" + author + """\">
<meta property=\"og:title\" content=\"""" + title + """\">
<meta property=\"og:description\" content=\"""" + description + """\">
<meta property=\"og:type\" content=\"website\">
<meta property=\"twitter:title\" content=\"""" + title + """\">
<meta property=\"twitter:description\" content=\"""" + description + """\">
"""
args += "v=" + version + "&"
formargs = ""
for i in argumentsDict:
formargs += "<input type=\"hidden\" name=\"" + html.escape(i) + "\" value=\"" + html.escape(argumentsDict[i]) + "\">"
errortext = ""
if "errortext" in argumentsDict:
errortext = "<p class=\"error\">" + html.escape(argumentsDict["errortext"]) + "</p>"
page = page.replace("<!--content-->", content)
page = page.replace("<!--head-->", head)
page = page.replace("<!--path-->", urllib.parse.quote(path, safe=''))
page = page.replace("<!--args-->", args)
page = page.replace("<!--formargs-->", formargs)
page = page.replace("<!--errortext-->", errortext)
page = page.replace("<!--updateddate-->", updateddate)
page = page.replace("\n", "")
page = page.replace(" ", "")
else:
responseType = "404 Not Found"
fullRequestPath = script_directory + "/webroot/404.html"
page = open(fullRequestPath).read()
responsecontent = page.encode()
else:
responseType = "301 Moved Permanently"
responseHeaders += "Location: /?p=/\n".encode()
page = "<html><head><title>Redirecting...</title><meta http-equiv=\"refresh\" content=\"0; url='/?p=/'\" /></head><body><p><a href=\"/?p=/\">Redirect</a></p></body></html>"
responsecontent = page.encode()
+42
View File
@@ -0,0 +1,42 @@
page = ""
args = ""
responseType = "200 OK"
contentType = "text/html"
if "username" in argumentsDict and "password" in argumentsDict:
username = urllib.parse.unquote(argumentsDict["username"])
password = urllib.parse.unquote(argumentsDict["password"])
# Validate and sanitize returnpath
returnpath = "/"
returnto = ""
if "returnto" in argumentsDict:
candidate = urllib.parse.unquote(argumentsDict["returnto"])
resolved = os.path.realpath(script_directory + "/webroot" + candidate)
webroot_real = os.path.realpath(script_directory + "/webroot")
if resolved.startswith(webroot_real + os.sep) or resolved == webroot_real:
returnpath = candidate
returnto = "&returnto=" + urllib.parse.quote(returnpath)
status, result = userManagement.login(username, password)
if status == "success":
token = result
responseHeaders += ("Set-Cookie: session=" + token + "; Path=/; HttpOnly; SameSite=Strict\n").encode()
url = "/?p=" + urllib.parse.quote(returnpath)
else:
url = "/?p=/login/" + returnto + "&errortext=" + urllib.parse.quote("Invalid username or password.")
page = ("<html><head><title>Redirecting...</title>"
"<meta http-equiv=\"refresh\" content=\"0; url='" + html.escape(url) + "'\" />"
"</head><body><p><a href=\"" + html.escape(url) + "\">Redirect</a></p></body></html>")
else:
responseType = "400 Bad Request"
fullRequestPath = script_directory + "/webroot/400.html"
page = open(fullRequestPath).read()
# Clear sensitive locals
username = ""
password = ""
responsecontent = page.encode()
+25
View File
@@ -0,0 +1,25 @@
responseType = "200 OK"
contentType = "text/html"
session_token = Cookies.get("session", "")
if session_token:
userManagement.logout(session_token)
responseHeaders += b"Set-Cookie: session=; Path=/; Max-Age=0; HttpOnly; SameSite=Strict\n"
returnpath = "/"
if "returnto" in argumentsDict:
candidate = urllib.parse.unquote(argumentsDict["returnto"])
resolved = os.path.realpath(script_directory + "/webroot" + candidate)
webroot_real = os.path.realpath(script_directory + "/webroot")
if resolved.startswith(webroot_real + os.sep) or resolved == webroot_real:
returnpath = candidate
url = "/?p=" + urllib.parse.quote(returnpath)
responseType = "302 Found"
responseHeaders += ("Location: " + url + "\n").encode()
responsecontent = (
"<html><head><title>Redirecting...</title>"
"<meta http-equiv=\"refresh\" content=\"0; url='" + html.escape(url) + "'\" />"
"</head><body><p><a href=\"" + html.escape(url) + "\">Redirect</a></p></body></html>"
).encode()
+45
View File
@@ -0,0 +1,45 @@
page = ""
responseType = "200 OK"
contentType = "text/html"
if "p" in argumentsDict:
path = urllib.parse.unquote(argumentsDict["p"])
webroot_real = os.path.realpath(script_directory + "/webroot")
filePath = os.path.realpath(script_directory + "/webroot/" + path)
if not filePath.startswith(webroot_real + os.sep) and filePath != webroot_real:
responseType = "403 Forbidden"
contentType = "text/html"
responsecontent = b"<h1>403 Forbidden</h1>"
elif os.path.exists(filePath):
if os.path.isdir(filePath):
filesList = list(os.scandir(filePath))
page = open(script_directory + "/webroot/Templates/fileList.html").read()
page = page.replace("<!--dir-->", html.escape(path))
listText = "<li>Folder: <a href=\"/raw/?p=" + html.escape(os.path.dirname(path)) + "\">..</a></li>"
for i in filesList:
temp = "File: "
if os.path.isdir(filePath + i.name):
temp = "Folder: "
link = path.rstrip("/") + "/" + i.name
listText += ("<li>" + temp + "<a href=\"/raw/?p=" + html.escape(link) + "\">" + html.escape(i.name) + "</a></li>")
page = page.replace("<!--fileList-->", listText)
responsecontent = page.encode()
else:
contentType = magic.Magic(mime=True).from_file(filePath)
if contentType in (contentTypes + ["application/x-python-code", "text/x-python"]):
if contentType.startswith("text"):
contentType = "text/plain"
responsecontent = open(filePath, "rb").read()
else:
responseType = "415 Unsupported Media Type"
contentType = ""
responsecontent = b""
else:
responseType = "404 Not Found"
fullRequestPath = script_directory + "/webroot/404.html"
responsecontent = open(fullRequestPath, "rb").read()
else:
responseType = "400 Bad Request"
fullRequestPath = script_directory + "/webroot/400.html"
responsecontent = open(fullRequestPath, "rb").read()
+4
View File
@@ -0,0 +1,4 @@
<html>
<head></head>
<body style='background-image: url("/Images/test.png")'></body>
</html>
+66
View File
@@ -0,0 +1,66 @@
User-agent: *
Disallow: /
User-agent: Googlebot
Disallow: /
User-agent: Bingbot
Disallow: /
User-agent: GPTBot
Disallow: /
User-agent: Claude-Web
Disallow: /
User-agent: ClaudeBot
Disallow: /
User-agent: CCBot
Disallow: /
User-agent: FacebookBot
Disallow: /
User-agent: Applebot-Extended
Disallow: /
User-agent: Applebot
Disallow: /
User-agent: Diffbot
Disallow: /
User-agent: Googlebot-Image
Disallow: /
User-agent: Googlebot-Video
Disallow: /
User-agent: Slurp
Disallow: /
User-agent: DuckDuckBot
Disallow: /
User-agent: Baiduspider
Disallow: /
User-agent: YandexBot
Disallow: /
User-agent: HTTrack
Disallow: /
User-agent: Jon Arbuckle
Disallow: /robots.txt
# trollage
User-agent: Garfield
Disallow: /Lasagne/
User-agent: archive.org_bot
Disallow:
User-agent: Engineer TF2
Disallow: /teleporting/bread
+55
View File
@@ -0,0 +1,55 @@
page = ""
args = ""
responseType = "200 OK"
contentType = "text/html"
if "username" in argumentsDict and "password" in argumentsDict and "password2" in argumentsDict:
username = urllib.parse.unquote(argumentsDict["username"])
password = urllib.parse.unquote(argumentsDict["password"])
password2 = urllib.parse.unquote(argumentsDict["password2"])
# Validate and sanitize returnpath
returnpath = "/"
returnto = ""
if "returnto" in argumentsDict:
candidate = urllib.parse.unquote(argumentsDict["returnto"])
resolved = os.path.realpath(script_directory + "/webroot" + candidate)
webroot_real = os.path.realpath(script_directory + "/webroot")
if resolved.startswith(webroot_real + os.sep) or resolved == webroot_real:
returnpath = candidate
returnto = "&returnto=" + urllib.parse.quote(returnpath)
error = ""
if password != password2:
error = "Passwords do not match."
elif username == password:
error = "Username cannot match password."
if error == "":
status, reason = userManagement.register(username, password)
if status == "fail":
error = reason
if error == "":
# Auto-login after successful registration
status, token = userManagement.login(username, password)
if status == "success":
responseHeaders += ("Set-Cookie: session=" + token + "; Path=/; HttpOnly; SameSite=Strict\n").encode()
url = "/?p=" + urllib.parse.quote(returnpath)
else:
url = "/?p=/signup/" + returnto + "&errortext=" + urllib.parse.quote(error)
page = ("<html><head><title>Redirecting...</title>"
"<meta http-equiv=\"refresh\" content=\"0; url='" + html.escape(url) + "'\" />"
"</head><body><p><a href=\"" + html.escape(url) + "\">Redirect</a></p></body></html>")
else:
responseType = "400 Bad Request"
fullRequestPath = script_directory + "/webroot/400.html"
page = open(fullRequestPath).read()
# Clear sensitive locals
username = ""
password = ""
password2 = ""
responsecontent = page.encode()
+189
View File
@@ -0,0 +1,189 @@
@font-face {
font-family: comicsansms;
src: url(/fonts/comic.ttf);
}
@supports (display: grid) {
#oldmodelink {
display: none;
}
}
.fitinpageplease {
max-width: 100%;
height: auto;
display: block;
}
body {
color: #f5b;
}
.bgimage {
background-color: #88a;
background-image: url("/Images/DingusSun.jpg");
background-position: center;
background-size: cover;
background-repeat: no-repeat;
position: fixed;
top:-10%;
left:-10%;
filter: blur(8px);
-webkit-filter: blur(8px);
width: 120%;
height: 130vh;
height: 130svh;
margin: none;
}
.gray {
background-color: #333;
color: #ccc;
}
footer {
padding: 1%;
margin: none;
}
header {
position: sticky;
top:0;
}
.bottomright {
position:fixed;
right:20;
bottom:20;
}
#grid-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
display:grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: 100%;
min-height: 100vh;
min-height: 100svh;
}
article {
width: 65%;
margin: auto;
margin-top: 0;
}
.article-content-block {
background-color: #222;
padding: 3%;
}
.navbar {
background-color: #333;
color: #fff;
height: 48px;
padding:none;
}
.nav-button {
position:relative;
display: inline-block;
vertical-align: top;
border: 2px solid #000;
padding: 6px;
background-color: #333;
height: 32px;
color: #fff;
}
.nav-content {
height: 32px;
color: #fff;
display: flex;
align-items: center;
}
.nav-content img {
height: 32px;
width: 32px;
object-fit: cover;
margin-right: 6px;
}
.definition {
cursor: help;
text-decoration: underline;
text-decoration-style: dotted;
}
.dropdown-content {
display: none;
position: absolute;
margin-left: -8px;
margin-top: 8px;
width: max-content;
background-color: #444;
z-index: 1;
border: 2px solid #000;
}
.nav-button a {
text-decoration: none;
}
.nav-button:hover {
background-color: #444;
}
h1, h2, h3, h4, h5, h6 {
color: #b5f;
margin-bottom: 0px;
}
a {
color: #88f;
}
li a {
color: #f5b;
}
h6 a {
color: #b5f;
}
.subtitle, .subtitle * {
font-size: 10pt;
color: #888;
}
.nav-button:hover .dropdown-content {
display: block;
}
.dropdown-content .nav-button {
width: calc(100% - 16px);
}
.error {
background-color: #f44;
color: #000;
width: fit-content;
padding: 0.3%
}
.container {
display: block;
}
@media print {
.no-print, .no-print * {
display: none !important;
}
article {
width: 100%;
margin: auto;
margin-top: 0;
}
.article-content-block {
break-inside: auto;
}
h2, h3 {
break-after: avoid;
}
p, ul, li {
break-inside: avoid;
}
#grid-container {
display: block;
position: static;
min-height: unset;
}
.bgimage {
display: none;
}
.container {
break-inside: avoid;
}
footer {
position: fixed;
bottom: 0;
}
}
+186
View File
@@ -0,0 +1,186 @@
@font-face {
font-family: comicsansms;
src: url(/fonts/comic.ttf);
}
@supports (display: grid) {
#oldmodelink {
display: none;
}
}
.fitinpageplease {
max-width: 100%;
height: auto;
display: block;
}
body {
color: #503;
}
.bgimage {
background-color: #0f8340;
background-image: url("/Images/DingusGrass.jpg");
background-position: center;
background-size: cover;
background-repeat: no-repeat;
position: fixed;
top:-10%;
left:-10%;
filter: blur(8px);
-webkit-filter: blur(8px);
width: 120%;
height: 130vh;
height: 130svh;
margin: none;
}
.gray {
background-color: #333;
color: #ccc;
}
footer {
padding: 1%;
margin: none;
}
header {
position: sticky;
top:0;
}
.bottomright {
position:fixed;
right:20;
bottom:20;
}
#grid-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
display:grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: 100%;
min-height: 100vh;
min-height: 100svh;
}
article {
width: 65%;
margin: auto;
margin-top: 0;
}
.article-content-block {
background-color: #ddd;
padding: 3%;
}
.navbar {
background-color: #333;
color: #fff;
height: 48px;
padding:none;
}
.nav-button {
position:relative;
display: inline-block;
vertical-align: top;
border: 2px solid #000;
padding: 6px;
background-color: #333;
height: 32px;
color: #fff;
}
.nav-content {
height: 32px;
color: #fff;
display: flex;
align-items: center;
}
.nav-content img {
height: 32px;
width: 32px;
object-fit: cover;
margin-right: 6px;
}
.definition {
cursor: help;
text-decoration: underline;
text-decoration-style: dotted;
}
.dropdown-content {
display: none;
position: absolute;
margin-left: -8px;
margin-top: 8px;
width: max-content;
background-color: #444;
z-index: 1;
border: 2px solid #000;
}
.nav-button a {
text-decoration: none;
}
.nav-button:hover {
background-color: #444;
}
h1, h2, h3, h4, h5, h6 {
color: #305;
margin-bottom: 0px;
}
h6 a {
color: #305;
}
.subtitle, .subtitle * {
font-size: 10pt;
color: #888;
}
li a {
color: #503;
}
.nav-button:hover .dropdown-content {
display: block;
}
.dropdown-content .nav-button {
width: calc(100% - 16px);
}
.error {
background-color: #f44;
color: #000;
width: fit-content;
padding: 0.3%
}
.container {
display: block;
}
@media print {
.no-print, .no-print * {
display: none !important;
}
article {
width: 100%;
margin: auto;
margin-top: 0;
}
.article-content-block {
break-inside: auto;
}
h2, h3 {
break-after: avoid;
}
p, ul, li {
break-inside: avoid;
}
#grid-container {
display: block;
position: static;
min-height: unset;
}
.bgimage {
display: none;
}
.container {
break-inside: avoid;
}
footer {
position: fixed;
bottom: 0;
}
}
+179
View File
@@ -0,0 +1,179 @@
@font-face {
font-family: comicsansms;
src: url(/fonts/comic.ttf);
}
@supports (display: grid) {
#oldmodelink {
display: none;
}
}
.fitinpageplease {
max-width: 100%;
height: auto;
display: block;
}
.bgimage {
background-position: center;
background-size: cover;
background-repeat: no-repeat;
position: fixed;
top:-10%;
left:-10%;
filter: blur(8px);
-webkit-filter: blur(8px);
width: 120%;
height: 130vh;
height: 130svh;
margin: none;
}
.gray {
background-color: #333;
color: #ccc;
}
footer {
padding: 1%;
margin: none;
}
header {
position: sticky;
top:0;
}
.bottomright {
position:fixed;
right:20;
bottom:20;
}
#grid-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
display:grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: 100%;
min-height: 100vh;
min-height: 100svh;
}
article {
width: 65%;
margin: auto;
margin-top: 0;
min-width: 600px;
}
.article-content-block {
padding: 3%;
}
.navbar {
background-color: #333;
color: #fff;
height: 48px;
padding:none;
}
.nav-button {
position:relative;
display: inline-block;
vertical-align: top;
border: 2px solid #000;
padding: 6px;
background-color: #333;
height: 32px;
color: #fff;
}
.nav-content {
height: 32px;
color: #fff;
display: flex;
align-items: center;
}
.nav-content img {
height: 32px;
width: 32px;
object-fit: cover;
margin-right: 6px;
}
.definition {
cursor: help;
text-decoration: underline;
text-decoration-style: dotted;
}
.dropdown-content {
display: none;
position: absolute;
margin-left: -8px;
margin-top: 8px;
width: max-content;
background-color: #444;
z-index: 1;
border: 2px solid #000;
}
.nav-button a {
text-decoration: none;
}
.nav-button:hover {
background-color: #444;
}
h1, h2, h3, h4, h5, h6 {
margin-bottom: 0px;
}
.subtitle, .subtitle * {
font-size: 10pt;
color: #888;
}
.nav-button:hover .dropdown-content {
display: block;
}
.dropdown-content .nav-button {
width: calc(100% - 16px);
}
.error {
background-color: #f44;
color: #000;
width: fit-content;
padding: 0.3%;
}
.container {
display: block;
}
@media print {
* * {
color: #000
}
.no-print, .no-print * {
display: none !important;
}
article {
width: 100%;
margin: auto;
margin-top: 0;
}
.article-content-block {
break-inside: auto;
}
h2, h3 {
break-after: avoid;
}
p, ul, li {
break-inside: avoid;
}
#grid-container {
display: block;
position: static;
min-height: unset;
}
.bgimage {
display: none;
}
.container {
break-inside: avoid;
}
footer {
position: fixed;
bottom: 0;
}
.fitinpageplease {
max-width: 50%;
}
}
+22
View File
@@ -0,0 +1,22 @@
body {
color: #f5b;
}
.bgimage {
background-color: #88a;
background-image: url("/Images/DingusSun.jpg");
}
.article-content-block {
background-color: #222;
}
h1, h2, h3, h4, h5, h6 {
color: #b5f;
}
a {
color: #88f;
}
li a {
color: #f5b;
}
h6 a {
color: #b5f;
}
+19
View File
@@ -0,0 +1,19 @@
body {
color: #503;
}
.bgimage {
background-color: #0f8340;
background-image: url("/Images/DingusGrass.jpg");
}
.article-content-block {
background-color: #ddd;
}
h1, h2, h3, h4, h5, h6 {
color: #305;
}
h6 a {
color: #305;
}
li a {
color: #503;
}
+30
View File
@@ -0,0 +1,30 @@
@font-face {
font-family: comicsansms;
src: url(/fonts/comic.ttf);
}
div {
margin-left: 32px;
}
body {
color: #503;
}
p {
width: 80%;
}
.definition {
cursor: help;
text-decoration: underline;
text-decoration-style: dotted;
}
h1, h2, h3, h4, h5, h6 {
color: #305;
}
a {
color: #503;
}
.error {
background-color: #f44;
color: #000;
width: fit-content;
padding: 0.3%
}