diff --git a/scripts/include b/scripts/include
index 697ff01..268d937 100755
--- a/scripts/include
+++ b/scripts/include
@@ -1,3 +1,3 @@
#!/bin/sh
-python3 -u -c "import utils; print(utils.parse_file('$1'))" | sed 's/\$PREV_URL\$/"{prev}"/'
+python3 -u -c "import sludge.src.lib.content; print(sludge.src.lib.content.parse_file('$1'))"
diff --git a/src/lib/__init__.py b/src/lib/__init__.py
deleted file mode 100644
index 1a8de40..0000000
--- a/src/lib/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from .method import Method
-from .path import Path
-from .headers import Headers
-from .body import Body
-from .request import Request
-from .response import Response
-from .responsecodes import ResponseCode
-from .server import serve
-from .content import *
diff --git a/src/lib/body.py b/src/lib/body.py
deleted file mode 100644
index db264da..0000000
--- a/src/lib/body.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from typing import Dict, Any
-import json
-from urllib.parse import parse_qs
-from requests_toolbelt.multipart import decoder
-
-class Body:
- def __init__(self, content: bytes, content_type: str):
- self.content = content
- self.content_type = content_type
- self.data = self.parse_body()
-
- def parse_body(self) -> Dict[str, Any]:
- if 'application/x-www-form-urlencoded' in self.content_type:
- return self.parse_form_urlencoded()
- elif 'application/json' in self.content_type:
- return self.parse_json()
- elif 'multipart/form-data' in self.content_type:
- boundary = self.content_type.split('boundary=')[1]
- return self.parse_multipart(boundary)
- else:
- return {}
-
- def parse_form_urlencoded(self) -> Dict[str, Any]:
- return {key: value[0] if len(value) == 1 else value for key, value in parse_qs(self.content.decode('utf-8')).items()}
-
- def parse_json(self) -> Dict[str, Any]:
- return json.loads(self.content.decode('utf-8'))
-
- def parse_multipart(self, boundary: str) -> Dict[str, Any]:
- multipart_data = decoder.MultipartDecoder(self.content, boundary)
- fields = {}
- for part in multipart_data.parts:
- fields[part.headers['Content-Disposition'].split('=')[1].strip('"')] = part.text
- return fields
-
- def __str__(self):
- return str(self.data)
-
- def __repr__(self):
- return f'Body({self.content=}, {self.content_type=}, {self.data=})'
diff --git a/src/lib/content.py b/src/lib/content.py
deleted file mode 100644
index e088750..0000000
--- a/src/lib/content.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from typing import Dict, Tuple
-import bleach
-import mimetypes
-import subprocess
-import re
-import os
-from .response import Response
-from .responsecodes import ResponseCode
-
-env = os.environ.copy()
-env["PATH"] = "./scripts/:" + env["PATH"]
-
-def execute_bash_code(match: re.Match) -> str:
- code = match.group(1)
- result = subprocess.check_output(code, shell=True, executable='bash', env=env)
- return result.decode().strip()
-
-def parse(string: str) -> str:
- return re.sub(r'\$\[(.*?)\]', execute_bash_code, string)
-
-def parse_file(filename: str, args: Dict[str, str]={}) -> str:
- with open(filename, 'r') as file:
- data = file.read()
-
- for k, v in args.items():
- data = data.replace('{'+k+'}', str(v))
-
- return parse(data)
-
-def raw_file_contents(file_path: str) -> Tuple[Dict[str, str], bytes]:
- mime_type, _ = mimetypes.guess_type('.' + file_path)
-
- if not mime_type:
- mime_type = 'text/plain'
-
- with open(file_path, 'rb') as f:
- data = f.read()
-
- return {'Content-Type': mime_type}, data
-
-
-def remove_html_tags(input_string: str) -> str:
- cleaned_string = bleach.clean(input_string, tags=[], attributes={})
- return cleaned_string
-
-def error_page(code: int) -> Response:
- type = ResponseCode(code)
- print('error page called')
- aoeu= Response(
- type,
- {'Content-Type': 'text/html'},
- parse(f'''
-
-
-
-
-
- {type.code}
- {type.message}
-
-
- ''').encode('utf-8')
- )
- print(aoeu)
- return aoeu
-
-def page(title, body):
- return parse("""
-
-
- """ + title + """
-
-
-
-
- $[include html/header.html]
-
- """ + body + """
-
- $[include html/footer.html]
-
-
- """).encode('utf-8')
diff --git a/src/lib/headers.py b/src/lib/headers.py
deleted file mode 100644
index cc79ac4..0000000
--- a/src/lib/headers.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from dataclasses import dataclass
-from typing import Dict
-
-@dataclass
-class Headers:
- headers: Dict[str, str]
-
- def has(self, key: str) -> bool:
- return key in self.headers.keys()
-
- def get(self, key: str) -> str | None:
- if self.has(key):
- return self.headers[key]
-
- return None
-
- def add(self, key, value) -> None:
- self.headers[key] = value
diff --git a/src/lib/method.py b/src/lib/method.py
deleted file mode 100644
index fb6579e..0000000
--- a/src/lib/method.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from enum import Enum
-
-class Method(Enum):
- GET = "GET"
- POST = "POST"
- PUT = "PUT"
- DELETE = "DELETE"
- PATCH = "PATCH"
- HEAD = "HEAD"
- OPTIONS = "OPTIONS"
-
- method: str
-
- def __new__(cls, method):
- obj = object.__new__(cls)
- obj.method = method
- return obj
-
- def __str__(self):
- return self.method
diff --git a/src/lib/patchers.py b/src/lib/patchers.py
deleted file mode 100644
index babb8be..0000000
--- a/src/lib/patchers.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from .response import Response
-
-from typing import Callable, List
-
-import re
-import random
-from bs4 import BeautifulSoup
-
-type Patcher = Callable[[Response, 'Request'], Response]
-
-def find_substring_in_lines(s, substring):
- for line_index, line in enumerate(s.splitlines()):
- position = line.find(substring)
- if position != -1:
- return line_index
-
- return 0
-
-def extract_words_from_line(line):
- clean_line = re.sub(r'<[^>]+>', '', line)
- words = clean_line.split()
- return words
-
-def uwuify_text(text):
- replacements = [
- (r'r', 'w'),
- (r'l', 'w'),
- (r'R', 'W'),
- (r'L', 'W'),
- (r'no', 'nyo'),
- (r'No', 'Nyo'),
- (r'u', 'uwu'),
- (r'U', 'Uwu')
- ]
-
- for pattern, replacement in replacements:
- text = re.sub(pattern, replacement, text)
-
- expressions = [" owo", " UwU", " rawr", " >w<"]
- sentences = text.split('. ')
- uwuified_sentences = []
-
- for sentence in sentences:
- sentence = sentence.strip()
- if sentence:
- uwuified_sentences.append(sentence + (random.choice(expressions) if random.randint(0, 5) > 4 else ''))
-
- return '. '.join(uwuified_sentences)
-
-def uwuify(body):
- body = body.decode('utf-8')
- soup = BeautifulSoup(body, 'html.parser')
-
- for text in soup.find_all(text=True):
- if text.parent.name not in ['script', 'style']:
- original_text = text.string
- words = extract_words_from_line(original_text)
- uwuified_words = [uwuify_text(word) for word in words]
- uwuified_text = ' '.join(uwuified_words)
- text.replace_with(uwuified_text)
-
- for a_tag in soup.find_all('a', href=True):
- original_href = a_tag['href']
- if '?' in original_href:
- new_href = f"{original_href}&uwu=true"
- else:
- new_href = f"{original_href}?uwu=true"
- a_tag['href'] = new_href
-
-
- return str(soup)
-
-def is_subdict(sub_dict, main_dict):
- for key, value in sub_dict.items():
- if key not in main_dict or main_dict[key] != value:
- return False
- return True
-
-patchers: List[Patcher] = [
- # lambda response, request: Response(
- # response.code,
- # response.headers,
- # "\n".join(line.replace('e', 'a') if index > find_substring_in_lines(response.body.decode('utf-8'), '') else line for index, line in enumerate(response.body.decode('utf-8').splitlines())).encode('utf-8')
- # ) if 'text/html' in response.headers.values() else response
- lambda response, request: Response(
- response.code,
- response.headers,
- uwuify(response.body).encode('utf-8')
- ) if 'text/html' in response.headers.values() and is_subdict({'uwu': 'true'}, request.path.params) else response
-]
diff --git a/src/lib/path.py b/src/lib/path.py
deleted file mode 100644
index fee0d75..0000000
--- a/src/lib/path.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from urllib.parse import urlsplit, unquote, parse_qs
-
-class Path:
- def __init__(self, route: str):
- self.route = route
- self.reduce_url()
- self.get_params()
-
- def reduce_url(self):
- _, _, path, _, _ = urlsplit(self.route)
-
- path = unquote(path)
-
- segments = []
- for segment in path.split('/'):
- if segment != '..':
- segments.append(segment)
- elif segments and segments[-1] != '..':
- segments.pop()
-
- reduced_path = '/'.join(segments)
-
- self.path = reduced_path
-
- def get_params(self):
- _, _, _, query, _ = urlsplit(self.route)
- self.params = {key: value[0] if len(value) == 1 else value for key, value in parse_qs(query).items()}
-
- def __repr__(self):
- return f"Path({self.route=}, {self.path=}, {self.params=})"
-
diff --git a/src/lib/request.py b/src/lib/request.py
deleted file mode 100644
index 24d3847..0000000
--- a/src/lib/request.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from dataclasses import dataclass
-
-from .method import Method
-from .path import Path
-from .headers import Headers
-from .body import Body
-from .router import routes
-
-@dataclass
-class Request:
- method: Method
- path: Path
- version: float
- headers: Headers
- body: Body
-
- @classmethod
- def from_bytes(cls, request_bytes: bytes):
- request_str = request_bytes.decode('utf-8')
- lines = request_str.split('\r\n')
-
- request_line = lines[0].split()
-
- if len(request_line) != 3:
- raise ValueError("Invalid request line")
-
- method, path, version_str = request_line
- version = float(version_str.split('/')[1])
-
- method = Method(method)
- path = Path(path)
-
- headers = Headers({})
- body = b''
-
- header_lines = lines[1:]
- for header_line in header_lines:
- if header_line == '':
- break
- key, value = header_line.split(':', 1)
- headers.add(key.strip(), value.strip())
-
- body_start = request_str.find('\r\n\r\n') + 4
- body = Body(request_bytes[body_start:], headers.get('Content-Type') or 'text/plain')
-
- return cls(method, path, version, headers, body)
-
- def match(self):
- for route in routes:
- if route.matches(self):
- print(route)
- return route
-
- def __repr__(self):
- path_repr = repr(self.path)
- body_repr = repr(self.body)
- return (f"Request(method={self.method!r}, path={path_repr}, version={self.version!r}, "
- f"headers={self.headers!r}, body={body_repr})")
-
diff --git a/src/lib/response.py b/src/lib/response.py
deleted file mode 100644
index bab79b5..0000000
--- a/src/lib/response.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from socket import socket
-from typing import Dict
-from .responsecodes import ResponseCode
-
-class Response:
- def __init__(self, code: ResponseCode, headers: Dict[str, str], body: bytes):
- self.code = code
- self.headers = headers
- self.body = body
-
- def build_response(self) -> bytes:
- return (
- f"HTTP/1.1 {str(self.code)}\r\n".encode('utf-8')
- + f"{''.join([f"{key}: {value}\r\n" for key, value in self.headers.items()])}\r\n".encode('utf-8')
- + self.body
- )
-
- def send(self, client: socket) -> None:
- print(self)
- client.sendall(self.build_response())
- client.close()
diff --git a/src/lib/responsecodes.py b/src/lib/responsecodes.py
deleted file mode 100644
index b13166f..0000000
--- a/src/lib/responsecodes.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from enum import Enum
-
-class ResponseCode(Enum):
- OK = (200, "OK")
- CREATED = (201, "Created")
- ACCEPTED = (202, "Accepted")
- NO_CONTENT = (204, "No Content")
- MOVED_PERMANENTLY = (301, "Moved Permanently")
- FOUND = (302, "Found")
- BAD_REQUEST = (400, "Bad Request")
- UNAUTHORIZED = (401, "Unauthorized")
- FORBIDDEN = (403, "Forbidden")
- NOT_FOUND = (404, "Not Found")
- METHOD_NOT_ALLOWED = (405, "Method Not Allowed")
- INTERNAL_SERVER_ERROR = (500, "Internal Server Error")
- NOT_IMPLEMENTED = (501, "Not Implemented")
- SERVICE_UNAVAILABLE = (503, "Service Unavailable")
-
- code: str
- message: int
-
- def __new__(cls, code, message):
- obj = object.__new__(cls)
- obj.code = code
- obj.message = message
- return obj
-
- def __str__(self):
- return f"{self.code} {self.message}"
-
- @classmethod
- def _missing_(cls, value):
- for member in cls:
- if member.code == value:
- return member
- return None
-
diff --git a/src/lib/router.py b/src/lib/router.py
deleted file mode 100644
index 51c62ff..0000000
--- a/src/lib/router.py
+++ /dev/null
@@ -1,159 +0,0 @@
-from dataclasses import dataclass
-from socket import socket
-from datetime import datetime
-from functools import reduce
-from typing import List, Callable, Tuple
-from .method import Method
-from .response import Response
-from .responsecodes import ResponseCode
-from .content import *
-from .patchers import patchers
-import os
-
-@dataclass
-class Route:
- matcher: Callable
- methods: List[Method]
- handler: Callable[['Request', socket, Tuple[str, int]], Response]
-
- def method_is_allowed(self, method: Method) -> bool:
- return method in self.methods
-
- def execute(self, *args):
- try:
- response = self.handler(*args)
- for patcher in patchers:
- response = patcher(response, args[0])
-
- return response
-
- except Exception as e:
- print(e)
- return error_page(500)
-
- def matches(self, request: 'Request') -> bool:
- if not self.method_is_allowed(request.method): return False
- return self.matcher(request.path)
-
-routes = [
- Route(
- lambda request: request.path == '/',
- [Method.GET, Method.POST],
- lambda request, *_: Response(
- ResponseCode.OK,
- {'Content-Type': 'text/html'},
- (parse_file('./home.html', dict(prev='\\/')).encode('utf-8') if request.method == Method.GET else (
- [
- (lambda form_data: (
- (lambda time: (
- print('\n\nFORM DATA!!!!',form_data,request, '\n\n'),
- f:=open(f'./files/posts-to-homepage/post_{time}.txt', 'w'),
- f.write(f"{form_data['name']}@{time}
{form_data['text']}
"),
- f.close()
- ))(datetime.now().strftime('%Y-%m-%d_%H:%M:%S-%f')[:-3]) if set(form_data.keys()) == set(['text', 'name']) else None
- ))(
- reduce(
- lambda acc, d: acc.update(d) or acc,
- map(lambda key_value_pair: {key_value_pair[0]: remove_html_tags(key_value_pair[1])}, request.body.data.items()),
- {}
- )),
- parse_file('./home.html').encode('utf-8')
- ][1]
- ))
- ) if len(request.body.data) > 0 or request.method != Method.POST else error_page(ResponseCode.BAD_REQUEST)
- ),
- Route(
- lambda path: os.path.isdir('.' + path.path),
- [Method.GET],
- lambda request, *_: Response(
- ResponseCode.OK,
- {'Content-Type': 'text/html'},
- parse_file('./dir_index.html', dict(path='.' + request.path.path, prev=request.headers.get('Referer').replace('/', '\\/') if request.headers.has('Referer') else '')).encode('utf-8')
- )
- ),
- Route(
- lambda path: os.path.isfile('.' + path.path) and path.path.startswith('/html/') and (path.path.endswith('.html') or '/thoughts/' in path.path),
- [Method.GET],
- lambda request, *_: Response(
- ResponseCode.OK,
- {'Content-Type': 'text/html'},
- parse_file('.' + request.path.path, dict(prev=request.headers.get('Referer').replace('/', '\\/') if request.headers.has('Referer') else '')).encode('utf-8')
- )
- ),
- Route(
- lambda path: os.path.isfile('.' + path.path) and (path.path.startswith('/font/') or path.path.startswith('/files/')),
- [Method.GET],
- lambda request, *_: Response(
- ResponseCode.OK,
- *raw_file_contents('.' + request.path.path)
- )
- ),
- Route(
- lambda request: request.path == '/status',
- [Method.GET],
- lambda *_: Response(
- ResponseCode.OK,
- {'Content-Type': 'text/html'},
- parse('$[neofetch | ansi2html]').encode('utf-8')
- )
- ),
- Route(
- lambda request: request.path == '/stats/is-its-computer-online',
- [Method.GET],
- lambda *_: Response(
- ResponseCode.OK,
- {'Content-Type': 'text/html'},
- page("online-p", """
- seconds since last heartbeat message (less than 60: online; less than 120: maybe; more than 120: probably not): $[echo $(( $(date +%s) - $(stat -c %Y ./files/stats/heartbeat) ))]
- """)
- )
- ),
- Route(
- lambda request: request.path == '/stats/what-song-is-it-listening-to',
- [Method.GET],
- lambda *_: Response(
- ResponseCode.OK,
- {'Content-type': 'text/html'},
- page("song?", """
- it is listening to $[cat ./files/stats/song] as of $[echo $(( $(date +%s) - $(stat -c %Y ./files/stats/song) ))] seconds ago.
- """)
- )
- ),
- Route(
- lambda request: request.path == '/stats/is-this-server-online',
- [Method.GET],
- lambda *_: Response(
- ResponseCode.OK,
- {'Content-type': 'text/html'},
- page("server online-p", """
- I think so.
- """)
- )
- ),
- Route(
- lambda request: request.path == '/stats/what-is-its-servers-uptime',
- [Method.GET],
- lambda *_: Response(
- ResponseCode.OK,
- {'Content-type': 'text/html'},
- page("uptime", """
- $[uptime]
- """)
- )
- ),
- Route(
- lambda request: request.path == '/stats',
- [Method.GET],
- lambda request, *_: Response(
- ResponseCode.OK,
- {'Content-Type': 'text/html'},
- parse_file('./html/stats.html', dict(prev=request.headers.get('Referer').replace('/', '\\/') if request.headers.has('Referer') else '')).encode('utf-8')
- )
- ),
- Route(
- lambda _: True,
- [Method.GET],
- lambda *_: error_page(404)
- )
-]
-
diff --git a/src/lib/server.py b/src/lib/server.py
deleted file mode 100644
index 587a960..0000000
--- a/src/lib/server.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import socket
-import threading
-from typing import Callable
-
-def serve(address: str, port: int, callback: Callable, wrapper: Callable[[socket.socket], socket.socket] = lambda s: s) -> None:
- server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- try:
- server_socket.bind((address, port))
- server_socket.listen(1)
- server_socket = wrapper(server_socket)
-
- while True:
- conn, addr = server_socket.accept()
- client_connection = threading.Thread(target=callback, args=(conn, addr))
- client_connection.start()
-
- finally: server_socket.close()
diff --git a/src/main.py b/src/main.py
deleted file mode 100644
index 136fc33..0000000
--- a/src/main.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from lib import Request, serve
-from typing import Tuple
-import threading
-import socket
-import ssl
-import os
-
-os.chdir('..')
-
-def handle_client(client: socket.socket, addr: Tuple[str, int]) -> None:
- request = bytes()
-
- while (data := client.recv(1024)):
- request += data
- print(len(data), data)
-
- if len(data) < 1024: break
-
- (request:=Request.from_bytes(request)) \
- .match() \
- .execute(request, client, addr) \
- .send(client)
-
-def main() -> None:
- http_thread = threading.Thread(name='http', target=serve, args=('0.0.0.0', 6000, handle_client))
- https_thread = threading.Thread(name='https', target=serve, args=('0.0.0.0', 6001, handle_client), kwargs=dict(wrapper=lambda socket: [
- ctx:=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER),
- ctx.load_cert_chain(certfile='./badcert.pem', keyfile='./badkey.pem'),
- ctx.wrap_socket(socket, server_side=True)
- ][-1]
- ))
-
- #http_thread.start()
- https_thread.start()
-
-if __name__ == '__main__':
- main()
-