diff -r 57b4279d8c4e -r 03e267d67478 app/django/views/static.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/django/views/static.py Fri Jul 18 18:22:23 2008 +0000 @@ -0,0 +1,133 @@ +""" +Views and functions for serving static files. These are only to be used +during development, and SHOULD NOT be used in a production setting. +""" + +import mimetypes +import os +import posixpath +import re +import stat +import urllib +from email.Utils import parsedate_tz, mktime_tz + +from django.template import loader +from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseNotModified +from django.template import Template, Context, TemplateDoesNotExist +from django.utils.http import http_date + +def serve(request, path, document_root=None, show_indexes=False): + """ + Serve static files below a given point in the directory structure. + + To use, put a URL pattern such as:: + + (r'^(?P.*)$', 'django.views.static.serve', {'document_root' : '/path/to/my/files/'}) + + in your URLconf. You must provide the ``document_root`` param. You may + also set ``show_indexes`` to ``True`` if you'd like to serve a basic index + of the directory. This index view will use the template hardcoded below, + but if you'd like to override it, you can create a template called + ``static/directory_index``. + """ + + # Clean up given path to only allow serving files below document_root. + path = posixpath.normpath(urllib.unquote(path)) + path = path.lstrip('/') + newpath = '' + for part in path.split('/'): + if not part: + # Strip empty path components. + continue + drive, part = os.path.splitdrive(part) + head, part = os.path.split(part) + if part in (os.curdir, os.pardir): + # Strip '.' and '..' in path. + continue + newpath = os.path.join(newpath, part).replace('\\', '/') + if newpath and path != newpath: + return HttpResponseRedirect(newpath) + fullpath = os.path.join(document_root, newpath) + if os.path.isdir(fullpath): + if show_indexes: + return directory_index(newpath, fullpath) + raise Http404, "Directory indexes are not allowed here." + if not os.path.exists(fullpath): + raise Http404, '"%s" does not exist' % fullpath + # Respect the If-Modified-Since header. + statobj = os.stat(fullpath) + if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), + statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): + return HttpResponseNotModified() + mimetype = mimetypes.guess_type(fullpath)[0] or 'application/octet-stream' + contents = open(fullpath, 'rb').read() + response = HttpResponse(contents, mimetype=mimetype) + response["Last-Modified"] = http_date(statobj[stat.ST_MTIME]) + response["Content-Length"] = len(contents) + return response + +DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ + + + + + + + Index of {{ directory|escape }} + + +

Index of {{ directory|escape }}

+ + + +""" + +def directory_index(path, fullpath): + try: + t = loader.get_template('static/directory_index') + except TemplateDoesNotExist: + t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default directory index template') + files = [] + for f in os.listdir(fullpath): + if not f.startswith('.'): + if os.path.isdir(os.path.join(fullpath, f)): + f += '/' + files.append(f) + c = Context({ + 'directory' : path + '/', + 'file_list' : files, + }) + return HttpResponse(t.render(c)) + +def was_modified_since(header=None, mtime=0, size=0): + """ + Was something modified since the user last downloaded it? + + header + This is the value of the If-Modified-Since header. If this is None, + I'll just return True. + + mtime + This is the modification time of the item we're talking about. + + size + This is the size of the item we're talking about. + """ + try: + if header is None: + raise ValueError + matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header, + re.IGNORECASE) + header_mtime = mktime_tz(parsedate_tz(matches.group(1))) + header_len = matches.group(3) + if header_len and int(header_len) != size: + raise ValueError + if mtime > header_mtime: + raise ValueError + except (AttributeError, ValueError): + return True + return False