from operator import attrgetter import os localDir = os.path.dirname(__file__) absDir = os.path.join(os.getcwd(), localDir) import re import cherrypy from cherrypy import tools lcase_sort = lambda x,y: cmp(x.lower(), y.lower()) def mount(filepath, script_name): """Mount a new HTTP drop box at the given path and URL.""" box = HTTPDrop(path=filepath) return cherrypy.tree.mount(box, script_name=script_name) class HTTPDrop: """HTTPDrop, a CherryPy 3 controller for file management via HTTP. To use as a "standalone" application: mount(filepath, script_name) To include within another CherryPy 3 application: root.path.to.box = HTTPDrop(path=filepath, section="/path/to/box") cherrypy.tree.mount(root) """ def __init__(self, path, section="", html_index=None): self.section = section.strip(r"\/") self.icons = tools.staticdir.handler(section="%s/icons" % self.section, dir=os.path.join(absDir, "icons")) # Normalize the path so we can test for uplevel attacks in abspath(). self.path = os.path.normpath(path) self.root = tools.staticdir.handler(section="%s/root" % self.section, dir=self.path) if html_index is None: html_index = os.path.join(absDir, "httpdrop.html") self.html_index = html_index def abspath(self, *path): """Join path to self.path and verify.""" path = [x.lstrip(r"\/") for x in path] newpath = os.path.normpath(os.path.join(self.path, *path)) if not newpath.startswith(self.path): cherrypy.log("Uplevel attack to: %s" % repr(path)) raise cherrypy.HTTPError(403, "Don't even *think* about trying " "to walk MY tree.") return newpath ajax_js = tools.staticfile.handler(os.path.join(absDir, "ajax.js")) def index(self, path=None): tools.staticfile.callable(self.html_index) return cherrypy.response.body index.exposed = True def dirs(self, path): """Return a JSON list of the path's subdirectories.""" cherrypy.response.headers["Content-Type"] = "application/x-json" path = self.abspath(path) for root, dirs, files in os.walk(path): dirs.sort(lcase_sort) return repr(dirs) else: # Something went wrong in os.walk. raise IOError("The path %s could not be accessed." % repr(path)) dirs.exposed = True def files(self, path): """Return a JSON list of the path's files.""" cherrypy.response.headers["Content-Type"] = "application/x-json" path = self.abspath(path) for root, dirs, files in os.walk(path): files.sort(lcase_sort) return repr(files) else: # Something went wrong in os.walk. raise IOError("The path %s could not be accessed." % repr(path)) files.exposed = True def upload(self, path, payload, unzip=False): # IE passes the full path in payload.filename, so split off the tail. tail = os.path.split(payload.filename)[1] fname = self.abspath(path, tail) outfile = open(fname, 'wb') while True: data = payload.file.read(8192) if not data: break outfile.write(data) outfile.close() if unzip: try: import zipfile f = zipfile.ZipFile(fname) for name in f.namelist(): open(self.abspath(path, name), "wb").write(f.read(name)) except: cherrypy.log(traceback=True) raise cherrypy.HTTPRedirect("./?path=%s" % path) upload.exposed = True def move(self, oldname, newname): """Move/rename the file at 'oldname' to 'newname'.""" oldname = self.abspath(oldname) newname = self.abspath(newname) os.rename(oldname, newname) cherrypy.response.status = "204 No Content" move.exposed = True def delete(self, path): path = self.abspath(path) os.remove(path) cherrypy.response.status = "204 No Content" delete.exposed = True