First commit
This commit is contained in:
commit
9f81b8964a
3 changed files with 201 additions and 0 deletions
5
README
Normal file
5
README
Normal file
|
@ -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.
|
24
UNLICENSE
Normal file
24
UNLICENSE
Normal file
|
@ -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]
|
172
gophersrv.py
Normal file
172
gophersrv.py
Normal file
|
@ -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 a new issue