def handle(self): """Handle Gopher+ request.""" self.handlemethod = None if self.gopherpstring[0] == '+': self.handlemethod = 'documentonly' elif self.gopherpstring == '!': self.handlemethod = 'infoonly' elif self.gopherpstring[0] == '$': self.handlemethod = 'gopherplusdir' try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() if self.handlemethod == 'infoonly': self.wfile.write("+-2\r\n") self.wfile.write(self.renderobjinfo(self.entry)) else: handler.prepare() self.wfile.write("+" + str(self.entry.getsize(-2)) + "\r\n") if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: handler.write(self.wfile) except GopherExceptions.FileNotFound as e: self.filenotfound(str(e)) except IOError as e: GopherExceptions.log(e, self, None) self.filenotfound(e[1])
def testlog_basic(self): try: raise IOError("foo") except IOError as e: GopherExceptions.log(e) self.assertEqual(self.stringfile.getvalue(), "unknown-address [None/None] EXCEPTION OSError: foo\n")
def handle(self): host, path, content_length = self.request.strip().split(" ") self.selector = urllib.parse.unquote(path, errors="surrogateescape") content_length = int(content_length) if content_length: data = self.rfile.read(content_length) self.searchrequest = data.decode(errors="surrogateescape") try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() handler.prepare() except GopherExceptions.FileNotFound as e: self.write_status(4, str(e)) return except IOError as e: GopherExceptions.log(e, self, None) self.write_status(5, e.args[1]) return if handler.isdir(): self.write_status(2, "text/gemini") self.writedir(self.entry, handler.getdirlist()) else: mimetype = self.adjust_mimetype(self.entry.getmimetype()) self.write_status(2, mimetype) self.handler.write(self.wfile)
def handle(self): """Handle Gopher+ request.""" self.handlemethod = None if self.gopherpstring[0] == "+": self.handlemethod = "documentonly" elif self.gopherpstring == "!": self.handlemethod = "infoonly" elif self.gopherpstring[0] == "$": self.handlemethod = "gopherplusdir" try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() if self.handlemethod == "infoonly": self.wfile.write(b"+-2\r\n") self.wfile.write( self.renderobjinfo(self.entry).encode(errors="surrogateescape") ) else: handler.prepare() self.wfile.write(f"+{self.entry.getsize(-2)}\r\n".encode()) if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: handler.write(self.wfile) except GopherExceptions.FileNotFound as e: self.filenotfound(str(e)) except IOError as e: GopherExceptions.log(e, self, None) self.filenotfound(e.args[1])
def handle(self): self.canhandlerequest() # To get self.requestparts self.iconmapping = eval( self.config.get("protocols.http.HTTPProtocol", "iconmapping")) self.headerslurp() splitted = self.requestparts[1].split('?') self.selector = splitted[0] self.selector = urllib.parse.unquote(self.selector) self.selector = self.slashnormalize(self.selector) self.formvals = {} if len(splitted) >= 2: self.formvals = cgi.parse_qs(splitted[1]) if 'searchrequest' in self.formvals: self.searchrequest = self.formvals['searchrequest'][0] icon = re.match('/PYGOPHERD-HTTPPROTO-ICONS/(.+)$', self.selector) if icon: iconname = icon.group(1) if iconname in icons: self.wfile.write(b"HTTP/1.0 200 OK\r\n") self.wfile.write( b"Last-Modified: Fri, 14 Dec 2001 21:19:47 GMT\r\n") self.wfile.write(b"Content-Type: image/gif\r\n\r\n") if self.requestparts[0] == 'HEAD': return self.wfile.write(binascii.unhexlify(icons[iconname])) return try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() handler.prepare() headers = "HTTP/1.0 200 OK\r\n" if self.entry.getmtime() != None: gmtime = time.gmtime(self.entry.getmtime()) mtime = time.strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime) headers += "Last-Modified: " + mtime + "\r\n" mimetype = self.entry.getmimetype() mimetype = self.adjustmimetype(mimetype) headers += "Content-Type: " + mimetype + "\r\n\r\n" self.wfile.write(headers.encode('ascii')) if self.requestparts[0] == 'GET': if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: self.handlerwrite(self.wfile) except GopherExceptions.FileNotFound as e: self.filenotfound(e.errno) except IOError as e: GopherExceptions.log(e, self, None) self.filenotfound(e.errno)
def handle(self): request = self.rfile.readline() protohandler = ProtocolMultiplexer.getProtocol( request, self.server, self, self.rfile, self.wfile, self.server.config ) try: protohandler.handle() except socket.error, e: if not (e[0] in [errno.ECONNRESET, errno.EPIPE]): traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None)
def handle(self): request = self.rfile.readline() protohandler = \ ProtocolMultiplexer.getProtocol(request, \ self.server, self, self.rfile, self.wfile, self.server.config) try: protohandler.handle() except socket.error, e: if not (e[0] in [errno.ECONNRESET, errno.EPIPE]): traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None)
def getserverobject(config): # Pick up the server type from the config. servertype = eval("socketserver." + config.get("pygopherd", "servertype")) class MyServer(servertype): allow_reuse_address = 1 def server_bind(self): """Override server_bind to store server name.""" servertype.server_bind(self) # Set a timeout. if config.has_option('pygopherd', 'timeout'): mytimeout = struct.pack( "ll", int(config.get('pygopherd', 'timeout')), 0) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, mytimeout) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, mytimeout) #self.socket.settimeout(int(config.get('pygopherd', 'timeout'))) host, port = self.socket.getsockname() if config.has_option("pygopherd", "servername"): self.server_name = config.get("pygopherd", "servername") else: self.server_name = socket.getfqdn(host) if config.has_option("pygopherd", "advertisedport"): self.server_port = config.getint("pygopherd", "advertisedport") else: self.server_port = port # Instantiate a server. Has to be done before the security so we can # get a privileged port if necessary. interface = '' if config.has_option('pygopherd', 'interface'): servername = config.get('pygopherd', 'interface') interface = config.get('pygopherd', 'interface') try: s = MyServer((interface, config.getint('pygopherd', 'port')), GopherRequestHandler) except: GopherExceptions.log(sys.exc_info()[1], None, None) logger.log("Application startup NOT successful!") raise s.config = config return s
def handle(self): """Handles the request.""" try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() handler.prepare() if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: handler.write(self.wfile) except GopherExceptions.FileNotFound as e: self.filenotfound(str(e)) except IOError as e: GopherExceptions.log(e, self, None) self.filenotfound(e[1])
def getserverobject(config): # Pick up the server type from the config. servertype = eval("SocketServer." + config.get("pygopherd", "servertype")) class MyServer(servertype): allow_reuse_address = 1 def server_bind(self): """Override server_bind to store server name.""" servertype.server_bind(self) # Set a timeout. if config.has_option("pygopherd", "timeout"): mytimeout = struct.pack("ll", long(config.get("pygopherd", "timeout")), 0) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, mytimeout) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, mytimeout) # self.socket.settimeout(int(config.get('pygopherd', 'timeout'))) host, port = self.socket.getsockname() if config.has_option("pygopherd", "servername"): self.server_name = config.get("pygopherd", "servername") else: self.server_name = socket.getfqdn(host) if config.has_option("pygopherd", "advertisedport"): self.server_port = config.getint("pygopherd", "advertisedport") else: self.server_port = port # Instantiate a server. Has to be done before the security so we can # get a privileged port if necessary. interface = "" if config.has_option("pygopherd", "interface"): servername = config.get("pygopherd", "interface") interface = config.get("pygopherd", "interface") try: s = MyServer((interface, config.getint("pygopherd", "port")), GopherRequestHandler) except: GopherExceptions.log(sys.exc_info()[1], None, None) logger.log("Application startup NOT successful!") raise s.config = config return s
def handle(self): request = self.rfile.readline() protohandler = \ ProtocolMultiplexer.getProtocol(request, \ self.server, self, self.rfile, self.wfile, self.server.config) try: protohandler.handle() except socket.error as e: if not (e[0] in [errno.ECONNRESET, errno.EPIPE]): traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None) except: if GopherExceptions.tracebacks: # Yes, this may be invalid. Not much else we can do. #traceback.print_exc(file = self.wfile) traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None)
def handle(self) -> None: request = self.rfile.readline().decode(errors="surrogateescape") protohandler = ProtocolMultiplexer.getProtocol( request, self.server, self, self.rfile, self.wfile, self.server.config ) try: protohandler.handle() except IOError as e: if not (e.errno in [errno.ECONNRESET, errno.EPIPE]): traceback.print_exc() GopherExceptions.log(e, protohandler, None) except Exception as e: if GopherExceptions.tracebacks: # Yes, this may be invalid. Not much else we can do. # traceback.print_exc(file = self.wfile) traceback.print_exc() GopherExceptions.log(e, protohandler, None)
class GopherPlusProtocol(GopherProtocol): """Implementation of Gopher+ protocol. Will handle Gopher+ queries ONLY.""" def canhandlerequest(self): """We can handle the request IF: * It has more than one parameter in the request list * The second parameter is ! or starts with + or $""" if len(self.requestlist) < 2: return 0 if len(self.requestlist) == 2: self.gopherpstring = self.requestlist[1] elif len(self.requestlist) == 3: self.gopherpstring = self.requestlist[2] self.searchrequest = self.requestlist[1] else: return 0 # Too many params. return self.gopherpstring[0] == '+' or \ self.gopherpstring == '!' or \ self.gopherpstring[0] == '$' def handle(self): """Handle Gopher+ request.""" self.handlemethod = None if self.gopherpstring[0] == '+': self.handlemethod = 'documentonly' elif self.gopherpstring == '!': self.handlemethod = 'infoonly' elif self.gopherpstring[0] == '$': self.handlemethod = 'gopherplusdir' try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() if self.handlemethod == 'infoonly': self.wfile.write("+-2\r\n") self.wfile.write(self.renderobjinfo(self.entry)) else: handler.prepare() self.wfile.write("+" + str(self.entry.getsize(-2)) + "\r\n") if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: handler.write(self.wfile) except GopherExceptions.FileNotFound, e: self.filenotfound(str(e)) except IOError, e: GopherExceptions.log(e, self, None) self.filenotfound(e[1])
def handle(self): # Be overly permissive here and ignore most request validation like # checking for a strict <CR><LF> or denying requests over 1024 bytes. url_parts = urllib.parse.urlparse(self.request.strip()) selector = url_parts.path searchrequest = url_parts.query if selector.startswith(self.query_prefix): self.handle_input(selector, searchrequest) return self.selector = urllib.parse.unquote(selector, errors="surrogateescape") self.searchrequest = urllib.parse.unquote(searchrequest, errors="surrogateescape") try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() handler.prepare() except GopherExceptions.FileNotFound as e: self.write_status(51, str(e)) return except IOError as e: GopherExceptions.log(e, self, None) self.write_status(51, e.args[1]) return if handler.isdir(): self.write_status(20, "text/gemini") self.writedir(self.entry, handler.getdirlist()) else: mimetype = self.adjust_mimetype(self.entry.getmimetype()) self.write_status(20, mimetype) self.handler.write(self.wfile)
def get_server( config: ConfigParser, context: typing.Optional[ssl.SSLContext] = None ) -> pygopherd.server.BaseServer: # Pick up the server type from the config. server_class: typing.Type[pygopherd.server.BaseServer] server_type = config.get("pygopherd", "servertype") if server_type == "ForkingTCPServer": server_class = pygopherd.server.ForkingTCPServer elif server_type == "ThreadingTCPServer": server_class = pygopherd.server.ThreadingTCPServer else: raise RuntimeError(f"Invalid servertype option: {server_type}") # Instantiate a server. Has to be done before the security so we can # get a privileged port if necessary. interface = "" if config.has_option("pygopherd", "interface"): interface = config.get("pygopherd", "interface") port = config.getint("pygopherd", "port") address = (interface, port) try: server = server_class(config, address, GopherRequestHandler, context=context) except Exception as e: GopherExceptions.log(e, None, None) logger.log("Application startup NOT successful!") raise return server
def getHandler( selector: str, searchrequest: str, protocol: BaseGopherProtocol, config: configparser.ConfigParser, handlerlist: typing.Optional[typing.List[BaseHandler]] = None, vfs: typing.Optional[VFS_Real] = None, ): """Called without handlerlist specified, uses the default as listed in config.""" global handlers, rootpath init_default_handlers(config) if vfs is None: vfs = VFS_Real(config) if handlerlist is None: handlerlist = handlers typing.cast(handlers, typing.List[BaseHandler]) typing.cast(rootpath, str) # SECURITY: assert that our absolute path is within the absolute # path of the site root. # if not os.path.abspath(rootpath + '/' + selector). \ # startswith(os.path.abspath(rootpath)): # raise GopherExceptions.FileNotFound, \ # [selector, "Requested document is outside the server root", # protocol] statresult = None try: statresult = vfs.stat(selector) except OSError: pass for handler in handlerlist: htry = handler(selector, searchrequest, protocol, config, statresult, vfs) if htry.isrequestforme(): return htry.gethandler() raise GopherExceptions.FileNotFound(selector, "no handler found", protocol)
def getHandler(selector, searchrequest, protocol, config, handlerlist=None, vfs=None): """Called without handlerlist specified, uses the default as listed in config.""" global handlers, rootpath if vfs == None: from pygopherd.handlers.base import VFS_Real vfs = VFS_Real(config) if not handlers: handlers = eval(config.get("handlers.HandlerMultiplexer", "handlers")) rootpath = config.get("pygopherd", "root") if handlerlist == None: handlerlist = handlers # TODO: assert that our absolute path is within the absolute # path of the site root. #if not os.path.abspath(rootpath + '/' + selector). \ # startswith(os.path.abspath(rootpath)): # raise GopherExceptions.FileNotFound, \ # [selector, "Requested document is outside the server root", # protocol] statresult = None try: statresult = vfs.stat(selector) except OSError: pass for handler in handlerlist: htry = handler(selector, searchrequest, protocol, config, statresult, vfs) if htry.isrequestforme(): return htry.gethandler() raise GopherExceptions.FileNotFound( [selector, "no handler found", protocol])
def testlog_basic(self): try: raise IOError, "foo" except IOError, e: GopherExceptions.log(e)
class HTTPProtocol(BaseGopherProtocol): def canhandlerequest(self): self.requestparts = map(lambda arg: arg.strip(), self.request.split(" ")) return len(self.requestparts) == 3 and \ (self.requestparts[0] == 'GET' or self.requestparts[0] == 'HEAD') and \ self.requestparts[2][0:5] == 'HTTP/' def headerslurp(self): if hasattr(self.requesthandler, 'pygopherd_http_slurped'): # Already slurped. self.httpheaders = self.requesthandler.pygopherd_http_slurped return # Slurp up remaining lines. self.httpheaders = {} while 1: line = self.rfile.readline() if not len(line): break line = line.strip() if not len(line): break splitline = line.split(':', 1) if len(splitline) == 2: self.httpheaders[splitline[0].lower()] = splitline[1] self.requesthandler.pygopherd_http_slurped = self.httpheaders def handle(self): self.canhandlerequest() # To get self.requestparts self.iconmapping = eval(self.config.get("protocols.http.HTTPProtocol", "iconmapping")) self.headerslurp() splitted = self.requestparts[1].split('?') self.selector = splitted[0] self.selector = urllib.unquote(self.selector) self.selector = self.slashnormalize(self.selector) self.formvals = {} if len(splitted) >= 2: self.formvals = cgi.parse_qs(splitted[1]) if self.formvals.has_key('searchrequest'): self.searchrequest = self.formvals['searchrequest'][0] icon = re.match('/PYGOPHERD-HTTPPROTO-ICONS/(.+)$', self.selector) if icon: iconname = icon.group(1) if icons.has_key(iconname): self.wfile.write("HTTP/1.0 200 OK\r\n") self.wfile.write("Last-Modified: Fri, 14 Dec 2001 21:19:47 GMT\r\n") self.wfile.write("Content-Type: image/gif\r\n\r\n") if self.requestparts[0] == 'HEAD': return self.wfile.write(binascii.unhexlify(icons[iconname])) return try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() handler.prepare() self.wfile.write("HTTP/1.0 200 OK\r\n") if self.entry.getmtime() != None: gmtime = time.gmtime(self.entry.getmtime()) mtime = time.strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime) self.wfile.write("Last-Modified: " + mtime + "\r\n") mimetype = self.entry.getmimetype() mimetype = self.adjustmimetype(mimetype) self.wfile.write("Content-Type: " + mimetype + "\r\n\r\n") if self.requestparts[0] == 'GET': if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: self.handlerwrite(self.wfile) except GopherExceptions.FileNotFound, e: self.filenotfound(str(e)) except IOError, e: GopherExceptions.log(e, self, None) self.filenotfound(e[1])
class BaseGopherProtocol: """Skeleton protocol -- includes commonly-used routines.""" def __init__(self, request, server, requesthandler, rfile, wfile, config): """Parameters are: request -- the raw request string. server -- a SocketServer object. rfile -- input file. The first line will already have been read. wfile -- output file. Where the output should be sent. config -- a ConfigParser object.""" self.request = request requestparts = map(lambda arg: arg.strip(), request.split("\t")) self.rfile = rfile self.wfile = wfile self.config = config self.server = server self.requesthandler = requesthandler self.requestlist = requestparts self.searchrequest = None self.handler = None selector = requestparts[0] selector = self.slashnormalize(selector) self.selector = selector def slashnormalize(self, selector): """Normalize slashes in the selector. Make sure it starts with a slash and does not end with one. If it is a root directory request, make sure it is exactly '/'. Returns result.""" if len(selector) and selector[-1] == '/': selector = selector[0:-1] if len(selector) == 0 or selector[0] != '/': selector = '/' + selector return selector def canhandlerequest(self): """Decides whether or not a given request is valid for this protocol. Should be overridden by all subclasses.""" return 0 def log(self, handler): """Log a handled request.""" logger.log("%s [%s/%s]: %s" % \ (self.requesthandler.client_address[0], re.search("[^.]+$", str(self.__class__)).group(0), re.search("[^.]+$", str(handler.__class__)).group(0), self.selector)) def handle(self): """Handles the request.""" try: handler = self.gethandler() self.log(handler) self.entry = handler.getentry() handler.prepare() if handler.isdir(): self.writedir(self.entry, handler.getdirlist()) else: handler.write(self.wfile) except GopherExceptions.FileNotFound, e: self.filenotfound(str(e)) except IOError, e: GopherExceptions.log(e, self, None) self.filenotfound(e[1])
def init_exceptions(config: ConfigParser) -> None: GopherExceptions.init(config.getboolean("pygopherd", "tracebacks"))
protohandler = ProtocolMultiplexer.getProtocol( request, self.server, self, self.rfile, self.wfile, self.server.config ) try: protohandler.handle() except socket.error, e: if not (e[0] in [errno.ECONNRESET, errno.EPIPE]): traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None) except: if GopherExceptions.tracebacks: # Yes, this may be invalid. Not much else we can do. # traceback.print_exc(file = self.wfile) traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None) def getserverobject(config): # Pick up the server type from the config. servertype = eval("SocketServer." + config.get("pygopherd", "servertype")) class MyServer(servertype): allow_reuse_address = 1 def server_bind(self): """Override server_bind to store server name.""" servertype.server_bind(self) # Set a timeout.
def initexceptions(config): GopherExceptions.init(config.getboolean("pygopherd", "tracebacks"))
protohandler = \ ProtocolMultiplexer.getProtocol(request, \ self.server, self, self.rfile, self.wfile, self.server.config) try: protohandler.handle() except socket.error, e: if not (e[0] in [errno.ECONNRESET, errno.EPIPE]): traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None) except: if GopherExceptions.tracebacks: # Yes, this may be invalid. Not much else we can do. #traceback.print_exc(file = self.wfile) traceback.print_exc() GopherExceptions.log(sys.exc_info()[1], protohandler, None) def getserverobject(config): # Pick up the server type from the config. servertype = eval("SocketServer." + config.get("pygopherd", "servertype")) class MyServer(servertype): allow_reuse_address = 1 def server_bind(self): """Override server_bind to store server name.""" servertype.server_bind(self) # Set a timeout. if config.has_option('pygopherd', 'timeout'):