blob: 35eded7fbf10e59d23f0c8f800c90cb9c9df528a [file] [log] [blame]
# From
# CherryPy code is covered under a BSD License:
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 staticdir
if match and not, 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 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 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)
st = os.stat(path)
except OSError:
# The above block of code copied from 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)
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._cptools.HandlerTool( staticdirindex )