[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