diff --git a/tools/sky_server b/tools/sky_server
index 5308b2de9ee4d81e26ee5f1c7a0b8f26da024342..bd40138855effbc00a1b9b2a17965075cc6803fb 100755
--- a/tools/sky_server
+++ b/tools/sky_server
@@ -6,6 +6,7 @@
import argparse
import os
import cherrypy
+import staticdirindex
BUILD_DIRECTORY = 'out'
@@ -16,6 +17,25 @@ SKY_ROOT = os.path.join(SRC_ROOT, 'sky')
GEN_ROOT = os.path.join(SRC_ROOT, BUILD_DIRECTORY, CONFIG_DIRECTORY, 'gen')
+# FIXME: This should be replaced by just json and inflated into DOM client-side.
+def skydir(section="", dir="", path="", **kwargs):
+ url = "%s%s" % (cherrypy.request.headers.get('Host', ''),
+ cherrypy.request.path_info)
+ sky = "
"
+ sky += ""
+ sky += ''
+ for _, dir_names, file_names in os.walk(path.rstrip(r"\/")):
+ for dir_name in sorted(dir_names):
+ sky += "%s/\n" % (dir_name, dir_name)
+
+ del dir_names[:] # limit to one level
+
+ for file_name in sorted(file_names):
+ sky += "%s\n" % (file_name, file_name)
+ return sky + ""
+
+
# FIXME: This doesn't yet support directory listings. We'll do something like:
# http://tools.cherrypy.org/wiki/staticdirindex
# but have it spit .sky instead of HTML
@@ -43,6 +63,7 @@ def main():
'/': {
'tools.staticdir.on': True,
'tools.staticdir.dir': os.path.abspath(args.app_path),
+ 'tools.staticdir.indexlister': skydir,
},
'/mojo': {
'tools.staticdir.on': True,
diff --git a/tools/staticdirindex.py b/tools/staticdirindex.py
new file mode 100644
index 0000000000000000000000000000000000000000..35eded7fbf10e59d23f0c8f800c90cb9c9df528a
--- /dev/null
+++ b/tools/staticdirindex.py
@@ -0,0 +1,120 @@
+# 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 )