From 5a394ea2d05ade7e8f2996e4272c947e816d8372 Mon Sep 17 00:00:00 2001 From: Fierelier Date: Fri, 9 Apr 2021 15:16:29 +0200 Subject: [PATCH] first commit --- .gitignore | 1 + clientBlaster.py | 209 ++++++++++++++++++ .../-parrot text server/module.py | 23 ++ .../[text server]/[api]/commands/module.py | 71 ++++++ modules/[text server]/[api]/main/module.py | 5 + .../[text server]/[api]/requests/module.py | 10 + .../[api]/threadedFiles/module.py | 2 + modules/[text server]/[api]/users/module.py | 92 ++++++++ modules/[text server]/[api]/utils/module.py | 4 + modules/[text server]/main/module.py | 60 +++++ parrot text client.py | 61 +++++ serverBlaster.py | 61 +++++ 12 files changed, 599 insertions(+) create mode 100644 .gitignore create mode 100644 clientBlaster.py create mode 100644 modules/[example modules]/-parrot text server/module.py create mode 100644 modules/[text server]/[api]/commands/module.py create mode 100644 modules/[text server]/[api]/main/module.py create mode 100644 modules/[text server]/[api]/requests/module.py create mode 100644 modules/[text server]/[api]/threadedFiles/module.py create mode 100644 modules/[text server]/[api]/users/module.py create mode 100644 modules/[text server]/[api]/utils/module.py create mode 100644 modules/[text server]/main/module.py create mode 100644 parrot text client.py create mode 100644 serverBlaster.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36f3148 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/textServer/ \ No newline at end of file diff --git a/clientBlaster.py b/clientBlaster.py new file mode 100644 index 0000000..530f193 --- /dev/null +++ b/clientBlaster.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +import sys + +oldexcepthook = sys.excepthook +def newexcepthook(type,value,traceback): + oldexcepthook(type,value,traceback) + input("Press ENTER to quit.") +sys.excepthook = newexcepthook + +import os +p = os.path.join +pUp = os.path.dirname +s = False +if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): + s = os.path.realpath(sys.executable) +else: + s = os.path.realpath(__file__) +sp = pUp(s) + +# script start +import threading +import socket +import struct + +addr = ("127.0.0.1",21779) + +threads = {} +threadId = 0 +threadsLock = threading.Lock() +close = False + +eventHandlers = {} +eventHandlersLock = threading.Lock() + +def runCode(str, lcs = False, description = "loose-code"): + if lcs == False: lcs = {} + code = compile(str,description,"exec") + exec(code,globals(),lcs) + return lcs + +def runScript(sf, lcs = False): + if lcs == False: lcs = {} + with open(sf) as script: + runCode(script.read(),lcs,sf) + return lcs + +def getModlist(path): + modList = [] + for root,dirs,files in os.walk(path): + for file in dirs: + ffile = p(root,file) + lfile = ffile.replace(path + os.path.sep,"",1) + if lfile[0] == "-": continue + if lfile[0] == "[" and lfile[-1] == "]": + modList = modList + sorted(getModlist(ffile)) + continue + + modList.append(ffile) + break + + return modList + +def triggerEvent(event,*args,**kwargs): + eventHandlersLock.acquire() + handlers = eventHandlers.copy() + eventHandlersLock.release() + + if not event in handlers: return + for func in handlers[event]: + cancel = func(event,*args,**kwargs) + if cancel: return True + + return False + +def addEventHandler(event,func): + eventHandlersLock.acquire() + if not event in eventHandlers: eventHandlers[event] = [] + eventHandlers[event].append(func) + eventHandlersLock.release() + +def sendResponse(connection,data): + connection.sendall(len(data).to_bytes(4,"big") + data) + +class connectionThread(threading.Thread): + global threadsLock + + def __init__(self,threadId,connection,address): + threading.Thread.__init__(self) + self.threadId = threadId + self.connection = connection + self.address = address + self.closed = False + self.user = False + self.lock = threading.Lock() + + def closeThread(self): + self.lock.acquire() + threadsLock.acquire() + try: + self.connection.close() + except: + print("failed to close connection, ignoring.") + pass + + del threads[str(self.threadId)] + print("thread closed: " +str(self.threadId)+ " (open: " +str(len(threads))+ ")") + self.closed = True + threadsLock.release() + self.lock.release() + + def run(self): + self.lock.acquire() + # inform about connection + print("thread opened: " +", ".join((str(self.threadId),str(self.address)))) + self.lock.release() + + while True: + try: + # get request length + data = b'' + data = self.connection.recv(4) + + if not data: + self.closeThread() + return + + requestLength = int.from_bytes(data,"big") + + # inform about request + cancel = triggerEvent("onPreRequest",self,requestLength) + self.lock.acquire() + if self.closed: + self.lock.release() + return + self.lock.release() + if cancel: continue + + # process request + cancel = triggerEvent("onRequest",self,requestLength) + self.lock.acquire() + if self.closed: + self.lock.release() + return + self.lock.release() + if cancel: continue + except Exception as e: + #self.lock.release() - fix this + cancel = False + try: + cancel = triggerEvent("onException",self,e) + except: + self.closeThread() + raise + + if cancel: continue + self.closeThread() + raise e + +modulesLoaded = [] +modulePath = p(sp,"modules") +def moduleRun(localModule): + if not localModule in modulesLoaded: modulesLoaded.append(localModule) + print("> " +localModule+ "...") + runScript(p(modulePath,localModule,"module.py")) + +def moduleDepends(localModules): + if type(localModules) == str: localModules = [localModules] + + for localModule in localModules: + if localModule in modulesLoaded: return + print("depend ",end="") + moduleRun(localModule) + +def main(): + print("Loading modules...") + for path in getModlist(modulePath): + if os.path.isfile(p(path,"module.py")): + localModule = path.replace(modulePath + os.path.sep,"",1) + if not localModule in modulesLoaded: + moduleRun(localModule) + + print("\nServing on " +":".join(map(str,addr))+ "!") + + global socketServer + socketServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + socketServer.bind(addr) + socketServer.listen(1000) + + global threadId + global close + while True: + connection, address = socketServer.accept() + threadsLock.acquire() + if close: threadsLock.release(); break + cancel = triggerEvent("onConnect",connection,address) + if close: threadsLock.release(); break + if cancel: continue + + threadId += 1 + while str(threadId) in threads: + threadId += 1 + + thread = connectionThread(threadId,connection,address) + threads[str(threadId)] = thread + thread.start() + threadsLock.release() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/modules/[example modules]/-parrot text server/module.py b/modules/[example modules]/-parrot text server/module.py new file mode 100644 index 0000000..d7bbd8f --- /dev/null +++ b/modules/[example modules]/-parrot text server/module.py @@ -0,0 +1,23 @@ +global textPreRequest +def textPreRequest(event,self,requestLength): + if requestLength <= 128: return + sendResponse(self.connection,"error: too long".encode("utf-8")) + self.closeThread() + return True +addEventHandler("onPreRequest",textPreRequest) + +global textRequest +def textRequest(event,self,requestLength): + data = self.connection.recv(requestLength) + sendResponse(self.connection,data) + text = data.decode("utf-8") + print(":".join(map(str,self.address))+ " > " +text) + if text == "exit": + threadsLock.acquire() + global close + close = True + threadsLock.release() + + self.closeThread() + return True +addEventHandler("onRequest",textRequest) \ No newline at end of file diff --git a/modules/[text server]/[api]/commands/module.py b/modules/[text server]/[api]/commands/module.py new file mode 100644 index 0000000..7db0040 --- /dev/null +++ b/modules/[text server]/[api]/commands/module.py @@ -0,0 +1,71 @@ +global textCommands +textCommands = {} +global textCommandsLock +textCommandsLock = threading.Lock() + +global textCommandRun +def textCommandRun(self,args): + textCommandsLock.acquire() + commands = textCommands.copy() + textCommandsLock.release() + + if not args[0] in commands: + return ["error","nonfatal","command_not_found"] + + return commands[args[0]](self,args[0],args[1:]) + +global textCommandAddHandler +def textCommandAddHandler(command,function): + textCommandsLock.acquire() + textCommands[command] = function + textCommandsLock.release() + +global textCommandToList +def textCommandToList(cmd): + args = [] + cArg = "" + escape = False + quoted = False + for letter in cmd: + if escape == True: + cArg += letter + escape = False + continue + + if letter == "\\": + escape = True + continue + + #if quoted == False and letter == ",": + if letter == ",": + if cArg == "": continue + args.append(cArg) + cArg = "" + continue + + #if letter == '"': + # quoted = not quoted + # continue + + cArg += letter + + args.append(cArg) + + return args + +global textListToCommand +def textListToCommand(lst): + cmd = "" + for arg in lst: + arg = arg.replace("\\","\\\\") + arg = arg.replace(",","\\,") + #arg = arg.replace('"','\\"') + #if " " in arg: arg = '"' +arg+ '"' + cmd += arg + "," + + return cmd[:-1] + +global textNop +def textNop(self,command,args): + return [""] +textCommandAddHandler("nop",textNop) \ No newline at end of file diff --git a/modules/[text server]/[api]/main/module.py b/modules/[text server]/[api]/main/module.py new file mode 100644 index 0000000..c46a31a --- /dev/null +++ b/modules/[text server]/[api]/main/module.py @@ -0,0 +1,5 @@ +moduleDepends(p("[text server]","[api]","utils")) + +global textBaseFolder +textBaseFolder = p(sp,"textServer") +textQuickFolder(textBaseFolder) \ No newline at end of file diff --git a/modules/[text server]/[api]/requests/module.py b/modules/[text server]/[api]/requests/module.py new file mode 100644 index 0000000..4e99d47 --- /dev/null +++ b/modules/[text server]/[api]/requests/module.py @@ -0,0 +1,10 @@ +moduleDepends(p("[text server]","[api]","commands")) + +global textRequest +def textRequest(self,command,args): + if len(args) < 2: + return ["error","nonfatal","syntax","Correct syntax: " +command+ ",,,[arg1],[arg2],..."] + + response = [command,args[0]] + textCommandRun(self,args[1:]) + return response +textCommandAddHandler("req",textRequest) \ No newline at end of file diff --git a/modules/[text server]/[api]/threadedFiles/module.py b/modules/[text server]/[api]/threadedFiles/module.py new file mode 100644 index 0000000..366a5df --- /dev/null +++ b/modules/[text server]/[api]/threadedFiles/module.py @@ -0,0 +1,2 @@ +global fileLock +fileLock = threading.Lock() \ No newline at end of file diff --git a/modules/[text server]/[api]/users/module.py b/modules/[text server]/[api]/users/module.py new file mode 100644 index 0000000..bc532a3 --- /dev/null +++ b/modules/[text server]/[api]/users/module.py @@ -0,0 +1,92 @@ +moduleDepends([ + p("[text server]","[api]","commands"), + p("[text server]","[api]","threadedFiles") +]) + +global textUserFolder +textUserFolder = p(textBaseFolder,"users") +textQuickFolder(textUserFolder) + +global textUserAllowedCharacters +textUserAllowedCharacters = "abcdefghijklmnopqrstuvwxyz0123456789.-_ " + +global textUserGetPath +def textUserGetPath(user): + return p(textUserFolder,user) + +global textUserRegister +def textUserRegister(self,command,args): + if len(args) != 2: + return ["error","nonfatal","syntax","Correct syntax: " +command+ ",,"] + + user = args[0].lower() + if len(user) < 1: + return ["error","nonfatal","name_too_short","Needs to be at least 1 character in length."] + + for symbol in user: + if not symbol in textUserAllowedCharacters: + return ["error","nonfatal","invalid_name","Allowed characters: " +", ".join([char for char in textUserAllowedCharacters])] + + userpath = textUserGetPath(user) + fileLock.acquire() + + if os.path.isdir(userpath): + fileLock.release() + return ["error","nonfatal","user_exists"] + + password = args[1] + + os.makedirs(userpath) + passFile = open(p(userpath,"pass.txt"),"w") + passFile.write(password) + passFile.close() + fileLock.release() + return ["ok"] +textCommandAddHandler("register",textUserRegister) + +global textUserLogin +def textUserLogin(self,command,args): + if len(args) != 2: + return ["error","nonfatal","syntax","Correct syntax: " +command+ ",,"] + + user = args[0].lower() + if len(user) < 1: + return ["error","nonfatal","name_too_short","Needs to be at least 1 character in length."] + + for symbol in user: + if not symbol in textUserAllowedCharacters: + fileLock.release() + return ["error","nonfatal","invalid_name","Allowed characters: " +", ".join([char for char in textUserAllowedCharacters])] + + userpath = textUserGetPath(user) + fileLock.acquire() + + if not os.path.isdir(userpath): + fileLock.release() + return ["error","nonfatal","wrong_user_or_password"] + + password = args[1] + + passFile = open(p(userpath,"pass.txt"),"r") + passw = passFile.read() + passFile.close() + fileLock.release() + if password != passw: + return ["error","nonfatal","wrong_user_or_password"] + + self.lock.acquire() + self.user = user + self.lock.release() + return ["ok"] +textCommandAddHandler("login",textUserLogin) + +global textUserGet +def textUserGet(self,command,args): + self.lock.acquire() + user = self.user + self.lock.release() + if not user: + return ["error","nonfatal","not_logged_in"] + + return [user] +textCommandAddHandler("whoami",textUserGet) \ No newline at end of file diff --git a/modules/[text server]/[api]/utils/module.py b/modules/[text server]/[api]/utils/module.py new file mode 100644 index 0000000..e5125b2 --- /dev/null +++ b/modules/[text server]/[api]/utils/module.py @@ -0,0 +1,4 @@ +global textQuickFolder +def textQuickFolder(path): + if not os.path.isdir(path): + os.makedirs(path) \ No newline at end of file diff --git a/modules/[text server]/main/module.py b/modules/[text server]/main/module.py new file mode 100644 index 0000000..f7b755b --- /dev/null +++ b/modules/[text server]/main/module.py @@ -0,0 +1,60 @@ +global textTimeout +textTimeout = 30 +global textKeepAliveTimeout +textKeepAliveTimeout = 600 # set to 0 for infinite time (not recommended) + +global textOnConnect +def textOnConnect(event,connection,address): + global textKeepAliveTimeout + connection.settimeout(textKeepAliveTimeout) +addEventHandler("onConnect",textOnConnect) + +global textOnPreRequest +def textOnPreRequest(event,self,requestLength): + global textTimeout + self.connection.settimeout(textTimeout) + if requestLength <= 128: return + try: + sendResponse(self.connection,textListToCommand(["error","fatal","request_too_long"]).encode("utf-8")) + except threading.timeout: + pass + + self.closeThread() + return True +addEventHandler("onPreRequest",textOnPreRequest) + +global textOnRequest +def textOnRequest(event,self,requestLength): + global textTimeout + global textKeepAliveTimeout + + self.connection.settimeout(textTimeout) + data = self.connection.recv(requestLength) + + text = data.decode("utf-8") + print(":".join(map(str,self.address))+ " > " +text) + if text == "close": + threadsLock.acquire() + global close + close = True + threadsLock.release() + + self.closeThread() + return True + + response = textCommandRun(self,textCommandToList(text)) + print("response: " +textListToCommand(response)) + sendResponse(self.connection,textListToCommand(response).encode("utf-8")) + + self.connection.settimeout(textKeepAliveTimeout) +addEventHandler("onRequest",textOnRequest) + +global textOnException +def textOnException(event,self,exc): + self.connection.settimeout(textTimeout) + if type(exc) == socket.timeout: + sendResponse(self.connection,textListToCommand(["error","fatal","timeout"]).encode("utf-8")) + return + + sendResponse(self.connection,textListToCommand(["error","fatal","unhandled",str(exc)]).encode("utf-8")) +addEventHandler("onException",textOnException) \ No newline at end of file diff --git a/parrot text client.py b/parrot text client.py new file mode 100644 index 0000000..c6a403a --- /dev/null +++ b/parrot text client.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import sys + +oldexcepthook = sys.excepthook +def newexcepthook(type,value,traceback): + oldexcepthook(type,value,traceback) + input("Press ENTER to quit.") +sys.excepthook = newexcepthook + +import os +p = os.path.join +pUp = os.path.dirname +s = False +if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): + s = os.path.realpath(sys.executable) +else: + s = os.path.realpath(__file__) +sp = pUp(s) + +# script start +import socket + +def sendRequest(connection,data): + connection.sendall(len(data).to_bytes(4,"big") + data) + +def getResponse(connection): + data = b'' + data = connection.recv(4) + + if not data: + connection.close() + return + + requestLength = int.from_bytes(data,"big") + data = connection.recv(requestLength) + return data + +def main(): + global connection + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + connection.connect(("127.0.0.1",21779)) + while True: + text = input("data: ") + data = text.encode("utf-8") + sendRequest(connection,data) + response = getResponse(connection).decode("utf-8") + print("server: " +response) + + if text == "exit": + connection.close() + break + + if text == "close": + connection.close() + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + connection.connect(("127.0.0.1",21779)) + connection.close() + break + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/serverBlaster.py b/serverBlaster.py new file mode 100644 index 0000000..c6a403a --- /dev/null +++ b/serverBlaster.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import sys + +oldexcepthook = sys.excepthook +def newexcepthook(type,value,traceback): + oldexcepthook(type,value,traceback) + input("Press ENTER to quit.") +sys.excepthook = newexcepthook + +import os +p = os.path.join +pUp = os.path.dirname +s = False +if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): + s = os.path.realpath(sys.executable) +else: + s = os.path.realpath(__file__) +sp = pUp(s) + +# script start +import socket + +def sendRequest(connection,data): + connection.sendall(len(data).to_bytes(4,"big") + data) + +def getResponse(connection): + data = b'' + data = connection.recv(4) + + if not data: + connection.close() + return + + requestLength = int.from_bytes(data,"big") + data = connection.recv(requestLength) + return data + +def main(): + global connection + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + connection.connect(("127.0.0.1",21779)) + while True: + text = input("data: ") + data = text.encode("utf-8") + sendRequest(connection,data) + response = getResponse(connection).decode("utf-8") + print("server: " +response) + + if text == "exit": + connection.close() + break + + if text == "close": + connection.close() + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + connection.connect(("127.0.0.1",21779)) + connection.close() + break + +if __name__ == '__main__': + main() \ No newline at end of file