MAJ woof
This commit is contained in:
parent
c3d86ec53a
commit
1cd39658f0
253
woof
253
woof
@ -24,17 +24,19 @@
|
||||
# FreeBSD support with the help from Andy Gimblett, <A.M.Gimblett@swansea.ac.uk>
|
||||
# Cygwin support by Stefan Reichör <stefan@xsteve.at>
|
||||
# tarfile usage suggested by Morgan Lefieux <comete@geekandfree.org>
|
||||
# File upload support loosely based on code from Stephen English <steve@secomputing.co.uk>
|
||||
|
||||
import sys, os, socket, getopt, commands
|
||||
import urllib, BaseHTTPServer
|
||||
import sys, os, errno, socket, getopt, commands, tempfile
|
||||
import cgi, urllib, BaseHTTPServer
|
||||
from SocketServer import ThreadingMixIn
|
||||
import ConfigParser
|
||||
import shutil, tarfile, zipfile
|
||||
import struct
|
||||
|
||||
maxdownloads = 1
|
||||
TM = object
|
||||
cpid = -1
|
||||
compressed = 'gz'
|
||||
upload = False
|
||||
|
||||
|
||||
class EvilZipStreamWrapper(TM):
|
||||
@ -91,59 +93,24 @@ class EvilZipStreamWrapper(TM):
|
||||
# reached from the outside. Quite nasty problem actually.
|
||||
|
||||
def find_ip ():
|
||||
if sys.platform == "cygwin":
|
||||
ipcfg = os.popen("ipconfig").readlines()
|
||||
for l in ipcfg:
|
||||
try:
|
||||
candidat = l.split(":")[1].strip()
|
||||
if candidat[0].isdigit():
|
||||
break
|
||||
except:
|
||||
pass
|
||||
return candidat
|
||||
|
||||
os.environ["PATH"] = "/sbin:/usr/sbin:/usr/local/sbin:" + os.environ["PATH"]
|
||||
platform = os.uname()[0];
|
||||
|
||||
if platform == "Linux":
|
||||
netstat = commands.getoutput ("LC_MESSAGES=C netstat -rn")
|
||||
defiface = [i.split ()[-1] for i in netstat.split ('\n')
|
||||
if i.split ()[0] == "0.0.0.0"]
|
||||
elif platform in ("Darwin", "FreeBSD", "NetBSD"):
|
||||
netstat = commands.getoutput ("LC_MESSAGES=C netstat -rn")
|
||||
defiface = [i.split ()[-1] for i in netstat.split ('\n')
|
||||
if len(i) > 2 and i.split ()[0] == "default"]
|
||||
elif platform == "SunOS":
|
||||
netstat = commands.getoutput ("LC_MESSAGES=C netstat -arn")
|
||||
defiface = [i.split ()[-1] for i in netstat.split ('\n')
|
||||
if len(i) > 2 and i.split ()[0] == "0.0.0.0"]
|
||||
else:
|
||||
print >>sys.stderr, "Unsupported platform; please add support for your platform in find_ip().";
|
||||
return None
|
||||
|
||||
if not defiface:
|
||||
return None
|
||||
|
||||
if platform == "Linux":
|
||||
ifcfg = commands.getoutput ("LC_MESSAGES=C ifconfig "
|
||||
+ defiface[0]).split ("inet addr:")
|
||||
elif platform in ("Darwin", "FreeBSD", "SunOS", "NetBSD"):
|
||||
ifcfg = commands.getoutput ("LC_MESSAGES=C ifconfig "
|
||||
+ defiface[0]).split ("inet ")
|
||||
|
||||
if len (ifcfg) != 2:
|
||||
return None
|
||||
ip_addr = ifcfg[1].split ()[0]
|
||||
|
||||
# sanity check
|
||||
try:
|
||||
ints = [ i for i in ip_addr.split (".") if 0 <= int(i) <= 255]
|
||||
if len (ints) != 4:
|
||||
return None
|
||||
except ValueError:
|
||||
return None
|
||||
# we get a UDP-socket for the TEST-networks reserved by IANA.
|
||||
# It is highly unlikely, that there is special routing used
|
||||
# for these networks, hence the socket later should give us
|
||||
# the ip address of the default route.
|
||||
# We're doing multiple tests, to guard against the computer being
|
||||
# part of a test installation.
|
||||
|
||||
candidates = []
|
||||
for test_ip in ["192.0.2.0", "198.51.100.0", "203.0.113.0"]:
|
||||
s = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect ((test_ip, 80))
|
||||
ip_addr = s.getsockname ()[0]
|
||||
s.close ()
|
||||
if ip_addr in candidates:
|
||||
return ip_addr
|
||||
candidates.append (ip_addr)
|
||||
|
||||
return candidates[0]
|
||||
|
||||
|
||||
# Main class implementing an HTTP-Requesthandler, that serves just a single
|
||||
@ -156,15 +123,114 @@ class FileServHTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
server_version = "Simons FileServer"
|
||||
protocol_version = "HTTP/1.0"
|
||||
|
||||
filename = "."
|
||||
filename = "-"
|
||||
|
||||
def log_request (self, code='-', size='-'):
|
||||
if code == 200:
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.log_request (self, code, size)
|
||||
|
||||
|
||||
def do_POST (self):
|
||||
global maxdownloads, upload
|
||||
|
||||
if not upload:
|
||||
self.send_error (501, "Unsupported method (POST)")
|
||||
return
|
||||
|
||||
maxdownloads -= 1
|
||||
|
||||
if maxdownloads < 1:
|
||||
httpd.shutdown()
|
||||
|
||||
# taken from
|
||||
# http://mail.python.org/pipermail/python-list/2006-September/402441.html
|
||||
|
||||
ctype, pdict = cgi.parse_header (self.headers.getheader ('Content-Type'))
|
||||
form = cgi.FieldStorage (fp = self.rfile,
|
||||
headers = self.headers,
|
||||
environ = {'REQUEST_METHOD' : 'POST'},
|
||||
keep_blank_values = 1,
|
||||
strict_parsing = 1)
|
||||
if not form.has_key ("upfile"):
|
||||
self.send_error (403, "No upload provided")
|
||||
return
|
||||
|
||||
upfile = form["upfile"]
|
||||
|
||||
if not upfile.file or not upfile.filename:
|
||||
self.send_error (403, "No upload provided")
|
||||
return
|
||||
|
||||
upfilename = upfile.filename
|
||||
|
||||
if "\\" in upfilename:
|
||||
upfilename = upfilename.split ("\\")[-1]
|
||||
|
||||
upfilename = os.path.basename (upfile.filename)
|
||||
|
||||
destfile = None
|
||||
for suffix in ["", ".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8", ".9"]:
|
||||
destfilename = os.path.join (".", upfilename + suffix)
|
||||
try:
|
||||
destfile = os.open (destfilename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0644)
|
||||
break
|
||||
except OSError, e:
|
||||
if e.errno == errno.EEXIST:
|
||||
continue
|
||||
raise
|
||||
|
||||
if not destfile:
|
||||
upfilename += "."
|
||||
destfile, destfilename = tempfile.mkstemp (prefix = upfilename, dir = ".")
|
||||
|
||||
print >>sys.stderr, "accepting uploaded file: %s -> %s" % (upfilename, destfilename)
|
||||
|
||||
shutil.copyfileobj (upfile.file, os.fdopen (destfile, "w"))
|
||||
|
||||
if upfile.done == -1:
|
||||
self.send_error (408, "upload interrupted")
|
||||
|
||||
txt = """\
|
||||
<html>
|
||||
<head><title>Woof Upload</title></head>
|
||||
<body>
|
||||
<h1>Woof Upload complete</title></h1>
|
||||
<p>Thanks a lot!</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
self.send_response (200)
|
||||
self.send_header ("Content-Type", "text/html")
|
||||
self.send_header ("Content-Length", str (len (txt)))
|
||||
self.end_headers ()
|
||||
self.wfile.write (txt)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def do_GET (self):
|
||||
global maxdownloads, cpid, compressed
|
||||
global maxdownloads, compressed, upload
|
||||
|
||||
# Form for uploading a file
|
||||
if upload:
|
||||
txt = """\
|
||||
<html>
|
||||
<head><title>Woof Upload</title></head>
|
||||
<body>
|
||||
<h1>Woof Upload</title></h1>
|
||||
<form name="upload" method="POST" enctype="multipart/form-data">
|
||||
<p><input type="file" name="upfile" /></p>
|
||||
<p><input type="submit" value="Upload!" /></p>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
self.send_response (200)
|
||||
self.send_header ("Content-Type", "text/html")
|
||||
self.send_header ("Content-Length", str (len (txt)))
|
||||
self.end_headers ()
|
||||
self.wfile.write (txt)
|
||||
return
|
||||
|
||||
# Redirect any request to the filename of the file to serve.
|
||||
# This hands over the filename to the client.
|
||||
@ -189,7 +255,7 @@ class FileServHTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
</html>\n""" % location
|
||||
self.send_response (302)
|
||||
self.send_header ("Location", location)
|
||||
self.send_header ("Content-type", "text/html")
|
||||
self.send_header ("Content-Type", "text/html")
|
||||
self.send_header ("Content-Length", str (len (txt)))
|
||||
self.end_headers ()
|
||||
self.wfile.write (txt)
|
||||
@ -197,27 +263,24 @@ class FileServHTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
|
||||
maxdownloads -= 1
|
||||
|
||||
# let a separate process handle the actual download, so that
|
||||
# multiple downloads can happen simultaneously.
|
||||
if maxdownloads < 1:
|
||||
httpd.shutdown()
|
||||
|
||||
cpid = os.fork ()
|
||||
|
||||
if cpid == 0:
|
||||
# Child process
|
||||
child = None
|
||||
type = None
|
||||
|
||||
if os.path.isfile (self.filename):
|
||||
type = "file"
|
||||
elif os.path.isdir (self.filename):
|
||||
type = "dir"
|
||||
elif self.filename == "-":
|
||||
type = "stdin"
|
||||
|
||||
if not type:
|
||||
print >> sys.stderr, "can only serve files or directories. Aborting."
|
||||
print >> sys.stderr, "can only serve files, directories or stdin. Aborting."
|
||||
sys.exit (1)
|
||||
|
||||
self.send_response (200)
|
||||
self.send_header ("Content-type", "application/octet-stream")
|
||||
self.send_header ("Content-Type", "application/octet-stream")
|
||||
if os.path.isfile (self.filename):
|
||||
self.send_header ("Content-Length",
|
||||
os.path.getsize (self.filename))
|
||||
@ -247,13 +310,20 @@ class FileServHTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
tfile.add (self.filename,
|
||||
arcname=os.path.basename(self.filename))
|
||||
tfile.close ()
|
||||
elif type == "stdin":
|
||||
datafile = sys.stdin
|
||||
shutil.copyfileobj (datafile, self.wfile)
|
||||
except Exception, e:
|
||||
print e
|
||||
print >>sys.stderr, "Connection broke. Aborting"
|
||||
|
||||
|
||||
class ThreadedHTTPServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
"""Handle requests in a separate thread"""
|
||||
|
||||
|
||||
def serve_files (filename, maxdown = 1, ip_addr = '', port = 8080):
|
||||
global maxdownloads
|
||||
global maxdownloads, httpd
|
||||
|
||||
maxdownloads = maxdown
|
||||
|
||||
@ -263,8 +333,7 @@ def serve_files (filename, maxdown = 1, ip_addr = '', port = 8080):
|
||||
FileServHTTPRequestHandler.filename = filename
|
||||
|
||||
try:
|
||||
httpd = BaseHTTPServer.HTTPServer ((ip_addr, port),
|
||||
FileServHTTPRequestHandler)
|
||||
httpd = ThreadedHTTPServer ((ip_addr, port), FileServHTTPRequestHandler)
|
||||
except socket.error:
|
||||
print >>sys.stderr, "cannot bind to IP address '%s' port %d" % (ip_addr, port)
|
||||
sys.exit (1)
|
||||
@ -274,20 +343,21 @@ def serve_files (filename, maxdown = 1, ip_addr = '', port = 8080):
|
||||
if ip_addr:
|
||||
print "Now serving on http://%s:%s/" % (ip_addr, httpd.server_port)
|
||||
|
||||
while cpid != 0 and maxdownloads > 0:
|
||||
httpd.handle_request ()
|
||||
httpd.serve_forever ()
|
||||
|
||||
|
||||
|
||||
def usage (defport, defmaxdown, errmsg = None):
|
||||
name = os.path.basename (sys.argv[0])
|
||||
print >>sys.stderr, """
|
||||
Usage: %s [-i <ip_addr>] [-p <port>] [-c <count>] <file>
|
||||
Usage: %s [-i <ip_addr>] [-p <port>] [-c <count>] [<file>]
|
||||
%s [-i <ip_addr>] [-p <port>] [-c <count>] [-z|-j|-Z|-u] <dir>
|
||||
%s [-i <ip_addr>] [-p <port>] [-c <count>] -s
|
||||
%s [-i <ip_addr>] [-p <port>] [-c <count>] -U
|
||||
|
||||
Serves a single file <count> times via http on port <port> on IP
|
||||
address <ip_addr>.
|
||||
When no filename is specified, or set to '-', then stdin will be read.
|
||||
When a directory is specified, an tar archive gets served. By default
|
||||
it is gzip compressed. You can specify -z for gzip compression,
|
||||
-j for bzip2 compression, -Z for ZIP compression or -u for no compression.
|
||||
@ -296,6 +366,8 @@ def usage (defport, defmaxdown, errmsg = None):
|
||||
|
||||
When -s is specified instead of a filename, %s distributes itself.
|
||||
|
||||
When -U is specified, woof provides an upload form and allows uploading files.
|
||||
|
||||
defaults: count = %d, port = %d
|
||||
|
||||
You can specify different defaults in two locations: /etc/woofrc
|
||||
@ -310,7 +382,8 @@ def usage (defport, defmaxdown, errmsg = None):
|
||||
count = 2
|
||||
ip = 127.0.0.1
|
||||
compressed = gz
|
||||
""" % (name, name, name, name, defmaxdown, defport)
|
||||
""" % (name, name, name, name, name, defmaxdown, defport)
|
||||
|
||||
if errmsg:
|
||||
print >>sys.stderr, errmsg
|
||||
print >>sys.stderr
|
||||
@ -319,7 +392,9 @@ def usage (defport, defmaxdown, errmsg = None):
|
||||
|
||||
|
||||
def main ():
|
||||
global cpid, compressed
|
||||
global cpid, upload, compressed
|
||||
|
||||
filename = '-'
|
||||
|
||||
maxdown = 1
|
||||
port = 8080
|
||||
@ -352,7 +427,7 @@ def main ():
|
||||
defaultmaxdown = maxdown
|
||||
|
||||
try:
|
||||
options, filenames = getopt.getopt (sys.argv[1:], "hszjZui:c:p:")
|
||||
options, filenames = getopt.getopt (sys.argv[1:], "hUszjZui:c:p:")
|
||||
except getopt.GetoptError, desc:
|
||||
usage (defaultport, defaultmaxdown, desc)
|
||||
|
||||
@ -383,6 +458,9 @@ def main ():
|
||||
elif option == '-h':
|
||||
usage (defaultport, defaultmaxdown)
|
||||
|
||||
elif option == '-U':
|
||||
upload = True
|
||||
|
||||
elif option == '-z':
|
||||
compressed = 'gz'
|
||||
elif option == '-j':
|
||||
@ -395,30 +473,29 @@ def main ():
|
||||
else:
|
||||
usage (defaultport, defaultmaxdown, "Unknown option: %r" % option)
|
||||
|
||||
if len (filenames) == 1:
|
||||
filename = os.path.abspath (filenames[0])
|
||||
else:
|
||||
if upload:
|
||||
if len (filenames) > 0:
|
||||
usage (defaultport, defaultmaxdown,
|
||||
"Can only serve single files/directories.")
|
||||
"Conflicting usage: simultaneous up- and download not supported.")
|
||||
filename = None
|
||||
|
||||
else:
|
||||
if len (filenames) == 1:
|
||||
if filenames[0] != "-":
|
||||
filename = os.path.abspath (filenames[0])
|
||||
|
||||
if not os.path.exists (filename):
|
||||
usage (defaultport, defaultmaxdown,
|
||||
"%s: No such file or directory" % filenames[0])
|
||||
"Can only serve single files/directories.")
|
||||
|
||||
if not (os.path.isfile (filename) or os.path.isdir (filename)):
|
||||
usage (defaultport, defaultmaxdown,
|
||||
"%s: Neither file nor directory" % filenames[0])
|
||||
else:
|
||||
filename = "-"
|
||||
|
||||
serve_files (filename, maxdown, ip_addr, port)
|
||||
|
||||
# wait for child processes to terminate
|
||||
if cpid != 0:
|
||||
try:
|
||||
while 1:
|
||||
os.wait ()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user