From cdcc03d0bf2c15237f65e3820ff3f7e67f03b1ac Mon Sep 17 00:00:00 2001 From: Fierelier Date: Mon, 14 Feb 2022 22:43:12 +0100 Subject: [PATCH] First commit --- fsockets.py | 90 ++ index/index.html | 6 + index/secret folder/.fhtpyaccess | 2 + .../another secret folder/index.html | 6 + index/test/index.pyp | 33 + modules/clients.py | 95 ++ modules/connlimit.py | 11 + modules/events.py | 30 + modules/exceptions.py | 19 + modules/helpers.py | 15 + modules/http/404.py | 8 + modules/http/file-handlers/binary.py | 55 + modules/http/file-handlers/htaccess.py | 18 + modules/http/file-handlers/mimetypes.py | 1180 +++++++++++++++++ modules/http/file-handlers/pyp.py | 17 + modules/http/file-handlers/text.py | 19 + modules/http/helpers.py | 211 +++ modules/http/main.mods | 11 + modules/http/main.py | 85 ++ modules/http/settings.py | 6 + modules/main.mods | 8 + modules/servers.py | 45 + modules/settings.py | 17 + 23 files changed, 1987 insertions(+) create mode 100644 fsockets.py create mode 100644 index/index.html create mode 100644 index/secret folder/.fhtpyaccess create mode 100644 index/secret folder/another secret folder/index.html create mode 100644 index/test/index.pyp create mode 100644 modules/clients.py create mode 100644 modules/connlimit.py create mode 100644 modules/events.py create mode 100644 modules/exceptions.py create mode 100644 modules/helpers.py create mode 100644 modules/http/404.py create mode 100644 modules/http/file-handlers/binary.py create mode 100644 modules/http/file-handlers/htaccess.py create mode 100644 modules/http/file-handlers/mimetypes.py create mode 100644 modules/http/file-handlers/pyp.py create mode 100644 modules/http/file-handlers/text.py create mode 100644 modules/http/helpers.py create mode 100644 modules/http/main.mods create mode 100644 modules/http/main.py create mode 100644 modules/http/settings.py create mode 100644 modules/main.mods create mode 100644 modules/servers.py create mode 100644 modules/settings.py diff --git a/fsockets.py b/fsockets.py new file mode 100644 index 0000000..aca0123 --- /dev/null +++ b/fsockets.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +import sys +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 +import threading +import queue + +# IMPORTANT! Obtain locks in this order, if you need multiple at once: +# - clientDataLock +# - clientsLock +# - serverThreadsLock +# - fileLock +modulePath = p(sp,"modules") + +mainQueue = queue.Queue() +clientsLock = threading.Lock() +clientID = 0 +clients = {} +clientDataLock = threading.Lock() +clientData = {} +serverThreadsLock = threading.Lock() +serverThreads = [] +fileLock = 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 = {} + + code = False + with fileLock: + with open(sf,"r",encoding="utf-8") as script: + code = script.read() + + runCode(code,lcs,sf) + return lcs + +def readModuleFile(path): + with open(path,"r",encoding="utf-8") as modulesFile: + for line in modulesFile: + line = line.split("#",1)[0].strip(" \t\r\n") + if line == "": continue + modType = line.rsplit(".",1)[-1].lower() + line = line.replace("\\","/") + + if modType == "mods": + print(">> " +line+ " <<") + else: + print("> " +line+ " ...") + + line = line.replace("/",os.path.sep) + if line.startswith("." +os.path.sep): + line = pUp(path) + line[1:] + else: + line = p(modulePath,line) + + if modType == "py": + runScript(line,locals()) + + if modType == "mods": + readModuleFile(line) + +def main(): + if os.path.isfile(p(modulePath,"main.mods")): + print("Loading modules...") + print(">> main.mods <<") + readModuleFile(p(modulePath,"main.mods")) + print("OK.\n") + + with serverThreadsLock: + for server in servers: + makeServer(*server) + + print("Serving!\n") + +main() \ No newline at end of file diff --git a/index/index.html b/index/index.html new file mode 100644 index 0000000..e1326f7 --- /dev/null +++ b/index/index.html @@ -0,0 +1,6 @@ + + + +

Hello world!

+ + \ No newline at end of file diff --git a/index/secret folder/.fhtpyaccess b/index/secret folder/.fhtpyaccess new file mode 100644 index 0000000..b1df8ad --- /dev/null +++ b/index/secret folder/.fhtpyaccess @@ -0,0 +1,2 @@ +env["handler"] = handle404 # Always pretend as if the file wasn't found +env["htaccessPropagate"] = False # Do not read .fhtpyaccess files in sub-folders \ No newline at end of file diff --git a/index/secret folder/another secret folder/index.html b/index/secret folder/another secret folder/index.html new file mode 100644 index 0000000..33a5d16 --- /dev/null +++ b/index/secret folder/another secret folder/index.html @@ -0,0 +1,6 @@ + + + +

You shouldn't be able to access this page.

