diff --git a/.gitignore b/.gitignore
index aac4a4a..62cbe07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
database.txt
-database.txt.bak
+*.bak
__pycache__/
*/videos/*
+userdata/*
+*server.log
diff --git a/server.py b/server.py
index 86262e5..4506016 100644
--- a/server.py
+++ b/server.py
@@ -13,7 +13,7 @@ import subprocess
running = True
-ALLOWED_METHODS = {"GET", "POST", "HEAD"}
+ALLOWED_METHODS = {"GET", "POST", "HEAD", "PUT"}
script_directory = os.path.dirname(os.path.abspath(sys.argv[0]))
webroot_real = os.path.realpath(script_directory + "/webroot")
@@ -47,7 +47,12 @@ def is_newer_than(path: str, date_str: str) -> bool:
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."""
+ for the body if present.
+
+ Returns (header_text, body_bytes). Headers are decoded as text (safe,
+ always ASCII/UTF-8 in practice); the body is returned as raw bytes so
+ binary uploads (images, gzip, etc.) aren't corrupted by a lossy text
+ decode. Callers that want the body as text can decode it themselves."""
sock.settimeout(timeout)
data = b""
try:
@@ -58,15 +63,15 @@ def recv_full(sock, timeout=5):
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)
+ header_bytes, body_start = 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)
+ header_bytes, body_start = data.split(b"\n\n", 1)
else:
- return data.decode(errors="replace")
+ return data.decode(errors="replace"), b""
# Check for Content-Length and read remaining body bytes
content_length = 0
- for line in header_part.decode(errors="replace").splitlines():
+ for line in header_bytes.decode(errors="replace").splitlines():
if line.lower().startswith("content-length:"):
try:
content_length = int(line.split(":", 1)[1].strip())
@@ -74,15 +79,25 @@ def recv_full(sock, timeout=5):
pass
break
+ # bytearray, not bytes: bytes += in a loop recopies the whole
+ # buffer on every chunk (quadratic time/memory as it grows).
+ # bytearray.extend() appends in place, so this stays linear even
+ # for multi-GB bodies.
+ body_so_far = bytearray(body_start)
while len(body_so_far) < content_length:
- chunk = sock.recv(4096)
+ chunk = sock.recv(65536)
if not chunk:
break
- body_so_far += chunk
+ body_so_far.extend(chunk)
- return (header_part + b"\r\n\r\n" + body_so_far).decode(errors="replace")
+ # Trim to Content-Length: guards against over-reads and, when no
+ # Content-Length was sent at all (content_length stays 0), drops
+ # any stray bytes so a bodyless request doesn't pick up leftovers.
+ body_so_far = bytes(body_so_far[:content_length])
+
+ return header_bytes.decode(errors="replace"), body_so_far
except TimeoutError:
- return data.decode(errors="replace")
+ return data.decode(errors="replace"), b""
blocklist_lock = threading.Lock()
@@ -116,10 +131,13 @@ def handle_connection(connectionSocket, addr):
fullRequestPath = ""
contentType = ""
- message = recv_full(connectionSocket)
+ header_text, requestBody = recv_full(connectionSocket)
connectionSocket.settimeout(None)
try:
- messages = message.replace('\r\n', '\n').split('\n')
+ # requestBody is raw bytes (unmodified body, e.g. for uploads).
+ # `messages` below is built only from headers, never the body, so
+ # it stays text-safe even when the body isn't valid UTF-8.
+ messages = header_text.replace('\r\n', '\n').split('\n')
requestheaders = []
for i in range(len(messages)):
@@ -242,7 +260,7 @@ def handle_connection(connectionSocket, addr):
responsecontent = b"
405 Method Not Allowed
"
fullRequestPath = ""
contentType = "text/html"
- responseHeaders += b"Allow: GET, POST, HEAD\n"
+ responseHeaders += b"Allow: GET, POST, HEAD, PUT\n"
else:
# URL-decode the path before sanitizing to catch encoded traversal sequences
raw_path = (messages[0].split()[1] + "?").split("?")[0]
@@ -276,32 +294,13 @@ def handle_connection(connectionSocket, addr):
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"):
+ if requestType == "PUT":
+ # Raw uploads (e.g. `curl -T localfile`) always route
+ # straight to the upload handler. The requested path is
+ # client-controlled (curl fills it in from the local filename)
+ # and generally won't correspond to a real file in webroot, so
+ # the normal exists()/extension routing below doesn't apply.
+ fullRequestPath = script_directory + "/webroot/upload/index.py"
with open(fullRequestPath) as script:
local_vars = locals()
exec(script.read(), globals(), local_vars)
@@ -310,9 +309,43 @@ def handle_connection(connectionSocket, addr):
responseHeaders = local_vars.get("responseHeaders", responseHeaders)
contentType = local_vars.get("contentType", contentType)
else:
- responseType = "415 Unsupported Media Type"
- contentType = ""
- responsecontent = b""
+ 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", ".gz", ".img"]
+ contentTypes = ["text/html", "text/json", "image/png", "image/gif", "image/x-icon", "image/jpeg", "text/css", "text/plain", "application/octet-stream", "video/mp4", "application/gzip", "application/octet-stream"]
+ 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)
diff --git a/webroot/500partitions.img b/webroot/500partitions.img
new file mode 100644
index 0000000..202dee2
Binary files /dev/null and b/webroot/500partitions.img differ
diff --git a/webroot/917partitions.img b/webroot/917partitions.img
new file mode 100644
index 0000000..db57d96
Binary files /dev/null and b/webroot/917partitions.img differ
diff --git a/webroot/Images/profileimages/superbeelavaman.gif b/webroot/Images/profileimages/superbeelavaman.gif
deleted file mode 100644
index 2d6998c..0000000
Binary files a/webroot/Images/profileimages/superbeelavaman.gif and /dev/null differ
diff --git a/webroot/Images/profileimages/tech64.gif b/webroot/Images/profileimages/tech64.gif
deleted file mode 100644
index 00b94c9..0000000
Binary files a/webroot/Images/profileimages/tech64.gif and /dev/null differ
diff --git a/webroot/Images/wl88x31_powered.gif b/webroot/Images/wl88x31_powered.gif
new file mode 100644
index 0000000..4859305
Binary files /dev/null and b/webroot/Images/wl88x31_powered.gif differ
diff --git a/webroot/Images/wl88x31mw.gif b/webroot/Images/wl88x31mw.gif
new file mode 100644
index 0000000..732e149
Binary files /dev/null and b/webroot/Images/wl88x31mw.gif differ
diff --git a/webroot/Templates/index.html b/webroot/Templates/index.html
index 355f9bd..7f933ab 100644
--- a/webroot/Templates/index.html
+++ b/webroot/Templates/index.html
@@ -18,7 +18,8 @@
diff --git a/webroot/Templates/oldindex.html b/webroot/Templates/oldindex.html
index a65a766..5860b5f 100644
--- a/webroot/Templates/oldindex.html
+++ b/webroot/Templates/oldindex.html
@@ -15,7 +15,7 @@