diff options
Diffstat (limited to 'main.py')
-rw-r--r-- | main.py | 167 |
1 files changed, 156 insertions, 11 deletions
diff --git a/main.py b/main.py index a92d4f9..2df74e6 100644 --- a/main.py +++ b/main.py @@ -9,8 +9,10 @@ import db import uuid import secrets import time +from urllib.parse import urlparse version = "Helium-0.0.0a" +attachment_whitelist = ["u.cubeupload.com", "files.catbox.moe", "litter.catbox.moe", "i.ibb.co", "cubeupload.com", "media.tenor.com", "tenor.com", "c.tenor.com", "meower.fraudulent.loan", "fraudulent.loan", "deer.fraudulent.loan"] addr = "localhost" port = 3636 @@ -21,15 +23,26 @@ error_contexts = { "malformedJson": "The JSON data sent to the server could not be parsed.", "lengthInvalid": "A value in the JSON data is longer or shorter than expected.", "invalidUsername": "Username is invalid. It may contain characters that are not permitted in usernames.", + "invalidFormat": "Value contains invalid characters, or is too long.", "invalidInvite": "The invite code you are trying to use is invalid or has expired.", "usernameTaken": "This username has been taken.", "notExists": "The requested value does not exist.", "lockdown": "Maintenance is in progress.", - "authed": "You are already authenticated." + "authed": "You are already authenticated.", + "unauthorized": "You must be authorized to perform this action.", + "deprecated": "This command is no longer supported." +} + +deprecated = { + "set_display_name": "set_property", + "set_avatar": "set_property", + "set_bio": "set_property", + "set_lastfm": "set_property" } ulist = {} -user_clients = {} +client_data = {} + clients = [] invite_codes = ["STANLEYYELNATSAB"] @@ -66,7 +79,7 @@ class util: "command": "greet", "version": version, "ulist": ulist, - "messages": [], + "messages": db.posts.get_recent(), "locked": locked }) @@ -82,12 +95,37 @@ class util: del data["profile"] return data - def authorize(username, conn_id, client=""): + def authorize(username, conn_id, websocket, client=""): ulist[username] = client - user_clients[conn_id] = {"username": username, "client": client} + client_data[conn_id] = {"username": username, "client": client, "websocket": websocket} data = db.acc.get(username) del data["secure"] return data + + def loggedout(username, conn_id, websocket): + del client_data[conn_id] + if username in ulist: + rm_user = True + for i in client_data.copy(): + if client_data[i]["username"] == username: + rm_user = False + if rm_user: + del ulist[username] + broadcast(clients, json.dumps({ + "command": "ulist", + "ulist": ulist + })) + + def forcekick(username): + if username in ulist: + del ulist[username] + for i in client_data.copy(): + if client_data[i]["username"] == username: + try: + client_data[i]["websocket"].close() + except: + pass + del client_data[i] async def handler(websocket): clients.append(websocket) @@ -109,7 +147,7 @@ async def handler(websocket): if fc != True: await websocket.send(util.error(fc, listener)) continue - if str(websocket.id) in user_clients: + if str(websocket.id) in client_data: await websocket.send(util.error("authed", listener)) continue if locked: @@ -161,7 +199,7 @@ async def handler(websocket): if fc != True: await websocket.send(util.error(fc, listener)) continue - if str(websocket.id) in user_clients: + if str(websocket.id) in client_data: await websocket.send(util.error("authed", listener)) continue r["username"] = r["username"].lower() @@ -175,7 +213,7 @@ async def handler(websocket): continue valid = db.acc.verify_pswd(r["username"], r["password"]) if type(valid) == dict: - userdata = util.authorize(r["username"], str(websocket.id)) + userdata = util.authorize(r["username"], str(websocket.id), websocket) await websocket.send(json.dumps({"error": False, "token": valid["token"], "user": userdata, "listener": listener})) util.ulist() continue @@ -190,7 +228,7 @@ async def handler(websocket): if fc != True: await websocket.send(util.error(fc, listener)) continue - if str(websocket.id) in user_clients: + if str(websocket.id) in client_data: await websocket.send(util.error("authed", listener)) continue if locked: @@ -202,19 +240,126 @@ async def handler(websocket): await websocket.send(util.error("banned", listener, db.acc.get_ban(valid["username"]))) continue else: - userdata = util.authorize(valid["username"], str(websocket.id)) + userdata = util.authorize(valid["username"], str(websocket.id), websocket) await websocket.send(json.dumps({"error": False, "user": userdata, "listener": listener})) util.ulist() continue else: await websocket.send(util.error(valid, listener)) continue + elif r["command"] == "get_user": + fc = util.field_check({"username": range(1,21)}, r) + if fc != True: + await websocket.send(util.error(fc, listener)) + continue + if str(websocket.id) not in client_data: + await websocket.send(util.error("unauthorized", listener)) + continue + data = db.acc.get(r["username"]) + if type(data) != dict: + await websocket.send(util.error(data, listener)) + continue + del data["secure"] + await websocket.send(json.dumps({"error": False, "user": data, "listener": listener})) + continue + elif r["command"] == "set_property": + fc = util.field_check({"property": range(1,64), "value": range(0,2048)}, r) + if fc != True: + await websocket.send(util.error(fc, listener)) + continue + if str(websocket.id) not in client_data: + await websocket.send(util.error("unauthorized", listener)) + continue + username = client_data[str(websocket.id)]["username"] + if r["property"] in ["bio", "lastfm"]: + result = db.acc.edit({f"profile.{r['property']}": r["value"]}, username) + if result: + await websocket.send(json.dumps({"error": False, "listener": listener})) + continue + else: + await websocket.send(util.error("fail", listener)) + continue + elif r["property"] == "display_name": + if not re.fullmatch("[a-zA-Z0-9-_,:🅱️. ]{1,20}", r["value"]): + await websocket.send(util.error("invalidFormat", listener)) + continue + result = db.acc.edit({r["property"]: r["value"]}, username) + if result: + await websocket.send(json.dumps({"error": False, "listener": listener})) + continue + else: + await websocket.send(util.error("fail", listener)) + continue + elif r["property"] == "avatar": + if not urlparse(r["value"]).hostname in attachment_whitelist: + await websocket.send(util.error("invalidFormat", listener)) + continue + result = db.acc.edit({r["property"]: r["value"]}, username) + if result: + await websocket.send(json.dumps({"error": False, "listener": listener})) + continue + else: + await websocket.send(util.error("fail", listener)) + continue + else: + await websocket.send(util.error("malformedJson", listener)) + continue + elif r["command"] == "get_inbox": + if str(websocket.id) not in client_data: + await websocket.send(util.error("unauthorized", listener)) + continue + data = db.inbox.get_recent() + await websocket.send(json.dumps({"error": False, "inbox": data, "listener": listener})) + elif r["command"] == "post": + fc = util.field_check({"content": range(0,3001), "replies": range(0,4), "attachments": range(0,4)}, r) + if fc != True: + await websocket.send(util.error(fc, listener)) + continue + if str(websocket.id) not in client_data: + await websocket.send(util.error("unauthorized", listener)) + continue + attachments = [] + for i in r["attachments"]: + if urlparse(i).hostname in attachment_whitelist: + attachments.append(i) + if len(r["content"]) == 0 and len(r["attachments"]) == 0: + await websocket.send(util.error("lengthInvalid", listener)) + continue + username = client_data[str(websocket.id)]["username"] + author = util.author_data(username) + replies = [] + for i in r["replies"]: + post = db.posts.get_by_id(i) + if type(post) == dict: + replies.append(post) + data = { + "_id": str(uuid.uuid4()), + "created": round(time.time()), + "content": r["content"], + "replies": replies, + "attachments": attachments, + "author": username + } + posted = db.posts.add(data) + if posted != True: + await websocket.send(util.error(fc, listener)) + continue + data["author"] = author + broadcast(clients, json.dumps({ + "command": "new_post", + "data": data + })) elif r["command"] == "ping": pass + elif r["command"] in deprecated: + await websocket.send(util.error("deprecated", listener, {"replacement": deprecated[r["command"]]})) + continue else: await websocket.send(util.error("malformedJson", listener)) + if str(websocket.id) in client_data: + util.loggedout(client_data[str(websocket.id)]["username"], str(websocket.id), websocket) if websocket in clients: - clients.append(websocket) + clients.remove(websocket) async def main(): async with serve(handler, addr, port) as server: |