#!/usr/bin/env python # webshare: Share a set of directories through a web interface. # Copyright 2003 Adam Sampson # The path to the configuration file. CONFIG_PATH = "/home/azz/.websharerc" # --- import time, sys, os, os.path, cgi, cgitb, stat, urllib, mimetypes, re cgitb.enable() script_name = os.getenv("SCRIPT_NAME") path_info = os.getenv("PATH_INFO") if path_info is None: path_info = "" else: path_info = os.path.normpath(path_info) settings = {None : {}} def get_setting(module, name): global settings if settings.has_key(module) and settings[module].has_key(name): return settings[module][name] elif settings[None].has_key(name): return settings[None][name] else: return None def load_settings(file): global settings module = None f = open(file) for l in f.readlines(): l = l.strip() if l == "" or l[0] == "#": pass elif l[0] == "[" and l[-1] == "]": module = l[1:-1] if not settings.has_key(module): settings[module] = {} else: (name, value) = l.split(" = ", 1) settings[module][name] = value f.close() def nice_size(size): """Return a nicely-formatted file size.""" sizes = [ "B", "KiB", "MiB", "GiB", "TiB" ] s = "empty" for n in range(len(sizes)): r = 1024 ** n if size > r: s = "%.1f %s" % (1.0 * size / r, sizes[n]) return s def href_to(path): global script_name return urllib.quote(script_name + "/" + path) def sendheader(s): print s + "\r\n", def html_headers(status = "200 OK"): sendheader("Status: " + status) sendheader("Content-Type: text/html") sendheader("") def html_top(title): print """ webshare: """ + title + """ """ def html_bottom(): print """ """ def error(status, message): html_headers(status) html_top(status) print "

" + status + "

" print "

" + message + "

" html_bottom() def error_403(): error("403 Forbidden", "File not accessible.") def error_404(): error("404 Not Found", "File not found.") def module_list(): html_headers() html_top("Available modules") print "

Available modules

" print "" html_bottom() if path_info != "" and path_info[0] == "/": path_info = path_info[1:] l = path_info.split("/", 1) if len(l) == 0: (module, path) = (None, "") elif len(l) == 1: (module, path) = (l[0], "") else: (module, path) = l load_settings(CONFIG_PATH) if module is None or module not in settings.keys(): module_list() elif get_setting(module, "disable") is not None: error_403() else: modpath = get_setting(module, "path") if modpath is None: raise "No path specified for module " + module destpath = os.path.join(modpath, path) try: st = os.stat(destpath) except OSError: st = None if st is None: error_404() elif stat.S_ISDIR(st.st_mode): try: l = os.listdir(destpath) except OSError: l = None if l is None: error_403() else: l.sort() html_headers() html_top(path_info) print "

Directory listing

" print """

""" a = "" p = "" for x in path_info.split("/"): a += x + "/" print p + "" + x + "" if p == "": p = "/ " print "

" rate = get_setting(module, "rate") if rate is not None and rate != "none": print """

Bandwidth cap on this directory:""" print nice_size(int(rate)) + "/s.

" print """""" for x in l: fn = os.path.join(destpath, x) try: st = os.stat(fn) if stat.S_ISDIR(st.st_mode): desc = "dir" size = "" elif stat.S_ISREG(st.st_mode): desc = "file" size = nice_size(st.st_size) except OSError: desc = "(error)" size = "" newpath = href_to(path_info + "/" + x) print """""" print "" print "" print "" print "
TypeNameSize
" + desc + "" print "" + x + "" + size + "
" html_bottom() elif stat.S_ISREG(st.st_mode): (type, encoding) = mimetypes.guess_type(destpath) if type is None: type = "application/octet-stream" size = st.st_size start = 0 count = size range = os.getenv("HTTP_RANGE") if range is not None: m = re.match(r'bytes=(\d*)-(\d*)', range) if m is not None: (a, b) = m.group(1, 2) if a == "" and b == "": pass elif a == "": count = int(b) start = size - count elif b == "": start = int(a) count = size - start else: start = int(a) count = int(b) - int(a) if start > size: start = size if count > (size - start): count = size - start try: f = open(destpath) f.seek(start) except IOError: f = None if f is None: error_403() else: if start != 0 or count != size: sendheader("Status: 206 Partial Content") sendheader("Content-Range: bytes " + str(start) + "-" + str(start + count) + "/" + str(size)) else: sendheader("Status: 200 OK") sendheader("Content-Type: " + type) sendheader("Content-Length: " + str(count)) sendheader("") rate = get_setting(module, "rate") if rate == "none": rate = None if rate is not None: rate = float(rate) start = time.time() sent = 0 while count > 0: amount = count if amount > 4096: amount = 4096 try: data = f.read(amount) if data == "": break sys.stdout.write(data) sys.stdout.flush() except IOError: break count -= len(data) sent += len(data) if rate is not None: now = time.time() expected_now = (sent / rate) + start if now < expected_now: time.sleep(expected_now - now) f.close() else: error_403() # request: # Range: bytes=x-y # (where x or y may be empty; "-y" implies "the last y bytes")