| # From http://tools.cherrypy.org/wiki/staticdirindex |
| # CherryPy code is covered under a BSD License: |
| # https://bitbucket.org/cherrypy/cherrypy/src/697c7af588b8/cherrypy/LICENSE.txt |
| |
| import os |
| import re |
| import stat |
| import urllib |
| import cherrypy |
| from cherrypy.lib import cptools, http |
| |
| # Undercover kludge to wrap staticdir |
| from cherrypy.lib.static import staticdir |
| |
| def staticdirindex(section, dir, root="", match="", content_types=None, |
| index="", indexlistermatch="", indexlister=None, **kwargs): |
| """Serve a directory index listing for a dir. |
| |
| Compatibility alert: staticdirindex is built on and is dependent on |
| staticdir and its configurations. staticdirindex only works effectively |
| in locations where staticdir is also configured. staticdirindex is |
| coded to allow easy integration with staticdir, if demand warrants. |
| |
| indexlister must be configured, or no function is performed. |
| indexlister should be a callable that accepts the following parameters: |
| section: same as for staticdir (and implicitly calculated for it) |
| dir: same as for staticdir, but already combined with root |
| path: combination of section and dir |
| |
| Other parameters that are configured for staticdirindex will be passed |
| on to indexlister. |
| |
| Should use priorty > than that of staticdir, so that only directories not |
| served by staticdir, call staticdirindex. |
| |
| """ |
| # first call old staticdir, and see if it does anything |
| sdret = staticdir( section, dir, root, match, content_types, index ) |
| if sdret: |
| return True |
| |
| # if not, then see if we are configured to do anything |
| if indexlister is None: |
| return False |
| |
| req = cherrypy.request |
| response = cherrypy.response |
| |
| match = indexlistermatch |
| |
| # N.B. filename ending in a slash or not does not imply a directory |
| # the following block of code directly copied from static.py staticdir |
| if match and not re.search(match, cherrypy.request.path_info): |
| return False |
| |
| # Allow the use of '~' to refer to a user's home directory. |
| dir = os.path.expanduser(dir) |
| |
| # If dir is relative, make absolute using "root". |
| if not os.path.isabs(dir): |
| if not root: |
| msg = "Static dir requires an absolute dir (or root)." |
| raise ValueError(msg) |
| dir = os.path.join(root, dir) |
| |
| # Determine where we are in the object tree relative to 'section' |
| # (where the static tool was defined). |
| if section == 'global': |
| section = "/" |
| section = section.rstrip(r"\/") |
| branch = cherrypy.request.path_info[len(section) + 1:] |
| branch = urllib.unquote(branch.lstrip(r"\/")) |
| |
| # If branch is "", filename will end in a slash |
| filename = os.path.join(dir, branch) |
| |
| # There's a chance that the branch pulled from the URL might |
| # have ".." or similar uplevel attacks in it. Check that the final |
| # filename is a child of dir. |
| if not os.path.normpath(filename).startswith(os.path.normpath(dir)): |
| raise cherrypy.HTTPError(403) # Forbidden |
| # the above block of code directly copied from static.py staticdir |
| # N.B. filename ending in a slash or not does not imply a directory |
| |
| # Check if path is a directory. |
| |
| path = filename |
| # The following block of code copied from static.py serve_file |
| |
| # If path is relative, users should fix it by making path absolute. |
| # That is, CherryPy should not guess where the application root is. |
| # It certainly should *not* use cwd (since CP may be invoked from a |
| # variety of paths). If using tools.static, you can make your relative |
| # paths become absolute by supplying a value for "tools.static.root". |
| if not os.path.isabs(path): |
| raise ValueError("'%s' is not an absolute path." % path) |
| |
| try: |
| st = os.stat(path) |
| except OSError: |
| # The above block of code copied from static.py serve_file |
| |
| return False |
| |
| if stat.S_ISDIR(st.st_mode): |
| |
| # Set the Last-Modified response header, so that |
| # modified-since validation code can work. |
| response.headers['Last-Modified'] = http.HTTPDate(st.st_mtime) |
| cptools.validate_since() |
| response.body = indexlister( section=section, dir=dir, path=path, |
| **kwargs ) |
| response.headers['Content-Type'] = 'text/html' |
| req.is_index = True |
| return True |
| |
| return False |
| |
| # Replace the real staticdir with our version |
| cherrypy.tools.staticdir = cherrypy._cptools.HandlerTool( staticdirindex ) |