commit
9f81b8964a
3 changed files with 201 additions and 0 deletions
@ -0,0 +1,5 @@
|
||||
It serves gopher, duh. |
||||
Modify lines 37 and 38 to configure. |
||||
Will automatically attempts to execute anything with execute bit (except directories). |
||||
Requires Python 2, as Python 3's string handling it just weird. |
||||
Also responds to HTTP queries. |
@ -0,0 +1,24 @@
|
||||
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. |
||||
|
||||
For more information, please refer to [http://unlicense.org] |
@ -0,0 +1,172 @@
|
||||
#!/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('<!DOCTYPE html>\n' |
||||
'<html>\n' |
||||
'\t<head>\n' |
||||
'\t\t<meta http-equiv="refresh" content="1;URL=%s">\n' |
||||
'\t</head>\n' |
||||
'\t<body>\n' |
||||
'\t\t<p><a href="%s">Redirect to %s</a></p>\n' |
||||
'\t</body>\n' |
||||
'</html>' % (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() |
Loading…
Reference in new issue