| Index: third_party/twisted_8_1/twisted/web/static.py
|
| diff --git a/third_party/twisted_8_1/twisted/web/static.py b/third_party/twisted_8_1/twisted/web/static.py
|
| deleted file mode 100644
|
| index 9d5dd562ef8b2b8bf85210df97cd15b2f96950fb..0000000000000000000000000000000000000000
|
| --- a/third_party/twisted_8_1/twisted/web/static.py
|
| +++ /dev/null
|
| @@ -1,466 +0,0 @@
|
| -# -*- test-case-name: twisted.web.test.test_web -*-
|
| -# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
|
| -# See LICENSE for details.
|
| -
|
| -
|
| -"""I deal with static resources.
|
| -"""
|
| -
|
| -from __future__ import nested_scopes
|
| -
|
| -# System Imports
|
| -import os, stat, string
|
| -import cStringIO
|
| -import traceback
|
| -import warnings
|
| -import types
|
| -StringIO = cStringIO
|
| -del cStringIO
|
| -import urllib
|
| -
|
| -# Sibling Imports
|
| -from twisted.web import server
|
| -from twisted.web import error
|
| -from twisted.web import resource
|
| -from twisted.web.util import redirectTo
|
| -
|
| -# Twisted Imports
|
| -from twisted.web import http
|
| -from twisted.python import threadable, log, components, failure, filepath
|
| -from twisted.internet import abstract, interfaces, defer
|
| -from twisted.spread import pb
|
| -from twisted.persisted import styles
|
| -from twisted.python.util import InsensitiveDict
|
| -from twisted.python.runtime import platformType
|
| -
|
| -
|
| -dangerousPathError = error.NoResource("Invalid request URL.")
|
| -
|
| -def isDangerous(path):
|
| - return path == '..' or '/' in path or os.sep in path
|
| -
|
| -
|
| -class Data(resource.Resource):
|
| - """
|
| - This is a static, in-memory resource.
|
| - """
|
| -
|
| - def __init__(self, data, type):
|
| - resource.Resource.__init__(self)
|
| - self.data = data
|
| - self.type = type
|
| -
|
| - def render(self, request):
|
| - request.setHeader("content-type", self.type)
|
| - request.setHeader("content-length", str(len(self.data)))
|
| - if request.method == "HEAD":
|
| - return ''
|
| - return self.data
|
| -
|
| -def addSlash(request):
|
| - qs = ''
|
| - qindex = string.find(request.uri, '?')
|
| - if qindex != -1:
|
| - qs = request.uri[qindex:]
|
| -
|
| - return "http%s://%s%s/%s" % (
|
| - request.isSecure() and 's' or '',
|
| - request.getHeader("host"),
|
| - (string.split(request.uri,'?')[0]),
|
| - qs)
|
| -
|
| -class Redirect(resource.Resource):
|
| - def __init__(self, request):
|
| - resource.Resource.__init__(self)
|
| - self.url = addSlash(request)
|
| -
|
| - def render(self, request):
|
| - return redirectTo(self.url, request)
|
| -
|
| -
|
| -class Registry(components.Componentized, styles.Versioned):
|
| - """
|
| - I am a Componentized object that will be made available to internal Twisted
|
| - file-based dynamic web content such as .rpy and .epy scripts.
|
| - """
|
| -
|
| - def __init__(self):
|
| - components.Componentized.__init__(self)
|
| - self._pathCache = {}
|
| -
|
| - persistenceVersion = 1
|
| -
|
| - def upgradeToVersion1(self):
|
| - self._pathCache = {}
|
| -
|
| - def cachePath(self, path, rsrc):
|
| - self._pathCache[path] = rsrc
|
| -
|
| - def getCachedPath(self, path):
|
| - return self._pathCache.get(path)
|
| -
|
| -
|
| -def loadMimeTypes(mimetype_locations=['/etc/mime.types']):
|
| - """
|
| - Multiple file locations containing mime-types can be passed as a list.
|
| - The files will be sourced in that order, overriding mime-types from the
|
| - files sourced beforehand, but only if a new entry explicitly overrides
|
| - the current entry.
|
| - """
|
| - import mimetypes
|
| - # Grab Python's built-in mimetypes dictionary.
|
| - contentTypes = mimetypes.types_map
|
| - # Update Python's semi-erroneous dictionary with a few of the
|
| - # usual suspects.
|
| - contentTypes.update(
|
| - {
|
| - '.conf': 'text/plain',
|
| - '.diff': 'text/plain',
|
| - '.exe': 'application/x-executable',
|
| - '.flac': 'audio/x-flac',
|
| - '.java': 'text/plain',
|
| - '.ogg': 'application/ogg',
|
| - '.oz': 'text/x-oz',
|
| - '.swf': 'application/x-shockwave-flash',
|
| - '.tgz': 'application/x-gtar',
|
| - '.wml': 'text/vnd.wap.wml',
|
| - '.xul': 'application/vnd.mozilla.xul+xml',
|
| - '.py': 'text/plain',
|
| - '.patch': 'text/plain',
|
| - }
|
| - )
|
| - # Users can override these mime-types by loading them out configuration
|
| - # files (this defaults to ['/etc/mime.types']).
|
| - for location in mimetype_locations:
|
| - if os.path.exists(location):
|
| - more = mimetypes.read_mime_types(location)
|
| - if more is not None:
|
| - contentTypes.update(more)
|
| -
|
| - return contentTypes
|
| -
|
| -def getTypeAndEncoding(filename, types, encodings, defaultType):
|
| - p, ext = os.path.splitext(filename)
|
| - ext = ext.lower()
|
| - if encodings.has_key(ext):
|
| - enc = encodings[ext]
|
| - ext = os.path.splitext(p)[1].lower()
|
| - else:
|
| - enc = None
|
| - type = types.get(ext, defaultType)
|
| - return type, enc
|
| -
|
| -class File(resource.Resource, styles.Versioned, filepath.FilePath):
|
| - """
|
| - File is a resource that represents a plain non-interpreted file
|
| - (although it can look for an extension like .rpy or .cgi and hand the
|
| - file to a processor for interpretation if you wish). Its constructor
|
| - takes a file path.
|
| -
|
| - Alternatively, you can give a directory path to the constructor. In this
|
| - case the resource will represent that directory, and its children will
|
| - be files underneath that directory. This provides access to an entire
|
| - filesystem tree with a single Resource.
|
| -
|
| - If you map the URL 'http://server/FILE' to a resource created as
|
| - File('/tmp'), then http://server/FILE/ will return an HTML-formatted
|
| - listing of the /tmp/ directory, and http://server/FILE/foo/bar.html will
|
| - return the contents of /tmp/foo/bar.html .
|
| -
|
| - @cvar childNotFound: L{Resource} used to render 404 Not Found error pages.
|
| - """
|
| -
|
| - contentTypes = loadMimeTypes()
|
| -
|
| - contentEncodings = {
|
| - ".gz" : "gzip",
|
| - ".bz2": "bzip2"
|
| - }
|
| -
|
| - processors = {}
|
| -
|
| - indexNames = ["index", "index.html", "index.htm", "index.trp", "index.rpy"]
|
| -
|
| - type = None
|
| -
|
| - ### Versioning
|
| -
|
| - persistenceVersion = 6
|
| -
|
| - def upgradeToVersion6(self):
|
| - self.ignoredExts = []
|
| - if self.allowExt:
|
| - self.ignoreExt("*")
|
| - del self.allowExt
|
| -
|
| - def upgradeToVersion5(self):
|
| - if not isinstance(self.registry, Registry):
|
| - self.registry = Registry()
|
| -
|
| - def upgradeToVersion4(self):
|
| - if not hasattr(self, 'registry'):
|
| - self.registry = {}
|
| -
|
| - def upgradeToVersion3(self):
|
| - if not hasattr(self, 'allowExt'):
|
| - self.allowExt = 0
|
| -
|
| - def upgradeToVersion2(self):
|
| - self.defaultType = "text/html"
|
| -
|
| - def upgradeToVersion1(self):
|
| - if hasattr(self, 'indexName'):
|
| - self.indexNames = [self.indexName]
|
| - del self.indexName
|
| -
|
| - def __init__(self, path, defaultType="text/html", ignoredExts=(), registry=None, allowExt=0):
|
| - """Create a file with the given path.
|
| - """
|
| - resource.Resource.__init__(self)
|
| - filepath.FilePath.__init__(self, path)
|
| - # Remove the dots from the path to split
|
| - self.defaultType = defaultType
|
| - if ignoredExts in (0, 1) or allowExt:
|
| - warnings.warn("ignoredExts should receive a list, not a boolean")
|
| - if ignoredExts or allowExt:
|
| - self.ignoredExts = ['*']
|
| - else:
|
| - self.ignoredExts = []
|
| - else:
|
| - self.ignoredExts = list(ignoredExts)
|
| - self.registry = registry or Registry()
|
| -
|
| - def ignoreExt(self, ext):
|
| - """Ignore the given extension.
|
| -
|
| - Serve file.ext if file is requested
|
| - """
|
| - self.ignoredExts.append(ext)
|
| -
|
| - childNotFound = error.NoResource("File not found.")
|
| -
|
| - def directoryListing(self):
|
| - from twisted.web.woven import dirlist
|
| - return dirlist.DirectoryLister(self.path,
|
| - self.listNames(),
|
| - self.contentTypes,
|
| - self.contentEncodings,
|
| - self.defaultType)
|
| -
|
| - def getChild(self, path, request):
|
| - """See twisted.web.Resource.getChild.
|
| - """
|
| - self.restat()
|
| -
|
| - if not self.isdir():
|
| - return self.childNotFound
|
| -
|
| - if path:
|
| - fpath = self.child(path)
|
| - else:
|
| - fpath = self.childSearchPreauth(*self.indexNames)
|
| - if fpath is None:
|
| - return self.directoryListing()
|
| -
|
| - if not fpath.exists():
|
| - fpath = fpath.siblingExtensionSearch(*self.ignoredExts)
|
| - if fpath is None:
|
| - return self.childNotFound
|
| -
|
| - if platformType == "win32":
|
| - # don't want .RPY to be different than .rpy, since that would allow
|
| - # source disclosure.
|
| - processor = InsensitiveDict(self.processors).get(fpath.splitext()[1])
|
| - else:
|
| - processor = self.processors.get(fpath.splitext()[1])
|
| - if processor:
|
| - return resource.IResource(processor(fpath.path, self.registry))
|
| - return self.createSimilarFile(fpath.path)
|
| -
|
| - # methods to allow subclasses to e.g. decrypt files on the fly:
|
| - def openForReading(self):
|
| - """Open a file and return it."""
|
| - return self.open()
|
| -
|
| - def getFileSize(self):
|
| - """Return file size."""
|
| - return self.getsize()
|
| -
|
| -
|
| - def render(self, request):
|
| - """You know what you doing."""
|
| - self.restat()
|
| -
|
| - if self.type is None:
|
| - self.type, self.encoding = getTypeAndEncoding(self.basename(),
|
| - self.contentTypes,
|
| - self.contentEncodings,
|
| - self.defaultType)
|
| -
|
| - if not self.exists():
|
| - return self.childNotFound.render(request)
|
| -
|
| - if self.isdir():
|
| - return self.redirect(request)
|
| -
|
| - #for content-length
|
| - fsize = size = self.getFileSize()
|
| -
|
| -# request.setHeader('accept-ranges','bytes')
|
| -
|
| - if self.type:
|
| - request.setHeader('content-type', self.type)
|
| - if self.encoding:
|
| - request.setHeader('content-encoding', self.encoding)
|
| -
|
| - try:
|
| - f = self.openForReading()
|
| - except IOError, e:
|
| - import errno
|
| - if e[0] == errno.EACCES:
|
| - return error.ForbiddenResource().render(request)
|
| - else:
|
| - raise
|
| -
|
| - if request.setLastModified(self.getmtime()) is http.CACHED:
|
| - return ''
|
| -
|
| -# Commented out because it's totally broken. --jknight 11/29/04
|
| -# try:
|
| -# range = request.getHeader('range')
|
| -#
|
| -# if range is not None:
|
| -# # This is a request for partial data...
|
| -# bytesrange = string.split(range, '=')
|
| -# assert bytesrange[0] == 'bytes',\
|
| -# "Syntactically invalid http range header!"
|
| -# start, end = string.split(bytesrange[1],'-')
|
| -# if start:
|
| -# f.seek(int(start))
|
| -# if end:
|
| -# end = int(end)
|
| -# size = end
|
| -# else:
|
| -# end = size
|
| -# request.setResponseCode(http.PARTIAL_CONTENT)
|
| -# request.setHeader('content-range',"bytes %s-%s/%s " % (
|
| -# str(start), str(end), str(size)))
|
| -# #content-length should be the actual size of the stuff we're
|
| -# #sending, not the full size of the on-server entity.
|
| -# fsize = end - int(start)
|
| -#
|
| -# request.setHeader('content-length', str(fsize))
|
| -# except:
|
| -# traceback.print_exc(file=log.logfile)
|
| -
|
| - request.setHeader('content-length', str(fsize))
|
| - if request.method == 'HEAD':
|
| - return ''
|
| -
|
| - # return data
|
| - FileTransfer(f, size, request)
|
| - # and make sure the connection doesn't get closed
|
| - return server.NOT_DONE_YET
|
| -
|
| - def redirect(self, request):
|
| - return redirectTo(addSlash(request), request)
|
| -
|
| - def listNames(self):
|
| - if not self.isdir():
|
| - return []
|
| - directory = self.listdir()
|
| - directory.sort()
|
| - return directory
|
| -
|
| - def listEntities(self):
|
| - return map(lambda fileName, self=self: self.createSimilarFile(os.path.join(self.path, fileName)), self.listNames())
|
| -
|
| - def createPickleChild(self, name, child):
|
| - if not os.path.isdir(self.path):
|
| - resource.Resource.putChild(self, name, child)
|
| - # xxx use a file-extension-to-save-function dictionary instead
|
| - if type(child) == type(""):
|
| - fl = open(os.path.join(self.path, name), 'wb')
|
| - fl.write(child)
|
| - else:
|
| - if '.' not in name:
|
| - name = name + '.trp'
|
| - fl = open(os.path.join(self.path, name), 'wb')
|
| - from pickle import Pickler
|
| - pk = Pickler(fl)
|
| - pk.dump(child)
|
| - fl.close()
|
| -
|
| - def createSimilarFile(self, path):
|
| - f = self.__class__(path, self.defaultType, self.ignoredExts, self.registry)
|
| - # refactoring by steps, here - constructor should almost certainly take these
|
| - f.processors = self.processors
|
| - f.indexNames = self.indexNames[:]
|
| - f.childNotFound = self.childNotFound
|
| - return f
|
| -
|
| -class FileTransfer(pb.Viewable):
|
| - """
|
| - A class to represent the transfer of a file over the network.
|
| - """
|
| - request = None
|
| -
|
| - def __init__(self, file, size, request):
|
| - self.file = file
|
| - self.size = size
|
| - self.request = request
|
| - self.written = self.file.tell()
|
| - request.registerProducer(self, 0)
|
| -
|
| - def resumeProducing(self):
|
| - if not self.request:
|
| - return
|
| - data = self.file.read(min(abstract.FileDescriptor.bufferSize, self.size - self.written))
|
| - if data:
|
| - self.written += len(data)
|
| - # this .write will spin the reactor, calling .doWrite and then
|
| - # .resumeProducing again, so be prepared for a re-entrant call
|
| - self.request.write(data)
|
| - if self.request and self.file.tell() == self.size:
|
| - self.request.unregisterProducer()
|
| - self.request.finish()
|
| - self.request = None
|
| -
|
| - def pauseProducing(self):
|
| - pass
|
| -
|
| - def stopProducing(self):
|
| - self.file.close()
|
| - self.request = None
|
| -
|
| - # Remotely relay producer interface.
|
| -
|
| - def view_resumeProducing(self, issuer):
|
| - self.resumeProducing()
|
| -
|
| - def view_pauseProducing(self, issuer):
|
| - self.pauseProducing()
|
| -
|
| - def view_stopProducing(self, issuer):
|
| - self.stopProducing()
|
| -
|
| -
|
| - synchronized = ['resumeProducing', 'stopProducing']
|
| -
|
| -threadable.synchronize(FileTransfer)
|
| -
|
| -"""I contain AsIsProcessor, which serves files 'As Is'
|
| - Inspired by Apache's mod_asis
|
| -"""
|
| -
|
| -class ASISProcessor(resource.Resource):
|
| -
|
| - def __init__(self, path, registry=None):
|
| - resource.Resource.__init__(self)
|
| - self.path = path
|
| - self.registry = registry or Registry()
|
| -
|
| - def render(self, request):
|
| - request.startedWriting = 1
|
| - res = File(self.path, registry=self.registry)
|
| - return res.render(request)
|
|
|