[CalendarServer-changes] [4006] CalendarServer/branches/exarkun/update-twisted-3816
source_changes at macosforge.org
source_changes at macosforge.org
Mon Apr 13 10:40:29 PDT 2009
Revision: 4006
http://trac.macosforge.org/projects/calendarserver/changeset/4006
Author: exarkun at twistedmatrix.com
Date: 2009-04-13 10:40:29 -0700 (Mon, 13 Apr 2009)
Log Message:
-----------
All the rest of these applied to the Twisted branch
Modified Paths:
--------------
CalendarServer/branches/exarkun/update-twisted-3816/run
Removed Paths:
-------------
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.auth.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.element.parser.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.delete.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.resource.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.static.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.auth.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.auth.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.auth.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,83 +0,0 @@
-Index: twisted/web2/dav/auth.py
-===================================================================
---- twisted/web2/dav/auth.py (revision 19773)
-+++ twisted/web2/dav/auth.py (working copy)
-@@ -5,7 +5,13 @@
- from twisted.web2.dav import davxml
- from twisted.web2.dav.davxml import twisted_private_namespace
-
--__all__ = ["PrincipalCredentials", "AuthenticationWrapper"]
-+__all__ = [
-+ "IPrincipal",
-+ "DavRealm",
-+ "IPrincipalCredentials",
-+ "PrincipalCredentials",
-+ "AuthenticationWrapper",
-+]
-
- class AuthenticationWrapper(WrapperResource):
- def __init__(self, resource, portal, credentialFactories, loginInterfaces):
-@@ -40,7 +46,7 @@
-
- def requestAvatar(self, avatarId, mind, *interfaces):
- if IPrincipal in interfaces:
-- return IPrincipal, davxml.Principal(davxml.HRef(avatarId))
-+ return IPrincipal, davxml.Principal(davxml.HRef(avatarId[0])), davxml.Principal(davxml.HRef(avatarId[1]))
-
- raise NotImplementedError("Only IPrincipal interface is supported")
-
-@@ -52,33 +58,44 @@
- class PrincipalCredentials(object):
- implements(IPrincipalCredentials)
-
-- def __init__(self, principal, principalURI, credentials):
-- self.principal = principal
-- self.principalURI = principalURI
-+ def __init__(self, authnPrincipal, authzPrincipal, credentials):
-+ """
-+ Initialize with both authentication and authorization values. Note that in most cases theses will be the same
-+ since HTTP auth makes no distinction between the two - but we may be layering some addition auth on top of this
-+ (.e.g.. proxy auth, cookies, forms etc) that make result in authentication and authorization being different.
-+
-+ @param authnPrincipal: L{IDAVPrincipalResource} for the authenticated principal.
-+ @param authnURI: C{str} containing the URI of the authenticated principal.
-+ @param authzPrincipal: L{IDAVPrincipalResource} for the authorized principal.
-+ @param authzURI: C{str} containing the URI of the authorized principal.
-+ @param credentials: L{ICredentials} for the authentication credentials.
-+ """
-+ self.authnPrincipal = authnPrincipal
-+ self.authzPrincipal = authzPrincipal
- self.credentials = credentials
-
- def checkPassword(self, password):
- return self.credentials.checkPassword(password)
-
-
--class TwistedPropertyChecker:
-+class TwistedPropertyChecker(object):
- implements(checkers.ICredentialsChecker)
-
- credentialInterfaces = (IPrincipalCredentials,)
-
-- def _cbPasswordMatch(self, matched, principalURI):
-+ def _cbPasswordMatch(self, matched, principalURIs):
- if matched:
-- return principalURI
-+ # We return both URIs
-+ return principalURIs
- else:
-- raise error.UnauthorizedLogin(
-- "Bad credentials for: %s" % (principalURI,))
-+ raise error.UnauthorizedLogin("Bad credentials for: %s" % (principalURIs[0],))
-
- def requestAvatarId(self, credentials):
- pcreds = IPrincipalCredentials(credentials)
-- pswd = str(pcreds.principal.readDeadProperty(TwistedPasswordProperty))
-+ pswd = str(pcreds.authnPrincipal.readDeadProperty(TwistedPasswordProperty))
-
- d = defer.maybeDeferred(credentials.checkPassword, pswd)
-- d.addCallback(self._cbPasswordMatch, pcreds.principalURI)
-+ d.addCallback(self._cbPasswordMatch, (pcreds.authnPrincipal.principalURL(), pcreds.authzPrincipal.principalURL()))
- return d
-
- ##
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.element.parser.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.element.parser.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.element.parser.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,81 +0,0 @@
-Index: twisted/web2/dav/element/parser.py
-===================================================================
---- twisted/web2/dav/element/parser.py (revision 19773)
-+++ twisted/web2/dav/element/parser.py (working copy)
-@@ -37,7 +37,7 @@
- "WebDAVDocument",
- ]
-
--import StringIO
-+import cStringIO as StringIO
- import xml.dom.minidom
- import xml.sax
-
-@@ -106,6 +106,12 @@
- "children" : [],
- }]
-
-+ # Keep a cache of the subclasses we create for unknown XML
-+ # elements, so that we don't create multiple classes for the
-+ # same element; it's fairly typical for elements to appear
-+ # multiple times in a document.
-+ self.unknownElementClasses = {}
-+
- def endDocument(self):
- top = self.stack[-1]
-
-@@ -115,6 +121,7 @@
- assert len(top["children"]) is 1, "Must have exactly one root element, got %d" % len(top["children"])
-
- self.dom = WebDAVDocument(top["children"][0])
-+ del(self.unknownElementClasses)
-
- def startElementNS(self, name, qname, attributes):
- attributes_dict = {}
-@@ -125,13 +132,17 @@
-
- tag_namespace, tag_name = name
-
-- if (name not in elements_by_tag_name):
-- class UnknownElement (WebDAVUnknownElement):
-- namespace = tag_namespace
-- name = tag_name
-- element_class = UnknownElement
-+ if name in elements_by_tag_name:
-+ element_class = elements_by_tag_name[name]
-+ elif name in self.unknownElementClasses:
-+ element_class = self.unknownElementClasses[name]
- else:
-- element_class = elements_by_tag_name[name]
-+ def element_class(*args, **kwargs):
-+ element = WebDAVUnknownElement(*args, **kwargs)
-+ element.namespace = tag_namespace
-+ element.name = tag_name
-+ return element
-+ self.unknownElementClasses[name] = element_class
-
- self.stack.append({
- "name" : name,
-@@ -158,7 +169,12 @@
- self.stack[-1]["children"].append(element)
-
- def characters(self, content):
-- self.stack[-1]["children"].append(PCDATAElement(content))
-+ # Coalesce adjacent PCDATAElements
-+ pcdata = PCDATAElement(content)
-+ if len(self.stack[-1]["children"]) and isinstance(self.stack[-1]["children"][-1], PCDATAElement):
-+ self.stack[-1]["children"][-1] = self.stack[-1]["children"][-1] + pcdata
-+ else:
-+ self.stack[-1]["children"].append(pcdata)
-
- def ignorableWhitespace(self, whitespace):
- self.characters(self, whitespace)
-@@ -194,6 +210,8 @@
- except xml.sax.SAXParseException, e:
- raise ValueError(e)
-
-+ #handler.dom.root_element.validate()
-+
- return handler.dom
-
- return parse
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,12 +0,0 @@
-Index: twisted/web2/dav/method/__init__.py
-===================================================================
---- twisted/web2/dav/method/__init__.py (revision 19773)
-+++ twisted/web2/dav/method/__init__.py (working copy)
-@@ -40,6 +40,7 @@
- "proppatch",
- "prop_common",
- "put",
-+ "put_common",
- "report",
- "report_acl_principal_prop_set",
- "report_expand",
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,76 +0,0 @@
-Index: twisted/web2/dav/method/copymove.py
-===================================================================
---- twisted/web2/dav/method/copymove.py (revision 19773)
-+++ twisted/web2/dav/method/copymove.py (working copy)
-@@ -34,11 +34,12 @@
- from twisted.python import log
- from twisted.internet.defer import waitForDeferred, deferredGenerator
- from twisted.web2 import responsecode
-+from twisted.web2.dav.fileop import move
- from twisted.web2.http import HTTPError, StatusResponse
- from twisted.web2.filter.location import addLocation
- from twisted.web2.dav import davxml
- from twisted.web2.dav.idav import IDAVResource
--from twisted.web2.dav.fileop import copy, move
-+from twisted.web2.dav.method import put_common
- from twisted.web2.dav.util import parentForURL
-
- # FIXME: This is circular
-@@ -81,7 +82,15 @@
- # May need to add a location header
- addLocation(request, destination_uri)
-
-- x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
-+ #x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
-+ x = waitForDeferred(put_common.storeResource(request,
-+ source=self,
-+ source_uri=request.uri,
-+ destination=destination,
-+ destination_uri=destination_uri,
-+ deletesource=False,
-+ depth=depth
-+ ))
- yield x
- yield x.getResult()
-
-@@ -100,7 +109,8 @@
- #
- # Check authentication and access controls
- #
-- parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
-+ parentURL = parentForURL(request.uri)
-+ parent = waitForDeferred(request.locateResource(parentURL))
- yield parent
- parent = parent.getResult()
-
-@@ -117,7 +127,8 @@
- yield x
- x.getResult()
- else:
-- destparent = waitForDeferred(request.locateResource(parentForURL(destination_uri)))
-+ destparentURL = parentForURL(destination_uri)
-+ destparent = waitForDeferred(request.locateResource(destparentURL))
- yield destparent
- destparent = destparent.getResult()
-
-@@ -144,7 +155,19 @@
- log.err(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
-
-- x = waitForDeferred(move(self.fp, request.uri, destination.fp, destination_uri, depth))
-+ # Lets optimise a move within the same directory to a new resource as a simple move
-+ # rather than using the full transaction based storeResource api. This allows simple
-+ # "rename" operations to work quickly.
-+ if (not destination.exists()) and destparent == parent:
-+ x = waitForDeferred(move(self.fp, request.uri, destination.fp, destination_uri, depth))
-+ else:
-+ x = waitForDeferred(put_common.storeResource(request,
-+ source=self,
-+ source_uri=request.uri,
-+ destination=destination,
-+ destination_uri=destination_uri,
-+ deletesource=True,
-+ depth=depth))
- yield x
- yield x.getResult()
-
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.delete.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.delete.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.delete.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,34 +0,0 @@
-Index: twisted/web2/dav/method/delete.py
-===================================================================
---- twisted/web2/dav/method/delete.py (revision 19773)
-+++ twisted/web2/dav/method/delete.py (working copy)
-@@ -58,8 +58,28 @@
- yield x
- x.getResult()
-
-+ # Do quota checks before we start deleting things
-+ myquota = waitForDeferred(self.quota(request))
-+ yield myquota
-+ myquota = myquota.getResult()
-+ if myquota is not None:
-+ old_size = waitForDeferred(self.quotaSize(request))
-+ yield old_size
-+ old_size = old_size.getResult()
-+ else:
-+ old_size = 0
-+
-+ # Do delete
- x = waitForDeferred(delete(request.uri, self.fp, depth))
- yield x
-- yield x.getResult()
-+ result = x.getResult()
-
-+ # Adjust quota
-+ if myquota is not None:
-+ d = waitForDeferred(self.quotaSizeAdjust(request, -old_size))
-+ yield d
-+ d.getResult()
-+
-+ yield result
-+
- http_DELETE = deferredGenerator(http_DELETE)
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,36 +0,0 @@
-Index: twisted/web2/dav/method/prop_common.py
-===================================================================
---- twisted/web2/dav/method/prop_common.py (revision 19773)
-+++ twisted/web2/dav/method/prop_common.py (working copy)
-@@ -23,19 +23,21 @@
- properties_by_status = waitForDeferred(propertiesForResource(request, propertyreq, resource))
- yield properties_by_status
- properties_by_status = properties_by_status.getResult()
--
-+
-+ propstats = []
-+
- for status in properties_by_status:
- properties = properties_by_status[status]
- if properties:
-- responses.append(
-- davxml.PropertyStatusResponse(
-- href,
-- davxml.PropertyStatus(
-- davxml.PropertyContainer(*properties),
-- davxml.Status.fromResponseCode(status)
-- )
-- )
-- )
-+ xml_status = davxml.Status.fromResponseCode(status)
-+ xml_container = davxml.PropertyContainer(*properties)
-+ xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
-+
-+ propstats.append(xml_propstat)
-+
-+ if propstats:
-+ responses.append(davxml.PropertyStatusResponse(href, *propstats))
-+
- else:
- responses.append(
- davxml.StatusResponse(
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,28 +0,0 @@
-Index: twisted/web2/dav/method/propfind.py
-===================================================================
---- twisted/web2/dav/method/propfind.py (revision 19773)
-+++ twisted/web2/dav/method/propfind.py (working copy)
-@@ -27,7 +27,10 @@
- WebDAV PROPFIND method
- """
-
--__all__ = ["http_PROPFIND"]
-+__all__ = [
-+ "http_PROPFIND",
-+ "propertyName",
-+]
-
- from twisted.python import log
- from twisted.python.failure import Failure
-@@ -200,7 +203,7 @@
-
- def propertyName(name):
- property_namespace, property_name = name
-- class PropertyName (davxml.WebDAVEmptyElement):
-- namespace = property_namespace
-- name = property_name
-- return PropertyName()
-+ pname = davxml.WebDAVUnknownElement()
-+ pname.namespace = property_namespace
-+ pname.name = property_name
-+ return pname
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,20 +0,0 @@
-Index: twisted/web2/dav/method/put.py
-===================================================================
---- twisted/web2/dav/method/put.py (revision 19773)
-+++ twisted/web2/dav/method/put.py (working copy)
-@@ -34,7 +34,7 @@
- from twisted.web2 import responsecode
- from twisted.web2.http import HTTPError, StatusResponse
- from twisted.web2.dav import davxml
--from twisted.web2.dav.fileop import put
-+from twisted.web2.dav.method import put_common
- from twisted.web2.dav.util import parentForURL
-
- def preconditions_PUT(self, request):
-@@ -107,4 +107,5 @@
- # to return a MULTI_STATUS response, which is WebDAV-specific (and PUT is
- # not).
- #
-- return put(request.stream, self.fp)
-+ #return put(request.stream, self.fp)
-+ return put_common.storeResource(request, destination=self, destination_uri=request.uri)
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,270 +0,0 @@
-Index: twisted/web2/dav/method/put_common.py
-===================================================================
---- twisted/web2/dav/method/put_common.py (revision 0)
-+++ twisted/web2/dav/method/put_common.py (revision 0)
-@@ -0,0 +1,265 @@
-+##
-+# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
-+#
-+# Licensed under the Apache License, Version 2.0 (the "License");
-+# you may not use this file except in compliance with the License.
-+# You may obtain a copy of the License at
-+#
-+# http://www.apache.org/licenses/LICENSE-2.0
-+#
-+# Unless required by applicable law or agreed to in writing, software
-+# distributed under the License is distributed on an "AS IS" BASIS,
-+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+# See the License for the specific language governing permissions and
-+# limitations under the License.
-+#
-+# DRI: Cyrus Daboo, cdaboo at apple.com
-+##
-+
-+"""
-+PUT/COPY/MOVE common behavior.
-+"""
-+
-+__version__ = "0.0"
-+
-+__all__ = ["storeCalendarObjectResource"]
-+
-+from twisted.internet.defer import deferredGenerator, maybeDeferred, waitForDeferred
-+from twisted.python import failure, log
-+from twisted.python.filepath import FilePath
-+from twisted.web2 import responsecode
-+from twisted.web2.dav import davxml
-+from twisted.web2.dav.element.base import dav_namespace
-+from twisted.web2.dav.fileop import copy, delete, put
-+from twisted.web2.dav.http import ErrorResponse
-+from twisted.web2.dav.resource import TwistedGETContentMD5
-+from twisted.web2.dav.stream import MD5StreamWrapper
-+from twisted.web2.http import HTTPError
-+from twisted.web2.http_headers import generateContentType
-+from twisted.web2.iweb import IResponse
-+from twisted.web2.stream import MemoryStream
-+
-+def storeResource(
-+ request,
-+ source=None, source_uri=None, data=None,
-+ destination=None, destination_uri=None,
-+ deletesource=False,
-+ depth="0"
-+):
-+ """
-+ Function that does common PUT/COPY/MOVE behaviour.
-+
-+ @param request: the L{twisted.web2.server.Request} for the current HTTP request.
-+ @param source: the L{DAVFile} for the source resource to copy from, or None if source data
-+ is to be read from the request.
-+ @param source_uri: the URI for the source resource.
-+ @param data: a C{str} to copy data from instead of the request stream.
-+ @param destination: the L{DAVFile} for the destination resource to copy into.
-+ @param destination_uri: the URI for the destination resource.
-+ @param deletesource: True if the source resource is to be deleted on successful completion, False otherwise.
-+ @param depth: a C{str} containing the COPY/MOVE Depth header value.
-+ @return: status response.
-+ """
-+
-+ try:
-+ assert request is not None and destination is not None and destination_uri is not None
-+ assert (source is None) or (source is not None and source_uri is not None)
-+ assert not deletesource or (deletesource and source is not None)
-+ except AssertionError:
-+ log.err("Invalid arguments to storeResource():")
-+ log.err("request=%s\n" % (request,))
-+ log.err("source=%s\n" % (source,))
-+ log.err("source_uri=%s\n" % (source_uri,))
-+ log.err("data=%s\n" % (data,))
-+ log.err("destination=%s\n" % (destination,))
-+ log.err("destination_uri=%s\n" % (destination_uri,))
-+ log.err("deletesource=%s\n" % (deletesource,))
-+ log.err("depth=%s\n" % (depth,))
-+ raise
-+
-+ class RollbackState(object):
-+ """
-+ This class encapsulates the state needed to rollback the entire PUT/COPY/MOVE
-+ transaction, leaving the server state the same as it was before the request was
-+ processed. The DoRollback method will actually execute the rollback operations.
-+ """
-+
-+ def __init__(self):
-+ self.active = True
-+ self.source_copy = None
-+ self.destination_copy = None
-+ self.destination_created = False
-+ self.source_deleted = False
-+
-+ def Rollback(self):
-+ """
-+ Rollback the server state. Do not allow this to raise another exception. If
-+ rollback fails then we are going to be left in an awkward state that will need
-+ to be cleaned up eventually.
-+ """
-+ if self.active:
-+ self.active = False
-+ log.err("Rollback: rollback")
-+ try:
-+ if self.source_copy and self.source_deleted:
-+ self.source_copy.moveTo(source.fp)
-+ log.err("Rollback: source restored %s to %s" % (self.source_copy.path, source.fp.path))
-+ self.source_copy = None
-+ self.source_deleted = False
-+ if self.destination_copy:
-+ destination.fp.remove()
-+ log.err("Rollback: destination restored %s to %s" % (self.destination_copy.path, destination.fp.path))
-+ self.destination_copy.moveTo(destination.fp)
-+ self.destination_copy = None
-+ elif self.destination_created:
-+ destination.fp.remove()
-+ log.err("Rollback: destination removed %s" % (destination.fp.path,))
-+ self.destination_created = False
-+ except:
-+ log.err("Rollback: exception caught and not handled: %s" % failure.Failure())
-+
-+ def Commit(self):
-+ """
-+ Commit the resource changes by wiping the rollback state.
-+ """
-+ if self.active:
-+ log.err("Rollback: commit")
-+ self.active = False
-+ if self.source_copy:
-+ self.source_copy.remove()
-+ log.err("Rollback: removed source backup %s" % (self.source_copy.path,))
-+ self.source_copy = None
-+ if self.destination_copy:
-+ self.destination_copy.remove()
-+ log.err("Rollback: removed destination backup %s" % (self.destination_copy.path,))
-+ self.destination_copy = None
-+ self.destination_created = False
-+ self.source_deleted = False
-+
-+ rollback = RollbackState()
-+
-+ try:
-+ """
-+ Handle validation operations here.
-+ """
-+
-+ """
-+ Handle rollback setup here.
-+ """
-+
-+ # Do quota checks on destination and source before we start messing with adding other files
-+ destquota = waitForDeferred(destination.quota(request))
-+ yield destquota
-+ destquota = destquota.getResult()
-+ if destquota is not None and destination.exists():
-+ old_dest_size = waitForDeferred(destination.quotaSize(request))
-+ yield old_dest_size
-+ old_dest_size = old_dest_size.getResult()
-+ else:
-+ old_dest_size = 0
-+
-+ if source is not None:
-+ sourcequota = waitForDeferred(source.quota(request))
-+ yield sourcequota
-+ sourcequota = sourcequota.getResult()
-+ if sourcequota is not None and source.exists():
-+ old_source_size = waitForDeferred(source.quotaSize(request))
-+ yield old_source_size
-+ old_source_size = old_source_size.getResult()
-+ else:
-+ old_source_size = 0
-+ else:
-+ sourcequota = None
-+ old_source_size = 0
-+
-+ # We may need to restore the original resource data if the PUT/COPY/MOVE fails,
-+ # so rename the original file in case we need to rollback.
-+ overwrite = destination.exists()
-+ if overwrite:
-+ rollback.destination_copy = FilePath(destination.fp.path)
-+ rollback.destination_copy.path += ".rollback"
-+ destination.fp.copyTo(rollback.destination_copy)
-+ else:
-+ rollback.destination_created = True
-+
-+ if deletesource:
-+ rollback.source_copy = FilePath(source.fp.path)
-+ rollback.source_copy.path += ".rollback"
-+ source.fp.copyTo(rollback.source_copy)
-+
-+ """
-+ Handle actual store operations here.
-+ """
-+
-+ # Do put or copy based on whether source exists
-+ if source is not None:
-+ response = maybeDeferred(copy, source.fp, destination.fp, destination_uri, depth)
-+ else:
-+ datastream = request.stream
-+ if data is not None:
-+ datastream = MemoryStream(data)
-+ md5 = MD5StreamWrapper(datastream)
-+ response = maybeDeferred(put, md5, destination.fp)
-+
-+ response = waitForDeferred(response)
-+ yield response
-+ response = response.getResult()
-+
-+ # Update the MD5 value on the resource
-+ if source is not None:
-+ # Copy MD5 value from source to destination
-+ if source.hasDeadProperty(TwistedGETContentMD5):
-+ md5 = source.readDeadProperty(TwistedGETContentMD5)
-+ destination.writeDeadProperty(md5)
-+ else:
-+ # Finish MD5 calc and write dead property
-+ md5.close()
-+ md5 = md5.getMD5()
-+ destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
-+
-+ # Update the content-type value on the resource if it is not been copied or moved
-+ if source is None:
-+ content_type = request.headers.getHeader("content-type")
-+ if content_type is not None:
-+ destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
-+
-+ response = IResponse(response)
-+
-+ # Do quota check on destination
-+ if destquota is not None:
-+ # Get size of new/old resources
-+ new_dest_size = waitForDeferred(destination.quotaSize(request))
-+ yield new_dest_size
-+ new_dest_size = new_dest_size.getResult()
-+ diff_size = new_dest_size - old_dest_size
-+ if diff_size >= destquota[0]:
-+ log.err("Over quota: available %d, need %d" % (destquota[0], diff_size))
-+ raise HTTPError(ErrorResponse(responsecode.INSUFFICIENT_STORAGE_SPACE, (dav_namespace, "quota-not-exceeded")))
-+ d = waitForDeferred(destination.quotaSizeAdjust(request, diff_size))
-+ yield d
-+ d.getResult()
-+
-+ if deletesource:
-+ # Delete the source resource
-+ if sourcequota is not None:
-+ delete_size = 0 - old_source_size
-+ d = waitForDeferred(source.quotaSizeAdjust(request, delete_size))
-+ yield d
-+ d.getResult()
-+
-+ delete(source_uri, source.fp, depth)
-+ rollback.source_deleted = True
-+
-+ # Can now commit changes and forget the rollback details
-+ rollback.Commit()
-+
-+ yield response
-+ return
-+
-+ except:
-+ # Roll back changes to original server state. Note this may do nothing
-+ # if the rollback has already ocurred or changes already committed.
-+ rollback.Rollback()
-+ raise
-+
-+storeResource = deferredGenerator(storeResource)
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.resource.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.resource.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,1180 +0,0 @@
-Index: twisted/web2/dav/resource.py
-===================================================================
---- twisted/web2/dav/resource.py (revision 26455)
-+++ twisted/web2/dav/resource.py (working copy)
-@@ -31,34 +31,43 @@
- "DAVResource",
- "DAVLeafResource",
- "DAVPrincipalResource",
-+ "DAVPrincipalCollectionResource",
- "AccessDeniedError",
- "isPrincipalResource",
- "TwistedACLInheritable",
-+ "TwistedGETContentMD5",
-+ "TwistedQuotaRootProperty",
- "allACL",
- "readonlyACL",
- "davPrivilegeSet",
- "unauthenticatedPrincipal",
- ]
-
--import urllib
-+import __builtin__
-+if not hasattr(__builtin__, "set"):
-+ import sets.Set as set
-+if not hasattr(__builtin__, "frozenset"):
-+ import sets.ImmutableSet as frozenset
-
- from zope.interface import implements
- from twisted.python import log
- from twisted.python.failure import Failure
- from twisted.internet.defer import Deferred, maybeDeferred, succeed
- from twisted.internet.defer import waitForDeferred, deferredGenerator
-+from twisted.cred.error import LoginFailed, UnauthorizedLogin
- from twisted.internet import reactor
- from twisted.web2 import responsecode
- from twisted.web2.http import HTTPError, RedirectResponse, StatusResponse
- from twisted.web2.http_headers import generateContentType
- from twisted.web2.iweb import IResponse
- from twisted.web2.resource import LeafResource
-+from twisted.web2.server import NoURLForResourceError
- from twisted.web2.static import MetaDataMixin, StaticRenderMixin
- from twisted.web2.auth.wrapper import UnauthorizedResponse
- from twisted.web2.dav import davxml
- from twisted.web2.dav.davxml import dav_namespace, lookupElement
- from twisted.web2.dav.davxml import twisted_dav_namespace, twisted_private_namespace
--from twisted.web2.dav.idav import IDAVResource, IDAVPrincipalResource
-+from twisted.web2.dav.idav import IDAVResource, IDAVPrincipalResource, IDAVPrincipalCollectionResource
- from twisted.web2.dav.http import NeedPrivilegesResponse
- from twisted.web2.dav.noneprops import NonePropertyStore
- from twisted.web2.dav.util import unimplemented, parentForURL, joinURL
-@@ -127,10 +136,13 @@
- #(dav_namespace, "group" ), # RFC 3744, section 5.2
- (dav_namespace, "supported-privilege-set" ), # RFC 3744, section 5.3
- (dav_namespace, "current-user-privilege-set"), # RFC 3744, section 5.4
-+ (dav_namespace, "current-user-principal" ), # draft-sanchez-webdav-current-principal
- (dav_namespace, "acl" ), # RFC 3744, section 5.5
- (dav_namespace, "acl-restrictions" ), # RFC 3744, section 5.6
- (dav_namespace, "inherited-acl-set" ), # RFC 3744, section 5.7
- (dav_namespace, "principal-collection-set" ), # RFC 3744, section 5.8
-+ (dav_namespace, "quota-available-bytes" ), # RFC 4331, section 3
-+ (dav_namespace, "quota-used-bytes" ), # RFC 4331, section 4
-
- (twisted_dav_namespace, "resource-class"),
- )
-@@ -167,6 +179,14 @@
- if qname[0] == twisted_private_namespace:
- return succeed(False)
-
-+ # Need to special case the dynamic live properties
-+ namespace, name = qname
-+ if namespace == dav_namespace:
-+ if name in ("quota-available-bytes", "quota-used-bytes"):
-+ d = self.hasQuota(request)
-+ d.addCallback(lambda result: result)
-+ return d
-+
- return succeed(qname in self.liveProperties or self.deadProperties().contains(qname))
-
- def readProperty(self, property, request):
-@@ -202,7 +222,6 @@
- mimeType = self.contentType()
- if mimeType is None:
- return None
-- mimeType.params = None # WebDAV getcontenttype property does not include parameters
- return davxml.GETContentType(generateContentType(mimeType))
-
- if name == "getcontentlength":
-@@ -240,8 +259,10 @@
- )
-
- if name == "supported-report-set":
-- supported = [davxml.SupportedReport(report,) for report in self.supportedReports()]
-- return davxml.SupportedReportSet(*supported)
-+ return davxml.SupportedReportSet(*[
-+ davxml.SupportedReport(report,)
-+ for report in self.supportedReports()
-+ ])
-
- if name == "supported-privilege-set":
- return self.supportedPrivileges(request)
-@@ -253,9 +274,10 @@
- return davxml.InheritedACLSet(*self.inheritedACLSet())
-
- if name == "principal-collection-set":
-- d = self.principalCollections(request)
-- d.addCallback(lambda collections: davxml.PrincipalCollectionSet(*collections))
-- return d
-+ return davxml.PrincipalCollectionSet(*[
-+ davxml.HRef(principalCollection.principalCollectionURL())
-+ for principalCollection in self.principalCollections()
-+ ])
-
- def ifAllowed(privileges, callback):
- def onError(failure):
-@@ -287,7 +309,36 @@
- d.addCallback(gotACL)
- return d
- return ifAllowed((davxml.ReadACL(),), callback)
-+
-+ if name == "current-user-principal":
-+ return davxml.CurrentUserPrincipal(self.currentPrincipal(request).children[0])
-
-+ if name == "quota-available-bytes":
-+ def callback(qvalue):
-+ if qvalue is None:
-+ raise HTTPError(StatusResponse(
-+ responsecode.NOT_FOUND,
-+ "Property %s does not exist." % (sname,)
-+ ))
-+ else:
-+ return davxml.QuotaAvailableBytes(str(qvalue[0]))
-+ d = self.quota(request)
-+ d.addCallback(callback)
-+ return d
-+
-+ if name == "quota-used-bytes":
-+ def callback(qvalue):
-+ if qvalue is None:
-+ raise HTTPError(StatusResponse(
-+ responsecode.NOT_FOUND,
-+ "Property %s does not exist." % (sname,)
-+ ))
-+ else:
-+ return davxml.QuotaUsedBytes(str(qvalue[1]))
-+ d = self.quota(request)
-+ d.addCallback(callback)
-+ return d
-+
- elif namespace == twisted_dav_namespace:
- if name == "resource-class":
- class ResourceClass (davxml.WebDAVTextElement):
-@@ -310,10 +361,7 @@
- """
- See L{IDAVResource.writeProperty}.
- """
-- assert (
-- isinstance(property, davxml.WebDAVElement),
-- "Not a property: %r" % (property,)
-- )
-+ assert isinstance(property, davxml.WebDAVElement), "Not a property: %r" % (property,)
-
- def defer():
- if property.protected:
-@@ -364,15 +412,28 @@
- """
- See L{IDAVResource.listProperties}.
- """
-- # FIXME: A set would be better here, that that's a python 2.4+ feature.
-- qnames = list(self.liveProperties)
-+ qnames = set(self.liveProperties)
-
-+ # Add dynamic live properties that exist
-+ dynamicLiveProperties = (
-+ (dav_namespace, "quota-available-bytes" ),
-+ (dav_namespace, "quota-used-bytes" ),
-+ )
-+ for dqname in dynamicLiveProperties:
-+ has = waitForDeferred(self.hasProperty(dqname, request))
-+ yield has
-+ has = has.getResult()
-+ if not has:
-+ qnames.remove(dqname)
-+
- for qname in self.deadProperties().list():
- if (qname not in qnames) and (qname[0] != twisted_private_namespace):
-- qnames.append(qname)
-+ qnames.add(qname)
-
-- return succeed(qnames)
-+ yield qnames
-
-+ listProperties = deferredGenerator(listProperties)
-+
- def listAllprop(self, request):
- """
- Some DAV properties should not be returned to a C{DAV:allprop} query.
-@@ -466,8 +527,22 @@
- return super(DAVPropertyMixIn, self).displayName()
-
- class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
-+ """
-+ WebDAV resource.
-+ """
- implements(IDAVResource)
-
-+ def __init__(self, principalCollections=None):
-+ """
-+ @param principalCollections: an iterable of L{IDAVPrincipalCollectionResource}s
-+ which contain principals to be used in ACLs for this resource.
-+ """
-+ if principalCollections is not None:
-+ self._principalCollections = frozenset([
-+ IDAVPrincipalCollectionResource(principalCollection)
-+ for principalCollection in principalCollections
-+ ])
-+
- ##
- # DAV
- ##
-@@ -554,12 +629,13 @@
- def supportedReports(self):
- """
- See L{IDAVResource.supportedReports}.
-- This implementation lists the three main ACL reports.
-+ This implementation lists the three main ACL reports and expand-property.
- """
- result = []
- result.append(davxml.Report(davxml.ACLPrincipalPropSet(),))
- result.append(davxml.Report(davxml.PrincipalMatch(),))
- result.append(davxml.Report(davxml.PrincipalPropertySearch(),))
-+ result.append(davxml.Report(davxml.ExpandProperty(),))
- return result
-
- ##
-@@ -571,7 +647,7 @@
- See L{IDAVResource.authorize}.
- """
- def onError(failure):
-- log.err("Invalid authentication details: %s" % (request,))
-+ log.err("Authentication failed: %s" % (failure.value,))
- d = UnauthorizedResponse.makeResponse(request.credentialFactories,
- request.remoteAddr)
- def _err(response):
-@@ -584,7 +660,7 @@
-
- # If we were unauthorized to start with (no Authorization header from client) then
- # we should return an unauthorized response instead to force the client to login if it can
-- if request.user == davxml.Principal(davxml.Unauthenticated()):
-+ if request.authnUser == davxml.Principal(davxml.Unauthenticated()):
- d = UnauthorizedResponse.makeResponse(request.credentialFactories,
- request.remoteAddr)
- def _fail(response):
-@@ -619,8 +695,9 @@
- hasattr(request, 'credentialFactories') and
- hasattr(request, 'loginInterfaces')
- ):
-- request.user = davxml.Principal(davxml.Unauthenticated())
-- return request.user
-+ request.authnUser = davxml.Principal(davxml.Unauthenticated())
-+ request.authzUser = davxml.Principal(davxml.Unauthenticated())
-+ return (request.authnUser, request.authzUser)
-
- authHeader = request.headers.getHeader('authorization')
-
-@@ -628,31 +705,42 @@
- if authHeader[0] not in request.credentialFactories:
- log.err("Client authentication scheme %s is not provided by server %s"
- % (authHeader[0], request.credentialFactories.keys()))
-- raise HTTPError(responsecode.FORBIDDEN)
-+ d = UnauthorizedResponse.makeResponse(
-+ request.credentialFactories,
-+ request.remoteAddr)
-+ def _fail(response):
-+ return Failure(HTTPError(response))
-+ return d.addCallback(_fail)
- else:
- factory = request.credentialFactories[authHeader[0]]
-
- d = factory.decode(authHeader[1], request)
-
- def gotCreds(creds):
-- return self.findPrincipalForAuthID(
-+ return self.principalsForAuthID(
- request, creds.username
- ).addCallback(gotDetails, creds)
- # Try to match principals in each principal collection on the resource
- def gotDetails(details, creds):
-- principal = IDAVPrincipalResource(details[0])
-- principalURI = details[1]
-- return PrincipalCredentials(principal, principalURI, creds)
-+ authnPrincipal = IDAVPrincipalResource(details[0])
-+ authzPrincipal = IDAVPrincipalResource(details[1])
-+ return PrincipalCredentials(authnPrincipal, authzPrincipal, creds)
-
- def login(pcreds):
- return request.portal.login(pcreds, None,
- *request.loginInterfaces
- ).addCallback(loginSuccess)
--
-- return d.addCallback(gotCreds).addCallback(login)
-+ def gotAuth(result):
-+ request.authnUser = result[1]
-+ request.authzUser = result[2]
-+ return (request.authnUser, request.authzUser)
-+ d.addCallback(gotCreds).addCallback(login).addCallback(gotAuth)
-+ return d
- else:
- request.user = davxml.Principal(davxml.Unauthenticated())
-- return request.user
-+ request.authnUser = davxml.Principal(davxml.Unauthenticated())
-+ request.authzUser = davxml.Principal(davxml.Unauthenticated())
-+ return (request.authnUser, request.authzUser)
-
- ##
- # ACL
-@@ -661,49 +749,23 @@
- def currentPrincipal(self, request):
- """
- @param request: the request being processed.
-- @return: the current principal, as derived from the given request.
-+ @return: the current authorized principal, as derived from the given request.
- """
-- if hasattr(request, "user"):
-- return request.user
-+ if hasattr(request, "authzUser"):
-+ return request.authzUser
- else:
- return unauthenticatedPrincipal
-
-- def principalCollections(self, request):
-+ def principalCollections(self):
- """
- See L{IDAVResource.accessControlList}.
--
-- This implementation tries to read the L{davxml.PrincipalCollectionSet}
-- from the dead property store of this resource and uses that. If not
-- present on this resource, it tries to get it from the parent, unless it
-- is the root or has no parent.
- """
-- try:
-- principalCollections = self.readDeadProperty(davxml.PrincipalCollectionSet).childrenOfType(davxml.HRef)
-- except HTTPError, e:
-- if e.response.code != responsecode.NOT_FOUND:
-- raise
-+ if hasattr(self, "_principalCollections"):
-+ return self._principalCollections
-+ else:
-+ return ()
-
-- principalCollections = []
--
-- # Try the parent
-- myURL = request.urlForResource(self)
-- if myURL != "/":
-- parentURL = parentForURL(myURL)
--
-- parent = waitForDeferred(request.locateResource(parentURL))
-- yield parent
-- parent = parent.getResult()
--
-- if parent:
-- principalCollections = waitForDeferred(parent.principalCollections(request))
-- yield principalCollections
-- principalCollections = principalCollections.getResult()
--
-- yield principalCollections
--
-- principalCollections = deferredGenerator(principalCollections)
--
-- def defaultAccessControlList(self):
-+ def defaultRootAccessControlList(self):
- """
- @return: the L{davxml.ACL} element containing the default access control
- list for this resource.
-@@ -715,6 +777,17 @@
- #
- return readonlyACL
-
-+ def defaultAccessControlList(self):
-+ """
-+ @return: the L{davxml.ACL} element containing the default access control
-+ list for this resource.
-+ """
-+ #
-+ # The default behaviour is no ACL; we should inherrit from the parent
-+ # collection.
-+ #
-+ return davxml.ACL()
-+
- def setAccessControlList(self, acl):
- """
- See L{IDAVResource.setAccessControlList}.
-@@ -753,13 +826,16 @@
- # 10. Verify that new acl is not in conflict with itself
- # 11. Update acl on the resource
-
-- old_acl = waitForDeferred(self.accessControlList(request))
-+ # Get the current access control list, preserving any private properties on the ACEs as
-+ # we will need to keep those when we change the ACL.
-+ old_acl = waitForDeferred(self.accessControlList(request, expanding=True))
- yield old_acl
- old_acl = old_acl.getResult()
-
- # Check disabled
- if old_acl is None:
- yield None
-+ return
-
- # Need to get list of supported privileges
- supported = []
-@@ -778,10 +854,7 @@
- yield supportedPrivs
- supportedPrivs = supportedPrivs.getResult()
- for item in supportedPrivs.children:
-- assert (
-- isinstance(item, davxml.SupportedPrivilege),
-- "Not a SupportedPrivilege: %r" % (item,)
-- )
-+ assert isinstance(item, davxml.SupportedPrivilege), "Not a SupportedPrivilege: %r" % (item,)
- addSupportedPrivilege(item)
-
- # Steps 1 - 6
-@@ -915,8 +988,7 @@
- supportedPrivs = supportedPrivs.getResult()
-
- # Other principals types don't make sense as actors.
-- assert (
-- principal.children[0].name in ("unauthenticated", "href"),
-+ assert principal.children[0].name in ("unauthenticated", "href"), (
- "Principal is not an actor: %r" % (principal,)
- )
-
-@@ -1024,15 +1096,16 @@
- def getMyURL():
- url = request.urlForResource(self)
-
-- assert url is not None, "urlForResource(self) returned None for resource %s" % (self,)
-+ assert url is not None, (
-+ "urlForResource(self) returned None for resource %s" % (self,)
-+ )
-
- return url
-
- try:
- acl = self.readDeadProperty(davxml.ACL)
- except HTTPError, e:
-- assert (
-- e.response.code == responsecode.NOT_FOUND,
-+ assert e.response.code == responsecode.NOT_FOUND, (
- "Expected %s response from readDeadProperty() exception, not %s"
- % (responsecode.NOT_FOUND, e.response.code)
- )
-@@ -1043,9 +1116,9 @@
-
- if myURL == "/":
- # If we get to the root without any ACLs, then use the default.
-+ acl = self.defaultRootAccessControlList()
-+ else:
- acl = self.defaultAccessControlList()
-- else:
-- acl = davxml.ACL()
-
- # Dynamically update privileges for those ace's that are inherited.
- if inheritance:
-@@ -1081,7 +1154,7 @@
- # Adjust ACE for inherit on this resource
- children = list(ace.children)
- children.remove(TwistedACLInheritable())
-- children.append(davxml.Inherited(davxml.HRef.fromString(parentURL)))
-+ children.append(davxml.Inherited(davxml.HRef(parentURL)))
- aces.append(davxml.ACE(*children))
- else:
- aces.extend(inherited_aces)
-@@ -1110,8 +1183,7 @@
- the child resource loop and supply those to the checkPrivileges on each child.
-
- @param request: the L{IRequest} for the request in progress.
-- @return: a C{list} of L{Ace}s that child resources of this one will
-- inherit and which will match the currently authenticated principal.
-+ @return: a C{list} of L{Ace}s that child resources of this one will inherit.
- """
-
- # Get the parent ACLs with inheritance and preserve the <inheritable> element.
-@@ -1133,21 +1205,9 @@
- # Adjust ACE for inherit on this resource
- children = list(ace.children)
- children.remove(TwistedACLInheritable())
-- children.append(davxml.Inherited(davxml.HRef.fromString(request.urlForResource(self))))
-+ children.append(davxml.Inherited(davxml.HRef(request.urlForResource(self))))
- aces.append(davxml.ACE(*children))
--
-- # Filter out those that do not have a principal match with the current principal
-- principal = self.currentPrincipal(request)
-- filteredaces = []
-- for ace in aces:
-- if self.matchPrincipal(principal, ace.principal, request):
-- if ace.invert:
-- continue
-- else:
-- if not ace.invert:
-- continue
-- filteredaces.append(ace)
-- yield filteredaces
-+ yield aces
-
- inheritedACEsforChildren = deferredGenerator(inheritedACEsforChildren)
-
-@@ -1157,49 +1217,69 @@
-
- This implementation returns an empty set.
- """
--
- return []
-
-- def findPrincipalForAuthID(self, request, authid):
-+ def principalsForAuthID(self, request, authid):
- """
-+ Return authentication and authorization prinicipal identifiers for the
-+ authentication identifer passed in. In this implementation authn and authz
-+ principals are the same.
-+
- @param request: the L{IRequest} for the request in progress.
- @param authid: a string containing the
- authentication/authorization identifier for the principal
- to lookup.
-- @return: a deferred tuple of C{(principal, principalURI)}
-- where: C{principal} is the L{Principal} that is found;
-- C{principalURI} is the C{str} URI of the principal.
-+ @return: a deferred tuple of two tuples. Each tuple is
-+ C{(principal, principalURI)} where: C{principal} is the L{Principal}
-+ that is found; {principalURI} is the C{str} URI of the principal.
-+ The first tuple corresponds to authentication identifiers,
-+ the second to authorization identifiers.
- It will errback with an HTTPError(responsecode.FORBIDDEN) if
- the principal isn't found.
- """
-- # Try to match principals in each principal collection on the resource
-- collections = waitForDeferred(self.principalCollections(request))
-- yield collections
-- collections = collections.getResult()
-+ authnPrincipal = self.findPrincipalForAuthID(authid)
-
-- for collection in collections:
-- principalURI = joinURL(str(collection), authid)
-+ if authnPrincipal is None:
-+ log.msg("Could not find the principal resource for user id: %s" % (authid,))
-+ raise HTTPError(responsecode.FORBIDDEN)
-
-- principal = waitForDeferred(request.locateResource(principalURI))
-- yield principal
-- principal = principal.getResult()
-+ d = self.authorizationPrincipal(request, authid, authnPrincipal)
-+ d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
-+ return d
-
-- if isPrincipalResource(principal):
-- yield (principal, principalURI)
-- return
-- else:
-- principalCollections = waitForDeferred(self.principalCollections(request))
-- yield principalCollections
-- principalCollections = principalCollections.getResult()
-+ def findPrincipalForAuthID(self, authid):
-+ """
-+ Return authentication and authoirization prinicipal identifiers for the
-+ authentication identifer passed in. In this implementation authn and authz
-+ principals are the same.
-
-- if len(principalCollections) == 0:
-- log.msg("DAV:principal-collection-set property cannot be found on the resource being authorized: %s" % self)
-- else:
-- log.msg("Could not find principal matching user id: %s" % authid)
-- raise HTTPError(responsecode.FORBIDDEN)
-+ @param authid: a string containing the
-+ authentication/authorization identifier for the principal
-+ to lookup.
-+ @return: a tuple of C{(principal, principalURI)} where: C{principal} is the L{Principal}
-+ that is found; {principalURI} is the C{str} URI of the principal.
-+ If not found return None.
-+ """
-+ for collection in self.principalCollections():
-+ principal = collection.principalForUser(authid)
-+ if principal is not None:
-+ return principal
-+ return None
-
-- findPrincipalForAuthID = deferredGenerator(findPrincipalForAuthID)
--
-+ def authorizationPrincipal(self, request, authid, authnPrincipal):
-+ """
-+ Determine the authorization principal for the given request and authentication principal.
-+ This implementation simply uses aht authentication principalk as the authoization principal.
-+
-+ @param request: the L{IRequest} for the request in progress.
-+ @param authid: a string containing the uthentication/authorization identifier
-+ for the principal to lookup.
-+ @param authnPrincipal: the L{IDAVPrincipal} for the authenticated principal
-+ @return: a deferred result C{tuple} of (L{IDAVPrincipal}, C{str}) containing the authorization principal
-+ resource and URI respectively.
-+ """
-+ return succeed(authnPrincipal)
-+
- def samePrincipal(self, principal1, principal2):
- """
- Check whether the two prinicpals are exactly the same in terms of
-@@ -1224,7 +1304,6 @@
- return False
-
- def matchPrincipal(self, principal1, principal2, request):
--
- """
- Check whether the principal1 is a principal in the set defined by
- principal2.
-@@ -1249,6 +1328,9 @@
- if isinstance(principal1, davxml.Unauthenticated):
- yield False
- return
-+ elif isinstance(principal1, davxml.All):
-+ yield False
-+ return
- else:
- yield True
- return
-@@ -1265,10 +1347,7 @@
- yield False
- return
-
-- assert (
-- isinstance(principal1, davxml.HRef),
-- "Not an HRef: %r" % (principal1,)
-- )
-+ assert isinstance(principal1, davxml.HRef), "Not an HRef: %r" % (principal1,)
-
- principal2 = waitForDeferred(self.resolvePrincipal(principal2, request))
- yield principal2
-@@ -1276,7 +1355,6 @@
-
- assert principal2 is not None, "principal2 is None"
-
--
- # Compare two HRefs and do group membership test as well
- if principal1 == principal2:
- yield True
-@@ -1294,6 +1372,7 @@
-
- matchPrincipal = deferredGenerator(matchPrincipal)
-
-+ @deferredGenerator
- def principalIsGroupMember(self, principal1, principal2, request):
- """
- Check whether one principal is a group member of another.
-@@ -1304,18 +1383,21 @@
- @return: L{Deferred} with result C{True} if principal1 is a member of principal2, C{False} otherwise
- """
-
-- def testGroup(group):
-- # Get principal resource for principal2
-- if group and isinstance(group, DAVPrincipalResource):
-- members = group.groupMembers()
-- if principal1 in members:
-- return True
--
-- return False
-+ d = waitForDeferred(request.locateResource(principal2))
-+ yield d
-+ group = d.getResult()
-
-- d = request.locateResource(principal2)
-- d.addCallback(testGroup)
-- return d
-+ # Get principal resource for principal2
-+ if group and isinstance(group, DAVPrincipalResource):
-+ d = waitForDeferred(group.expandedGroupMembers())
-+ yield d
-+ members = d.getResult()
-+ for member in members:
-+ if member.principalURL() == principal1:
-+ yield True
-+ return
-+
-+ yield False
-
- def validPrincipal(self, ace_principal, request):
- """
-@@ -1356,11 +1438,16 @@
- @return C{True} if C{href_principal} is valid, C{False} otherwise.
-
- This implementation tests for a href element that corresponds to
-- a principal resource.
-+ a principal resource and matches the principal-URL.
- """
-- # Must have the principal resource type
-+
-+ # Must have the principal resource type and must match the principal-URL
-+
-+ def _matchPrincipalURL(resource):
-+ return isPrincipalResource(resource) and resource.principalURL() == str(href_principal)
-+
- d = request.locateResource(str(href_principal))
-- d.addCallback(isPrincipalResource)
-+ d.addCallback(_matchPrincipalURL)
- return d
-
- def resolvePrincipal(self, principal, request):
-@@ -1409,8 +1496,7 @@
- try:
- principal = principal.getResult()
- except HTTPError, e:
-- assert (
-- e.response.code == responsecode.NOT_FOUND,
-+ assert e.response.code == responsecode.NOT_FOUND, (
- "Expected %s response from readProperty() exception, not %s"
- % (responsecode.NOT_FOUND, e.response.code)
- )
-@@ -1437,15 +1523,15 @@
- log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
- yield None
- return
-- principal = davxml.HRef.fromString(self.principalURL())
-+ principal = davxml.HRef(self.principalURL())
-
- if isinstance(principal, davxml.HRef):
- yield principal
-+ return
- else:
- yield None
-
-- assert (
-- isinstance(principal, (davxml.All, davxml.Authenticated, davxml.Unauthenticated)),
-+ assert isinstance(principal, (davxml.All, davxml.Authenticated, davxml.Unauthenticated)), (
- "Not a meta-principal: %r" % (principal,)
- )
-
-@@ -1522,6 +1608,280 @@
- return None
-
- ##
-+ # Quota
-+ ##
-+
-+ """
-+ The basic policy here is to define a private 'quota-root' property on a collection.
-+ That property will contain the maximum allowed bytes for the collections and all
-+ its contents.
-+
-+ In order to determine the quota property values on a resource, the server must look
-+ for the private property on that resource and any of its parents. If found on a parent,
-+ then that parent should be queried for quota information. If not found, no quota
-+ exists for the resource.
-+
-+ To determine tha actual quota in use we will cache the used byte count on the quota-root
-+ collection in another private property. It is the servers responsibility to
-+ keep that property up to date by adjusting it after every PUT, DELETE, COPY,
-+ MOVE, MKCOL, PROPPATCH, ACL, POST or any other method that may affect the size of
-+ stored data. If the private property is not present, the server will fall back to
-+ getting the size by iterating over all resources (this is done in static.py).
-+
-+ """
-+
-+ def quota(self, request):
-+ """
-+ Get current available & used quota values for this resource's quota root
-+ collection.
-+
-+ @return: an L{Defered} with result C{tuple} containing two C{int}'s the first is
-+ quota-available-bytes, the second is quota-used-bytes, or
-+ C{None} if quota is not defined on the resource.
-+ """
-+
-+ # See if already cached
-+ if not hasattr(request, "quota"):
-+ request.quota = {}
-+ if request.quota.has_key(self):
-+ yield request.quota[self]
-+ return
-+
-+ # Check this resource first
-+ if self.isCollection():
-+ qroot = self.quotaRoot(request)
-+ if qroot is not None:
-+ used = waitForDeferred(self.currentQuotaUse(request))
-+ yield used
-+ used = used.getResult()
-+ available = qroot - used
-+ if available < 0:
-+ available = 0
-+ request.quota[self] = (available, used)
-+ yield request.quota[self]
-+ return
-+
-+ # Check the next parent
-+ url = request.urlForResource(self)
-+ if url != "/":
-+ parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+ yield parent
-+ parent = parent.getResult()
-+ d = waitForDeferred(parent.quota(request))
-+ yield d
-+ request.quota[self] = d.getResult()
-+ else:
-+ request.quota[self] = None
-+
-+ yield request.quota[self]
-+ return
-+
-+ quota = deferredGenerator(quota)
-+
-+ def hasQuota(self, request):
-+ """
-+ Check whether this resource is undre quota control by checking each parent to see if
-+ it has a quota root.
-+
-+ @return: C{True} if under quota control, C{False} if not.
-+ """
-+
-+ # Check this one first
-+ if self.hasQuotaRoot(request):
-+ yield True
-+ return
-+
-+ # Look at each parent
-+ try:
-+ url = request.urlForResource(self)
-+ if url != "/":
-+ parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+ yield parent
-+ parent = parent.getResult()
-+ d = waitForDeferred(parent.hasQuota(request))
-+ yield d
-+ yield d.getResult()
-+ else:
-+ yield False
-+ except NoURLForResourceError:
-+ yield False
-+
-+ hasQuota = deferredGenerator(hasQuota)
-+
-+ def hasQuotaRoot(self, request):
-+ """
-+ @return: a C{True} if this resource has quota root, C{False} otherwise.
-+ """
-+ return self.hasDeadProperty(TwistedQuotaRootProperty)
-+
-+ def quotaRoot(self, request):
-+ """
-+ @return: a C{int} containing the maximum allowed bytes if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ if self.hasDeadProperty(TwistedQuotaRootProperty):
-+ return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
-+ else:
-+ return None
-+
-+ def quotaRootParent(self, request):
-+ """
-+ Return the next quota root above this resource.
-+
-+ @return: L{DAVResource} or C{None}
-+ """
-+
-+ # Check the next parent
-+ url = request.urlForResource(self)
-+ while (url != "/"):
-+ url = parentForURL(url)
-+ parent = waitForDeferred(request.locateResource(url))
-+ yield parent
-+ parent = parent.getResult()
-+ if parent.hasQuotaRoot(request):
-+ yield parent
-+ return
-+
-+ yield None
-+
-+ quotaRootParent = deferredGenerator(quotaRootParent)
-+
-+ def setQuotaRoot(self, request, maxsize):
-+ """
-+ @param maxsize: a C{int} containing the maximum allowed bytes for the contents
-+ of this collection, or C{None} tp remove quota restriction.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+ assert maxsize is None or isinstance(maxsize, int), "maxsize must be an int or None"
-+
-+ if maxsize is not None:
-+ self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
-+ else:
-+ # Remove both the root and the cached used value
-+ self.removeDeadProperty(TwistedQuotaRootProperty)
-+ self.removeDeadProperty(TwistedQuotaUsedProperty)
-+
-+ def quotaSize(self, request):
-+ """
-+ Get the size of this resource (if its a collection get total for all children as well).
-+ TODO: Take into account size of dead-properties.
-+
-+ @return: a C{int} containing the size of the resource.
-+ """
-+ unimplemented(self)
-+
-+ def checkQuota(self, request, available):
-+ """
-+ Check to see whether all quota roots have sufficient available bytes.
-+ We currently do not use hierarchical quota checks - i.e. only the most
-+ immediate quota root parent is checked for quota.
-+
-+ @param available: a C{int} containing the additional quota required.
-+ @return: C{True} if there is sufficient quota remaining on all quota roots,
-+ C{False} otherwise.
-+ """
-+
-+ quotaroot = self
-+ while(quotaroot is not None):
-+ # Check quota on this root (if it has one)
-+ quota = quotaroot.quotaRoot(request)
-+ if quota is not None:
-+ if available > quota[0]:
-+ yield False
-+ return
-+
-+ # Check the next parent with a quota root
-+ quotaroot = waitForDeferred(quotaroot.quotaRootParent(request))
-+ yield quotaroot
-+ quotaroot = quotaroot.getResult()
-+
-+ yield True
-+
-+ checkQuota = deferredGenerator(checkQuota)
-+
-+ def quotaSizeAdjust(self, request, adjust):
-+ """
-+ Update the quota used value on all quota root parents of this resource.
-+
-+ @param adjust: a C{int} containing the number of bytes added (positive) or
-+ removed (negative) that should be used to adjust the cached total.
-+ """
-+
-+ # Check this resource first
-+ if self.isCollection():
-+ if self.hasQuotaRoot(request):
-+ d = waitForDeferred(self.updateQuotaUse(request, adjust))
-+ yield d
-+ d.getResult()
-+ yield None
-+ return
-+
-+ # Check the next parent
-+ url = request.urlForResource(self)
-+ if url != "/":
-+ parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+ yield parent
-+ parent = parent.getResult()
-+ d = waitForDeferred(parent.quotaSizeAdjust(request, adjust))
-+ yield d
-+ d.getResult()
-+
-+ yield None
-+
-+ quotaSizeAdjust = deferredGenerator(quotaSizeAdjust)
-+
-+ def currentQuotaUse(self, request):
-+ """
-+ Get the cached quota use value, or if not present (or invalid) determine
-+ quota use by brute force.
-+
-+ @return: an L{Deferred} with a C{int} result containing the current used byte if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+ assert self.hasQuotaRoot(request), "Quota use only on quota root collection"
-+
-+ # Try to get the cached value property
-+ if self.hasDeadProperty(TwistedQuotaUsedProperty):
-+ return succeed(int(str(self.readDeadProperty(TwistedQuotaUsedProperty))))
-+ else:
-+ # Do brute force size determination and cache the result in the private property
-+ def _defer(result):
-+ self.writeDeadProperty(TwistedQuotaUsedProperty(str(result)))
-+ return result
-+ d = self.quotaSize(request)
-+ d.addCallback(_defer)
-+ return d
-+
-+ def updateQuotaUse(self, request, adjust):
-+ """
-+ Update the quota used value on this resource.
-+
-+ @param adjust: a C{int} containing the number of bytes added (positive) or
-+ removed (negative) that should be used to adjust the cached total.
-+ @return: an L{Deferred} with a C{int} result containing the current used byte if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+
-+ # Get current value
-+ def _defer(size):
-+ size += adjust
-+
-+ # Sanity check the resulting size
-+ if size >= 0:
-+ self.writeDeadProperty(TwistedQuotaUsedProperty(str(size)))
-+ else:
-+ # Remove the dead property and re-read to do brute force quota calc
-+ log.msg("Attempt to set quota used to a negative value: %s (adjustment: %s)" % (size, adjust,))
-+ self.removeDeadProperty(TwistedQuotaUsedProperty)
-+ return self.currentQuotaUse(request)
-+
-+ d = self.currentQuotaUse(request)
-+ d.addCallback(_defer)
-+ return d
-+
-+ ##
- # HTTP
- ##
-
-@@ -1530,15 +1890,11 @@
- #litmus = request.headers.getRawHeaders("x-litmus")
- #if litmus: log.msg("*** Litmus test: %s ***" % (litmus,))
-
-- # FIXME: Learn how to use twisted logging facility, wsanchez
-- protocol = "HTTP/%s.%s" % request.clientproto
-- log.msg("%s %s %s" % (request.method, urllib.unquote(request.uri), protocol))
--
- #
- # If this is a collection and the URI doesn't end in "/", redirect.
- #
-- if self.isCollection() and request.uri[-1:] != "/":
-- return RedirectResponse(request.uri + "/")
-+ if self.isCollection() and request.path[-1:] != "/":
-+ return RedirectResponse(request.unparseURL(path=request.path+'/'))
-
- def setHeaders(response):
- response = IResponse(response)
-@@ -1572,7 +1928,7 @@
- def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
- return succeed(None)
-
--class DAVPrincipalResource (DAVLeafResource):
-+class DAVPrincipalResource (DAVResource):
- """
- Resource representing a WebDAV principal. (RFC 3744, section 2)
- """
-@@ -1582,7 +1938,7 @@
- # WebDAV
- ##
-
-- liveProperties = DAVLeafResource.liveProperties + (
-+ liveProperties = DAVResource.liveProperties + (
- (dav_namespace, "alternate-URI-set"),
- (dav_namespace, "principal-URL" ),
- (dav_namespace, "group-member-set" ),
-@@ -1590,14 +1946,11 @@
- )
-
- def davComplianceClasses(self):
-- return ("1",)
-+ return ("1", "access-control",)
-
- def isCollection(self):
- return False
-
-- def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
-- return succeed(None)
--
- def readProperty(self, property, request):
- def defer():
- if type(property) is tuple:
-@@ -1615,10 +1968,20 @@
- return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
-
- if name == "group-member-set":
-- return davxml.GroupMemberSet(*[davxml.HRef(p) for p in self.groupMembers()])
-+ def callback(members):
-+ return davxml.GroupMemberSet(*[davxml.HRef(p.principalURL()) for p in members])
-+
-+ d = self.groupMembers()
-+ d.addCallback(callback)
-+ return d
-
- if name == "group-membership":
-- return davxml.GroupMembership(*[davxml.HRef(g) for g in self.groupMemberships()])
-+ def callback(memberships):
-+ return davxml.GroupMembership(*[davxml.HRef(g.principalURL()) for g in memberships])
-+
-+ d = self.groupMemberships()
-+ d.addCallback(callback)
-+ return d
-
- if name == "resourcetype":
- if self.isCollection():
-@@ -1660,7 +2023,7 @@
- principals. Subclasses should override this method to provide member
- URLs for this resource if appropriate.
- """
-- return ()
-+ return succeed(())
-
- def groupMemberships(self):
- """
-@@ -1671,6 +2034,7 @@
- """
- unimplemented(self)
-
-+ @deferredGenerator
- def principalMatch(self, href):
- """
- Check whether the supplied principal matches this principal or is a
-@@ -1680,10 +2044,33 @@
- """
- uri = str(href)
- if self.principalURL() == uri:
-- return True
-+ yield True
-+ return
- else:
-- return uri in self.groupMembers()
-+ d = waitForDeferred(self.expandedGroupMembers())
-+ yield d
-+ members = d.getResult()
-+ member_uris = [member.principalURL() for member in members]
-+ yield uri in member_uris
-
-+class DAVPrincipalCollectionResource (DAVResource):
-+ """
-+ WebDAV principal collection resource. (RFC 3744, section 5.8)
-+ """
-+ implements(IDAVPrincipalCollectionResource)
-+
-+ def __init__(self, url, principalCollections=()):
-+ """
-+ @param url: This resource's URL.
-+ """
-+ DAVResource.__init__(self, principalCollections=principalCollections)
-+
-+ assert url.endswith("/"), "Collection URL must end in '/'"
-+ self._url = url
-+
-+ def principalCollectionURL(self):
-+ return self._url
-+
- class AccessDeniedError(Exception):
- def __init__(self, errors):
- """
-@@ -1723,6 +2110,37 @@
- davxml.registerElement(TwistedACLInheritable)
- davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
-
-+class TwistedGETContentMD5 (davxml.WebDAVTextElement):
-+ """
-+ MD5 hash of the resource content.
-+ """
-+ namespace = twisted_dav_namespace
-+ name = "getcontentmd5"
-+
-+davxml.registerElement(TwistedGETContentMD5)
-+
-+"""
-+When set on a collection, this property indicates that the collection has a quota limit for
-+the size of all resources stored in the collection (and any associate meta-data such as properties).
-+The value is a number - the maximum size in bytes allowed.
-+"""
-+class TwistedQuotaRootProperty (davxml.WebDAVTextElement):
-+ namespace = twisted_private_namespace
-+ name = "quota-root"
-+
-+davxml.registerElement(TwistedQuotaRootProperty)
-+
-+"""
-+When set on a collection, this property contains the cached running total of the size of all
-+resources stored in the collection (and any associate meta-data such as properties).
-+The value is a number - the size in bytes used.
-+"""
-+class TwistedQuotaUsedProperty (davxml.WebDAVTextElement):
-+ namespace = twisted_private_namespace
-+ name = "quota-used"
-+
-+davxml.registerElement(TwistedQuotaUsedProperty)
-+
- allACL = davxml.ACL(
- davxml.ACE(
- davxml.Principal(davxml.All()),
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.static.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.static.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,161 +0,0 @@
-Index: twisted/web2/dav/static.py
-===================================================================
---- twisted/web2/dav/static.py (revision 19773)
-+++ twisted/web2/dav/static.py (working copy)
-@@ -28,16 +28,17 @@
-
- __all__ = ["DAVFile"]
-
--import os
--
-+from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
-+from twisted.python.filepath import InsecurePath
- from twisted.python import log
--from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
--from twisted.web2.static import File
-+from twisted.web2 import http_headers
- from twisted.web2 import responsecode, dirlist
--from twisted.web2.http import RedirectResponse
- from twisted.web2.dav import davxml
- from twisted.web2.dav.resource import DAVResource, davPrivilegeSet
-+from twisted.web2.dav.resource import TwistedGETContentMD5
- from twisted.web2.dav.util import bindMethods
-+from twisted.web2.http import HTTPError, StatusResponse, RedirectResponse
-+from twisted.web2.static import File
-
- try:
- from twisted.web2.dav.xattrprops import xattrPropertyStore as DeadPropertyStore
-@@ -52,9 +53,11 @@
-
- Extends twisted.web2.static.File to handle WebDAV methods.
- """
-- def __init__(self, path,
-- defaultType="text/plain",
-- indexNames=None):
-+ def __init__(
-+ self, path,
-+ defaultType="text/plain", indexNames=None,
-+ principalCollections=()
-+ ):
- """
- @param path: the path of the file backing this resource.
- @param defaultType: the default mime type (as a string) for this
-@@ -62,11 +65,14 @@
- @param indexNames: a sequence of index file names.
- @param acl: an L{IDAVAccessControlList} with the .
- """
-- super(DAVFile, self).__init__(path,
-- defaultType = defaultType,
-- ignoredExts = (),
-- processors = None,
-- indexNames = indexNames)
-+ File.__init__(
-+ self, path,
-+ defaultType = defaultType,
-+ ignoredExts = (),
-+ processors = None,
-+ indexNames = indexNames,
-+ )
-+ DAVResource.__init__(self, principalCollections=principalCollections)
-
- def __repr__(self):
- return "<%s: %s>" % (self.__class__.__name__, self.fp.path)
-@@ -75,6 +81,13 @@
- # WebDAV
- ##
-
-+ def etag(self):
-+ if not self.fp.exists(): return None
-+ if self.hasDeadProperty(TwistedGETContentMD5):
-+ return http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5)))
-+ else:
-+ return super(DAVFile, self).etag()
-+
- def davComplianceClasses(self):
- return ("1", "access-control") # Add "2" when we have locking
-
-@@ -87,7 +100,6 @@
- """
- See L{IDAVResource.isCollection}.
- """
-- for child in self.listChildren(): return True
- return self.fp.isdir()
-
- ##
-@@ -98,6 +110,50 @@
- return succeed(davPrivilegeSet)
-
- ##
-+ # Quota
-+ ##
-+
-+ def quotaSize(self, request):
-+ """
-+ Get the size of this resource.
-+ TODO: Take into account size of dead-properties. Does stat
-+ include xattrs size?
-+
-+ @return: an L{Deferred} with a C{int} result containing the size of the resource.
-+ """
-+ if self.isCollection():
-+ def walktree(top):
-+ """
-+ Recursively descend the directory tree rooted at top,
-+ calling the callback function for each regular file
-+
-+ @param top: L{FilePath} for the directory to walk.
-+ """
-+
-+ total = 0
-+ for f in top.listdir():
-+ child = top.child(f)
-+ if child.isdir():
-+ # It's a directory, recurse into it
-+ result = waitForDeferred(walktree(child))
-+ yield result
-+ total += result.getResult()
-+ elif child.isfile():
-+ # It's a file, call the callback function
-+ total += child.getsize()
-+ else:
-+ # Unknown file type, print a message
-+ pass
-+
-+ yield total
-+
-+ walktree = deferredGenerator(walktree)
-+
-+ return walktree(self.fp)
-+ else:
-+ return succeed(self.fp.getsize())
-+
-+ ##
- # Workarounds for issues with File
- ##
-
-@@ -112,8 +168,12 @@
- See L{IResource}C{.locateChild}.
- """
- # If getChild() finds a child resource, return it
-- child = self.getChild(segments[0])
-- if child is not None: return (child, segments[1:])
-+ try:
-+ child = self.getChild(segments[0])
-+ if child is not None:
-+ return (child, segments[1:])
-+ except InsecurePath:
-+ raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Invalid URL path"))
-
- # If we're not backed by a directory, we have no children.
- # But check for existance first; we might be a collection resource
-@@ -132,7 +192,9 @@
- return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
-
- def createSimilarFile(self, path):
-- return self.__class__(path, defaultType=self.defaultType, indexNames=self.indexNames[:])
-+ return self.__class__(
-+ path, defaultType=self.defaultType, indexNames=self.indexNames[:],
-+ principalCollections=self.principalCollections())
-
- #
- # Attach method handlers to DAVFile
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,15 +0,0 @@
-Index: twisted/web2/dav/test/data/quota_100.txt
-===================================================================
---- twisted/web2/dav/test/data/quota_100.txt (revision 0)
-+++ twisted/web2/dav/test/data/quota_100.txt (revision 0)
-@@ -0,0 +1,10 @@
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,173 +0,0 @@
-Index: twisted/web2/dav/test/test_acl.py
-===================================================================
---- twisted/web2/dav/test/test_acl.py (revision 26346)
-+++ twisted/web2/dav/test/test_acl.py (working copy)
-@@ -30,6 +30,7 @@
- from twisted.web2.auth import basic
- from twisted.web2.stream import MemoryStream
- from twisted.web2.dav import davxml
-+from twisted.web2.dav.resource import DAVPrincipalCollectionResource
- from twisted.web2.dav.util import davXMLFromStream
- from twisted.web2.dav.auth import TwistedPasswordProperty, IPrincipal, DavRealm, TwistedPropertyChecker, AuthenticationWrapper
- from twisted.web2.dav.fileop import rmdir
-@@ -39,74 +40,87 @@
- from twisted.web2.dav.test.util import Site, serialize
- from twisted.web2.dav.test.test_resource import TestResource, TestDAVPrincipalResource
-
-+class TestPrincipalsCollection(DAVPrincipalCollectionResource, TestResource):
-+ def __init__(self, url, children):
-+ DAVPrincipalCollectionResource.__init__(self, url)
-+ TestResource.__init__(self, url, children, principalCollections=(self,))
-+
-+ def principalForUser(self, user):
-+ return self.principalForShortName('users', user)
-+
-+ def principalForAuthID(self, creds):
-+ return self.principalForShortName('users', creds.username)
-+
-+ def principalForShortName(self, type, shortName):
-+ typeResource = self.children.get(type, None)
-+ user = None
-+ if typeResource:
-+ user = typeResource.children.get(shortName, None)
-+
-+ return user
-+
- class ACL(twisted.web2.dav.test.util.TestCase):
- """
- RFC 3744 (WebDAV ACL) tests.
- """
-- def _getDocumentRoot(self):
-- if not hasattr(self, "_docroot"):
-- docroot = self.mktemp()
-- os.mkdir(docroot)
-- rootresource = self.resource_class(docroot)
-+ def createDocumentRoot(self):
-+ docroot = self.mktemp()
-+ os.mkdir(docroot)
-
-- portal = Portal(DavRealm())
-- portal.registerChecker(TwistedPropertyChecker())
-+ userResource = TestDAVPrincipalResource("/principals/users/user01")
-+ userResource.writeDeadProperty(TwistedPasswordProperty("user01"))
-
-- credentialFactories = (basic.BasicCredentialFactory(""),)
-+ principalCollection = TestPrincipalsCollection(
-+ "/principals/",
-+ children={"users": TestPrincipalsCollection(
-+ "/principals/users/",
-+ children={"user01": userResource})})
-
-- loginInterfaces = (IPrincipal,)
-+ rootResource = self.resource_class(
-+ docroot, principalCollections=(principalCollection,))
-
-- self.site = Site(AuthenticationWrapper(
-- rootresource,
-- portal,
-- credentialFactories,
-- loginInterfaces
-- ))
-+ portal = Portal(DavRealm())
-+ portal.registerChecker(TwistedPropertyChecker())
-
-- rootresource.setAccessControlList(self.grant(davxml.All()))
-+ credentialFactories = (basic.BasicCredentialFactory(""),)
-
-- userresource = TestDAVPrincipalResource("/principals/user01")
-- userresource.writeDeadProperty(TwistedPasswordProperty.fromString("user01"))
-+ loginInterfaces = (IPrincipal,)
-
-- rootresource.putChild(
-- "principals",
-- TestResource("/principals", {"user01": userresource})
-- )
-+ self.site = Site(AuthenticationWrapper(
-+ rootResource,
-+ portal,
-+ credentialFactories,
-+ loginInterfaces
-+ ))
-
-- rootresource.writeDeadProperty(
-- davxml.PrincipalCollectionSet(davxml.HRef("/principals/"))
-- )
-+ rootResource.setAccessControlList(self.grant(davxml.All()))
-
-- for name, acl in (
-- ("none" , self.grant()),
-- ("read" , self.grant(davxml.Read())),
-- ("read-write" , self.grant(davxml.Read(), davxml.Write())),
-- ("unlock" , self.grant(davxml.Unlock())),
-- ("all" , self.grant(davxml.All())),
-- ):
-- filename = os.path.join(docroot, name)
-- if not os.path.isfile(filename):
-- file(filename, "w").close()
-- resource = self.resource_class(filename)
-- resource.setAccessControlList(acl)
-+ for name, acl in (
-+ ("none" , self.grant()),
-+ ("read" , self.grant(davxml.Read())),
-+ ("read-write" , self.grant(davxml.Read(), davxml.Write())),
-+ ("unlock" , self.grant(davxml.Unlock())),
-+ ("all" , self.grant(davxml.All())),
-+ ):
-+ filename = os.path.join(docroot, name)
-+ if not os.path.isfile(filename):
-+ file(filename, "w").close()
-+ resource = self.resource_class(filename)
-+ resource.setAccessControlList(acl)
-
-- for name, acl in (
-- ("nobind" , self.grant()),
-- ("bind" , self.grant(davxml.Bind())),
-- ("unbind" , self.grant(davxml.Bind(), davxml.Unbind())),
-- ):
-- dirname = os.path.join(docroot, name)
-- if not os.path.isdir(dirname):
-- os.mkdir(dirname)
-- resource = self.resource_class(dirname)
-- resource.setAccessControlList(acl)
-+ for name, acl in (
-+ ("nobind" , self.grant()),
-+ ("bind" , self.grant(davxml.Bind())),
-+ ("unbind" , self.grant(davxml.Bind(), davxml.Unbind())),
-+ ):
-+ dirname = os.path.join(docroot, name)
-+ if not os.path.isdir(dirname):
-+ os.mkdir(dirname)
-+ resource = self.resource_class(dirname)
-+ resource.setAccessControlList(acl)
-+ return docroot
-
-- self._docroot = docroot
-
-- return self._docroot
--
-- docroot = property(_getDocumentRoot)
--
- def restore(self):
- if hasattr(self, "_docroot"):
- print "*"*40
-@@ -342,9 +356,7 @@
- if method == "GET":
- ok = responsecode.OK
- elif method == "REPORT":
-- # BAD_REQUEST in the allowed case, because we're not actually
-- # including the required XML in the request body.
-- ok = responsecode.BAD_REQUEST
-+ ok = responsecode.MULTI_STATUS
- else:
- raise AssertionError("We shouldn't be here. (method = %r)" % (method,))
-
-@@ -358,6 +370,9 @@
- path = os.path.join(self.docroot, name)
-
- request = SimpleRequest(self.site, method, "/" + name)
-+ if method == "REPORT":
-+ request.stream = MemoryStream(davxml.PrincipalPropertySearch().toxml())
-+
- _add_auth_header(request)
-
- def test(response, code=code, path=path):
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,24 +0,0 @@
-Index: twisted/web2/dav/test/test_copy.py
-===================================================================
---- twisted/web2/dav/test/test_copy.py (revision 19773)
-+++ twisted/web2/dav/test/test_copy.py (working copy)
-@@ -22,9 +22,9 @@
- # DRI: Wilfredo Sanchez, wsanchez at apple.com
- ##
-
-+from hashlib import md5
- import os
- import urllib
--import md5
-
- import twisted.web2.dav.test.util
- from twisted.web2 import responsecode
-@@ -161,7 +161,7 @@
- yield (request, do_test)
-
- def sumFile(path):
-- m = md5.new()
-+ m = md5()
-
- if os.path.isfile(path):
- f = file(path)
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,74 +0,0 @@
-Index: twisted/web2/dav/test/test_pipeline.py
-===================================================================
---- twisted/web2/dav/test/test_pipeline.py (revision 0)
-+++ twisted/web2/dav/test/test_pipeline.py (revision 0)
-@@ -0,0 +1,69 @@
-+##
-+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
-+#
-+# Permission is hereby granted, free of charge, to any person obtaining a copy
-+# of this software and associated documentation files (the "Software"), to deal
-+# in the Software without restriction, including without limitation the rights
-+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+# copies of the Software, and to permit persons to whom the Software is
-+# furnished to do so, subject to the following conditions:
-+#
-+# The above copyright notice and this permission notice shall be included in all
-+# copies or substantial portions of the Software.
-+#
-+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+# SOFTWARE.
-+#
-+# DRI: Wilfredo Sanchez, wsanchez at apple.com
-+##
-+from twisted.internet import utils
-+from twisted.web2.test import test_server
-+from twisted.web2 import resource
-+from twisted.web2 import http
-+from twisted.web2.test import test_http
-+import sys
-+
-+from twisted.internet.defer import waitForDeferred, deferredGenerator
-+
-+from twisted.python import util
-+
-+class Pipeline(test_server.BaseCase):
-+ """
-+ Pipelined request
-+ """
-+ class TestResource(resource.LeafResource):
-+ def render(self, req):
-+ return http.Response(stream="Host:%s, Path:%s"%(req.host, req.path))
-+
-+ def setUp(self):
-+ self.root = self.TestResource()
-+
-+ def chanrequest(self, root, uri, length, headers, method, version, prepath, content):
-+ self.cr = super(Pipeline, self).chanrequest(root, uri, length, headers, method, version, prepath, content)
-+ return self.cr
-+
-+ def test_root(self):
-+
-+ def _testStreamRead(x):
-+ self.assertTrue(self.cr.request.stream.length == 0)
-+
-+ return self.assertResponse(
-+ (self.root, 'http://host/path', {"content-type":"text/plain",}, "PUT", None, '', "This is some text."),
-+ (405, {}, None)).addCallback(_testStreamRead)
-+
-+class SSLPipeline(test_http.SSLServerTest):
-+
-+ @deferredGenerator
-+ def testAdvancedWorkingness(self):
-+ args = ('-u', util.sibpath(__file__, "tworequest_client.py"), "basic",
-+ str(self.port), self.type)
-+ d = waitForDeferred(utils.getProcessOutputAndValue(sys.executable, args=args))
-+ yield d; out,err,code = d.getResult()
-+
-+ self.assertEquals(code, 0, "Error output:\n%s" % (err,))
-+ self.assertEquals(out, "HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\nHTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n")
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,69 +0,0 @@
-Index: twisted/web2/dav/test/test_prop.py
-===================================================================
---- twisted/web2/dav/test/test_prop.py (revision 19773)
-+++ twisted/web2/dav/test/test_prop.py (working copy)
-@@ -21,6 +21,8 @@
- #
- # DRI: Wilfredo Sanchez, wsanchez at apple.com
- ##
-+from twisted.web2.dav.element.rfc4331 import QuotaUsedBytes
-+from twisted.web2.dav.element.rfc4331 import QuotaAvailableBytes
-
- import random
-
-@@ -37,8 +39,13 @@
- from twisted.web2.dav.test.util import serialize
- import twisted.web2.dav.test.util
-
--live_properties = [lookupElement(qname)() for qname in DAVResource.liveProperties if qname[0] == dav_namespace]
-+# Remove dynamic live properties that exist
-+dynamicLiveProperties = (
-+ (dav_namespace, "quota-available-bytes" ),
-+ (dav_namespace, "quota-used-bytes" ),
-+)
-
-+
- #
- # See whether dead properties are available
- #
-@@ -49,6 +56,10 @@
- """
- PROPFIND, PROPPATCH requests
- """
-+
-+ def liveProperties(self):
-+ return [lookupElement(qname)() for qname in self.resource_class.liveProperties if (qname[0] == dav_namespace) and qname not in dynamicLiveProperties]
-+
- def test_PROPFIND_basic(self):
- """
- PROPFIND request
-@@ -85,7 +96,7 @@
- self.fail("PROPFIND failed (status %s) to locate live properties: %s"
- % (status.code, properties))
-
-- properties_to_find = [p.qname() for p in live_properties]
-+ properties_to_find = [p.qname() for p in self.liveProperties()]
-
- for property in properties:
- qname = property.qname()
-@@ -102,7 +113,7 @@
- else:
- self.fail("No response for URI /")
-
-- query = davxml.PropertyFind(davxml.PropertyContainer(*live_properties))
-+ query = davxml.PropertyFind(davxml.PropertyContainer(*self.liveProperties()))
-
- request = SimpleRequest(self.site, "PROPFIND", "/")
-
-@@ -146,9 +157,9 @@
- % (status.code, properties))
-
- if which.name == "allprop":
-- properties_to_find = [p.qname() for p in live_properties if not p.hidden]
-+ properties_to_find = [p.qname() for p in self.liveProperties() if not p.hidden]
- else:
-- properties_to_find = [p.qname() for p in live_properties]
-+ properties_to_find = [p.qname() for p in self.liveProperties()]
-
- for property in properties:
- qname = property.qname()
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,206 +0,0 @@
-Index: twisted/web2/dav/test/test_quota.py
-===================================================================
---- twisted/web2/dav/test/test_quota.py (revision 0)
-+++ twisted/web2/dav/test/test_quota.py (revision 0)
-@@ -0,0 +1,201 @@
-+##
-+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
-+#
-+# Permission is hereby granted, free of charge, to any person obtaining a copy
-+# of this software and associated documentation files (the "Software"), to deal
-+# in the Software without restriction, including without limitation the rights
-+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+# copies of the Software, and to permit persons to whom the Software is
-+# furnished to do so, subject to the following conditions:
-+#
-+# The above copyright notice and this permission notice shall be included in all
-+# copies or substantial portions of the Software.
-+#
-+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+# SOFTWARE.
-+#
-+# DRI: Wilfredo Sanchez, wsanchez at apple.com
-+##
-+
-+from twisted.web2 import responsecode
-+from twisted.web2.iweb import IResponse
-+from twisted.web2.stream import FileStream
-+
-+import twisted.web2.dav.test.util
-+from twisted.web2.test.test_server import SimpleRequest
-+from twisted.web2.dav.test.util import Site
-+from twisted.web2.dav import davxml
-+import os
-+
-+class QuotaBase(twisted.web2.dav.test.util.TestCase):
-+
-+ def createDocumentRoot(self):
-+ docroot = self.mktemp()
-+ os.mkdir(docroot)
-+ rootresource = self.resource_class(docroot)
-+ rootresource.setAccessControlList(self.grantInherit(davxml.All()))
-+ self.site = Site(rootresource)
-+ self.site.resource.setQuotaRoot(None, 100000)
-+ return docroot
-+
-+
-+ def checkQuota(self, value):
-+ def _defer(quota):
-+ self.assertEqual(quota, value)
-+
-+ d = self.site.resource.currentQuotaUse(None)
-+ d.addCallback(_defer)
-+ return d
-+
-+class QuotaEmpty(QuotaBase):
-+
-+ def test_Empty_Quota(self):
-+
-+ return self.checkQuota(0)
-+
-+class QuotaPUT(QuotaBase):
-+
-+ def test_Quota_PUT(self):
-+ """
-+ Quota change on PUT
-+ """
-+ dst_uri = "/dst"
-+
-+ def checkResult(response):
-+ response = IResponse(response)
-+
-+ if response.code != responsecode.CREATED:
-+ self.fail("Incorrect response code for PUT (%s != %s)"
-+ % (response.code, responsecode.CREATED))
-+
-+ return self.checkQuota(100)
-+
-+ request = SimpleRequest(self.site, "PUT", dst_uri)
-+ request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
-+ return self.send(request, checkResult)
-+
-+class QuotaDELETE(QuotaBase):
-+
-+ def test_Quota_DELETE(self):
-+ """
-+ Quota change on DELETE
-+ """
-+ dst_uri = "/dst"
-+
-+ def checkPUTResult(response):
-+ response = IResponse(response)
-+
-+ if response.code != responsecode.CREATED:
-+ self.fail("Incorrect response code for PUT (%s != %s)"
-+ % (response.code, responsecode.CREATED))
-+
-+ def doDelete(_ignore):
-+ def checkDELETEResult(response):
-+ response = IResponse(response)
-+
-+ if response.code != responsecode.NO_CONTENT:
-+ self.fail("Incorrect response code for PUT (%s != %s)"
-+ % (response.code, responsecode.NO_CONTENT))
-+
-+ return self.checkQuota(0)
-+
-+ request = SimpleRequest(self.site, "DELETE", dst_uri)
-+ return self.send(request, checkDELETEResult)
-+
-+ d = self.checkQuota(100)
-+ d.addCallback(doDelete)
-+ return d
-+
-+ request = SimpleRequest(self.site, "PUT", dst_uri)
-+ request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
-+ return self.send(request, checkPUTResult)
-+
-+class OverQuotaPUT(QuotaBase):
-+
-+ def test_Quota_PUT(self):
-+ """
-+ Quota change on PUT
-+ """
-+ dst_uri = "/dst"
-+
-+ self.site.resource.setQuotaRoot(None, 90)
-+
-+ def checkResult(response):
-+ response = IResponse(response)
-+
-+ if response.code != responsecode.INSUFFICIENT_STORAGE_SPACE:
-+ self.fail("Incorrect response code for PUT (%s != %s)"
-+ % (response.code, responsecode.INSUFFICIENT_STORAGE_SPACE))
-+
-+ return self.checkQuota(0)
-+
-+ request = SimpleRequest(self.site, "PUT", dst_uri)
-+ request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
-+ return self.send(request, checkResult)
-+
-+class QuotaOKAdjustment(QuotaBase):
-+
-+ def test_Quota_OK_Adjustment(self):
-+ """
-+ Quota adjustment OK
-+ """
-+ dst_uri = "/dst"
-+
-+ def checkPUTResult(response):
-+ response = IResponse(response)
-+
-+ if response.code != responsecode.CREATED:
-+ self.fail("Incorrect response code for PUT (%s != %s)"
-+ % (response.code, responsecode.CREATED))
-+
-+ def doOKAdjustment(_ignore):
-+ def checkAdjustmentResult(_ignore):
-+ return self.checkQuota(10)
-+
-+ d = self.site.resource.quotaSizeAdjust(None, -90)
-+ d.addCallback(checkAdjustmentResult)
-+ return d
-+
-+ d = self.checkQuota(100)
-+ d.addCallback(doOKAdjustment)
-+ return d
-+
-+ request = SimpleRequest(self.site, "PUT", dst_uri)
-+ request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
-+ return self.send(request, checkPUTResult)
-+
-+class QuotaBadAdjustment(QuotaBase):
-+
-+ def test_Quota_Bad_Adjustment(self):
-+ """
-+ Quota adjustment too much
-+ """
-+ dst_uri = "/dst"
-+
-+ def checkPUTResult(response):
-+ response = IResponse(response)
-+
-+ if response.code != responsecode.CREATED:
-+ self.fail("Incorrect response code for PUT (%s != %s)"
-+ % (response.code, responsecode.CREATED))
-+
-+ def doBadAdjustment(_ignore):
-+ def checkAdjustmentResult(_ignore):
-+ return self.checkQuota(100)
-+
-+ d = self.site.resource.quotaSizeAdjust(None, -200)
-+ d.addCallback(checkAdjustmentResult)
-+ return d
-+
-+ d = self.checkQuota(100)
-+ d.addCallback(doBadAdjustment)
-+ return d
-+
-+ request = SimpleRequest(self.site, "PUT", dst_uri)
-+ request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
-+ return self.send(request, checkPUTResult)
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,87 +0,0 @@
-Index: twisted/web2/dav/test/test_resource.py
-===================================================================
---- twisted/web2/dav/test/test_resource.py (revision 26343)
-+++ twisted/web2/dav/test/test_resource.py (working copy)
-@@ -203,12 +203,10 @@
- TestCase.setUp(self)
-
- gooduser = TestDAVPrincipalResource("/users/gooduser")
-+ gooduser.writeDeadProperty(TwistedPasswordProperty("goodpass"))
-
-- gooduser.writeDeadProperty(
-- TwistedPasswordProperty.fromString("goodpass"))
--
- baduser = TestDAVPrincipalResource("/users/baduser")
-- baduser.writeDeadProperty(TwistedPasswordProperty.fromString("badpass"))
-+ baduser.writeDeadProperty(TwistedPasswordProperty("badpass"))
-
- protected = TestResource("/protected")
- protected.setAccessControlList(davxml.ACL(
-@@ -290,7 +288,8 @@
-
- # Has auth; should allow
- request = SimpleRequest(site, "GET", "/")
-- request.user = davxml.Principal(davxml.HRef("/users/d00d"))
-+ request.authnUser = davxml.Principal(davxml.HRef("/users/d00d"))
-+ request.authzUser = davxml.Principal(davxml.HRef("/users/d00d"))
- d = request.locateResource("/")
- d.addCallback(_checkPrivileges)
- d.addCallback(expectOK)
-@@ -306,6 +305,8 @@
- )
- return self.checkSecurity(request)
-
-+ test_authorize.todo = "Needs refactoring"
-+
- def test_badUsernameOrPassword(self):
- request = SimpleRequest(self.site, "GET", "/protected")
- request.headers.setHeader(
-@@ -316,6 +317,8 @@
- d.addCallback(self.assertErrorResponse, responsecode.UNAUTHORIZED)
- return d
-
-+ test_badUsernameOrPassword.todo = "Needs refactoring."
-+
- def test_lacksPrivileges(self):
- request = SimpleRequest(self.site, "GET", "/protected")
- request.headers.setHeader(
-@@ -345,11 +348,12 @@
- )
- )
-
-- def __init__(self, uri=None, children=None):
-+ def __init__(self, uri=None, children=None, principalCollections=()):
- """
- @param uri: A string respresenting the URI of the given resource
- @param children: a dictionary of names to Resources
- """
-+ DAVResource.__init__(self, principalCollections=principalCollections)
- self.children = children
- self.uri = uri
-
-@@ -375,8 +379,8 @@
- return succeed(davPrivilegeSet)
-
- def currentPrincipal(self, request):
-- if hasattr(request, "user"):
-- return request.user
-+ if hasattr(request, "authzUser"):
-+ return request.authzUser
- else:
- return davxml.Principal(davxml.Unauthenticated())
-
-@@ -398,7 +402,7 @@
-
- class AuthAllResource (TestResource):
- """
-- Give Authenticated principals all privileges deny everything else
-+ Give Authenticated principals all privileges and deny everyone else.
- """
- acl = davxml.ACL(
- davxml.ACE(
-@@ -413,3 +417,5 @@
- """
- Get deadProperties from TestResource
- """
-+ def principalURL(self):
-+ return self.uri
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,10 +0,0 @@
-Index: twisted/web2/dav/test/test_static.py
-===================================================================
---- twisted/web2/dav/test/test_static.py (revision 19773)
-+++ twisted/web2/dav/test/test_static.py (working copy)
-@@ -60,3 +60,5 @@
- d.addCallback(assertListing)
-
- return d
-+
-+ test_renderPrivileges.todo = "We changed the rules here; test needs an update"
Deleted: CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch 2009-04-13 17:40:29 UTC (rev 4006)
@@ -1,52 +0,0 @@
-Index: twisted/web2/dav/test/tworequest_client.py
-===================================================================
---- twisted/web2/dav/test/tworequest_client.py (revision 0)
-+++ twisted/web2/dav/test/tworequest_client.py (revision 0)
-@@ -0,0 +1,47 @@
-+import socket, sys
-+
-+test_type = sys.argv[1]
-+port = int(sys.argv[2])
-+socket_type = sys.argv[3]
-+
-+s = socket.socket(socket.AF_INET)
-+s.connect(("127.0.0.1", port))
-+s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 40000)
-+
-+if socket_type == 'ssl':
-+ s2 = socket.ssl(s)
-+ send=s2.write
-+ recv=s2.read
-+else:
-+ send=s.send
-+ recv=s.recv
-+
-+print >> sys.stderr, ">> Making %s request to port %d" % (socket_type, port)
-+
-+send("PUT /forbidden HTTP/1.1\r\n")
-+send("Host: localhost\r\n")
-+
-+print >> sys.stderr, ">> Sending lots of data"
-+send("Content-Length: 100\r\n\r\n")
-+send("X"*100)
-+
-+send("PUT /forbidden HTTP/1.1\r\n")
-+send("Host: localhost\r\n")
-+
-+print >> sys.stderr, ">> Sending lots of data"
-+send("Content-Length: 100\r\n\r\n")
-+send("X"*100)
-+
-+#import time
-+#time.sleep(5)
-+print >> sys.stderr, ">> Getting data"
-+data=''
-+while len(data) < 299999:
-+ try:
-+ x=recv(10000)
-+ except:
-+ break
-+ if x == '':
-+ break
-+ data+=x
-+sys.stdout.write(data)
Modified: CalendarServer/branches/exarkun/update-twisted-3816/run
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816/run 2009-04-13 16:51:08 UTC (rev 4005)
+++ CalendarServer/branches/exarkun/update-twisted-3816/run 2009-04-13 17:40:29 UTC (rev 4006)
@@ -636,7 +636,7 @@
;;
esac;
svn_uri="${proto}://svn.twistedmatrix.com/svn/Twisted/branches/dav-take-two-3081-3";
-svn_get "Twisted" "${twisted}" "${svn_uri}" 26455;
+svn_get "Twisted" "${twisted}" "${svn_uri}" 26689;
# No py_build step, since we tend to do edit Twisted, we want the sources in
# PYTHONPATH, not a build directory.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090413/606a752b/attachment-0001.html>
More information about the calendarserver-changes
mailing list