+ + \ No newline at end of file diff --git a/index/test/index.pyp b/index/test/index.pyp new file mode 100644 index 0000000..bad766d --- /dev/null +++ b/index/test/index.pyp @@ -0,0 +1,33 @@ +global json +import json + +response = ''' + + + + +''' + +for key in env: + value = str(env[key]) + if value == "": value = " " + response += "" +html.escape(key)+ ":
\n" +html.escape(value)+ "
\n" +response += "" + +simpleResponse( + env["self"].connection,"200 OK", + { + "Content-Type": "text/html; charset=UTF-8", + },(response).encode("utf-8") +) \ No newline at end of file diff --git a/modules/clients.py b/modules/clients.py new file mode 100644 index 0000000..567909a --- /dev/null +++ b/modules/clients.py @@ -0,0 +1,95 @@ +global clientThreadIn +class clientThreadIn(threading.Thread): + def __init__(self,cID,connection,address): + threading.Thread.__init__(self) + self.cID = cID + self.connection = connection + self.address = address + + def run(self): + try: + clientLoopIn(self) + except Exception as e: + handleException(e) + + with clientDataLock: + with clientsLock: + closeClient(self.cID,0) + +global clientThreadOut +class clientThreadOut(threading.Thread): + def __init__(self,cID,connection,address): + threading.Thread.__init__(self) + self.cID = cID + self.connection = connection + self.address = address + + def run(self): + try: + clientLoopOut(self) + except Exception as e: + handleException(e) + + with clientDataLock: + with clientsLock: + closeClient(self.cID,1) + +global closeClient +def closeClient(cID,threadType = None): + try: # Close connection + clients[cID][0].close() + except: + pass + + try: # Set reference of connection to false, to denote the client is to not be served + clients[cID][0] = False + except: + pass + + try: # Set reference of the thread to false, to denote that it is closed + if threadType != None: + clients[cID][1 + threadType] = False + except: + pass + + try: # Get rid of leftover data to free memory + if clients[cID] == [False,False,False]: + del clients[cID] + del clientData[cID] + except: + pass + +global setClientData +def setClientData(cID,key,data): + clientData[cID][key] = data + +global getClientData +def getClientData(cID,key): + if not key in clientData[cID]: return None + return clientData[cID][key] + +def main(): + def onConnectionEvent(event,eEnv,connection,address): + with clientDataLock: + with clientsLock: + global clientID + clientID += 1 + cID = str(clientID) + threadIn = clientThreadIn(cID,connection,address) + threadOut = False + if enableOutThread: + threadOut = clientThreadOut(cID,connection,address) + clients[cID] = [connection,threadIn,threadOut] + clientData[cID] = {"address":address} + threadIn.start() + if enableOutThread: + threadOut.start() + + if clientDebug: + print("---") + print("Clients: " +str(len(clients))) + print("Threads: " +str(threading.active_count())) + return True + addEventHandler("onConnection",onConnectionEvent) + +main() \ No newline at end of file diff --git a/modules/connlimit.py b/modules/connlimit.py new file mode 100644 index 0000000..0259902 --- /dev/null +++ b/modules/connlimit.py @@ -0,0 +1,11 @@ +def main(): + def onConnectionEvent(event,eEnv,connection,address): + count = 0 + with clientDataLock: + for cID in clientData: + if getClientData(cID,"address") == address: + count += 1 + if count >= maxConnections: return False + return True + addEventHandler("onConnection",onConnectionEvent) +main() \ No newline at end of file diff --git a/modules/events.py b/modules/events.py new file mode 100644 index 0000000..6c17f80 --- /dev/null +++ b/modules/events.py @@ -0,0 +1,30 @@ +global eventHandlers +eventHandlers = {} + +global addEventHandler +def addEventHandler(event,handler): + if not event in eventHandlers: eventHandlers[event] = [] + try: + eventHandlers[event].remove(handler) + except: + pass + eventHandlers[event].append(handler) + +global removeEventHandler +def removeEventHandler(event,handler): + if not event in eventHandlers: return + try: + eventHandlers[event].remove(handler) + except: + pass + if len(eventHandlers[event]) == 0: + del eventHandlers[event] + +global triggerEvent +def triggerEvent(event,*args,eEnv=False,**kwargs): + if not eEnv: eEnv = {} + if not event in eventHandlers: return + for func in eventHandlers[event]: + result = func(event,eEnv,*args,**kwargs) + if result == False: return False + return True \ No newline at end of file diff --git a/modules/exceptions.py b/modules/exceptions.py new file mode 100644 index 0000000..f15dcf4 --- /dev/null +++ b/modules/exceptions.py @@ -0,0 +1,19 @@ +global traceback +import traceback + +global excConnectionClosed +class excConnectionClosed(Exception): pass + +global handleException +def handleException(e): + try: + if printExceptions: + print(traceback.format_exc()) + except: + try: + print(e) + except: + try: + print("Printing exception failed!") + except: + pass \ No newline at end of file diff --git a/modules/helpers.py b/modules/helpers.py new file mode 100644 index 0000000..50013fe --- /dev/null +++ b/modules/helpers.py @@ -0,0 +1,15 @@ +global time +import time + +global recv +def recv(conn,l): + start = time.process_time() + timeo = conn.gettimeout() + bytes = b"" + while l > 0: + b = conn.recv(l) + if b == b"": raise ConnectionResetError + if time.process_time() - start > timeo: raise TimeoutError + bytes += b + l -= len(b) + return bytes \ No newline at end of file diff --git a/modules/http/404.py b/modules/http/404.py new file mode 100644 index 0000000..fc9a598 --- /dev/null +++ b/modules/http/404.py @@ -0,0 +1,8 @@ +global handle404 +def handle404(env): + newPath = "/" + pathToURL(env["pathFixed"]) + rawArgs = env["protocolHeaderList"][1].split("?",1) + + if len(rawArgs) > 1: + newPath += "?" +rawArgs[-1] + notFound(env["self"].connection,newPath) \ No newline at end of file diff --git a/modules/http/file-handlers/binary.py b/modules/http/file-handlers/binary.py new file mode 100644 index 0000000..6de361f --- /dev/null +++ b/modules/http/file-handlers/binary.py @@ -0,0 +1,55 @@ +global handleBinary +def handleBinary(env): + filePath = env["fPath"] + fileExt = env["fileExt"] + connection = env["self"].connection + length = 0 + with fileLock: + length = os.path.getsize(filePath) + + rangeDefined = False + rangeStart = 0 + rangeEnd = None + if "range" in env["headerList"]: + rangeDefined = True + rangeStart,rangeEnd = getRange(env["headerList"]["range"]) + + rangeStart,rangeEnd = convertRanges(rangeStart,rangeEnd,length) + if rangeStart == None: + raise # tell the client the request is invalid + + mimetype = "application/octet-stream" + if fileExt in mimetypesBinary: + mimetype = mimetypesBinary[fileExt] + + if not rangeDefined: + simpleResponse(connection,"200 OK",{ + "Content-Length": str(length), + "Content-Type": mimetype, + "Accept-Ranges": "bytes" + }) + else: + simpleResponse(connection,"206 Partial Content",{ + "Content-Range": "bytes " +str(rangeStart)+ "-" +str(rangeEnd - 1)+ "/" +str(length), + "Content-Length": str(rangeEnd - rangeStart), + "Content-Type": mimetype, + "Accept-Ranges": "bytes" + }) + + print(rangeStart,rangeEnd) + cByte = rangeStart + while cByte < rangeEnd: + bytes = b"" + rSize = readBufferSize + if cByte + rSize > rangeEnd: + rSize = rangeEnd - cByte + with fileLock: + with open(filePath,"rb") as file: + file.seek(cByte) + bytes = file.read(rSize) + connection.sendall(bytes) + cByte += rSize + +fileHandlers[".*"] = handleBinary +for t in mimetypesBinary: + fileHandlers[t] = handleBinary \ No newline at end of file diff --git a/modules/http/file-handlers/htaccess.py b/modules/http/file-handlers/htaccess.py new file mode 100644 index 0000000..f2b64ba --- /dev/null +++ b/modules/http/file-handlers/htaccess.py @@ -0,0 +1,18 @@ +def main(): + def handleHTTP(event,eenv,env): + env["htaccessPropagate"] = True + paths = [indexPath] + env["lPath"].split(os.path.sep)[:-1] + pathl = [] + for pathbit in paths: + pathl.append(pathbit) + path = p(*pathl,".fhtpyaccess") + if not os.path.isfile(path): continue + handlePYP(env,path) + if env["htaccessPropagate"] == False: + return True + + addEventHandler("handleHTTP",handleHTTP) + +main() +fileHandlers["fhtpyaccess"] = handle404 +fileHandlers["htaccess"] = handle404 \ No newline at end of file diff --git a/modules/http/file-handlers/mimetypes.py b/modules/http/file-handlers/mimetypes.py new file mode 100644 index 0000000..42ca030 --- /dev/null +++ b/modules/http/file-handlers/mimetypes.py @@ -0,0 +1,1180 @@ +global mimetypesBinary +mimetypesBinary = { + "123": "application/vnd.lotus-1-2-3", + "1km": "application/vnd.1000minds.decision-model+xml", + "3ds": "image/x-3ds", + "3g2": "video/3gpp2", + "3gp": "video/3gpp", + "3gpp": "video/3gpp", + "3mf": "model/3mf", + "7z": "application/x-7z-compressed", + "aab": "application/x-authorware-bin", + "aac": "audio/x-aac", + "aam": "application/x-authorware-map", + "aas": "application/x-authorware-seg", + "abw": "application/x-abiword", + "ac": "application/vnd.nokia.n-gage.ac+xml", + "acc": "application/vnd.americandynamics.acc", + "ace": "application/x-ace-compressed", + "acu": "application/vnd.acucobol", + "acutc": "application/vnd.acucorp", + "adp": "audio/adpcm", + "aep": "application/vnd.audiograph", + "afm": "application/x-font-type1", + "afp": "application/vnd.ibm.modcap", + "ahead": "application/vnd.ahead.space", + "ai": "application/postscript", + "aif": "audio/x-aiff", + "aifc": "audio/x-aiff", + "aiff": "audio/x-aiff", + "air": "application/vnd.adobe.air-application-installer-package+zip", + "ait": "application/vnd.dvb.ait", + "ami": "application/vnd.amiga.ami", + "amr": "audio/amr", + "apk": "application/vnd.android.package-archive", + "apng": "image/apng", + "application": "application/x-ms-application", + "apr": "application/vnd.lotus-approach", + "arc": "application/x-freearc", + "arj": "application/x-arj", + "asc": "application/pgp-signature", + "asf": "video/x-ms-asf", + "aso": "application/vnd.accpac.simply.aso", + "asx": "video/x-ms-asf", + "atc": "application/vnd.acucorp", + "atom": "application/atom+xml", + "atomcat": "application/atomcat+xml", + "atomdeleted": "application/atomdeleted+xml", + "atomsvc": "application/atomsvc+xml", + "atx": "application/vnd.antix.game-component", + "au": "audio/basic", + "avi": "video/x-msvideo", + "avif": "image/avif", + "aw": "application/applixware", + "azf": "application/vnd.airzip.filesecure.azf", + "azs": "application/vnd.airzip.filesecure.azs", + "azv": "image/vnd.airzip.accelerator.azv", + "azw": "application/vnd.amazon.ebook", + "b16": "image/vnd.pco.b16", + "bat": "application/x-msdownload", + "bcpio": "application/x-bcpio", + "bdf": "application/x-font-bdf", + "bdm": "application/vnd.syncml.dm+wbxml", + "bdoc": "application/x-bdoc", + "bed": "application/vnd.realvnc.bed", + "bh2": "application/vnd.fujitsu.oasysprs", + "bin": "application/octet-stream", + "blb": "application/x-blorb", + "blorb": "application/x-blorb", + "bmi": "application/vnd.bmi", + "bmml": "application/vnd.balsamiq.bmml+xml", + "bmp": "image/x-ms-bmp", + "book": "application/vnd.framemaker", + "box": "application/vnd.previewsystems.box", + "boz": "application/x-bzip2", + "bpk": "application/octet-stream", + "bsp": "model/vnd.valve.source.compiled-map", + "btif": "image/prs.btif", + "buffer": "application/octet-stream", + "bz": "application/x-bzip", + "bz2": "application/x-bzip2", + "c11amc": "application/vnd.cluetrust.cartomobile-config", + "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg", + "c4d": "application/vnd.clonk.c4group", + "c4f": "application/vnd.clonk.c4group", + "c4g": "application/vnd.clonk.c4group", + "c4p": "application/vnd.clonk.c4group", + "c4u": "application/vnd.clonk.c4group", + "cab": "application/vnd.ms-cab-compressed", + "caf": "audio/x-caf", + "cap": "application/vnd.tcpdump.pcap", + "car": "application/vnd.curl.car", + "cat": "application/vnd.ms-pki.seccat", + "cb7": "application/x-cbr", + "cba": "application/x-cbr", + "cbr": "application/x-cbr", + "cbt": "application/x-cbr", + "cbz": "application/x-cbr", + "cco": "application/x-cocoa", + "cct": "application/x-director", + "ccxml": "application/ccxml+xml", + "cdbcmsg": "application/vnd.contact.cmsg", + "cdf": "application/x-netcdf", + "cdfx": "application/cdfx+xml", + "cdkey": "application/vnd.mediastation.cdkey", + "cdmia": "application/cdmi-capability", + "cdmic": "application/cdmi-container", + "cdmid": "application/cdmi-domain", + "cdmio": "application/cdmi-object", + "cdmiq": "application/cdmi-queue", + "cdx": "chemical/x-cdx", + "cdxml": "application/vnd.chemdraw+xml", + "cdy": "application/vnd.cinderella", + "cer": "application/pkix-cert", + "cfs": "application/x-cfs-compressed", + "cgm": "image/cgm", + "chat": "application/x-chat", + "chm": "application/vnd.ms-htmlhelp", + "chrt": "application/vnd.kde.kchart", + "cif": "chemical/x-cif", + "cii": "application/vnd.anser-web-certificate-issue-initiation", + "cil": "application/vnd.ms-artgalry", + "cjs": "application/node", + "cla": "application/vnd.claymore", + "class": "application/java-vm", + "clkk": "application/vnd.crick.clicker.keyboard", + "clkp": "application/vnd.crick.clicker.palette", + "clkt": "application/vnd.crick.clicker.template", + "clkw": "application/vnd.crick.clicker.wordbank", + "clkx": "application/vnd.crick.clicker", + "clp": "application/x-msclip", + "cmc": "application/vnd.cosmocaller", + "cmdf": "chemical/x-cmdf", + "cml": "chemical/x-cml", + "cmp": "application/vnd.yellowriver-custom-menu", + "cmx": "image/x-cmx", + "cod": "application/vnd.rim.cod", + "com": "application/x-msdownload", + "cpio": "application/x-cpio", + "cpt": "application/mac-compactpro", + "crd": "application/x-mscardfile", + "crl": "application/pkix-crl", + "crt": "application/x-x509-ca-cert", + "crx": "application/x-chrome-extension", + "cryptonote": "application/vnd.rig.cryptonote", + "csh": "application/x-csh", + "csl": "application/vnd.citationstyles.style+xml", + "csml": "chemical/x-csml", + "csp": "application/vnd.commonspace", + "cst": "application/x-director", + "cu": "application/cu-seeme", + "cww": "application/prs.cww", + "cxt": "application/x-director", + "dae": "model/vnd.collada+xml", + "daf": "application/vnd.mobius.daf", + "dart": "application/vnd.dart", + "dataless": "application/vnd.fdsn.seed", + "davmount": "application/davmount+xml", + "dbf": "application/vnd.dbf", + "dbk": "application/docbook+xml", + "dcr": "application/x-director", + "dd2": "application/vnd.oma.dd2+xml", + "ddd": "application/vnd.fujixerox.ddd", + "ddf": "application/vnd.syncml.dmddf+xml", + "dds": "image/vnd.ms-dds", + "deb": "application/x-debian-package", + "deploy": "application/octet-stream", + "der": "application/x-x509-ca-cert", + "dfac": "application/vnd.dreamfactory", + "dgc": "application/x-dgc-compressed", + "dir": "application/x-director", + "dis": "application/vnd.mobius.dis", + "disposition-notification": "message/disposition-notification", + "dist": "application/octet-stream", + "distz": "application/octet-stream", + "djv": "image/vnd.djvu", + "djvu": "image/vnd.djvu", + "dll": "application/x-msdownload", + "dmg": "application/x-apple-diskimage", + "dmp": "application/vnd.tcpdump.pcap", + "dms": "application/octet-stream", + "dna": "application/vnd.dna", + "doc": "application/msword", + "docm": "application/vnd.ms-word.document.macroenabled.12", + "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "dot": "application/msword", + "dotm": "application/vnd.ms-word.template.macroenabled.12", + "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", + "dp": "application/vnd.osgi.dp", + "dpg": "application/vnd.dpgraph", + "dra": "audio/vnd.dra", + "drle": "image/dicom-rle", + "dssc": "application/dssc+der", + "dtb": "application/x-dtbook+xml", + "dtd": "application/xml-dtd", + "dts": "audio/vnd.dts", + "dtshd": "audio/vnd.dts.hd", + "dump": "application/octet-stream", + "dvb": "video/vnd.dvb.file", + "dvi": "application/x-dvi", + "dwd": "application/atsc-dwd+xml", + "dwf": "model/vnd.dwf", + "dwg": "image/vnd.dwg", + "dxf": "image/vnd.dxf", + "dxp": "application/vnd.spotfire.dxp", + "dxr": "application/x-director", + "ear": "application/java-archive", + "ecelp4800": "audio/vnd.nuera.ecelp4800", + "ecelp7470": "audio/vnd.nuera.ecelp7470", + "ecelp9600": "audio/vnd.nuera.ecelp9600", + "ecma": "application/ecmascript", + "edm": "application/vnd.novadigm.edm", + "edx": "application/vnd.novadigm.edx", + "efif": "application/vnd.picsel", + "ei6": "application/vnd.pg.osasli", + "elc": "application/octet-stream", + "emf": "image/emf", + "eml": "message/rfc822", + "emma": "application/emma+xml", + "emotionml": "application/emotionml+xml", + "emz": "application/x-msmetafile", + "eol": "audio/vnd.digital-winds", + "eot": "application/vnd.ms-fontobject", + "eps": "application/postscript", + "epub": "application/epub+zip", + "es": "application/ecmascript", + "es3": "application/vnd.eszigno3+xml", + "esa": "application/vnd.osgi.subsystem", + "esf": "application/vnd.epson.esf", + "et3": "application/vnd.eszigno3+xml", + "eva": "application/x-eva", + "evy": "application/x-envoy", + "exe": "application/x-msdownload", + "exi": "application/exi", + "exp": "application/express", + "exr": "image/aces", + "ext": "application/vnd.novadigm.ext", + "ez": "application/andrew-inset", + "ez2": "application/vnd.ezpix-album", + "ez3": "application/vnd.ezpix-package", + "f4v": "video/x-f4v", + "fbs": "image/vnd.fastbidsheet", + "fcdt": "application/vnd.adobe.formscentral.fcdt", + "fcs": "application/vnd.isac.fcs", + "fdf": "application/vnd.fdf", + "fdt": "application/fdt+xml", + "fe_launch": "application/vnd.denovo.fcselayout-link", + "fg5": "application/vnd.fujitsu.oasysgp", + "fgd": "application/x-director", + "fh": "image/x-freehand", + "fh4": "image/x-freehand", + "fh5": "image/x-freehand", + "fh7": "image/x-freehand", + "fhc": "image/x-freehand", + "fig": "application/x-xfig", + "fits": "image/fits", + "flac": "audio/x-flac", + "fli": "video/x-fli", + "flo": "application/vnd.micrografx.flo", + "flv": "video/x-flv", + "flw": "application/vnd.kde.kivio", + "fm": "application/vnd.framemaker", + "fnc": "application/vnd.frogans.fnc", + "fo": "application/vnd.software602.filler.form+xml", + "fpx": "image/vnd.fpx", + "frame": "application/vnd.framemaker", + "fsc": "application/vnd.fsc.weblaunch", + "fst": "image/vnd.fst", + "ftc": "application/vnd.fluxtime.clip", + "fti": "application/vnd.anser-web-funds-transfer-initiation", + "fvt": "video/vnd.fvt", + "fxp": "application/vnd.adobe.fxp", + "fxpl": "application/vnd.adobe.fxp", + "fzs": "application/vnd.fuzzysheet", + "g2w": "application/vnd.geoplan", + "g3": "image/g3fax", + "g3w": "application/vnd.geospace", + "gac": "application/vnd.groove-account", + "gam": "application/x-tads", + "gbr": "application/rpki-ghostbusters", + "gca": "application/x-gca-compressed", + "gdl": "model/vnd.gdl", + "gdoc": "application/vnd.google-apps.document", + "geo": "application/vnd.dynageo", + "geojson": "application/geo+json", + "gex": "application/vnd.geometry-explorer", + "ggb": "application/vnd.geogebra.file", + "ggt": "application/vnd.geogebra.tool", + "ghf": "application/vnd.groove-help", + "gif": "image/gif", + "gim": "application/vnd.groove-identity-message", + "glb": "model/gltf-binary", + "gltf": "model/gltf+json", + "gml": "application/gml+xml", + "gmx": "application/vnd.gmx", + "gnumeric": "application/x-gnumeric", + "gph": "application/vnd.flographit", + "gpx": "application/gpx+xml", + "gqf": "application/vnd.grafeq", + "gqs": "application/vnd.grafeq", + "gram": "application/srgs", + "gramps": "application/x-gramps-xml", + "gre": "application/vnd.geometry-explorer", + "grv": "application/vnd.groove-injector", + "grxml": "application/srgs+xml", + "gsf": "application/x-font-ghostscript", + "gsheet": "application/vnd.google-apps.spreadsheet", + "gslides": "application/vnd.google-apps.presentation", + "gtar": "application/x-gtar", + "gtm": "application/vnd.groove-tool-message", + "gtw": "model/vnd.gtw", + "gxf": "application/gxf", + "gxt": "application/vnd.geonext", + "gz": "application/gzip", + "h261": "video/h261", + "h263": "video/h263", + "h264": "video/h264", + "hal": "application/vnd.hal+xml", + "hbci": "application/vnd.hbci", + "hdd": "application/x-virtualbox-hdd", + "hdf": "application/x-hdf", + "heic": "image/heic", + "heics": "image/heic-sequence", + "heif": "image/heif", + "heifs": "image/heif-sequence", + "hej2": "image/hej2k", + "held": "application/atsc-held+xml", + "hjson": "application/hjson", + "hlp": "application/winhlp", + "hpgl": "application/vnd.hp-hpgl", + "hpid": "application/vnd.hp-hpid", + "hps": "application/vnd.hp-hps", + "hqx": "application/mac-binhex40", + "hsj2": "image/hsj2", + "htke": "application/vnd.kenameaapp", + "hvd": "application/vnd.yamaha.hv-dic", + "hvp": "application/vnd.yamaha.hv-voice", + "hvs": "application/vnd.yamaha.hv-script", + "i2g": "application/vnd.intergeo", + "icc": "application/vnd.iccprofile", + "ice": "x-conference/x-cooltalk", + "icm": "application/vnd.iccprofile", + "ico": "image/x-icon", + "ief": "image/ief", + "ifm": "application/vnd.shana.informed.formdata", + "iges": "model/iges", + "igl": "application/vnd.igloader", + "igm": "application/vnd.insors.igm", + "igs": "model/iges", + "igx": "application/vnd.micrografx.igx", + "iif": "application/vnd.shana.informed.interchange", + "img": "application/octet-stream", + "imp": "application/vnd.accpac.simply.imp", + "ims": "application/vnd.ms-ims", + "ink": "application/inkml+xml", + "inkml": "application/inkml+xml", + "install": "application/x-install-instructions", + "iota": "application/vnd.astraea-software.iota", + "ipfix": "application/ipfix", + "ipk": "application/vnd.shana.informed.package", + "irm": "application/vnd.ibm.rights-management", + "irp": "application/vnd.irepository.package+xml", + "iso": "application/x-iso9660-image", + "itp": "application/vnd.shana.informed.formtemplate", + "its": "application/its+xml", + "ivp": "application/vnd.immervision-ivp", + "ivu": "application/vnd.immervision-ivu", + "jam": "application/vnd.jam", + "jar": "application/java-archive", + "jardiff": "application/x-java-archive-diff", + "jhc": "image/jphc", + "jisp": "application/vnd.jisp", + "jls": "image/jls", + "jlt": "application/vnd.hp-jlyt", + "jng": "image/x-jng", + "jnlp": "application/x-java-jnlp-file", + "joda": "application/vnd.joost.joda-archive", + "jp2": "image/jp2", + "jpe": "image/jpeg", + "jpeg": "image/jpeg", + "jpf": "image/jpx", + "jpg": "image/jpeg", + "jpg2": "image/jp2", + "jpgm": "video/jpm", + "jpgv": "video/jpeg", + "jph": "image/jph", + "jpm": "video/jpm", + "jpx": "image/jpx", + "js": "application/javascript", + "json": "application/json", + "json5": "application/json5", + "jsonld": "application/ld+json", + "jsonml": "application/jsonml+json", + "jxr": "image/jxr", + "jxra": "image/jxra", + "jxrs": "image/jxrs", + "jxs": "image/jxs", + "jxsc": "image/jxsc", + "jxsi": "image/jxsi", + "jxss": "image/jxss", + "kar": "audio/midi", + "karbon": "application/vnd.kde.karbon", + "kdbx": "application/x-keepass2", + "key": "application/x-iwork-keynote-sffkey", + "kfo": "application/vnd.kde.kformula", + "kia": "application/vnd.kidspiration", + "kml": "application/vnd.google-earth.kml+xml", + "kmz": "application/vnd.google-earth.kmz", + "kne": "application/vnd.kinar", + "knp": "application/vnd.kinar", + "kon": "application/vnd.kde.kontour", + "kpr": "application/vnd.kde.kpresenter", + "kpt": "application/vnd.kde.kpresenter", + "kpxx": "application/vnd.ds-keypoint", + "ksp": "application/vnd.kde.kspread", + "ktr": "application/vnd.kahootz", + "ktx": "image/ktx", + "ktx2": "image/ktx2", + "ktz": "application/vnd.kahootz", + "kwd": "application/vnd.kde.kword", + "kwt": "application/vnd.kde.kword", + "lasxml": "application/vnd.las.las+xml", + "latex": "application/x-latex", + "lbd": "application/vnd.llamagraphics.life-balance.desktop", + "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml", + "les": "application/vnd.hhe.lesson-player", + "lgr": "application/lgr+xml", + "lha": "application/x-lzh-compressed", + "link66": "application/vnd.route66.link66+xml", + "list3820": "application/vnd.ibm.modcap", + "listafp": "application/vnd.ibm.modcap", + "lnk": "application/x-ms-shortcut", + "lostxml": "application/lost+xml", + "lrf": "application/octet-stream", + "lrm": "application/vnd.ms-lrm", + "ltf": "application/vnd.frogans.ltf", + "luac": "application/x-lua-bytecode", + "lvp": "audio/vnd.lucent.voice", + "lwp": "application/vnd.lotus-wordpro", + "lzh": "application/x-lzh-compressed", + "m13": "application/x-msmediaview", + "m14": "application/x-msmediaview", + "m1v": "video/mpeg", + "m21": "application/mp21", + "m2a": "audio/mpeg", + "m2v": "video/mpeg", + "m3a": "audio/mpeg", + "m3u": "audio/x-mpegurl", + "m3u8": "application/vnd.apple.mpegurl", + "m4a": "audio/x-m4a", + "m4p": "application/mp4", + "m4s": "video/iso.segment", + "m4u": "video/vnd.mpegurl", + "m4v": "video/x-m4v", + "ma": "application/mathematica", + "mads": "application/mads+xml", + "maei": "application/mmt-aei+xml", + "mag": "application/vnd.ecowin.chart", + "maker": "application/vnd.framemaker", + "map": "application/json", + "mar": "application/octet-stream", + "mathml": "application/mathml+xml", + "mb": "application/mathematica", + "mbk": "application/vnd.mobius.mbk", + "mbox": "application/mbox", + "mc1": "application/vnd.medcalcdata", + "mcd": "application/vnd.mcd", + "mdb": "application/x-msaccess", + "mdi": "image/vnd.ms-modi", + "mesh": "model/mesh", + "meta4": "application/metalink4+xml", + "metalink": "application/metalink+xml", + "mets": "application/mets+xml", + "mfm": "application/vnd.mfmp", + "mft": "application/rpki-manifest", + "mgp": "application/vnd.osgeo.mapguide.package", + "mgz": "application/vnd.proteus.magazine", + "mid": "audio/midi", + "midi": "audio/midi", + "mie": "application/x-mie", + "mif": "application/vnd.mif", + "mime": "message/rfc822", + "mj2": "video/mj2", + "mjp2": "video/mj2", + "mjs": "application/javascript", + "mk3d": "video/x-matroska", + "mka": "audio/x-matroska", + "mks": "video/x-matroska", + "mkv": "video/x-matroska", + "mlp": "application/vnd.dolby.mlp", + "mmd": "application/vnd.chipnuts.karaoke-mmd", + "mmf": "application/vnd.smaf", + "mmr": "image/vnd.fujixerox.edmics-mmr", + "mng": "video/x-mng", + "mny": "application/x-msmoney", + "mobi": "application/x-mobipocket-ebook", + "mods": "application/mods+xml", + "mov": "video/quicktime", + "movie": "video/x-sgi-movie", + "mp2": "audio/mpeg", + "mp21": "application/mp21", + "mp2a": "audio/mpeg", + "mp3": "audio/mpeg", + "mp4": "video/mp4", + "mp4a": "audio/mp4", + "mp4s": "application/mp4", + "mp4v": "video/mp4", + "mpc": "application/vnd.mophun.certificate", + "mpd": "application/dash+xml", + "mpe": "video/mpeg", + "mpeg": "video/mpeg", + "mpg": "video/mpeg", + "mpg4": "video/mp4", + "mpga": "audio/mpeg", + "mpkg": "application/vnd.apple.installer+xml", + "mpm": "application/vnd.blueice.multipass", + "mpn": "application/vnd.mophun.application", + "mpp": "application/vnd.ms-project", + "mpt": "application/vnd.ms-project", + "mpy": "application/vnd.ibm.minipay", + "mqy": "application/vnd.mobius.mqy", + "mrc": "application/marc", + "mrcx": "application/marcxml+xml", + "mscml": "application/mediaservercontrol+xml", + "mseed": "application/vnd.fdsn.mseed", + "mseq": "application/vnd.mseq", + "msf": "application/vnd.epson.msf", + "msg": "application/vnd.ms-outlook", + "msh": "model/mesh", + "msi": "application/x-msdownload", + "msl": "application/vnd.mobius.msl", + "msm": "application/octet-stream", + "msp": "application/octet-stream", + "msty": "application/vnd.muvee.style", + "mtl": "model/mtl", + "mts": "model/vnd.mts", + "mus": "application/vnd.musician", + "musd": "application/mmt-usd+xml", + "musicxml": "application/vnd.recordare.musicxml+xml", + "mvb": "application/x-msmediaview", + "mvt": "application/vnd.mapbox-vector-tile", + "mwf": "application/vnd.mfer", + "mxf": "application/mxf", + "mxl": "application/vnd.recordare.musicxml", + "mxmf": "audio/mobile-xmf", + "mxml": "application/xv+xml", + "mxs": "application/vnd.triscape.mxs", + "mxu": "video/vnd.mpegurl", + "n-gage": "application/vnd.nokia.n-gage.symbian.install", + "nb": "application/mathematica", + "nbp": "application/vnd.wolfram.player", + "nc": "application/x-netcdf", + "ncx": "application/x-dtbncx+xml", + "ngdat": "application/vnd.nokia.n-gage.data", + "nitf": "application/vnd.nitf", + "nlu": "application/vnd.neurolanguage.nlu", + "nml": "application/vnd.enliven", + "nnd": "application/vnd.noblenet-directory", + "nns": "application/vnd.noblenet-sealer", + "nnw": "application/vnd.noblenet-web", + "npx": "image/vnd.net-fpx", + "nq": "application/n-quads", + "nsc": "application/x-conference", + "nsf": "application/vnd.lotus-notes", + "nt": "application/n-triples", + "ntf": "application/vnd.nitf", + "numbers": "application/x-iwork-numbers-sffnumbers", + "nzb": "application/x-nzb", + "oa2": "application/vnd.fujitsu.oasys2", + "oa3": "application/vnd.fujitsu.oasys3", + "oas": "application/vnd.fujitsu.oasys", + "obd": "application/x-msbinder", + "obgx": "application/vnd.openblox.game+xml", + "obj": "model/obj", + "oda": "application/oda", + "odb": "application/vnd.oasis.opendocument.database", + "odc": "application/vnd.oasis.opendocument.chart", + "odf": "application/vnd.oasis.opendocument.formula", + "odft": "application/vnd.oasis.opendocument.formula-template", + "odg": "application/vnd.oasis.opendocument.graphics", + "odi": "application/vnd.oasis.opendocument.image", + "odm": "application/vnd.oasis.opendocument.text-master", + "odp": "application/vnd.oasis.opendocument.presentation", + "ods": "application/vnd.oasis.opendocument.spreadsheet", + "odt": "application/vnd.oasis.opendocument.text", + "oga": "audio/ogg", + "ogex": "model/vnd.opengex", + "ogg": "audio/ogg", + "ogv": "video/ogg", + "ogx": "application/ogg", + "omdoc": "application/omdoc+xml", + "onepkg": "application/onenote", + "onetmp": "application/onenote", + "onetoc": "application/onenote", + "onetoc2": "application/onenote", + "opf": "application/oebps-package+xml", + "oprc": "application/vnd.palm", + "opus": "audio/ogg", + "osf": "application/vnd.yamaha.openscoreformat", + "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml", + "osm": "application/vnd.openstreetmap.data+xml", + "otc": "application/vnd.oasis.opendocument.chart-template", + "otf": "font/otf", + "otg": "application/vnd.oasis.opendocument.graphics-template", + "oth": "application/vnd.oasis.opendocument.text-web", + "oti": "application/vnd.oasis.opendocument.image-template", + "otp": "application/vnd.oasis.opendocument.presentation-template", + "ots": "application/vnd.oasis.opendocument.spreadsheet-template", + "ott": "application/vnd.oasis.opendocument.text-template", + "ova": "application/x-virtualbox-ova", + "ovf": "application/x-virtualbox-ovf", + "owl": "application/rdf+xml", + "oxps": "application/oxps", + "oxt": "application/vnd.openofficeorg.extension", + "p10": "application/pkcs10", + "p12": "application/x-pkcs12", + "p7b": "application/x-pkcs7-certificates", + "p7c": "application/pkcs7-mime", + "p7m": "application/pkcs7-mime", + "p7r": "application/x-pkcs7-certreqresp", + "p7s": "application/pkcs7-signature", + "p8": "application/pkcs8", + "pac": "application/x-ns-proxy-autoconfig", + "pages": "application/x-iwork-pages-sffpages", + "paw": "application/vnd.pawaafile", + "pbd": "application/vnd.powerbuilder6", + "pbm": "image/x-portable-bitmap", + "pcap": "application/vnd.tcpdump.pcap", + "pcf": "application/x-font-pcf", + "pcl": "application/vnd.hp-pcl", + "pclxl": "application/vnd.hp-pclxl", + "pct": "image/x-pict", + "pcurl": "application/vnd.curl.pcurl", + "pcx": "image/x-pcx", + "pdb": "application/x-pilot", + "pdf": "application/pdf", + "pem": "application/x-x509-ca-cert", + "pfa": "application/x-font-type1", + "pfb": "application/x-font-type1", + "pfm": "application/x-font-type1", + "pfr": "application/font-tdpfr", + "pfx": "application/x-pkcs12", + "pgm": "image/x-portable-graymap", + "pgn": "application/x-chess-pgn", + "pgp": "application/pgp-encrypted", + "php": "application/x-httpd-php", + "pic": "image/x-pict", + "pkg": "application/octet-stream", + "pki": "application/pkixcmp", + "pkipath": "application/pkix-pkipath", + "pkpass": "application/vnd.apple.pkpass", + "pl": "application/x-perl", + "plb": "application/vnd.3gpp.pic-bw-large", + "plc": "application/vnd.mobius.plc", + "plf": "application/vnd.pocketlearn", + "pls": "application/pls+xml", + "pm": "application/x-perl", + "pml": "application/vnd.ctc-posml", + "png": "image/png", + "pnm": "image/x-portable-anymap", + "portpkg": "application/vnd.macports.portpkg", + "pot": "application/vnd.ms-powerpoint", + "potm": "application/vnd.ms-powerpoint.template.macroenabled.12", + "potx": "application/vnd.openxmlformats-officedocument.presentationml.template", + "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12", + "ppd": "application/vnd.cups-ppd", + "ppm": "image/x-portable-pixmap", + "pps": "application/vnd.ms-powerpoint", + "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12", + "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", + "ppt": "application/vnd.ms-powerpoint", + "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", + "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "pqa": "application/vnd.palm", + "prc": "application/x-pilot", + "pre": "application/vnd.lotus-freelance", + "prf": "application/pics-rules", + "provx": "application/provenance+xml", + "ps": "application/postscript", + "psb": "application/vnd.3gpp.pic-bw-small", + "psd": "image/vnd.adobe.photoshop", + "psf": "application/x-font-linux-psf", + "pskcxml": "application/pskc+xml", + "pti": "image/prs.pti", + "ptid": "application/vnd.pvi.ptid1", + "pub": "application/x-mspublisher", + "pvb": "application/vnd.3gpp.pic-bw-var", + "pwn": "application/vnd.3m.post-it-notes", + "pya": "audio/vnd.ms-playready.media.pya", + "pyv": "video/vnd.ms-playready.media.pyv", + "qam": "application/vnd.epson.quickanime", + "qbo": "application/vnd.intu.qbo", + "qfx": "application/vnd.intu.qfx", + "qps": "application/vnd.publishare-delta-tree", + "qt": "video/quicktime", + "qwd": "application/vnd.quark.quarkxpress", + "qwt": "application/vnd.quark.quarkxpress", + "qxb": "application/vnd.quark.quarkxpress", + "qxd": "application/vnd.quark.quarkxpress", + "qxl": "application/vnd.quark.quarkxpress", + "qxt": "application/vnd.quark.quarkxpress", + "ra": "audio/x-realaudio", + "ram": "audio/x-pn-realaudio", + "raml": "application/raml+yaml", + "rapd": "application/route-apd+xml", + "rar": "application/x-rar-compressed", + "ras": "image/x-cmu-raster", + "rcprofile": "application/vnd.ipunplugged.rcprofile", + "rdf": "application/rdf+xml", + "rdz": "application/vnd.data-vision.rdz", + "relo": "application/p2p-overlay+xml", + "rep": "application/vnd.businessobjects", + "res": "application/x-dtbresource+xml", + "rgb": "image/x-rgb", + "rif": "application/reginfo+xml", + "rip": "audio/vnd.rip", + "ris": "application/x-research-info-systems", + "rl": "application/resource-lists+xml", + "rlc": "image/vnd.fujixerox.edmics-rlc", + "rld": "application/resource-lists-diff+xml", + "rm": "application/vnd.rn-realmedia", + "rmi": "audio/midi", + "rmp": "audio/x-pn-realaudio-plugin", + "rms": "application/vnd.jcp.javame.midlet-rms", + "rmvb": "application/vnd.rn-realmedia-vbr", + "rnc": "application/relax-ng-compact-syntax", + "rng": "application/xml", + "roa": "application/rpki-roa", + "rp9": "application/vnd.cloanto.rp9", + "rpm": "application/x-redhat-package-manager", + "rpss": "application/vnd.nokia.radio-presets", + "rpst": "application/vnd.nokia.radio-preset", + "rq": "application/sparql-query", + "rs": "application/rls-services+xml", + "rsat": "application/atsc-rsat+xml", + "rsd": "application/rsd+xml", + "rsheet": "application/urc-ressheet+xml", + "rss": "application/rss+xml", + "run": "application/x-makeself", + "rusd": "application/route-usd+xml", + "s3m": "audio/s3m", + "saf": "application/vnd.yamaha.smaf-audio", + "sbml": "application/sbml+xml", + "sc": "application/vnd.ibm.secure-container", + "scd": "application/x-msschedule", + "scm": "application/vnd.lotus-screencam", + "scq": "application/scvp-cv-request", + "scs": "application/scvp-cv-response", + "sda": "application/vnd.stardivision.draw", + "sdc": "application/vnd.stardivision.calc", + "sdd": "application/vnd.stardivision.impress", + "sdkd": "application/vnd.solent.sdkm+xml", + "sdkm": "application/vnd.solent.sdkm+xml", + "sdp": "application/sdp", + "sdw": "application/vnd.stardivision.writer", + "sea": "application/x-sea", + "see": "application/vnd.seemail", + "seed": "application/vnd.fdsn.seed", + "sema": "application/vnd.sema", + "semd": "application/vnd.semd", + "semf": "application/vnd.semf", + "senmlx": "application/senml+xml", + "sensmlx": "application/sensml+xml", + "ser": "application/java-serialized-object", + "setpay": "application/set-payment-initiation", + "setreg": "application/set-registration-initiation", + "sfd-hdstx": "application/vnd.hydrostatix.sof-data", + "sfs": "application/vnd.spotfire.sfs", + "sgi": "image/sgi", + "sgl": "application/vnd.stardivision.writer-global", + "sh": "application/x-sh", + "shar": "application/x-shar", + "shf": "application/shf+xml", + "sid": "image/x-mrsid-image", + "sieve": "application/sieve", + "sig": "application/pgp-signature", + "sil": "audio/silk", + "silo": "model/mesh", + "sis": "application/vnd.symbian.install", + "sisx": "application/vnd.symbian.install", + "sit": "application/x-stuffit", + "sitx": "application/x-stuffitx", + "siv": "application/sieve", + "skd": "application/vnd.koan", + "skm": "application/vnd.koan", + "skp": "application/vnd.koan", + "skt": "application/vnd.koan", + "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12", + "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", + "sls": "application/route-s-tsid+xml", + "slt": "application/vnd.epson.salt", + "sm": "application/vnd.stepmania.stepchart", + "smf": "application/vnd.stardivision.math", + "smi": "application/smil+xml", + "smil": "application/smil+xml", + "smv": "video/x-smv", + "smzip": "application/vnd.stepmania.package", + "snd": "audio/basic", + "snf": "application/x-font-snf", + "so": "application/octet-stream", + "spc": "application/x-pkcs7-certificates", + "spf": "application/vnd.yamaha.smaf-phrase", + "spl": "application/x-futuresplash", + "spp": "application/scvp-vp-response", + "spq": "application/scvp-vp-request", + "spx": "audio/ogg", + "sql": "application/x-sql", + "src": "application/x-wais-source", + "srt": "application/x-subrip", + "sru": "application/sru+xml", + "srx": "application/sparql-results+xml", + "ssdl": "application/ssdl+xml", + "sse": "application/vnd.kodak-descriptor", + "ssf": "application/vnd.epson.ssf", + "ssml": "application/ssml+xml", + "st": "application/vnd.sailingtracker.track", + "stc": "application/vnd.sun.xml.calc.template", + "std": "application/vnd.sun.xml.draw.template", + "stf": "application/vnd.wt.stf", + "sti": "application/vnd.sun.xml.impress.template", + "stk": "application/hyperstudio", + "stl": "model/stl", + "stpx": "model/step+xml", + "stpxz": "model/step-xml+zip", + "stpz": "model/step+zip", + "str": "application/vnd.pg.format", + "stw": "application/vnd.sun.xml.writer.template", + "sus": "application/vnd.sus-calendar", + "susp": "application/vnd.sus-calendar", + "sv4cpio": "application/x-sv4cpio", + "sv4crc": "application/x-sv4crc", + "svc": "application/vnd.dvb.service", + "svd": "application/vnd.svd", + "svg": "image/svg+xml", + "svgz": "image/svg+xml", + "swa": "application/x-director", + "swf": "application/x-shockwave-flash", + "swi": "application/vnd.aristanetworks.swi", + "swidtag": "application/swid+xml", + "sxc": "application/vnd.sun.xml.calc", + "sxd": "application/vnd.sun.xml.draw", + "sxg": "application/vnd.sun.xml.writer.global", + "sxi": "application/vnd.sun.xml.impress", + "sxm": "application/vnd.sun.xml.math", + "sxw": "application/vnd.sun.xml.writer", + "t3": "application/x-t3vm-image", + "t38": "image/t38", + "taglet": "application/vnd.mynfc", + "tao": "application/vnd.tao.intent-module-archive", + "tap": "image/vnd.tencent.tap", + "tar": "application/x-tar", + "tcap": "application/vnd.3gpp2.tcap", + "tcl": "application/x-tcl", + "td": "application/urc-targetdesc+xml", + "teacher": "application/vnd.smart.teacher", + "tei": "application/tei+xml", + "teicorpus": "application/tei+xml", + "tex": "application/x-tex", + "texi": "application/x-texinfo", + "texinfo": "application/x-texinfo", + "tfi": "application/thraud+xml", + "tfm": "application/x-tex-tfm", + "tfx": "image/tiff-fx", + "tga": "image/x-tga", + "thmx": "application/vnd.ms-officetheme", + "tif": "image/tiff", + "tiff": "image/tiff", + "tk": "application/x-tcl", + "tmo": "application/vnd.tmobile-livetv", + "toml": "application/toml", + "torrent": "application/x-bittorrent", + "tpl": "application/vnd.groove-tool-template", + "tpt": "application/vnd.trid.tpt", + "tra": "application/vnd.trueapp", + "trig": "application/trig", + "trm": "application/x-msterminal", + "ts": "video/mp2t", + "tsd": "application/timestamped-data", + "ttc": "font/collection", + "ttf": "font/ttf", + "ttml": "application/ttml+xml", + "twd": "application/vnd.simtech-mindmapper", + "twds": "application/vnd.simtech-mindmapper", + "txd": "application/vnd.genomatix.tuxedo", + "txf": "application/vnd.mobius.txf", + "u32": "application/x-authorware-bin", + "u8dsn": "message/global-delivery-status", + "u8hdr": "message/global-headers", + "u8mdn": "message/global-disposition-notification", + "u8msg": "message/global", + "ubj": "application/ubjson", + "udeb": "application/x-debian-package", + "ufd": "application/vnd.ufdl", + "ufdl": "application/vnd.ufdl", + "ulx": "application/x-glulx", + "umj": "application/vnd.umajin", + "unityweb": "application/vnd.unity", + "uoml": "application/vnd.uoml+xml", + "usdz": "model/vnd.usdz+zip", + "ustar": "application/x-ustar", + "utz": "application/vnd.uiq.theme", + "uva": "audio/vnd.dece.audio", + "uvd": "application/vnd.dece.data", + "uvf": "application/vnd.dece.data", + "uvg": "image/vnd.dece.graphic", + "uvh": "video/vnd.dece.hd", + "uvi": "image/vnd.dece.graphic", + "uvm": "video/vnd.dece.mobile", + "uvp": "video/vnd.dece.pd", + "uvs": "video/vnd.dece.sd", + "uvt": "application/vnd.dece.ttml+xml", + "uvu": "video/vnd.uvvu.mp4", + "uvv": "video/vnd.dece.video", + "uvva": "audio/vnd.dece.audio", + "uvvd": "application/vnd.dece.data", + "uvvf": "application/vnd.dece.data", + "uvvg": "image/vnd.dece.graphic", + "uvvh": "video/vnd.dece.hd", + "uvvi": "image/vnd.dece.graphic", + "uvvm": "video/vnd.dece.mobile", + "uvvp": "video/vnd.dece.pd", + "uvvs": "video/vnd.dece.sd", + "uvvt": "application/vnd.dece.ttml+xml", + "uvvu": "video/vnd.uvvu.mp4", + "uvvv": "video/vnd.dece.video", + "uvvx": "application/vnd.dece.unspecified", + "uvvz": "application/vnd.dece.zip", + "uvx": "application/vnd.dece.unspecified", + "uvz": "application/vnd.dece.zip", + "vbox": "application/x-virtualbox-vbox", + "vbox-extpack": "application/x-virtualbox-vbox-extpack", + "vcd": "application/x-cdlink", + "vcg": "application/vnd.groove-vcard", + "vcx": "application/vnd.vcx", + "vdi": "application/x-virtualbox-vdi", + "vds": "model/vnd.sap.vds", + "vhd": "application/x-virtualbox-vhd", + "vis": "application/vnd.visionary", + "viv": "video/vnd.vivo", + "vmdk": "application/x-virtualbox-vmdk", + "vob": "video/x-ms-vob", + "vor": "application/vnd.stardivision.writer", + "vox": "application/x-authorware-bin", + "vrml": "model/vrml", + "vsd": "application/vnd.visio", + "vsf": "application/vnd.vsf", + "vss": "application/vnd.visio", + "vst": "application/vnd.visio", + "vsw": "application/vnd.visio", + "vtf": "image/vnd.valve.source.texture", + "vtu": "model/vnd.vtu", + "vxml": "application/voicexml+xml", + "w3d": "application/x-director", + "wad": "application/x-doom", + "wadl": "application/vnd.sun.wadl+xml", + "war": "application/java-archive", + "wasm": "application/wasm", + "wav": "audio/x-wav", + "wax": "audio/x-ms-wax", + "wbmp": "image/vnd.wap.wbmp", + "wbs": "application/vnd.criticaltools.wbs+xml", + "wbxml": "application/vnd.wap.wbxml", + "wcm": "application/vnd.ms-works", + "wdb": "application/vnd.ms-works", + "wdp": "image/vnd.ms-photo", + "weba": "audio/webm", + "webapp": "application/x-web-app-manifest+json", + "webm": "video/webm", + "webmanifest": "application/manifest+json", + "webp": "image/webp", + "wg": "application/vnd.pmi.widget", + "wgt": "application/widget", + "wks": "application/vnd.ms-works", + "wm": "video/x-ms-wm", + "wma": "audio/x-ms-wma", + "wmd": "application/x-ms-wmd", + "wmf": "image/wmf", + "wmlc": "application/vnd.wap.wmlc", + "wmlsc": "application/vnd.wap.wmlscriptc", + "wmv": "video/x-ms-wmv", + "wmx": "video/x-ms-wmx", + "wmz": "application/x-msmetafile", + "woff": "font/woff", + "woff2": "font/woff2", + "wpd": "application/vnd.wordperfect", + "wpl": "application/vnd.ms-wpl", + "wps": "application/vnd.ms-works", + "wqd": "application/vnd.wqd", + "wri": "application/x-mswrite", + "wrl": "model/vrml", + "wsc": "message/vnd.wfa.wsc", + "wsdl": "application/wsdl+xml", + "wspolicy": "application/wspolicy+xml", + "wtb": "application/vnd.webturbo", + "wvx": "video/x-ms-wvx", + "x32": "application/x-authorware-bin", + "x3d": "model/x3d+xml", + "x3db": "model/x3d+fastinfoset", + "x3dbz": "model/x3d+binary", + "x3dv": "model/x3d-vrml", + "x3dvz": "model/x3d+vrml", + "x3dz": "model/x3d+xml", + "x_b": "model/vnd.parasolid.transmit.binary", + "x_t": "model/vnd.parasolid.transmit.text", + "xaml": "application/xaml+xml", + "xap": "application/x-silverlight-app", + "xar": "application/vnd.xara", + "xav": "application/xcap-att+xml", + "xbap": "application/x-ms-xbap", + "xbd": "application/vnd.fujixerox.docuworks.binder", + "xbm": "image/x-xbitmap", + "xca": "application/xcap-caps+xml", + "xcs": "application/calendar+xml", + "xdf": "application/xcap-diff+xml", + "xdm": "application/vnd.syncml.dm+xml", + "xdp": "application/vnd.adobe.xdp+xml", + "xdssc": "application/dssc+xml", + "xdw": "application/vnd.fujixerox.docuworks", + "xel": "application/xcap-el+xml", + "xenc": "application/xenc+xml", + "xer": "application/patch-ops-error+xml", + "xfdf": "application/vnd.adobe.xfdf", + "xfdl": "application/vnd.xfdl", + "xht": "application/xhtml+xml", + "xhtml": "application/xhtml+xml", + "xhvml": "application/xv+xml", + "xif": "image/vnd.xiff", + "xla": "application/vnd.ms-excel", + "xlam": "application/vnd.ms-excel.addin.macroenabled.12", + "xlc": "application/vnd.ms-excel", + "xlf": "application/xliff+xml", + "xlm": "application/vnd.ms-excel", + "xls": "application/vnd.ms-excel", + "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12", + "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", + "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "xlt": "application/vnd.ms-excel", + "xltm": "application/vnd.ms-excel.template.macroenabled.12", + "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", + "xlw": "application/vnd.ms-excel", + "xm": "audio/xm", + "xns": "application/xcap-ns+xml", + "xo": "application/vnd.olpc-sugar", + "xop": "application/xop+xml", + "xpi": "application/x-xpinstall", + "xpl": "application/xproc+xml", + "xpm": "image/x-xpixmap", + "xpr": "application/vnd.is-xpr", + "xps": "application/vnd.ms-xpsdocument", + "xpw": "application/vnd.intercon.formnet", + "xpx": "application/vnd.intercon.formnet", + "xsd": "application/xml", + "xsl": "application/xslt+xml", + "xslt": "application/xslt+xml", + "xsm": "application/vnd.syncml+xml", + "xspf": "application/xspf+xml", + "xul": "application/vnd.mozilla.xul+xml", + "xvm": "application/xv+xml", + "xvml": "application/xv+xml", + "xwd": "image/x-xwindowdump", + "xyz": "chemical/x-xyz", + "xz": "application/x-xz", + "yang": "application/yang", + "yin": "application/yin+xml", + "z1": "application/x-zmachine", + "z2": "application/x-zmachine", + "z3": "application/x-zmachine", + "z4": "application/x-zmachine", + "z5": "application/x-zmachine", + "z6": "application/x-zmachine", + "z7": "application/x-zmachine", + "z8": "application/x-zmachine", + "zaz": "application/vnd.zzazz.deck+xml", + "zip": "application/zip", + "zir": "application/vnd.zul", + "zirz": "application/vnd.zul", + "zmm": "application/vnd.handheld-entertainment+xml" +} + +global mimetypesText +mimetypesText = { + "3dml": "text/vnd.in3d.3dml", + "appcache": "text/cache-manifest", + "asm": "text/x-asm", + "c": "text/x-c", + "cc": "text/x-c", + "coffee": "text/coffeescript", + "conf": "text/plain", + "cpp": "text/x-c", + "css": "text/css", + "csv": "text/csv", + "curl": "text/vnd.curl", + "cxx": "text/x-c", + "dcurl": "text/vnd.curl.dcurl", + "def": "text/plain", + "dic": "text/x-c", + "dsc": "text/prs.lines.tag", + "etx": "text/x-setext", + "f": "text/x-fortran", + "f77": "text/x-fortran", + "f90": "text/x-fortran", + "flx": "text/vnd.fmi.flexstor", + "fly": "text/vnd.fly", + "for": "text/x-fortran", + "gv": "text/vnd.graphviz", + "h": "text/x-c", + "hbs": "text/x-handlebars-template", + "hh": "text/x-c", + "htc": "text/x-component", + "htm": "text/html", + "html": "text/html", + "ics": "text/calendar", + "ifb": "text/calendar", + "in": "text/plain", + "ini": "text/plain", + "jad": "text/vnd.sun.j2me.app-descriptor", + "jade": "text/jade", + "java": "text/x-java-source", + "jsx": "text/jsx", + "less": "text/less", + "list": "text/plain", + "litcoffee": "text/coffeescript", + "log": "text/plain", + "lua": "text/x-lua", + "man": "text/troff", + "manifest": "text/cache-manifest", + "markdown": "text/markdown", + "mcurl": "text/vnd.curl.mcurl", + "md": "text/markdown", + "mdx": "text/mdx", + "me": "text/troff", + "mkd": "text/x-markdown", + "mml": "text/mathml", + "ms": "text/troff", + "n3": "text/n3", + "nfo": "text/x-nfo", + "opml": "text/x-opml", + "org": "text/x-org", + "p": "text/x-pascal", + "pas": "text/x-pascal", + "pde": "text/x-processing", + "roff": "text/troff", + "rtf": "text/rtf", + "rtx": "text/richtext", + "s": "text/x-asm", + "sass": "text/x-sass", + "scss": "text/x-scss", + "scurl": "text/vnd.curl.scurl", + "sfv": "text/x-sfv", + "sgm": "text/sgml", + "sgml": "text/sgml", + "shex": "text/shex", + "shtml": "text/html", + "slim": "text/slim", + "slm": "text/slim", + "spdx": "text/spdx", + "spot": "text/vnd.in3d.spot", + "styl": "text/stylus", + "stylus": "text/stylus", + "sub": "text/vnd.dvb.subtitle", + "t": "text/troff", + "text": "text/plain", + "tr": "text/troff", + "tsv": "text/tab-separated-values", + "ttl": "text/turtle", + "txt": "text/plain", + "uri": "text/uri-list", + "uris": "text/uri-list", + "urls": "text/uri-list", + "uu": "text/x-uuencode", + "vcard": "text/vcard", + "vcf": "text/x-vcard", + "vcs": "text/x-vcalendar", + "vtt": "text/vtt", + "wml": "text/vnd.wap.wml", + "wmls": "text/vnd.wap.wmlscript", + "xml": "text/xml", + "yaml": "text/yaml", + "yml": "text/yaml", + "ymp": "text/x-suse-ymp" +} \ No newline at end of file diff --git a/modules/http/file-handlers/pyp.py b/modules/http/file-handlers/pyp.py new file mode 100644 index 0000000..a703d24 --- /dev/null +++ b/modules/http/file-handlers/pyp.py @@ -0,0 +1,17 @@ +global handlePYP +def handlePYP(env,cpath = False,getlock = True): + code = False + if not cpath: cpath = env["fPath"] + + if getlock: + with fileLock: + with open(cpath,encoding="utf-8") as cfile: + code = cfile.read() + else: + with open(cpath,encoding="utf-8") as cfile: + code = cfile.read() + env = runCode(code,{"env":env},cpath)["env"] +fileHandlers["pyp"] = handlePYP + +global indexFiles +indexFiles = ["index.pyp"] + indexFiles \ No newline at end of file diff --git a/modules/http/file-handlers/text.py b/modules/http/file-handlers/text.py new file mode 100644 index 0000000..cc75142 --- /dev/null +++ b/modules/http/file-handlers/text.py @@ -0,0 +1,19 @@ +global handleText +def handleText(env): + data = b"" + with fileLock: + with open(env["fPath"],"rb") as textFile: + data = textFile.read() + + simpleResponse( + env["self"].connection,"200 OK", + { + "Content-Type": mimetypesText[env["fileExt"]]+ "; charset=UTF-8", + "Accept-Ranges": "bytes" + },data + ) + +for t in mimetypesText: + fileHandlers[t] = handleText + +indexFiles.append("index.html") \ No newline at end of file diff --git a/modules/http/helpers.py b/modules/http/helpers.py new file mode 100644 index 0000000..4859f5d --- /dev/null +++ b/modules/http/helpers.py @@ -0,0 +1,211 @@ +global time +import time +global urllib +import urllib.parse +global html +import html + +global getHeaderFromConnection +def getHeaderFromConnection(connection): + start = time.process_time() + timeo = connection.gettimeout() + l = 0 + nl = 0 + header = "" + while True: + b = connection.recv(1) + if b == b"": raise ConnectionResetError + if time.process_time() - start > timeo: raise TimeoutError + l += 1 + if l > maxHeaderLength: + connection.sendall("""\ +HTTP 1.1 413 Payload Too Large\r +\r +""".encode("ascii")) + raise excConnectionClosed + + bd = None + try: + bd = b.decode("ascii") + except: + connection.sendall("""\ +HTTP 1.1 400 Bad Request\r +\r +""".encode("ASCII")) + raise excConnectionClosed + + if bd == "\n": + nl += 1 + if nl == 2: + return header + else: + if bd != "\r": + nl = 0 + header += bd + +global parseHeader +def parseHeader(headers): + headers = headers.replace("\r","").split("\n") + del headers[-1] + for i in range(len(headers)): + headers[i] = headers[i].strip(" \t") + + mainHeader = headers.pop(0).split(" ") + index = 0 + length = len(mainHeader) + while index < length: + val = mainHeader[index] + val = val.strip(" \t") + if val == "": + del mainHeader[index] + length -= 1 + continue + index += 1 + mainHeader[0] = mainHeader[0].lower() + mainHeader[-1] = mainHeader[-1].lower() + + headerList = {} + for header in headers: + header = header.split(":",1) + if len(header) != 2: continue + headerKey = header[0].strip(" \t").lower() + headerValue = header[1].strip(" \t") + if headerKey in headerList: + headers[headerKey] += ", " +headerValue + else: + headerList[headerKey] = headerValue + + return mainHeader,headerList + +global parseHeaderPath +def parseHeaderPath(path): + path = path.split("?",1) + if len(path) < 2: path.append("") + args = {} + for arg in path[1].split("&"): + arg = arg.split("=",1) + if len(arg) < 2: arg.append("") + args[urllib.parse.unquote(arg[0]).lower()] = urllib.parse.unquote(arg[1]) + return urllib.parse.unquote(path[0]),args + +global fixUserPath +def fixUserPath(path): + path = path.replace("\\","/") # Replace backslash with forward slash + path = path.lstrip("/") + npath = "" + for pathbit in path.split("/"): + pathbit = pathbit.strip(" \t\r\n") # Remove spaces, tabs, line return, and new line + if pathbit in [".",".."]: # Remove . and .. + continue + npath += pathbit + "/" + npath = npath[:-1] + + while "//" in npath: npath = npath.replace("//","/") # Remove double slashes + return npath + +global simpleResponse +def simpleResponse(connection,status,headers = None,content = None,autolength = True): + if headers == None: + headers = {} + + if not "Accept-Ranges" in headers: + headers["Accept-Ranges"] = "none" + + if content != None and autolength == True: + headers["Content-Length"] = str(len(content)) + + response = 'HTTP/1.1 ' +status+ '\r\n' + for header in headers: + response += header + ": " +headers[header] + "\r\n" + response += "\r\n" + + connection.sendall(response.encode("ascii")) + if content != None: + connection.sendall(content) + +global refer +def refer(connection,path): + simpleResponse( + connection,"302 Found", + { + "Content-Type": "text/html; charset=ASCII", + "Location": path + },('''\ + + + +Referring you to ''' +html.escape(path)+ '''... + +''').encode("ascii") + ) + +global notFound +def notFound(connection,path): + simpleResponse( + connection,"404 Not Found", + { + "Content-Type": "text/html; charset=ASCII" + },('''\ + + + +Not found: ''' +html.escape(path)+ ''' + +''').encode("ascii") + ) + +global pathToURL +def pathToURL(path): + path = path.split("/") + length = len(path) + index = 0 + while index < length: + path[index] = urllib.parse.quote(path[index]) + index += 1 + path = "/".join(path) + return path + +# Can return the following: +# positive integer, None: Send entire file content starting at arg 1 +# negative integer, None: Send entire file content starting at file end + arg 1 +# positive integer, positive integer: Send entire file, from arg 1 to arg 2, not including arg 2 +global getRange +def getRange(range): + try: + range = range.split("=",1) + if range[0].strip("\t ") != "bytes": return None,None + range = range[1].split(",")[0].split("-") + range[0] = range[0].strip("\t ") + range[1] = range[1].strip("\t ") + if range[0] == "": + return 0 - int(range[1]),None + + if range[1] == "": + return int(range[0]),None + + return int(range[0]),int(range[1]) + 1 + except: + return 0,None + +global convertRanges +def convertRanges(rangeStart,rangeEnd,length): + # Convert given ranges into complete ranges + if rangeStart < 0: + rangeStart = length - rangeStart + rangeEnd = length + else: + if rangeEnd == None: + rangeEnd = length + + # Check if the ranges make sense + if rangeStart < 0: + return None,None + + if rangeEnd > length: + return None,None + + if rangeStart > rangeEnd: + return None,None + + # OK + return rangeStart,rangeEnd \ No newline at end of file diff --git a/modules/http/main.mods b/modules/http/main.mods new file mode 100644 index 0000000..40832da --- /dev/null +++ b/modules/http/main.mods @@ -0,0 +1,11 @@ +./settings.py # Settings +./helpers.py # Helper functions +./main.py # Main loop and functions +./404.py # 404 page + +# File handlers: +./file-handlers/mimetypes.py # List of file endings and the mimetypes they belong to +./file-handlers/binary.py # Images, video, audio, executables, etc... +./file-handlers/text.py # HTML, XML, TXT, etc... +./file-handlers/pyp.py # pyp, fhttpy's script format +./file-handlers/htaccess.py # .fhtpyaccess - can be used to override handlers on an entire folder \ No newline at end of file diff --git a/modules/http/main.py b/modules/http/main.py new file mode 100644 index 0000000..376f5e9 --- /dev/null +++ b/modules/http/main.py @@ -0,0 +1,85 @@ +global email +import email.utils + +global fileHandlers +fileHandlers = {} + +global indexFiles +indexFiles = [] + +global pathHandlers +pathHandlers = {} + +global clientLoopIn +def clientLoopIn(self): + env = {} + env["self"] = self + env["requestTime"] = time.time() + env["header"] = getHeaderFromConnection(self.connection) + env["protocolHeaderList"],env["headerList"] = parseHeader(env["header"]) + env["cmd"] = env["protocolHeaderList"][0] + env["path"],env["args"] = parseHeaderPath(env["protocolHeaderList"][1]) + env["pathFixed"] = fixUserPath(env["path"]) + + env["lPath"] = env["pathFixed"].replace("/",os.path.sep) + env["fPath"] = p(indexPath,env["lPath"]) + env["fileExt"] = "." + + if not env["pathFixed"] == "" and not os.path.isfile(env["fPath"]) and os.path.isdir(env["fPath"]) and env["pathFixed"][-1] != "/": env["pathFixed"] += "/" # This is dirty, since it possibly circumvents .fhtpyaccess (You can see if a folder exists or not by probing) + + if "/" + env["pathFixed"] != env["path"]: + newPath = "/" + pathToURL(env["pathFixed"]) + rawArgs = env["protocolHeaderList"][1].split("?",1) + + if len(rawArgs) > 1: + newPath += "?" +rawArgs[-1] + + refer(self.connection,newPath) + return + + if env["pathFixed"] in pathHandlers: + pathHandlers[env["pathFixed"]](env) + return + + if not os.path.isfile(env["fPath"]): + if not os.path.isdir(env["fPath"]): + handle404(env) + return + + found = False + for file in indexFiles: + if os.path.isfile(p(env["fPath"],file)): + found = file + break + + if found == False: + env["fileExt"] = ".d" + env["lPath"] = p(env["lPath"],".") + env["fPath"] = p(indexPath,env["lPath"]) + else: + env["lPath"] = p(env["lPath"],found) + env["fPath"] = p(indexPath,env["lPath"]) + lPathSplit = env["lPath"].rsplit(os.path.sep,1)[-1].rsplit(".",1) + if len(lPathSplit) > 1: + env["fileExt"] = lPathSplit[-1].lower() + else: + lPathSplit = env["lPath"].rsplit(os.path.sep,1)[-1].rsplit(".",1) + if len(lPathSplit) > 1: + env["fileExt"] = lPathSplit[-1].lower() + + env["fPathDir"] = pUp(env["fPath"]) + env["requestTimeFormatted"] = email.utils.formatdate(int(env["requestTime"])).replace("-0000","GMT") + + env["handler"] = False + if env["fileExt"] in fileHandlers: + env["handler"] = fileHandlers[env["fileExt"]] + elif ".*" in fileHandlers: + env["handler"] = fileHandlers[".*"] + + if triggerEvent("handleHTTP",env) == False: return + + if env["handler"]: + env["handler"](env) + else: + handle404(env) + return \ No newline at end of file diff --git a/modules/http/settings.py b/modules/http/settings.py new file mode 100644 index 0000000..4f83cea --- /dev/null +++ b/modules/http/settings.py @@ -0,0 +1,6 @@ +global maxHeaderLength +maxHeaderLength = 4096 +global indexPath +indexPath = p(sp,"index") +global readBufferSize +readBufferSize = 32768 \ No newline at end of file diff --git a/modules/main.mods b/modules/main.mods new file mode 100644 index 0000000..e503eda --- /dev/null +++ b/modules/main.mods @@ -0,0 +1,8 @@ +settings.py # User settings +helpers.py # Helper functions +events.py # Event/event handler implementation +exceptions.py # Handle exceptions, close connections +servers.py # Create sockets, optionally with SSL/TLS +connlimit.py # Optional: Limit the amount of connections made by one IP +clients.py # Create and remove client sessions and connections +http/main.mods # HTTP server \ No newline at end of file diff --git a/modules/servers.py b/modules/servers.py new file mode 100644 index 0000000..8b6103d --- /dev/null +++ b/modules/servers.py @@ -0,0 +1,45 @@ +global ssl +import ssl + +global serverThread +class serverThread(threading.Thread): + def __init__(self,socket): + threading.Thread.__init__(self) + self.socket = socket + + def run(self): + connection = False + address = False + while True: + try: + connection,address = self.socket.accept() + except: + continue + + try: + connection.settimeout(timeout) + if not triggerEvent("onConnection",connection,address): raise excConnectionClosed + except Exception as e: + handleException(e) + try: + connection.close() + except: + pass + +global makeServer +def makeServer(host,port,https): + print("Opening " +str(host)+ ":" +str(port)+ " (" +str(https)+ ") ...") + serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serverSocket.bind((host,port)) + serverSocket.settimeout(5) + if https: + serverSocket = ssl.wrap_socket( + serverSocket, + server_side = True, + certfile = https, + ssl_version = ssl.PROTOCOL_TLS + ) + serverSocket.listen(65535) + thread = serverThread(serverSocket) + serverThreads.append(thread) + thread.start() \ No newline at end of file diff --git a/modules/settings.py b/modules/settings.py new file mode 100644 index 0000000..a4910d8 --- /dev/null +++ b/modules/settings.py @@ -0,0 +1,17 @@ +global servers +servers = [ +# Host Port SSL Certificate + ("127.0.0.1", 80, False), +# ("127.0.0.1", 443, "localhost.pem") +] + +global timeout +timeout = 15 # Seconds until the connection should be timed out +global maxConnections +maxConnections = 50 # Maximum connections per IP, needs connlimit.py to be activated +global enableOutThread +enableOutThread = False # Use a seperate thread for data output? +global printExceptions +printExceptions = False # Print exceptions as they happen, enable if you're developing +global clientDebug +clientDebug = False # Print how many clients and threads there are \ No newline at end of file