import socketserver import subprocess import os class GopherError(BaseException): pass class RequestError(GopherError): pass print(os.getcwd()) def recieveRequest(context): data = b'' while data[-2:] != b'\r\n': data += context.request.recv(1) decoded = data[:-2].decode('utf-8') return decoded def requestParser(request): if '..' in request: raise RequestError try: if request[0] == '/': request = request[1:] except IndexError: pass request = request.replace('?', '\t').split('\t') try: return (request[0], request[1]) except IndexError: return (request[0], "") def returnRelative(file): gph = False if file == "": ret = "gophermap" gph = True elif os.path.isdir(file): ret = file + "/gophermap" gph = True elif os.path.isfile(file): ret = file else: raise RequestError('unreachable state') return (ret, gph) def fileSendable(file): return os.access(file, os.F_OK|os.R_OK) def fileCGI(file): return os.access(file, os.F_OK|os.R_OK|os.X_OK) def notFound(context): context.request.sendall(b"3error: file not found!\r\n.\r\n") def invalid(context): context.request.sendall(b"3error: selector contains '..'!\r\n.\r\n") def sendFile(file, context): with open(file, "r") as fd: sendFileArray(fd.readlines(), context) def sendFileArray(arr, context): [context.request.sendall( (i.rstrip() + "\r\n").encode("utf-8") ) for i in arr] context.request.sendall(b".\r\n") def cgi(file, query, context): env = {} env['QUERY_STRING'] = query env['SCRIPT_NAME'] = "/" + file env['REMOTE_ADDR'] = context.client_address[0] proc = subprocess.Popen( [os.getcwd() + "/" + file], stdout = subprocess.PIPE, env = env ) try: out, err = proc.communicate(timeout=10) except TimeoutExpired: proc.kill() out, err = proc.communicate() return out.decode('utf-8').replace('\r', '').split('\n') class gopherHandler(socketserver.BaseRequestHandler): def handle(self): decoded = recieveRequest(self) try: parsed = requestParser(decoded) except RequestError: invalid(self) return try: file = returnRelative(parsed[0]) except RequestError: notFound(self) return if fileCGI(file[0]): sendFileArray(cgi(file[0], parsed[1], self), self) elif fileSendable(file[0]): sendFile(file[0], self) else: notFound(self) if __name__ == "__main__": HOST, PORT = "localhost", 75 with socketserver.TCPServer((HOST, PORT), gopherHandler) as server: server.serve_forever()