diff --git a/clientBlaster.py b/clientBlaster.py index ccd0d50..717d31f 100644 --- a/clientBlaster.py +++ b/clientBlaster.py @@ -44,11 +44,99 @@ heartbeatTime = 600 threadCount = 0 threadCountLock = threading.Lock() +commands = {} + +def commandlineToList(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 + +def listToCommandline(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] + printLock = threading.Lock() def tprint(st): with printLock: print(st) +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 + +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 addThread(): global threadCount with threadCountLock: @@ -138,6 +226,15 @@ class connectionThreadIn(threading.Thread): return connections[self.connectionId]["connection"] return False + def runCommand(self,cmd): + command = False + if not cmd[0] in commands: + return ["error","nonfatal","command_not_found"] + command = commands[cmd[0]] + rtn = command["function"](self,cmd) + time.sleep(pauseBetweenCommands) + return rtn + def run(self): try: while True: @@ -151,14 +248,16 @@ class connectionThreadIn(threading.Thread): with connectionsLock: closeConnection(self.connectionId) return + queue = False with connectionsLock: - if self.connectionId in connections: - queue = connections[self.connectionId]["threadOut"].queue - if queue.qsize() >= maxQueueSize: - closeConnection(self.connectionId) - return - queue.put(data) - time.sleep(pauseBetweenCommands) + queue = connections[self.connectionId]["threadOut"].queue + if queue.qsize() >= maxQueueSize: + closeConnection(self.connectionId) + return + + dataString = data.decode(encoding="utf-8") + commandList = commandlineToList(dataString) + queue.put(listToCommandline(self.runCommand(commandList)).encode(encoding="utf-8")) except Exception as e: with connectionsLock: closeConnection(self.connectionId) with printLock: @@ -169,6 +268,13 @@ class connectionThreadIn(threading.Thread): removeThread() 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) + global connectionsId serverSocket.bind(serverAddr) serverSocket.listen(65535) diff --git a/modules/-[example modules]/parrot text server/module.py b/modules/-[example modules]/parrot text server/module.py deleted file mode 100644 index 68f5364..0000000 --- a/modules/-[example modules]/parrot text server/module.py +++ /dev/null @@ -1,22 +0,0 @@ -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": - with threadsLock: - global close - close = True - - 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 deleted file mode 100644 index 65856b2..0000000 --- a/modules/[text server]/[api]/commands/module.py +++ /dev/null @@ -1,69 +0,0 @@ -global textCommands -textCommands = {} -global textCommandsLock -textCommandsLock = threading.Lock() - -global textCommandRun -def textCommandRun(self,args): - with textCommandsLock: - commands = textCommands.copy() - - 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): - with textCommandsLock: - textCommands[command] = function - -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]/communication/module.py b/modules/[text server]/[api]/communication/module.py deleted file mode 100644 index 3bd9e09..0000000 --- a/modules/[text server]/[api]/communication/module.py +++ /dev/null @@ -1,29 +0,0 @@ -moduleDepends([ - p("[text server]","[api]","commands"), -]) - -global textSend -def textSend(self,command,args): - if len(args) < 2: - return ["error","nonfatal","syntax","Correct syntax: " +command+ ",,,[argument 1],[argument 2],..."] - - user = args[0].lower() - if len(user) < 1: - return ["error","nonfatal","name_too_short","Needs to be at least 1 character in length."] - - me = "" - with self.lock: - me = self.user - - if not me: - return ["error","nonfatal","not_logged_in"] - - with threadsLock: - for threadId in threads: - thread = threads[threadId] - with thread.lock: - if thread.user != user: continue - thread.sendResponse(textListToCommand(["send",me] + args[1:]).encode("utf-8"),lock = False) - - return ["ok"] -textCommandAddHandler("send",textSend) \ No newline at end of file diff --git a/modules/[text server]/[api]/main/module.py b/modules/[text server]/[api]/main/module.py deleted file mode 100644 index c46a31a..0000000 --- a/modules/[text server]/[api]/main/module.py +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 4e99d47..0000000 --- a/modules/[text server]/[api]/requests/module.py +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 366a5df..0000000 --- a/modules/[text server]/[api]/threadedFiles/module.py +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 3c72773..0000000 --- a/modules/[text server]/[api]/users/module.py +++ /dev/null @@ -1,87 +0,0 @@ -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) - - with fileLock: - if os.path.isdir(userpath): - return ["error","nonfatal","user_exists"] - - password = args[1] - - os.makedirs(userpath) - passFile = open(p(userpath,"pass.txt"),"w") - passFile.write(password) - passFile.close() - 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: - return ["error","nonfatal","invalid_name","Allowed characters: " +", ".join([char for char in textUserAllowedCharacters])] - - userpath = textUserGetPath(user) - - with fileLock: - if not os.path.isdir(userpath): - return ["error","nonfatal","wrong_user_or_password"] - - password = args[1] - - passFile = open(p(userpath,"pass.txt"),"r") - passw = passFile.read() - passFile.close() - if password != passw: - return ["error","nonfatal","wrong_user_or_password"] - - with self.lock: - self.user = user - - return ["ok"] -textCommandAddHandler("login",textUserLogin) - -global textUserGet -def textUserGet(self,command,args): - with self.lock: - user = self.user - - if not user: - return ["error","nonfatal","not_logged_in"] - - return ["user",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 deleted file mode 100644 index e5125b2..0000000 --- a/modules/[text server]/[api]/utils/module.py +++ /dev/null @@ -1,4 +0,0 @@ -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]/[commands]/nop/module.py b/modules/[text server]/[commands]/nop/module.py new file mode 100644 index 0000000..3db33bb --- /dev/null +++ b/modules/[text server]/[commands]/nop/module.py @@ -0,0 +1,5 @@ +global commands +commands["nop"] = {} +def f(self,cmd): + return [""] +commands["nop"]["function"] = f \ No newline at end of file diff --git a/modules/[text server]/[commands]/req/module.py b/modules/[text server]/[commands]/req/module.py new file mode 100644 index 0000000..f7693bf --- /dev/null +++ b/modules/[text server]/[commands]/req/module.py @@ -0,0 +1,9 @@ +global commands +commands["req"] = {} +def f(self,cmd): + if len(cmd) < 3: + return ["error","nonfatal","syntax","need at least 3 arguments"] + + rtn = cmd[:2] + self.runCommand(cmd[2:]) + return rtn +commands["req"]["function"] = f \ No newline at end of file diff --git a/modules/[text server]/main/module.py b/modules/[text server]/main/module.py deleted file mode 100644 index b5cc90b..0000000 --- a/modules/[text server]/main/module.py +++ /dev/null @@ -1,51 +0,0 @@ -global textTimeout -textTimeout = 30 -global textKeepAliveTimeout -textKeepAliveTimeout = 600 # set to None 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 <= 100000: 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) - response = textCommandRun(self,textCommandToList(text)) - print("response: " +textListToCommand(response)) - self.sendResponse(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: - self.sendResponse(textListToCommand(["error","fatal","timeout"]).encode("utf-8")) - return - - self.sendResponse(textListToCommand(["error","fatal","unhandled",str(exc)]).encode("utf-8")) -addEventHandler("onException",textOnException) \ No newline at end of file