diff MoinMoin/util/send_file.py @ 0:5568cf133caf moin20-repo-reboot

create moin/2.0 repo, drop all history (see notes below) Up to now, we used the moin/2.0-dev repository (which was cloned from another, older moin repo quite some time ago). Over the years, these repositories got rather fat (>200MB) and were a pain to clone over slow, high-latency or unreliable connections. After having finished most of the dirty work in moin2, having killed all the 3rd party code we had bundled with (is now installed by quickinstall / pip / setuptools), it is now a good time to get rid of the history (the history made up most of the repository's size). If you need to look at the history, look there: http://hg.moinmo.in/moin/2.0-dev The new moin/2.0 repository has the files as of this changesets: http://hg.moinmo.in/moin/2.0-dev/rev/075132a755dc The changeset hashes that link the repositories will be tagged (in both repositories) as "moin20-repo-reboot".
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 20 Feb 2011 20:53:45 +0100
parents
children 3e888522973b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/util/send_file.py	Sun Feb 20 20:53:45 2011 +0100
@@ -0,0 +1,156 @@
+"""
+A better send_file
+------------------
+
+Initially, this was a modified implementation of flask 0.6.0's send_file(),
+trying to be as compatible as possible.
+
+For details see: https://github.com/mitsuhiko/flask/issues/issue/104 and the
+history of this file in our repository. This code fixes all the issues
+described in the bug report.
+
+As we forked send_file, we later modified it (without trying to stay
+compatible), because we can easily adapt anyway and the code can be much
+simpler without compatibility code.
+
+This code is under same license as flask.
+Modifications were done by Thomas Waldmann.
+"""
+
+import os
+import mimetypes
+from time import time
+from zlib import adler32
+
+from werkzeug import Headers, wrap_file
+from flask import current_app, request
+
+
+def send_file(filename=None, file=None,
+              mimetype=None,
+              as_attachment=False, attachment_filename=None,
+              mtime=None, cache_timeout=60 * 60 * 12,
+              add_etags=True, etag=None, conditional=False):
+    """Sends the contents of a file to the client.
+
+    A file can be either a filesystem file or a file-like object (this code
+    is careful about not assuming that every file is a filesystem file).
+
+    This will use the most efficient method available, configured and possible
+    (for filesystem files some more optimizations may be possible that for
+    file-like objects not having a filesystem filename).
+    By default it will try to use the WSGI server's file_wrapper support.
+    Alternatively you can set the application's :attr:`~Flask.use_x_sendfile`
+    attribute to ``True`` to directly emit an `X-Sendfile` header.  This
+    however requires support of the underlying webserver for `X-Sendfile`.
+
+    send_file will try to guess some stuff for you if you do not provide them:
+
+    * mimetype (based on filename / attachment_filename)
+    * mtime (based on filesystem file's metadata)
+    * etag (based on filename, mtime, filesystem file size)
+
+    If you do not provide enough information, send_file might raise a
+    TypeError.
+
+    For extra security you probably want to sent certain files as attachment
+    (HTML for instance).
+
+    Please never pass filenames to this function from user sources without
+    checking them first.  Something like this is usually sufficient to
+    avoid security problems::
+
+        if '..' in filename or filename.startswith('/'):
+            abort(404)
+
+    :param filename: the filesystem filename of the file to send (relative to
+                     the :attr:`~Flask.root_path` if a relative path is
+                     specified).
+                     If you just have an open filesystem file object f, give
+                     `f.name` here.
+                     If you don't have a filesystem file nor a filesystem file
+                     name, but just a file-like obj, don't use this argument.
+    :param file: a file (or file-like) object, you may give it if you either do
+                 not have a filesystem filename or if you already have an open
+                 file anyway.
+    :param mimetype: the mimetype of the file if provided, otherwise
+                     auto detection happens based on the filename or
+                     attachment_filename.
+    :param as_attachment: set to `True` if you want to send this file with
+                          a ``Content-Disposition: attachment`` header.
+    :param attachment_filename: the filename for the attachment if it
+                                differs from the filename argument.
+    :param mtime: the modification time of the file if provided, otherwise
+                  it will be determined automatically for filesystem files
+    :param cache_timeout: the timeout in seconds for the headers.
+    :param conditional: set to `True` to enable conditional responses.
+    :param add_etags: set to `False` to disable attaching of etags.
+    :param etag: you can give an etag here, None means to try to compute the
+                 etag from the file's filesystem metadata (the latter of course
+                 only works for filesystem files). If you do not give a
+                 filename, but you use add_etags, you must explicitely provide
+                 the etag as it can't compute it for that case.
+    """
+    if filename and not os.path.isabs(filename):
+        filename = os.path.join(current_app.root_path, filename)
+
+    if mimetype is None and (filename or attachment_filename):
+        mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
+    if mimetype is None:
+        mimetype = 'application/octet-stream'
+
+    headers = Headers()
+    if as_attachment:
+        if attachment_filename is None:
+            if not filename:
+                raise TypeError('filename unavailable, required for sending as attachment')
+            attachment_filename = os.path.basename(filename)
+        headers.add('Content-Disposition', 'attachment', filename=attachment_filename)
+
+    if current_app.use_x_sendfile and filename:
+        if file:
+            file.close()
+        headers['X-Sendfile'] = filename
+        data = None
+    else:
+        if filename:
+            if not file:
+                file = open(filename, 'rb')
+            if mtime is None:
+                mtime = os.path.getmtime(filename)
+        data = wrap_file(request.environ, file)
+
+    rv = current_app.response_class(data, mimetype=mimetype, headers=headers, direct_passthrough=True)
+
+    # if we know the file modification date, we can store it as the
+    # current time to better support conditional requests.  Werkzeug
+    # as of 0.6.1 will override this value however in the conditional
+    # response with the current time.  This will be fixed in Werkzeug
+    # with a new release, however many WSGI servers will still emit
+    # a separate date header.
+    if mtime is not None:
+        rv.date = int(mtime)
+
+    rv.cache_control.public = True
+    if cache_timeout:
+        rv.cache_control.max_age = cache_timeout
+        rv.expires = int(time() + cache_timeout)
+
+    if add_etags:
+        if etag is None and filename:
+            etag = 'flask-%s-%s-%s' % (
+                mtime or os.path.getmtime(filename),
+                os.path.getsize(filename),
+                adler32(filename) & 0xffffffff
+            )
+        if etag is None:
+            raise TypeError("can't determine etag - please give etag or filename")
+        rv.set_etag(etag)
+        if conditional:
+            rv = rv.make_conditional(request)
+            # make sure we don't send x-sendfile for servers that
+            # ignore the 304 status code for x-sendfile.
+            if rv.status_code == 304:
+                rv.headers.pop('x-sendfile', None)
+    return rv
+