#!/usr/bin/python # Software written by Juhani Haverinen (nortti). Influenced in idea and # some implementation details by https://github.com/puckipedia/pyGopher/ # ----------------------------------------------------------------------- # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # ----------------------------------------------------------------------- # NOTE: Requires python 2 due to python 3's braindeadness. import os import socket import stat import subprocess import threading # Config port = 7070 gopherroot = os.environ['HOME']+'/gopher' # Set up socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('', port)) sock.listen(1) # Helper functions def exist(path): return os.access(path, os.F_OK) def isdir(path): st = os.stat(path) return stat.S_ISDIR(st.st_mode) def isexecutable(path): return os.access(path, os.X_OK) def normalizepath(path): path = path.split('/') while '' in path: path.remove('') while '..' in path: i = path.index('..') if i == 0: return None # Attempted to access something outside gopherroot path = path[:i-1] + path[i+1:] return '/'.join(path) # Error handling def notfounderror(conn, path): conn.sendall('3"%s" does not exist\t/\t(null)\t0\n' % path) def notallowederror(conn, path): conn.sendall('3Access denied\t/\t(null)\t0\n') # Server implementation def getrequest(conn): data = '' while True: chunk = conn.recv(1024) if not chunk: return None data += chunk if data[-1] == '\n': break while len(data) > 0 and data[-1] in ('\r', '\n'): data = data[:-1] # Minimal HTTP support if len(data) >= 4 and data[:4] == 'GET ': data = data.split()[1] return data.split('\t') def serveurlredirect(conn, path): path = path[4:] conn.sendall('\n' '\n' '\t\n' '\t\t\n' '\t\n' '\t\n' '\t\t

Redirect to %s

\n' '\t\n' '' % (path, path, path)) def servecommon(conn, fd): for line in fd: conn.sendall(line) fd.close() def servecgi(conn, path): proc = subprocess.Popen([path], stdout=subprocess.PIPE) servecommon(conn, proc.stdout) def servefile(conn, path): f = open(path, 'r') servecommon(conn, f) def serverequest(conn, request): # URL link extension if len(request[0]) >= 4 and request[0][:4] == 'URL:': return serveurlredirect(conn, request[0]) reqpath = normalizepath(request[0]) if reqpath == None: return notallowederror(conn, reqpath) path = gopherroot + '/' + reqpath if not exist(path): return notfounderror(conn, reqpath) if isdir(path): if exist(path + '/gophermap'): path = path + '/gophermap' else: return notfounderror(conn, reqpath) if isexecutable(path): servecgi(conn, path) else: servefile(conn, path) class Serve(threading.Thread): def __init__(self, conn): self.conn = conn threading.Thread.__init__(self) def run(self): try: request = getrequest(self.conn) if not request: self.conn.shutdown(socket.SHUT_RDWR) self.conn.close() return serverequest(self.conn, request) self.conn.shutdown(socket.SHUT_RDWR) self.conn.close() except socket.error: self.conn.close() while True: conn, addr = sock.accept() Serve(conn).start()