From 364a647d91565cc275fd4b29e3252449de367100 Mon Sep 17 00:00:00 2001 From: Simon Budig Date: Sat, 20 Jan 2007 00:00:00 +0100 Subject: ancient woof version --- woof | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100755 woof diff --git a/woof b/woof new file mode 100755 index 0000000..1050dc5 --- /dev/null +++ b/woof @@ -0,0 +1,334 @@ +#!/usr/bin/env python +# +# woof -- an ad-hoc single file webserver +# Copyright (C) 2004 Simon Budig +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# A copy of the GNU General Public License is available at +# http://www.fsf.org/licenses/gpl.txt, you can also write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# Darwin support with the help from Mat Caughron, +# Solaris support by Colin Marquardt, +# FreeBSD support with the help from Andy Gimblett, + +import sys, os, popen2, signal, select, socket, getopt, commands +import urllib, BaseHTTPServer +import ConfigParser + +maxdownloads = 1 +cpid = -1 +compressed = True + + +# Utility function to guess the IP (as a string) where the server can be +# reached from the outside. Quite nasty problem actually. + +def find_ip (): + 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"): + 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"): + 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 + + return ip_addr + + +# Main class implementing an HTTP-Requesthandler, that serves just a single +# file and redirects all other requests to this file (this passes the actual +# filename to the client). +# Currently it is impossible to serve different files with different +# instances of this class. + +class FileServHTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler): + server_version = "Simons FileServer" + protocol_version = "HTTP/1.0" + + filename = "." + + def log_request (self, code='-', size='-'): + if code == 200: + BaseHTTPServer.BaseHTTPRequestHandler.log_request (self, code, size) + + + def do_GET (self): + global maxdownloads, cpid, compressed + + # Redirect any request to the filename of the file to serve. + # This hands over the filename to the client. + + self.path = urllib.quote (urllib.unquote (self.path)) + location = "/" + urllib.quote (os.path.basename (self.filename)) + if os.path.isdir (self.filename): + if compressed: + location += ".tar.gz" + else: + location += ".tar" + + if self.path != location: + txt = """\ + + 302 Found + 302 Found here. + \n""" % location + self.send_response (302) + self.send_header ("Location", location) + self.send_header ("Content-type", "text/html") + self.send_header ("Content-Length", str (len (txt))) + self.end_headers () + self.wfile.write (txt) + return + + maxdownloads -= 1 + + # let a separate process handle the actual download, so that + # multiple downloads can happen simultaneously. + + cpid = os.fork () + os.setpgrp () + + if cpid == 0: + # Child process + size = -1 + datafile = None + child = None + + if os.path.isfile (self.filename): + size = os.path.getsize (self.filename) + datafile = open (self.filename) + elif os.path.isdir (self.filename): + os.environ['woof_dir'], os.environ['woof_file'] = os.path.split (self.filename) + if compressed: + arg = 'z' + else: + arg = '' + child = popen2.Popen3 ('cd "$woof_dir";tar c%sf - "$woof_file"' % arg) + datafile = child.fromchild + + self.send_response (200) + self.send_header ("Content-type", "application/octet-stream") + if size >= 0: + self.send_header ("Content-Length", size) + self.end_headers () + + try: + try: + while 1: + if select.select ([datafile], [], [], 2)[0]: + c = datafile.read (1024) + if c: + self.wfile.write (c) + else: + datafile.close () + break + except: + print >>sys.stderr, "Connection broke. Aborting" + + finally: + # for some reason tar doesnt stop working when the pipe breaks + if child: + if child.poll (): + os.killpg (os.getpgid (child.pid), signal.SIGTERM) + + +def serve_files (filename, maxdown = 1, ip_addr = '', port = 8080): + global maxdownloads + + maxdownloads = maxdown + + # We have to somehow push the filename of the file to serve to the + # class handling the requests. This is an evil way to do this... + + FileServHTTPRequestHandler.filename = filename + + try: + httpd = BaseHTTPServer.HTTPServer ((ip_addr, port), + FileServHTTPRequestHandler) + except socket.error: + print >>sys.stderr, "cannot bind to IP address '%s' port %d" % (ip_addr, port) + sys.exit (1) + + if not ip_addr: + ip_addr = find_ip () + if ip_addr: + print "Now serving on http://%s:%s/" % (ip_addr, httpd.server_port) + + while cpid != 0 and maxdownloads > 0: + httpd.handle_request () + + + +def usage (defport, defmaxdown, errmsg = None): + name = os.path.basename (sys.argv[0]) + print >>sys.stderr, """ + Usage: %s [-i ] [-p ] [-c ] [-u] + %s [-i ] [-p ] [-c ] [-u] -s + + Serves a single file times via http on port on IP + address . + When a directory is specified, a .tar.gz archive gets served (or an + uncompressed tar archive when -u is specified), when -s is specified + instead of a filename, %s distributes itself. + + defaults: count = %d, port = %d + + You can specify different defaults in two locations: /etc/woofrc + and ~/.woofrc can be INI-style config files containing the default + port and the default count. The file in the home directory takes + precedence. + + Sample file: + + [main] + port = 8008 + count = 2 + ip = 127.0.0.1 + compressed = true + """ % (name, name, name, defmaxdown, defport) + if errmsg: + print >>sys.stderr, errmsg + print >>sys.stderr + sys.exit (1) + + + +def main (): + global cpid, compressed + + maxdown = 1 + port = 8080 + ip_addr = '' + + config = ConfigParser.ConfigParser() + config.read (['/etc/woofrc', os.path.expanduser('~/.woofrc')]) + + if config.has_option ('main', 'port'): + port = config.getint ('main', 'port') + + if config.has_option ('main', 'count'): + maxdown = config.getint ('main', 'count') + + if config.has_option ('main', 'ip'): + ip_addr = config.get ('main', 'ip') + + if config.has_option ('main', 'compressed'): + compressed = config.getboolean ('main', 'compressed') + + defaultport = port + defaultmaxdown = maxdown + + try: + options, filenames = getopt.getopt (sys.argv[1:], "hsui:c:p:") + except getopt.GetoptError, desc: + usage (defaultport, defaultmaxdown, desc) + + for option, val in options: + if option == '-c': + try: + maxdown = int (val) + if maxdown <= 0: + raise ValueError + except ValueError: + usage (defaultport, defaultmaxdown, + "invalid download count: %r. " + "Please specify an integer >= 0." % val) + + elif option == '-i': + ip_addr = val + + elif option == '-p': + try: + port = int (val) + except ValueError: + usage (defaultport, defaultmaxdown, + "invalid port number: %r. Please specify an integer" % val) + + elif option == '-s': + filenames.append (__file__) + + elif option == '-h': + usage (defaultport, defaultmaxdown) + + elif option == '-u': + compressed = False + + else: + usage (defaultport, defaultmaxdown, "Unknown option: %r" % option) + + if len (filenames) == 1: + filename = os.path.abspath (filenames[0]) + else: + usage (defaultport, defaultmaxdown, + "Can only serve single files/directories.") + + if not os.path.exists (filename): + usage (defaultport, defaultmaxdown, + "%s: No such file or directory" % filenames[0]) + + if not (os.path.isfile (filename) or os.path.isdir (filename)): + usage (defaultport, defaultmaxdown, + "%s: Neither file nor directory" % filenames[0]) + + 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__': + try: + main () + except KeyboardInterrupt: + pass + -- cgit v1.2.3