[CalendarServer-changes] [118] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Sep 11 13:51:26 PDT 2006


Revision: 118
Author:   cdaboo at apple.com
Date:     2006-09-11 13:51:22 -0700 (Mon, 11 Sep 2006)

Log Message:
-----------
Result of merge from branches/users/cdaboo/acl-merge r53:r117.

Modified Paths:
--------------
    CalendarServer/trunk/bin/caldavd
    CalendarServer/trunk/conf/repository-static.xml
    CalendarServer/trunk/run
    CalendarServer/trunk/twistedcaldav/__init__.py
    CalendarServer/trunk/twistedcaldav/authkerb.py
    CalendarServer/trunk/twistedcaldav/directory.py
    CalendarServer/trunk/twistedcaldav/itip.py
    CalendarServer/trunk/twistedcaldav/method/copymove.py
    CalendarServer/trunk/twistedcaldav/method/mkcalendar.py
    CalendarServer/trunk/twistedcaldav/method/mkcol.py
    CalendarServer/trunk/twistedcaldav/method/post.py
    CalendarServer/trunk/twistedcaldav/method/put.py
    CalendarServer/trunk/twistedcaldav/method/put_common.py
    CalendarServer/trunk/twistedcaldav/method/report_calquery.py
    CalendarServer/trunk/twistedcaldav/method/report_common.py
    CalendarServer/trunk/twistedcaldav/method/report_freebusy.py
    CalendarServer/trunk/twistedcaldav/method/report_multiget.py
    CalendarServer/trunk/twistedcaldav/method/schedule.py
    CalendarServer/trunk/twistedcaldav/method/schedule_common.py
    CalendarServer/trunk/twistedcaldav/repository.py
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/static.py
    CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py
    CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py
    CalendarServer/trunk/twistedcaldav/test/test_freebusyquery.py
    CalendarServer/trunk/twistedcaldav/test/test_mkcalendar.py
    CalendarServer/trunk/twistedcaldav/test/test_multiget.py
    CalendarServer/trunk/twistedcaldav/test/test_options.py
    CalendarServer/trunk/twistedcaldav/test/test_props.py

Added Paths:
-----------
    CalendarServer/trunk/patches/Twisted/twisted.web2.auth.basic.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.auth.digest.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.auth.interfaces.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.auth.wrapper.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.filter.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.server.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.test.test_httpauth.patch

Removed Paths:
-------------
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.acl.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.auth.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.element.rfc3744.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.http.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch

Modified: CalendarServer/trunk/bin/caldavd
===================================================================
--- CalendarServer/trunk/bin/caldavd	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/bin/caldavd	2006-09-11 20:51:22 UTC (rev 118)
@@ -352,10 +352,13 @@
     else:
         raise IOError("SSL Certificate file does not exist: %%s" %% (certfile,))
 
-from twisted.application.service import Application, IServiceCollection
+from twisted.application.service  import Application, IServiceCollection
 from twisted.application.internet import TCPServer
-from twisted.web2.server import Site
-from twisted.web2.channel.http import HTTPFactory
+from twisted.cred.portal 		  import Portal
+from twisted.web2.auth			  import basic
+from twisted.web2.dav             import davxml, auth
+from twisted.web2.server          import Site
+from twisted.web2.channel.http    import HTTPFactory
 
 if dossl:
     from twisted.application.internet import SSLServer
@@ -365,10 +368,23 @@
 
 builder = RepositoryBuilder(docroot, doAccounts=doacct, resetACLs=doacl)
 builder.buildFromFile(repo)
+rootresource = builder.docRoot.collection.resource
 
 application = Application("CalDAVServer")
 parent      = IServiceCollection(application)
-site        = Site(builder.docRoot.collection.resource)
+
+portal = Portal(auth.DavRealm())
+portal.registerChecker(auth.TwistedPropertyChecker())
+
+credentialFactories = (basic.BasicCredentialFactory(""),)
+
+loginInterfaces = (auth.IPrincipal,)
+
+site = Site(auth.AuthenticationWrapper(rootresource, 
+                                            portal,
+                                            credentialFactories,
+                                            loginInterfaces))
+
 factory     = HTTPFactory(site)
 
 if not onlyssl:

Modified: CalendarServer/trunk/conf/repository-static.xml
===================================================================
--- CalendarServer/trunk/conf/repository-static.xml	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/conf/repository-static.xml	2006-09-11 20:51:22 UTC (rev 118)
@@ -155,8 +155,8 @@
           <protected/>
           <inheritable/>
         </ace>
+      </acl>
       <autorespond/>
-      </acl>
     </user>
      -->
   </accounts>

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.auth.basic.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.basic.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.auth.basic.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.auth.basic.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,13 @@
+Index: twisted/web2/auth/basic.py
+===================================================================
+--- twisted/web2/auth/basic.py	(revision 17932)
++++ twisted/web2/auth/basic.py	(working copy)
+@@ -20,7 +20,7 @@
+     def getChallenge(self, peer):
+         return {'realm': self.realm}
+ 
+-    def decode(self, response, method=None):
++    def decode(self, response, request):
+         try:
+             creds = (response + '===').decode('base64')
+         except:

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.auth.digest.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.digest.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.auth.digest.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.auth.digest.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,19 @@
+Index: twisted/web2/auth/digest.py
+===================================================================
+--- twisted/web2/auth/digest.py	(revision 17932)
++++ twisted/web2/auth/digest.py	(working copy)
+@@ -154,7 +154,7 @@
+                 'algorithm': self.algorithm,
+                 'realm': self.realm}
+ 
+-    def decode(self, response, method='GET'):
++    def decode(self, response, request):
+         def unq(s):
+             if s[0] == s[-1] == '"':
+                 return s[1:-1]
+@@ -172,4 +172,4 @@
+ 
+         del self.outstanding[auth['opaque']]
+             
+-        return DigestedCredentials(username, method, self.realm, auth)
++        return DigestedCredentials(username, request.method, self.realm, auth)

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.auth.interfaces.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.interfaces.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.auth.interfaces.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.auth.interfaces.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,24 @@
+Index: twisted/web2/auth/interfaces.py
+===================================================================
+--- twisted/web2/auth/interfaces.py	(revision 17932)
++++ twisted/web2/auth/interfaces.py	(working copy)
+@@ -13,7 +13,7 @@
+         @return: dictionary of challenge arguments
+         """
+ 
+-    def decode(response, method=None):
++    def decode(response, request):
+         """Create a credentials object from the given response.
+         May raise twisted.cred.error.LoginFailed if the response is invalid.
+     
+@@ -20,8 +20,8 @@
+         @type response: C{str}
+         @param response: scheme specific response string
+ 
+-        @type method: C{str}
+-        @param method: the method by which this response was sent
++        @type request: L{twisted.web2.server.Request}
++        @param request: the request being processed
+ 
+         @return: ICredentials
+         """

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.auth.wrapper.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.wrapper.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.auth.wrapper.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.auth.wrapper.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,13 @@
+Index: twisted/web2/auth/wrapper.py
+===================================================================
+--- twisted/web2/auth/wrapper.py	(revision 17932)
++++ twisted/web2/auth/wrapper.py	(working copy)
+@@ -87,7 +87,7 @@
+             return UnauthorizedResource(self.credentialFactories)
+ 
+         try:
+-            creds = factory.decode(response, req.method)
++            creds = factory.decode(response, req)
+         except error.LoginFailed:
+             return UnauthorizedResource(self.credentialFactories)
+ 

Deleted: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.acl.patch
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.acl.patch	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.acl.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -1,48 +0,0 @@
-Index: twisted/web2/dav/acl.py
-===================================================================
---- twisted/web2/dav/acl.py	(revision 17005)
-+++ twisted/web2/dav/acl.py	(working copy)
-@@ -50,8 +50,8 @@
-     ##
- 
-     liveProperties = DAVLeafResource.liveProperties + (
--        (dav_namespace, "alternate-uri-set"),
--        (dav_namespace, "principal-url"    ),
-+        (dav_namespace, "alternate-URI-set"),
-+        (dav_namespace, "principal-URL"    ),
-         (dav_namespace, "group-member-set" ),
-         (dav_namespace, "group-membership" ),
-     )
-@@ -68,10 +68,8 @@
-     def readProperty(self, property, request):
-         if type(property) is tuple:
-             qname = property
--            sname = "{%s}%s" % property
-         else:
-             qname = property.qname()
--            sname = property.sname()
- 
-         namespace, name = qname
- 
-@@ -76,10 +74,10 @@
-         namespace, name = qname
- 
-         if namespace == dav_namespace:
--            if name == "alternate-uri-set":
-+            if name == "alternate-URI-set":
-                 return davxml.AlternateURISet(*[davxml.HRef(u) for u in self.alternateURIs()])
- 
--            if name == "principal-url":
-+            if name == "principal-URL":
-                 return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
- 
-             if name == "group-member-set":
-@@ -86,7 +84,7 @@
-                 return davxml.GroupMemberSet(*[davxml.HRef(p) for p in self.groupMembers()])
- 
-             if name == "group-membership":
--                return davxml.GroupMemberSet(*[davxml.HRef(g) for g in self.groupMemberships()])
-+                return davxml.GroupMembership(*[davxml.HRef(g) for g in self.groupMemberships()])
- 
-             if name == "resourcetype":
-                 if self.isCollection():

Deleted: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.auth.patch
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.auth.patch	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.auth.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -1,20 +0,0 @@
-Index: twisted/web2/dav/auth.py
-===================================================================
---- twisted/web2/dav/auth.py	(revision 17005)
-+++ twisted/web2/dav/auth.py	(working copy)
-@@ -190,7 +190,6 @@
-                 log.err("Client authentication password for %s incorrect" % (self.username,))
-                 return False, None
-         else:
--            log.err("Client authentication user id %s does not match a principal on the server" % (self.username,))
-             return False, None
-         
- class BasicAuthorizer:
-@@ -306,7 +305,6 @@
-                 log.err("Client authentication password for %s incorrect" % (self.username,))
-                 return False, None
-         else:
--            log.err("Client authentication user id %s does not match a principal on the server" % (self.username,))
-             return False, None
-         
-     def checkPassword(self, password):

Deleted: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.element.rfc3744.patch
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.element.rfc3744.patch	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.element.rfc3744.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -1,22 +0,0 @@
-Index: twisted/web2/dav/element/rfc3744.py
-===================================================================
---- twisted/web2/dav/element/rfc3744.py	(revision 17005)
-+++ twisted/web2/dav/element/rfc3744.py	(working copy)
-@@ -147,7 +147,7 @@
-     Property which contains the URIs of network resources with additional
-     descriptive information about the principal. (RFC 3744, section 4.1)
-     """
--    name = "alternate-uri-set"
-+    name = "alternate-URI-set"
-     hidden = True
-     protected = True
- 
-@@ -158,7 +158,7 @@
-     Property which contains the URL that must be used to identify this principal
-     in an ACL request. (RFC 3744, section 4.2)
-     """
--    name = "principal-url"
-+    name = "principal-URL"
-     hidden = True
-     protected = True
- 

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.filter.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.filter.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.filter.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.filter.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,82 @@
+Index: twisted/web2/dav/filter/__init__.py
+===================================================================
+--- twisted/web2/dav/filter/__init__.py	(revision 0)
++++ twisted/web2/dav/filter/__init__.py	(revision 0)
+@@ -0,0 +1,31 @@
++##
++# Copyright (c) 2006 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: Cyrus Daboo, cdaboo at apple.com
++##
++
++"""
++Output filters.
++"""
++
++__all__ = [
++    "location",
++]
+Index: twisted/web2/dav/filter/location.py
+===================================================================
+--- twisted/web2/dav/filter/location.py	(revision 0)
++++ twisted/web2/dav/filter/location.py	(revision 0)
+@@ -0,0 +1,41 @@
++"""
++Filter that adds a Location header to a response if the status is 201 Created.
++"""
++
++__all__ = ['locationfilter']
++
++from twisted.web2 import responsecode
++import urlparse
++
++def addlocation(request, location):
++    """
++    Ensure that the supplied location URI is added to any response to this request.
++
++    @param request:  L{IRequest} the request whose response is to be modified.
++    @param location: the C{str} containing the path relative URI for the location header value.
++    """
++
++    def locationfilter(request, response): #@UnusedVariable
++        """
++        Add a Location header to the response if the status is 201. Note that the location
++        value must be a valid URI (already % encoded etc).
++    
++        @param request:  L{IRequest} for the current request.
++        @param response: L{IResponse} for the response to add the header to.
++        @return: the original L{IResponse} with (or without) the added header.
++        """
++        
++        if (response.code == responsecode.CREATED):
++            # Check to see whether we have an absolute URI or not. If not, have the request
++            # turn it into an absolute URI.
++            (scheme, host, path, params, querystring, fragment) = urlparse.urlparse(location)
++            if scheme == '':
++                abslocation = request.unparseURL(path=location)
++            else:
++                abslocation = location
++        
++            response.headers.setHeader('location', abslocation)
++        return response
++
++    request.addResponseFilter(locationfilter)
++    return

Deleted: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.http.patch
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.http.patch	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.http.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -1,24 +0,0 @@
-Index: twisted/web2/dav/http.py
-===================================================================
---- twisted/web2/dav/http.py	(revision 17005)
-+++ twisted/web2/dav/http.py	(working copy)
-@@ -102,7 +102,8 @@
-             else:
-                 uri = joinURL(base_uri, subpath)
- 
--            denials.append(davxml.Resource(davxml.HRef(uri), *[davxml.Privilege(p) for p in privileges]))
-+            for p in privileges:
-+                denials.append(davxml.Resource(davxml.HRef(uri), davxml.Privilege(p)))
- 
-         super(NeedPrivilegesResponse, self).__init__(responsecode.FORBIDDEN, davxml.NeedPrivileges(*denials))
- 
-@@ -115,9 +116,6 @@
-         """
-         @param xml_responses: an interable of davxml.Response objects.
-         """
--        multistatus = davxml.MultiStatus(*xml_responses)
--        output = multistatus.toxml()
--
-         Response.__init__(self, code=responsecode.MULTI_STATUS,
-                           stream=davxml.MultiStatus(*xml_responses).toxml())
- 

Deleted: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -1,429 +0,0 @@
-Index: twisted/web2/dav/resource.py
-===================================================================
---- twisted/web2/dav/resource.py	(revision 17097)
-+++ twisted/web2/dav/resource.py	(working copy)
-@@ -193,7 +193,13 @@
-                 return davxml.GETContentType(generateContentType(mimeType))
-         
-             if name == "getcontentlength":
--                return davxml.GETContentLength(str(self.contentLength()))
-+                length = self.contentLength()
-+                if length is None:
-+                    # TODO: really we should "render" the resource and determine its size from that,
-+                    # but for now we just return an empty element.
-+                    return davxml.GETContentLength("")
-+                else:
-+                    return davxml.GETContentLength(str(length))
- 
-             if name == "getlastmodified":
-                 return davxml.GETLastModified.fromDate(self.lastModified())
-@@ -227,7 +233,10 @@
-                 # MUST have DAV:read-acl ACL to return this
-                 if self.checkAccess(request, (davxml.ReadACL(),)):
-                     raise HTTPError(StatusResponse(responsecode.UNAUTHORIZED, "Access denied while reading property %s." % (sname,)))
--                return self.accessControlList(request)
-+                acl = self.accessControlList(request)
-+                if acl is None:
-+                    acl = davxml.ACL()
-+                return acl
- 
-             if name == "acl-restrictions":
-                 return davxml.ACLRestrictions()
-@@ -700,6 +709,10 @@
-         
-         oldacl = self.accessControlList(request)
-         
-+        # Check disabled
-+        if oldacl is None:
-+            return None
-+
-         # Need to get list of supported privileges
-         supported = []
-         def addSupportedPrivilege(sp):
-@@ -727,9 +740,17 @@
-                         log.err("Attempt to overwrite protected ace %r on resource %r" % (oldace, self))
-                         return (davxml.dav_namespace, "no-protected-ace-conflict")
-                     # Step 2
--                    elif oldace.inherited:
--                        log.err("Attempt to overwrite inherited ace %r on resource %r" % (oldace, self))
--                        return (davxml.dav_namespace, "no-inherited-ace-conflict")
-+                    """
-+                    RFC3744 says that we either enforce the inherited ace conflict or we ignore it
-+                    # but use access control evaluation to determine whether there us any impact. Given that we
-+                    # have the 'inheritable' behavior it does not make sense to disallow overrides of inherited ACEs
-+                    # since 'inheritable' cannot itself be controlled via protocol.
-+                    
-+                    So the following 3 lines have been commented out for now.
-+                    """
-+                    #elif oldace.inherited:
-+                    #    log.err("Attempt to overwrite inherited ace %r on resource %r" % (oldace, self))
-+                    #    return (davxml.dav_namespace, "no-inherited-ace-conflict")
-             
-             # Step 3
-             if ace.allow and got_deny:
-@@ -776,10 +797,27 @@
-         # FIXME: verify acl is self-consistent
-         
-         # Step 11
--        self.writeDeadProperty(TwistedACLProperty(davxml.ACL(*newset)))
-+        self.writeNewACEs(newset)
-         return None
- 
--    def checkAccess(self, request, privileges, recurse=False, principal=None):
-+    def writeNewACEs(self, newaces):
-+        """
-+        Write a new ACL to the resource's property store.
-+        NB We have this as a separate method so that it can be overridden by resources that need to do extra
-+        processing of ACLs being set via the ACL command.
-+        
-+        @param newaces: C{list} of L{ACE} for ACL being set.
-+        """
-+        self.writeDeadProperty(TwistedACLProperty(davxml.ACL(*newaces)))
-+
-+    def matchPrivilege(self, privilege, ace_privileges, supportedPrivileges):
-+        for ace_privilege in ace_privileges:
-+            if privilege == ace_privilege or ace_privilege.isAggregateOf(privilege, supportedPrivileges):
-+                return True
-+
-+        return False
-+
-+    def checkAccess(self, request, privileges, recurse=False, principal=None, inheritedaces=None):
-         """
-         Check whether the given principal has the given privileges.
-         (RFC 3744, section 5.5)
-@@ -789,6 +827,8 @@
-         @param recurse: a boolean.  C{True} if a recursive check on all child
-             resources should be performed as well, C{False} otherwise.  (Has
-             no effect if this resource is not a collection resource.)
-+        @param inheritedaces: a list of L{Aces} corresponding to the precomputed
-+            inheritable aces from the parent resource hierarchy.
-         @return: a sequence of tuples, one for each resource for which one or
-             more of the given privileges are not granted, in the form
-             C{(uri, privileges)}, where uri is a URL path relative to this
-@@ -800,80 +840,8 @@
-             principal = self.currentPrincipal(request)
-         supportedPrivileges = self.supportedPrivileges(request)
- 
--        # The interesting part of a principal is it's one child
--        principal = principal.children[0]
--
-         # Other principals types don't make sense as actors.
--        assert principal.name in ("unauthenticated", "href")
--
--        def match_principal(ace_principal):
--            """
--            Returns True if ace_principal matches principal.
--            """
--            # See RFC 3744, section 5.5.1
--
--            # The interesting part of a principal is it's one child
--            ace_principal = ace_principal.children[0]
--
--            if isinstance(ace_principal, davxml.Property):
--                #FIXME: I think this is wrong - we need to get the ns, name from the first child of DAV:property
--                namespace = ace_principal.attributes.get(["namespace"], dav_namespace)
--                name = ace_principal.attributes["name"]
--
--                try:
--                    ace_principal = self.readProperty((namespace, name), request)
--                except HTTPError, e:
--                    assert e.response.code == responsecode.NOT_FOUND
--                    return False
--
--                if len(ace_principal) != 1:
--                    return False
--
--                # The interesting part of a principal is it's one child
--                ace_principal = ace_principal.children[0]
--
--                if not isinstance(ace_principal, davxml.HRef):
--                    return False
--
--                raise NotImplementedError("Need to verify that ACE principal URL is valid")
--
--                # Fall through...
--                
--            if isinstance(ace_principal, davxml.Self):
--                try:
--                    self = IDAVPrincipalResource(self)
--                except TypeError:
--                    log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
--                    return False
--
--                if isinstance(principal, davxml.Unauthenticated):
--                    return False
--
--                if isinstance(principal, davxml.HRef):
--                    return str(principal) == self.principalURL()
--
--                raise AssertionError("We shouldn't be here")
--
--            if isinstance(ace_principal, davxml.All):
--                return True
--
--            if isinstance(ace_principal, davxml.Authenticated):
--                if isinstance(principal, davxml.HRef):
--                    return True
--                else:
--                    return False
--
--            if isinstance(ace_principal, davxml.Unauthenticated) or isinstance(ace_principal, davxml.HRef):
--                return ace_principal == principal
--
--            raise AssertionError("We shouldn't be here")
--
--        def match_privilege(privilege, ace_privileges):
--            for ace_privilege in ace_privileges:
--                if privilege == ace_privilege or ace_privilege.isAggregateOf(privilege, supportedPrivileges):
--                    return True
--
--            return False
-+        assert principal.children[0].name in ("unauthenticated", "href")
- 
-         if recurse:
-             depth="infinity"
-@@ -883,7 +851,13 @@
-         errors = []
- 
-         for resource, subpath in itertools.chain(((self, None),), self.findChildren(depth=depth)):
--            acl = resource.accessControlList(request)
-+            acl = resource.accessControlList(request, inheritedaces=inheritedaces)
-+            
-+            # Check for disabled
-+            if acl is None:
-+                errors.append((subpath, list(privileges)))
-+                continue
-+
-             pending = list(privileges)
-             denied = []
- 
-@@ -889,10 +863,10 @@
- 
-             for ace in acl.children:
-                 for privilege in tuple(pending):
--                    if not match_privilege(davxml.Privilege(privilege), ace.privileges):
-+                    if not self.matchPrivilege(davxml.Privilege(privilege), ace.privileges, supportedPrivileges):
-                         continue
- 
--                    if match_principal(ace.principal):
-+                    if self.matchPrincipal(principal, ace.principal, request):
-                         if ace.invert:
-                             continue
-                     else:
-@@ -930,7 +904,7 @@
-         current = self.currentPrincipal(request)
-         return self.privilegesForPrincipal(current, request)
- 
--    def accessControlList(self, request, inheritance=True, expanding=False):
-+    def accessControlList(self, request, inheritance=True, expanding=False, inheritedaces=None):
-         """
-         See L{IDAVResource.accessControlList}.
- 
-@@ -938,6 +912,7 @@
-         C{(L{twisted_private_namespace}, "acl")}.
-         If no ACL has been stored for this resource, it returns the value
-         returned by C{defaultAccessControlList}.
-+        If access is disabled it will return C{None}.
-         """
-         
-         """
-@@ -949,6 +924,12 @@
-                 all parent resources of the current one looking for any <Twisted:inheritable> elements.
-                 If those are defined, the relevant ace is applied to the ACL on the current resource.
-         """
-+
-+        # Check disabled
-+        disabled = self.hasDeadProperty(TwistedAccessDisabledProperty)
-+        if disabled:
-+            return None
-+
-         try:
-             acl = self.readDeadProperty(TwistedACLProperty).getValue()
-         except HTTPError, e:
-@@ -965,22 +946,30 @@
-         if inheritance:
-             aces = list(acl.children)
- 
--            if self.getURI(request) != "/":
--                parentURL = parentForURL(self.getURI(request))
--                parent = self.locateSiblingResource(request, parentURL)
--
--                if parent:
--                    parent_acl = parent.accessControlList(request, inheritance=True, expanding=True)
--
--                    for ace in parent_acl.children:
--                        if ace.inherited:
--                            aces.append(ace)
--                        elif TwistedACLInheritable() in ace.children:
--                            # Adjust ACE for inherit on this resource
--                            children = list(ace.children)
--                            children.remove(TwistedACLInheritable())
--                            children.append(davxml.Inherited(davxml.HRef.fromString(parentURL)))
--                            aces.append(davxml.ACE(*children))
-+            if inheritedaces is None:
-+                if self.getURI(request) != "/":
-+                    parentURL = parentForURL(self.getURI(request))
-+                    parent = self.locateSiblingResource(request, parentURL)
-+    
-+                    if parent:
-+                        parent_acl = parent.accessControlList(request, inheritance=True, expanding=True)
-+                        
-+                        # Check disabled
-+                        if parent_acl is None:
-+                            return None
-+    
-+                        for ace in parent_acl.children:
-+                            if ace.inherited:
-+                                aces.append(ace)
-+                            elif TwistedACLInheritable() in ace.children:
-+                                # Adjust ACE for inherit on this resource
-+                                children = list(ace.children)
-+                                children.remove(TwistedACLInheritable())
-+                                children.append(davxml.Inherited(davxml.HRef.fromString(parentURL)))
-+                                aces.append(davxml.ACE(*children))
-+                        
-+            else:
-+                aces.extend(inheritedaces)
- 
-             # Always filter out any remaining private properties when we are
-             # returning the ACL for the final resource after doing parent
-@@ -998,6 +987,48 @@
- 
-         return acl
- 
-+    def inheritedACEsforChildren(self, request):
-+        """
-+        Do some optimisation of access control calculation by determining any inherited ACLs outside of
-+        the child resource loop and supply those to the checkAccess 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.
-+        """
-+        
-+        # Get the parent ACLs with inheritance and preserve the <inheritable> element.
-+        parent_acl = self.accessControlList(request, inheritance=True, expanding=True)
-+        
-+        # Check disabled
-+        if parent_acl is None:
-+            return None
-+
-+        # Filter out those that are not inheritable (and remove the inheritable element from those that are)
-+        aces = []
-+        for ace in parent_acl.children:
-+            if ace.inherited:
-+                aces.append(ace)
-+            elif TwistedACLInheritable() in ace.children:
-+                # Adjust ACE for inherit on this resource
-+                children = list(ace.children)
-+                children.remove(TwistedACLInheritable())
-+                children.append(davxml.Inherited(davxml.HRef.fromString(self.getURI(request))))
-+                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)
-+        return filteredaces
-+
-     def inheritedACLSet(self):
-         """
-         @return: a sequence of L{davxml.HRef}s from which ACLs are inherited.
-@@ -1023,6 +1054,10 @@
-             if isPrincipalResource(principal):
-                 return (principal, principalURI)
-         else:
-+            if len(self.principalCollections(request)) == 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)
-             return None
-         
-     def samePrincipal(self, principal1, principal2):
-@@ -1109,11 +1144,36 @@
-             else:
-                 return False
- 
--        if isinstance(principal2, davxml.Unauthenticated) or isinstance(principal2, davxml.HRef):
-+        if isinstance(principal2, davxml.Unauthenticated):
-             return principal2 == principal1
- 
-+        # Compare two HRefs and do group membership test as well
-+        if isinstance(principal2, davxml.HRef):
-+            return (principal2 == principal1 or
-+                    self.principalIsGroupMember(str(principal1), str(principal2), request))
-+
-         raise AssertionError("We shouldn't be here")
- 
-+    def principalIsGroupMember(self, principal1, principal2, request):
-+        """
-+        Check whether one principal is a group member of another.
-+        
-+        @param principal1: C{str} principalURL for principal to test.
-+        @param principal2: C{str} principalURL for possible group principal to test against.
-+        @param request: the request being processed.
-+        @return: C{True} if principal1 is a member of principal2, C{False} otherwise
-+        """
-+        
-+        # Get principal resource for principal2
-+        group = self.locateSiblingResource(request, principal2)
-+        from twisted.web2.dav.acl import DAVPrincipalResource
-+        if group and isinstance(group, DAVPrincipalResource):
-+            members = group.groupMembers()
-+            if principal1 in members:
-+                return True
-+            
-+        return False
-+        
-     def validPrincipal(self, ace_principal, request):
-         """
-         Check whether the supplied principal is valid for this resource.
-@@ -1228,6 +1288,11 @@
-         NB Return aggregate privileges expanded.
-         """
-         acl = self.accessControlList(request)
-+
-+        # Check disabled
-+        if acl is None:
-+            return []
-+
-         granted = []
-         denied = []
-         for ace in acl.children:
-@@ -1374,6 +1439,11 @@
- parser.registerElement(TwistedPrincipalCollectionSetProperty)
- 
- class TwistedACLInheritable (davxml.WebDAVEmptyElement):
-+    """
-+    When set on an ACE, this indicates that the ACE privileges should be inherited by
-+    all child resources within the resource with this ACE.
-+    """
-+    
-     namespace = twisted_dav_namespace
-     name = "inheritable"
- 
-@@ -1380,6 +1450,17 @@
- parser.registerElement(TwistedACLInheritable)
- davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
- 
-+"""
-+When set on a resource, this property indicates that all access to the resource and any of its children
-+is completely disabled - irrespective of any other privileges directly set or inherited.
-+"""
-+class TwistedAccessDisabledProperty (davxml.WebDAVEmptyElement):
-+    namespace = twisted_private_namespace
-+    name = "access-disabled"
-+
-+parser.registerElement(TwistedAccessDisabledProperty)
-+
-+
- allACL = davxml.ACL(
-     davxml.ACE(
-         davxml.Principal(davxml.All()),

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.resource.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.resource.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,13 @@
+Index: twisted/web2/dav/resource.py
+===================================================================
+--- twisted/web2/dav/resource.py	(revision 18074)
++++ twisted/web2/dav/resource.py	(working copy)
+@@ -599,7 +599,7 @@
+             else:
+                 factory = request.credentialFactories[authHeader[0]]
+ 
+-                creds = factory.decode(authHeader[1], request.method)
++                creds = factory.decode(authHeader[1], request)
+ 
+                 # Try to match principals in each principal collection on 
+                 # the resource

Deleted: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -1,164 +0,0 @@
-Index: twisted/web2/dav/static.py
-===================================================================
---- twisted/web2/dav/static.py	(revision 17005)
-+++ twisted/web2/dav/static.py	(working copy)
-@@ -32,8 +32,10 @@
- import urllib
- 
- from twisted.python import log
--from twisted.web2.static import File
--from twisted.web2.server import StopTraversal
-+from twisted.web2 import dirlist
-+from twisted.web2 import http
-+from twisted.web2 import responsecode
-+from twisted.web2 import stream
- from twisted.web2.dav import davxml
- from twisted.web2.dav.idav import IDAVResource
- from twisted.web2.dav.resource import DAVResource
-@@ -38,6 +40,8 @@
- from twisted.web2.dav.idav import IDAVResource
- from twisted.web2.dav.resource import DAVResource
- from twisted.web2.dav.util import bindMethods
-+from twisted.web2.server import StopTraversal
-+from twisted.web2.static import File
- 
- try:
-     from twisted.web2.dav.xattrprops import xattrPropertyStore as DeadPropertyStore
-@@ -117,7 +121,51 @@
-         """
-         assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
-         if depth != "0" and self.isCollection():
--            for name in self.listChildren():
-+            # Do some optimisation of access control calculation by determining any inherited ACLs outside of
-+            # the child resource loop and supply those to the checkAccess on each child.
-+            filteredaces = self.inheritedACEsforChildren(request)
-+
-+            # Check for disabled access
-+            if filteredaces is not None:
-+                for name in self.listChildren():
-+                    try:
-+                        child = IDAVResource(self.getChild(name))
-+                    except TypeError:
-+                        child = None
-+    
-+                    if child is not None:
-+                        # Check privileges of child - skip if access denied
-+                        if child.checkAccess(request, privileges, inheritedaces=filteredaces):
-+                            continue
-+    
-+                        if child.isCollection():
-+                            yield (child, name + "/")
-+                            if depth == "infinity":
-+                                for grandchild in child.findChildrenWithPrivileges(depth, privileges, request):
-+                                    yield (grandchild[0], name + "/" + grandchild[1])
-+                        else:
-+                            yield (child, name)
-+
-+    def listChildrenWithPrivileges(self, privileges, request):
-+        """
-+        NB This method looks at the actual objects in the file system.
-+
-+        @return: a sequence of the names of all known children of this resource that have the
-+            associated privileges set.
-+        """
-+        children = self.putChildren.keys()
-+        if self.fp.isdir():
-+            children += [c for c in self.fp.listdir() if c not in children]
-+        
-+        result = []
-+
-+        # Do some optimisation of access control calculation by determining any inherited ACLs outside of
-+        # the child resource loop and supply those to the checkAccess on each child.
-+        filteredaces = self.inheritedACEsforChildren(request)
-+
-+        # Check for disabled access
-+        if filteredaces is not None:
-+            for name in children:
-                 try:
-                     child = IDAVResource(self.getChild(name))
-                 except TypeError:
-@@ -122,19 +170,14 @@
-                     child = IDAVResource(self.getChild(name))
-                 except TypeError:
-                     child = None
--
-+    
-                 if child is not None:
-                     # Check privileges of child - skip if access denied
--                    if child.checkAccess(request, privileges):
-+                    if child.checkAccess(request, privileges, inheritedaces=filteredaces):
-                         continue
--
--                    if child.isCollection():
--                        yield (child, name + "/")
--                        if depth == "infinity":
--                            for grandchild in child.findChildrenWithPrivileges(depth, privileges, request):
--                                yield (grandchild[0], name + "/" + grandchild[1])
--                    else:
--                        yield (child, name)
-+                    result.append(name)
-+            
-+        return result
- 
-     ##
-     # ACL
-@@ -256,6 +299,59 @@
-         if len(upath) == 0:
-             upath = "/"
-         return urllib.quote(upath)
-+
-+    def render(self, req):
-+        """
-+        This is a direct copy of webs.static.render with the listChildren behavior replaced with
-+        findChildrenWithPrivileges to ensure that the current authenticated principal can only list
-+        directory contents that they have read permissions for.
-+        """
-+        if not self.fp.exists():
-+            return responsecode.NOT_FOUND
-+
-+        if self.fp.isdir():
-+            if req.uri[-1] != "/":
-+                # Redirect to include trailing '/' in URI
-+                return http.RedirectResponse(req.unparseURL(path=req.path+'/'))
-+            else:
-+                ifp = self.fp.childSearchPreauth(*self.indexNames)
-+                if ifp:
-+                    # Render from the index file
-+                    standin = self.createSimilarFile(ifp.path)
-+                else:
-+                    # Render from a DirectoryLister
-+                    standin = dirlist.DirectoryLister(
-+                        self.fp.path,
-+                        self.listChildrenWithPrivileges((davxml.Read(),), req),
-+                        self.contentTypes,
-+                        self.contentEncodings,
-+                        self.defaultType
-+                    )
-+                return standin.render(req)
-+
-+        try:
-+            f = self.fp.open()
-+        except IOError, e:
-+            import errno
-+            if e[0] == errno.EACCES:
-+                return responsecode.FORBIDDEN
-+            elif e[0] == errno.ENOENT:
-+                return responsecode.NOT_FOUND
-+            else:
-+                raise
-+
-+        response = http.Response()
-+        response.stream = stream.FileStream(f, 0, self.fp.getsize())
-+
-+        for (header, value) in (
-+            ("content-type", self.contentType()),
-+            ("content-encoding", self.contentEncoding()),
-+        ):
-+            if value is not None:
-+                response.headers.setHeader(header, value)
-+
-+        return response
-+
-         
- #
- # Attach method handlers to DAVFile

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.static.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.dav.static.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,13 @@
+Index: twisted/web2/dav/static.py
+===================================================================
+--- twisted/web2/dav/static.py	(revision 18074)
++++ twisted/web2/dav/static.py	(working copy)
+@@ -34,6 +34,8 @@
+ from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
+ from twisted.web2.static import File
+ from twisted.web2 import dirlist
++from twisted.web2 import http
++from twisted.web2 import responsecode
+ from twisted.web2.dav import davxml
+ from twisted.web2.dav.idav import IDAVResource
+ from twisted.web2.dav.resource import DAVResource

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.server.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.server.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.server.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.server.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,44 @@
+Index: twisted/web2/server.py
+===================================================================
+--- twisted/web2/server.py	(revision 17932)
++++ twisted/web2/server.py	(working copy)
+@@ -11,7 +11,7 @@
+ import cStringIO as StringIO
+ 
+ import cgi, time, urlparse
+-from urllib import unquote
++from urllib import quote, unquote
+ from urlparse import urlsplit
+ 
+ import weakref
+@@ -316,7 +316,12 @@
+         if newpath is StopTraversal:
+             # We need to rethink how to do this.
+             #if newres is res:
+-                self._rememberURLForResource(path, res)
++                if path:
++                    url = "/" + "/".join(path)
++                else:
++                    url = "/"
++        
++                self._rememberURLForResource(quote(url), res)
+                 return res
+             #else:
+             #    raise ValueError("locateChild must not return StopTraversal with a resource other than self.")
+@@ -337,7 +342,7 @@
+                 self.prepath.append(self.postpath.pop(0))
+ 
+         child = self._getChild(None, newres, newpath, updatepaths=updatepaths)
+-        self._rememberURLForResource(url, child)
++        self._rememberURLForResource(quote(url), child)
+ 
+         return child
+ 
+@@ -404,6 +409,7 @@
+         segments = path.split("/")
+         assert segments[0] == "", "URL path didn't begin with '/': %s" % (path,)
+         segments = segments[1:]
++        segments = map(unquote, segments)
+ 
+         def notFound(f):
+             f.trap(http.HTTPError)

Copied: CalendarServer/trunk/patches/Twisted/twisted.web2.test.test_httpauth.patch (from rev 117, CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.test.test_httpauth.patch)
===================================================================
--- CalendarServer/trunk/patches/Twisted/twisted.web2.test.test_httpauth.patch	                        (rev 0)
+++ CalendarServer/trunk/patches/Twisted/twisted.web2.test.test_httpauth.patch	2006-09-11 20:51:22 UTC (rev 118)
@@ -0,0 +1,71 @@
+Index: twisted/web2/test/test_httpauth.py
+===================================================================
+--- twisted/web2/test/test_httpauth.py	(revision 17932)
++++ twisted/web2/test/test_httpauth.py	(working copy)
+@@ -2,6 +2,7 @@
+ from twisted.internet import defer
+ from twisted.cred import error
+ from twisted.web2.auth import basic, digest, wrapper
++from twisted.web2.test.test_server import SimpleRequest
+ 
+ from twisted.web2.test import test_server
+ 
+@@ -25,7 +26,7 @@
+                 self.username,
+                 self.password))
+ 
+-        creds = self.credentialFactory.decode(response)
++        creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
+         self.failUnless(creds.checkPassword(self.password))
+ 
+     def testIncorrectPassword(self):
+@@ -33,7 +34,7 @@
+                 self.username,
+                 'incorrectPassword'))
+ 
+-        creds = self.credentialFactory.decode(response)
++        creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
+         self.failIf(creds.checkPassword(self.password))
+ 
+     def testIncorrectPadding(self):
+@@ -43,7 +44,7 @@
+ 
+         response = response.strip('=')
+ 
+-        creds = self.credentialFactory.decode(response)
++        creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
+         self.failUnless(creds.checkPassword(self.password))
+ 
+     def testInvalidCredentials(self):
+@@ -51,7 +52,7 @@
+ 
+         self.assertRaises(error.LoginFailed, 
+                           self.credentialFactory.decode, 
+-                          response)
++                          response, SimpleRequest(None, 'GET', '/'))
+ 
+ challengeResponse = ('digest', {'nonce': '178288758716122392881254770685', 
+                                 'qop': 'auth', 'realm': 'test realm', 
+@@ -74,7 +75,7 @@
+     def testResponse(self):
+         challenge = self.credentialFactory.getChallenge(None)
+ 
+-        creds = self.credentialFactory.decode(authRequest, 'GET')
++        creds = self.credentialFactory.decode(authRequest, SimpleRequest(None, 'GET', '/'))
+         self.failUnless(creds.checkPassword('password'))
+ 
+     def testFailsWithDifferentMethod(self):
+@@ -80,11 +81,11 @@
+     def testFailsWithDifferentMethod(self):
+         challenge = self.credentialFactory.getChallenge(None)
+         
+-        creds = self.credentialFactory.decode(authRequest, 'POST')
++        creds = self.credentialFactory.decode(authRequest, SimpleRequest(None, 'POST', '/'))
+         self.failIf(creds.checkPassword('password'))
+ 
+     def testNoUsername(self):
+-        self.assertRaises(error.LoginFailed, self.credentialFactory.decode, namelessAuthRequest, 'GET')
++        self.assertRaises(error.LoginFailed, self.credentialFactory.decode, namelessAuthRequest, SimpleRequest(None, 'GET', '/'))
+ 
+ from zope.interface import Interface, implements
+ from twisted.cred import portal, checkers

Modified: CalendarServer/trunk/run
===================================================================
--- CalendarServer/trunk/run	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/run	2006-09-11 20:51:22 UTC (rev 118)
@@ -331,7 +331,7 @@
 # PyKerberos
 #
 
-svn_uri_base="$(svn info "${caldav}" | sed -n -e 's|/CalendarServer/trunk$||' -e 's|^URL: ||p')";
+svn_uri_base="$(svn info "${caldav}" | sed -n 's|Repository Root: ||p')";
 
 if [ -z "${svn_uri_base}" ]; then
     echo "Unable to locate subversion base URI for sources.";
@@ -406,12 +406,12 @@
 
 if ! "${disable_setup}"; then
   if [ "${USER}" == "wsanchez" ]; then
-    svn_uri="svn+ssh://svn.twistedmatrix.com/svn/Twisted/branches/wsanchez/acl-1608-2";
+    svn_uri="svn+ssh://svn.twistedmatrix.com/svn/Twisted/branches/acl-1608-8";
   else
-    svn_uri="svn://svn.twistedmatrix.com/svn/Twisted/branches/wsanchez/acl-1608-2";
+    svn_uri="svn://svn.twistedmatrix.com/svn/Twisted/branches/acl-1608-8";
   fi;
 
-  svn_get "Twisted" "${twisted}" "${svn_uri}" 17097;
+  svn_get "Twisted" "${twisted}" "${svn_uri}" 18078;
 fi;
 py_install "Twisted" "${twisted}";
  

Modified: CalendarServer/trunk/twistedcaldav/__init__.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/__init__.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/__init__.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -44,7 +44,7 @@
     __version__ = None
 
 # Load in suitable file extension/content-type map from OS X
-File.contentTypes = loadMimeTypes(["/etc/httpd/mime.types"])
+File.contentTypes = loadMimeTypes(("/etc/apache2/mime.types", "/etc/httpd/mime.types",))
 
 import twisted.web2.dav.davxml
 import twistedcaldav.caldavxml

Modified: CalendarServer/trunk/twistedcaldav/authkerb.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/authkerb.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/authkerb.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -29,22 +29,23 @@
      that implements full GSSAPI authentication.
 """
 
-from twisted.web2.dav import auth
-from zope import interface
-from twisted.cred.credentials import ICredentials
 
 __all__ = [
     "BasicKerberosCredentials",
     "BasicKerberosAuthorizer",
-    #"NegotiateCredentials",
-    #"NegotiateAuthorizer",
+    "BasicKerberosCredentialsChecker",
+    "NegotiateCredentials",
+    "NegotiateAuthorizer",
+    "NegotiateCredentialsChecker",
 ]
 
-from twisted.cred import credentials, error
-from twisted.python import log
-from twisted.web2.dav import davxml
 from zope.interface import implements
 
+from twisted.cred import checkers, credentials, error
+from twisted.internet.defer import succeed
+from twisted.web2.auth.interfaces import ICredentialFactory
+from twisted.web2.dav.auth import IPrincipalCredentials
+
 import kerberos
 
 class BasicKerberosCredentials(credentials.UsernamePassword):
@@ -58,36 +59,8 @@
         # Convert Kerberos principal spec into service and realm
         self.service = service
         self.default_realm = realm
-
-    def verify(self, request, resource):
-        """
-        Check this set of credentials to verify they are correct and extract the current principal
-        authorization identifier.
         
-        @param request:  the L{IRequest} for the request in progress.
-        @param resource: the L{DAVResource} for which credentials are being supplied.
-        @return:         tuple of (result, principal) where: result is True if the credentials match,
-            or false otherwise; principal is the L{davxml.Principal} that matches the credentials if result
-            is True, or None if result is False.
-        """
-
-        # In our default setup the user's password is stored as a property on the principal, so
-        # we first find the matching principal and then get the password and do the comparison.
-        
-        # Try to match principals in each principal collection on the resource
-        result = kerberos.checkPassword(self.username, self.password, self.service, self.default_realm)
-        if not result:
-            log.err("Client authentication password for %s incorrect" % (self.username,))
-            return False, None
-
-        pdetails = resource.findPrincipalForAuthID(request, self.username)
-        if pdetails:
-            principalURI = pdetails[1]
-            return True, davxml.Principal(davxml.HRef().fromString(principalURI))
-        else:
-            return False, None
-        
-class BasicKerberosAuthorizer:
+class BasicKerberosCredentialFactory:
     """
     Authorizer for insecure Basic (base64-encoded plaintext) authentication.
 
@@ -95,121 +68,63 @@
     Right now we do not check for that.
     """
 
-    implements(auth.IAuthorizer)
+    implements(ICredentialFactory)
 
-    def __init__(self, realm):
+    scheme = 'basic'
 
+    def __init__(self, service, realm):
+        """
+        The realm string can be of the form service/realm at domain. We split that
+        into service at domain, and realm.
+        """
+        self.service = service
         self.realm = realm
 
-        self.service = ""
-        if len(self.realm) > 0:
-            splits = self.realm.split('/', 1)
-            if len(splits) == 2:
-                service = splits[0]
-                splits = splits[1].split('@', 1)
-                if len(splits) == 2:
-                    self.service = service + "@" + splits[1]
-                    self.realm = splits[0]
+    def getChallenge(self, peer):
+        return {'realm': self.realm}
 
-    def validForRequest(self, request): #@UnusedVariable
-        """
-        Determine whether this authorizer type is valid for the current request.
-        This is where we should check whether SSL is in use or not and reject authorizer
-        that are insecure if SSL is not being used.
+    def decode(self, response, request): #@UnusedVariable
+        try:
+            creds = (response + '===').decode('base64')
+        except:
+            raise error.LoginFailed('Invalid credentials')
 
-        @param request: the L{IRequest} for the request in progress.
-        @return:        True if the authorizer can safely be used during this request, False otherwise.
-        """
-        # Always available irrespective of SSL.
-        return True
+        creds = creds.split(':', 1)
+        if len(creds) == 2:
+            c = BasicKerberosCredentials(creds[0], creds[1], self.service, self.realm)
+            return c
+        raise error.LoginFailed('Invalid credentials')
 
-    def getScheme(self):
-        return "basic"
+class BasicKerberosCredentialsChecker:
 
-    def hasChallenge(self):
-        """
-        Indicates whether this authenticator sends some data in the initial WWW-Authenticate challenge.
-        
-        @return: True if a challenge needs to be sent back, False if not.
-        """
-        return True
-    
-    def getChallenge(self):
-        return 'realm="%s"' % self.realm
+    implements(checkers.ICredentialsChecker)
 
-    def hasResponse(self):
-        """
-        Indicates whether this authenticator sends back a WWW-Authenticate response after
-        the initial client challenge.
-        
-        @return: True if a response needs to be sent back, False if not.
-        """
-        return False
+    credentialInterfaces = (IPrincipalCredentials,)
 
-    def getResponse(self):
-        """
-        The response to send back to the client.
-        
-        @return: the C{str} for the response to send back.
-        """
-        return ""
+    def requestAvatarId(self, credentials):
 
-    def decode(self, response, method=None): #@UnusedVariable
-        # At least one SIP client improperly pads its Base64 encoded messages
-        for i in range(3):
-            try:
-                creds = (response + ('=' * i)).decode('base64')
-            except:
-                pass
-            else:
-                break
-        else:
-            # Totally bogus
-            raise error.LoginFailed('Invalid credentials')
-        p = creds.split(':', 1)
-        if len(p) == 2:
-            c = BasicKerberosCredentials(p[0], p[1], self.service, self.realm)
-            return c
-        raise error.LoginFailed('Invalid credentials')
+        # If there is no calendar principal URI then the calendar user is disabled.
+        pcreds = IPrincipalCredentials(credentials)
 
+        creds = pcreds.credentials
+        if isinstance(creds, BasicKerberosCredentials):
+            if kerberos.checkPassword(creds.username, creds.password, creds.service, creds.default_realm):
+                return succeed(pcreds.principalURI)
+        
+        raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.principalURI,))
 
-class NegotiateCredentials(credentials.UsernamePassword):
+class NegotiateCredentials:
     """
     A set of user/password credentials that checks itself against Kerberos.
     """
 
-    interface.implements(ICredentials)
+    implements(credentials.ICredentials)
 
-    def __init__(self, user):
+    def __init__(self, username):
         
-        self.username = user
-
-    def verify(self, request, resource):
-        """
-        Check this set of credentials to verify they are correct and extract the current principal
-        authorization identifier.
+        self.username = username
         
-        @param request:  the L{IRequest} for the request in progress.
-        @param resource: the L{DAVResource} for which credentials are being supplied.
-        @return:         tuple of (result, principal) where: result is True if the credentials match,
-            or false otherwise; principal is the L{davxml.Principal} that matches the credentials if result
-            is True, or None if result is False.
-        """
-
-        # When we get here we know that Kerberos authentication succeeded if the user name is not empty.
-        if len(self.username) == 0:
-            log.err("Client authentication failed")
-            return False, None
-        
-        # Try to match principals in each principal collection on the resource
-        pdetails = resource.findPrincipalForAuthID(request, self.username)
-        if pdetails:
-            principalURI = pdetails[1]
-            return True, davxml.Principal(davxml.HRef().fromString(principalURI))
-        else:
-            return False, None
-        
-class NegotiateAuthorizer:
+class NegotiateCredentialFactory:
     """
     Authorizer for insecure Basic (base64-encoded plaintext) authentication.
 
@@ -217,70 +132,30 @@
     Right now we do not check for that.
     """
 
-    implements(auth.IAuthorizer)
+    implements(ICredentialFactory)
 
+    scheme = 'negotiate'
+
     def __init__(self, service):
 
         self.service = service
-        self.response = ""
 
-    def validForRequest(self, request): #@UnusedVariable
-        """
-        Determine whether this authorizer type is valid for the current request.
-        This is where we should check whether SSL is in use or not and reject authorizer
-        that are insecure if SSL is not being used.
+    def getChallenge(self, peer):
+        return {}
 
-        @param request: the L{IRequest} for the request in progress.
-        @return:        True if the authorizer can safely be used during this request, False otherwise.
-        """
-         # Always available irrespective of SSL.
-        return True
-
-    def getScheme(self):
-        return "negotiate"
-
-    def hasChallenge(self):
-        """
-        Indicates whether this authenticator sends some data in the initial WWW-Authenticate challenge.
+    def decode(self, base64data, request):
         
-        @return: True if a challenge needs to be sent back, False if not.
-        """
-        return False
-    
-    def getChallenge(self):
-        return ""
-
-    def hasResponse(self):
-        """
-        Indicates whether this authenticator sends back a WWW-Authenticate response after
-        the initial client challenge.
-        
-        @return: True if a response needs to be sent back, False if not.
-        """
-        return True
-
-    def getResponse(self):
-        """
-        The response to send back to the client.
-        
-        @return: the C{str} for the response to send back.
-        """
-        return self.response
-
-    def decode(self, response, method=None): #@UnusedVariable
-        
         # Init GSSAPI first
         result, context = kerberos.authGSSServerInit(self.service);
         if result != 1:
             raise error.LoginFailed('Authentication System Failure')
 
         # Do the GSSAPI step and get response and username
-        result = kerberos.authGSSServerStep(context, response);
+        result = kerberos.authGSSServerStep(context, base64data);
         if result == -1:
-            self.response = ""
-            username = ""
+            raise error.UnauthorizedLogin("Bad credentials for")
         else:
-            self.response = kerberos.authGSSServerResponse(context)
+            response = kerberos.authGSSServerResponse(context)
             username = kerberos.authGSSServerUserName(context)
             
             # Username may include realm suffix which we want to strip
@@ -292,4 +167,37 @@
         if result != 1:
             raise error.LoginFailed('Authentication System Failure')
         
+        # If we successfully decoded and verified the Kerberos credentials we need to add the Kerberos
+        # response data to the outgoing request
+
+        wwwauth = '%s %s' % (self.scheme, response)
+
+        def responseFilterAddWWWAuthenticate(request, response): #@UnusedVariable
+            response.headers.addRawHeader('www-authenticate', wwwauth)
+            return response
+
+        responseFilterAddWWWAuthenticate.handleErrors = True
+
+        request.addResponseFilter(responseFilterAddWWWAuthenticate)
+
         return NegotiateCredentials(username)
+
+class NegotiateCredentialsChecker:
+
+    implements(checkers.ICredentialsChecker)
+
+    credentialInterfaces = (IPrincipalCredentials,)
+
+    def requestAvatarId(self, credentials):
+        # NB If we get here authentication has already succeeded as it is done in NegotiateCredentialsFactory.decode
+        # So all we need to do is return the principal URI from the credentials.
+
+        # If there is no calendar principal URI then the calendar user is disabled.
+        pcreds = IPrincipalCredentials(credentials)
+
+        creds = pcreds.credentials
+        if isinstance(creds, NegotiateCredentials):
+            return succeed(pcreds.principalURI)
+        
+        raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.principalURI,))
+

Modified: CalendarServer/trunk/twistedcaldav/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/directory.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -19,18 +19,23 @@
 """
 Implements a directory-backed principal hierarchy.
 """
+from zope.interface import implements
 
-from twisted.cred import credentials
+from twisted.cred import checkers, credentials, error
+from twisted.cred.credentials import UsernamePassword
 from twisted.internet import reactor
 from twisted.internet import task
+from twisted.internet.defer import succeed
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
+from twisted.web2.dav.auth import IPrincipalCredentials
 from twisted.web2.dav.resource import TwistedAccessDisabledProperty
 from twisted.web2.dav.static import DAVFile
 from twisted.web2.dav.util import joinURL
 from twisted.web2.http import HTTPError
 from twisted.web2.http import StatusResponse
+
 from twistedcaldav import caldavxml
 from twistedcaldav import customxml
 from twistedcaldav.principalindex import GroupIndex
@@ -38,6 +43,7 @@
 from twistedcaldav.principalindex import UserIndex
 from twistedcaldav.resource import CalendarPrincipalCollectionResource
 from twistedcaldav.static import CalendarPrincipalFile
+
 import dsattributes
 import opendirectory
 import os
@@ -51,6 +57,28 @@
     "DirectoryPrincipalProvisioningResource",
 ]
 
+
+class DirectoryCredentialsChecker:
+    implements(checkers.ICredentialsChecker)
+
+    credentialInterfaces = (IPrincipalCredentials,)
+
+    def requestAvatarId(self, credentials):
+
+        # If there is no calendar principal URI then the calendar user is disabled.
+        pcreds = IPrincipalCredentials(credentials)
+        if not pcreds.principal.hasDeadProperty(customxml.TwistedCalendarPrincipalURI):
+            raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.principalURI,))
+
+        creds = pcreds.credentials
+        if isinstance(creds, UsernamePassword):
+            user = creds.username
+            pswd = creds.password
+            if opendirectory.authenticateUser(pcreds.principal.directory(), user, pswd):
+                return succeed(pcreds.principalURI)
+        
+        raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.principalURI,))
+
 class DirectoryPrincipalFile (CalendarPrincipalFile):
     """
     Directory principal resource.
@@ -82,6 +110,15 @@
         else:
             return False
 
+    def directory(self):
+        """
+        Get the directory object used for directory operations.
+        
+        @return:      C{object} for the directory instance
+        """
+
+        return self._parent.directory
+
     def groupMembers(self):
         """
         See L{IDAVPrincipalResource.groupMembers}.
@@ -152,7 +189,7 @@
         # Only return the calendar prinicpal URI when calendar-user-address-set is requested.
         if namespace == caldavxml.caldav_namespace:
             if name == "calendar-user-address-set":
-                return caldavxml.CalendarUserAddressSet(davxml.HRef().fromString(self.getPropertyValue(customxml.TwistedCalendarPrincipalURI)))
+                return succeed(caldavxml.CalendarUserAddressSet(davxml.HRef().fromString(self.getPropertyValue(customxml.TwistedCalendarPrincipalURI))))
 
         return super(DirectoryPrincipalFile, self).readProperty(qname, request)
 
@@ -242,10 +279,21 @@
         newname = self.principalUID() + "-" + self.getPropertyValue(customxml.TwistedGUIDProperty)
         
         try:
+            # Make sure the new name is not already in use
+            if os.path.exists(newname):
+                count = 1
+                tempname = newname + "-%d"
+                while(os.path.exists(tempname % count)):
+                    count += 1
+                newname = tempname % count 
             os.rename(calrsrc.fp.path, calrsrc.fp.sibling(newname).path)
         except OSError:
             log.msg("Directory: Failed to rename %s to %s when deleting a principal" %
                     (calrsrc.fp.path, calrsrc.fp.sibling(newname).path))
+            
+            # Remove the disabled property to prevent lock out in the future
+            calrsrc.removeDeadProperty(TwistedAccessDisabledProperty())
+           
 
 class DirectoryTypePrincipalProvisioningResource (CalendarPrincipalCollectionResource, DAVFile):
     """

Modified: CalendarServer/trunk/twistedcaldav/itip.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/itip.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/itip.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -55,6 +55,9 @@
     "canAutoRespond",
 ]
 
+class iTipException(Exception):
+    pass
+
 def handleRequest(request, principal, inbox, calendar, child):
     """
     Handle an iTIP response automatically using a deferredGenerator.
@@ -75,7 +78,7 @@
     elif method == "CANCEL":
         f = processCancel
 
-    return maybeDeferred(deferredGenerator(f), request, principal, inbox, calendar, child)
+    return f(request, principal, inbox, calendar, child)
 
 def processRequest(request, principal, inbox, calendar, child):
     """
@@ -128,41 +131,46 @@
         for i in info:
             # For any that are older, delete them.
             if compareSyncInfo(i, newinfo) < 0:
-                d = waitForDeferred(deleteResource(inbox, i[0]))
-                yield d
                 try:
+                    d = waitForDeferred(deleteResource(inbox, i[0]))
+                    yield d
                     d.getResult()
+                    logging.info("[ITIP]: deleted iTIP message %s in Inbox that was older than the new one." % (i[0],))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                logging.info("[ITIP]: deleted iTIP message %s in Inbox that was older than the new one." % (i[0],))
+                    raise iTipException
             else:
                 # For any that are newer or the same, mark the new one to be deleted.
                 delete_child = True
 
         # Delete the new one if so marked.
         if delete_child:
-            d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
-            yield d
             try:
+                d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
+                yield d
                 d.getResult()
+                logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than existing ones." % (child.fp.basename(),))
             except:
                 log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                return
-            logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than existing ones." % (child.fp.basename(),))
+                raise iTipException
+            yield None
             return
 
         # Next we want to try and find a match to any components on existing calendars listed as contributing
         # to free-busy as we will need to update those with the new one.
         
         # Find the current recipients calendar-free-busy-set
-        fbset = principal.calendarFreeBusySet(request)
+        fbset = waitForDeferred(principal.calendarFreeBusySet(request))
+        yield fbset
+        fbset = fbset.getResult()
 
         # Find the first calendar in the list with a component matching the one we are processing
         calmatch = None
         for href in fbset.children:
             calURL = str(href)
-            updatecal = inbox.locateSiblingResource(request, calURL)
+            updatecal = waitForDeferred(request.locateResource(calURL))
+            yield updatecal
+            updatecal = updatecal.getResult()
             if updatecal is None or not updatecal.exists() or not isCalendarCollectionResource(updatecal):
                 # We will ignore missing calendars. If the recipient has failed to
                 # properly manage the free busy set that should not prevent us from working.
@@ -173,7 +181,9 @@
                 break
         
         # If we have a match then we need to check whether we are updating etc
-        doreply, replycal, accepted = checkForReply(request, principal, calendar)
+        d = waitForDeferred(checkForReply(request, principal, calendar))
+        yield d
+        doreply, replycal, accepted = d.getResult()
         if calmatch:
             # See whether the current component is older than any existing ones and throw it away if so
             cal = updatecal.iCalendar(calmatch[0])
@@ -181,80 +191,73 @@
             if compareSyncInfo(info, newinfo) < 0:
                 # Re-write existing resource with new one, if accepted, otherwise delete existing as the
                 # update to it was not accepted.
-                if accepted:
-                    d, newchild = writeResource(request, calURL, updatecal, calmatch[0], calendar)
-                    d = waitForDeferred(d)
-                else:
-                    d = waitForDeferred(deleteResource(updatecal, calmatch[0]))
-                yield d
                 try:
-                    d.getResult()
+                    if accepted:
+                        newchild = waitForDeferred(writeResource(request, calURL, updatecal, calmatch[0], calendar))
+                        yield newchild
+                        newchild = newchild.getResult()
+                        logging.info("[ITIP]: replaced calendar component %s with new iTIP message in %s." % (calmatch[0], calURL))
+                    else:
+                        d = waitForDeferred(deleteResource(updatecal, calmatch[0]))
+                        yield d
+                        d.getResult()
+                        logging.info("[ITIP]: deleted calendar component %s in %s as update was not accepted." % (calmatch[0], calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                if accepted:
-                    logging.info("[ITIP]: replaced calendar component %s with new iTIP message in %s." % (calmatch[0], calURL))
-                else:
-                    logging.info("[ITIP]: deleted calendar component %s in %s as update was not accepted." % (calmatch[0], calURL))
+                    raise iTipException
+
             else:
                 # Delete new one in Inbox as it is old
-                d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
-                yield d
                 try:
+                    d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
+                    yield d
                     d.getResult()
+                    logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch[0], calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch[0], calURL))
+                    raise iTipException
+                yield None
                 return
         else:
             # Write new resource into first calendar in f-b-set
             if len(fbset.children) != 0 and accepted:
                 calURL = str(fbset.children[0])
-                updatecal = inbox.locateSiblingResource(request, calURL)
-                d, newchild = writeResource(request, calURL, updatecal, None, calendar)
-                d = waitForDeferred(d)
-                yield d
+                updatecal = waitForDeferred(request.locateResource(calURL))
+                yield updatecal
+                updatecal = updatecal.getResult()
                 try:
-                    d.getResult()
+                    newchild = waitForDeferred(writeResource(request, calURL, updatecal, None, calendar))
+                    yield newchild
+                    newchild.getResult()
+                    logging.info("[ITIP]: added new calendar component in %s." % (calURL,))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                logging.info("[ITIP]: added new calendar component in %s." % (calURL,))
+                    raise iTipException
         
         # If we get here we have a new iTIP message that we want to process. Any previous ones
         # have been removed (so we won't run in to problems when we check that there is free time
         # to book the new one). 
         if doreply:
             logging.info("[ITIP]: sending iTIP REPLY %s" % (("declined","accepted")[accepted],))
-            d, newchild = writeReply(request, principal, replycal, inbox)
-            d = waitForDeferred(d)
-            if d:
-                yield d
-                try:
-                    d.getResult()
-                except:
-                    log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                newInboxResource(child, newchild)
+            newchild = waitForDeferred(writeReply(request, principal, replycal, inbox))
+            yield newchild
+            newchild = newchild.getResult()
+            newInboxResource(child, newchild)
             logging.info("[ITIP]: saving iTIP REPLY %s" % (("declined","accepted")[accepted],))
-            d, newchild = saveReply(request, principal, replycal, inbox)
-            d = waitForDeferred(d)
-            if d:
-                yield d
-                try:
-                    d.getResult()
-                except:
-                    log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
+            newchild = waitForDeferred(saveReply(request, principal, replycal, inbox))
+            yield newchild
+            newchild = newchild.getResult()
 
         # Store CALDAV:schedule-state property
         assert child.fp.exists()
         child.writeDeadProperty(caldavxml.ScheduleState(caldavxml.Processed()))
+        yield None
         return
     else:
         raise NotImplementedError
-    
+
+processRequest = deferredGenerator(processRequest)
+
 def processAdd(request, principal, inbox, calendar, child):
     """
     Process a METHOD=ADD.
@@ -270,6 +273,8 @@
 
     raise NotImplementedError
 
+processAdd = deferredGenerator(processAdd)
+
 def processCancel(request, principal, inbox, calendar, child):
     """
     Process a METHOD=CANCEL.
@@ -326,41 +331,46 @@
         for i in info:
             # For any that are older, delete them.
             if compareSyncInfo(i, newinfo) < 0:
-                d = waitForDeferred(deleteResource(inbox, i[0]))
-                yield d
                 try:
+                    d = waitForDeferred(deleteResource(inbox, i[0]))
+                    yield d
                     d.getResult()
+                    logging.info("[ITIP]: deleted iTIP message %s in Inbox that was older than the new one." % (i[0],))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                logging.info("[ITIP]: deleted iTIP message %s in Inbox that was older than the new one." % (i[0],))
+                    raise iTipException
             else:
                 # For any that are newer or the same, mark the new one to be deleted.
                 delete_child = True
 
         # Delete the new one if so marked.
         if delete_child:
-            d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
-            yield d
             try:
+                d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
+                yield d
                 d.getResult()
+                logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than existing ones." % (child.fp.basename(),))
             except:
                 log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                return
-            logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than existing ones." % (child.fp.basename(),))
+                raise iTipException
+            yield None
             return
 
         # Next we want to try and find a match to any components on existing calendars listed as contributing
         # to free-busy as we will need to update those with the new one.
         
         # Find the current recipients calendar-free-busy-set
-        fbset = principal.calendarFreeBusySet(request)
+        fbset = waitForDeferred(principal.calendarFreeBusySet(request))
+        yield fbset
+        fbset = fbset.getResult()
 
         # Find the first calendar in the list with a component matching the one we are processing
         calmatch = None
         for href in fbset.children:
             calURL = str(href)
-            updatecal = inbox.locateSiblingResource(request, calURL)
+            updatecal = waitForDeferred(request.locateResource(calURL))
+            yield updatecal
+            updatecal = updatecal.getResult()
             if updatecal is None or not updatecal.exists() or not isCalendarCollectionResource(updatecal):
                 # We will ignore missing calendars. If the recipient has failed to
                 # properly manage the free busy set that should not prevent us from working.
@@ -377,24 +387,25 @@
             info = getSyncInfo(calmatch[0], cal)
             if compareSyncInfo(info, newinfo) < 0:
                 # Re-write existing resource with new one
-                d = waitForDeferred(deleteResource(updatecal, calmatch[0],))
-                yield d
                 try:
+                    d = waitForDeferred(deleteResource(updatecal, calmatch[0],))
+                    yield d
                     d.getResult()
+                    logging.info("[ITIP]: delete calendar component %s in %s as it was cancelled." % (calmatch[0], calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                logging.info("[ITIP]: delete calendar component %s in %s as it was cancelled." % (calmatch[0], calURL))
+                    raise iTipException
             else:
                 # Delete new one in Inbox as it is old
-                d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
-                yield d
                 try:
+                    d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
+                    yield d
                     d.getResult()
+                    logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch[0], calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    return
-                logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch[0], calURL))
+                    raise iTipException
+                yield None
                 return
         else:
             # Nothing to do
@@ -407,10 +418,13 @@
         # Store CALDAV:schedule-state property
         assert child.fp.exists()
         child.writeDeadProperty(caldavxml.ScheduleState(caldavxml.Processed()))
+        yield None
         return
     else:
         raise NotImplementedError
 
+processCancel = deferredGenerator(processCancel)
+
 def checkForReply(request, principal, calendar):
     """
     Check whether a reply to the given iTIP message is needed. A reply will be needed if the
@@ -438,10 +452,14 @@
     uid = comp.propertyValue("UID")
 
     # Now compare each instance time-range with the index and see if there is an overlap
-    fbset = principal.calendarFreeBusySet(request)
+    fbset = waitForDeferred(principal.calendarFreeBusySet(request))
+    yield fbset
+    fbset = fbset.getResult()
     for href in fbset.children:
         calURL = str(href)
-        testcal = principal.locateSiblingResource(request, calURL)
+        testcal = waitForDeferred(request.locateResource(calURL))
+        yield testcal
+        testcal = testcal.getResult()
         
         # First list is BUSY, second BUSY-TENTATIVE, third BUSY-UNAVAILABLE
         fbinfo = ([], [], [])
@@ -452,7 +470,9 @@
                 tr = caldavxml.TimeRange(start="20000101", end="20000101")
                 tr.start = instance.start
                 tr.end = instance.end
-                report_common.generateFreeBusyInfo(request, testcal, fbinfo, tr, 0, uid)
+                d = waitForDeferred(report_common.generateFreeBusyInfo(request, testcal, fbinfo, tr, 0, uid))
+                yield d
+                d.getResult()
                 
                 # If any fbinfo entries exist we have an overlap
                 if len(fbinfo[0]) or len(fbinfo[1]) or len(fbinfo[2]):
@@ -470,14 +490,17 @@
     cuas = principal.calendarUserAddressSet()
     attendeeProp = calendar.getAttendeeProperty(cuas)
     if attendeeProp is None:
-        return False, None, accepted
+        yield False, None, accepted
+        return
 
     # Look for specific parameters
     if "RSVP" in attendeeProp.params():
         if attendeeProp.params()["RSVP"][0] != "TRUE":
-            return False, None, accepted
+            yield False, None, accepted
+            return
     else:
-        return False, None, accepted
+        yield False, None, accepted
+        return
     
     # Now modify the original component
     del attendeeProp.params()["RSVP"]
@@ -514,8 +537,10 @@
         if (attendee.value() != attendeeProp.value()):
             replycal.mainComponent().removeProperty(attendee)
 
-    return True, replycal, accepted
+    yield True, replycal, accepted
 
+checkForReply = deferredGenerator(checkForReply)
+
 def writeReply(request, principal, replycal, ainbox):
     """
     Write an iTIP message reply into the specified Inbox.
@@ -529,20 +554,32 @@
     # Get the Inbox of the ORGANIZER
     organizer = replycal.getOrganizer()
     assert organizer is not None
-    inboxURL = CalendarPrincipalCollectionResource.inboxForCalendarUser(request, organizer)
+    inboxURL = waitForDeferred(CalendarPrincipalCollectionResource.inboxForCalendarUser(request, organizer))
+    yield inboxURL
+    inboxURL = inboxURL.getResult()
     assert inboxURL
     
     # Determine whether current principal has CALDAV:schedule right on that Inbox
-    inbox = ainbox.locateSiblingResource(request, inboxURL)
+    inbox = waitForDeferred(request.locateResource(inboxURL))
+    yield inbox
+    inbox = inbox.getResult()
 
-    errors = inbox.checkAccess(request, (caldavxml.Schedule(),), principal=davxml.Principal(davxml.HRef.fromString(principal.principalURL())))
-    if errors:
+    try:
+        d = waitForDeferred(inbox.checkAccess(request, (caldavxml.Schedule(),), principal=davxml.Principal(davxml.HRef.fromString(principal.principalURL()))))
+        yield d
+        d.getResult()
+    except:
         logging.info("[ITIP]: could not send reply as %s does not have CALDAV:schedule permission on %s Inbox." % (principal.principalURL(), organizer))
-        return None, None
+        yield None
+        return
     
     # Now deposit the new calendar into the inbox
-    return writeResource(request, inboxURL, inbox, None, replycal)
-    
+    d = waitForDeferred(writeResource(request, inboxURL, inbox, None, replycal))
+    yield d
+    yield d.getResult()
+
+writeReply = deferredGenerator(writeReply)
+
 def saveReply(request, principal, replycal, ainbox):
     """
     Write an iTIP message reply into the specified principal's Outbox.
@@ -558,16 +595,26 @@
     assert outboxURL
     
     # Determine whether current principal has CALDAV:schedule right on that Outbox
-    outbox = ainbox.locateSiblingResource(request, outboxURL)
+    outbox = waitForDeferred(request.locateResource(outboxURL))
+    yield outbox
+    outbox = outbox.getResult()
 
-    errors = outbox.checkAccess(request, (caldavxml.Schedule(),), principal=davxml.Principal(davxml.HRef.fromString(principal.principalURL())))
-    if errors:
+    try:
+        d = waitForDeferred(outbox.checkAccess(request, (caldavxml.Schedule(),), principal=davxml.Principal(davxml.HRef.fromString(principal.principalURL()))))
+        yield d
+        d.getResult()
+    except:
         logging.info("[ITIP]: could not save reply as %s does not have CALDAV:schedule permission on their Outbox." % (principal.principalURL(),))
-        return None, None
+        yield None
+        return
     
     # Now deposit the new calendar into the inbox
-    return writeResource(request, outboxURL, outbox, None, replycal)
-    
+    d = waitForDeferred(writeResource(request, outboxURL, outbox, None, replycal))
+    yield d
+    yield d.getResult()
+
+saveReply = deferredGenerator(saveReply)    
+
 def writeResource(request, collURL, collection, name, calendar):
     """
     Write out the calendar resource (iTIP) message to the specified calendar, either over-writing the named
@@ -603,6 +650,11 @@
     newchildURL = joinURL(collURL, name)
     
     # Copy calendar to inbox (doing fan-out)
+    def _defer(result):
+        return newchild
+    def _deferErr(f):
+        return None
+
     d = maybeDeferred(
             storeCalendarObjectResource,
             request=request,
@@ -614,7 +666,8 @@
             destinationcal = True,
             isiTIP = itipper
         )
-    return d, newchild
+    d.addCallbacks(_defer, _deferErr)
+    return d
 
 def newInboxResource(child, newchild):
     """

Modified: CalendarServer/trunk/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/copymove.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/copymove.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,12 +24,14 @@
 
 from urlparse import urlsplit
 
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.python import log
 from twisted.web2 import responsecode
-from twisted.web2.http import StatusResponse
 from twisted.web2.dav import davxml
 from twisted.web2.dav.filter.location import addlocation
 from twisted.web2.dav.http import ErrorResponse
+from twisted.web2.dav.util import parentForURL
+from twisted.web2.http import StatusResponse, HTTPError
 
 from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.method.put_common import storeCalendarObjectResource
@@ -42,60 +44,79 @@
     is not being changed in any way. We do need to do an index update for
     the destination if its a calendar collection.
     """
-    result, sourcecal, destination_uri, destination, destinationcal = checkForCalendarAction(self, request)
+
+    r = waitForDeferred(checkForCalendarAction(self, request))
+    yield r
+    result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = r.getResult()
     if not result or not destinationcal:
         # Do default WebDAV action
-        return super(CalDAVFile, self).http_COPY(request)
+        d = waitForDeferred(super(CalDAVFile, self).http_COPY(request))
+        yield d
+        yield d.getResult()
+        return
 
     #
     # Check authentication and access controls
     #
-    self.securityCheck(request, (davxml.Read(),), recurse=True)
+    x = waitForDeferred(self.securityCheck(request, (davxml.Read(),), recurse=True))
+    yield x
+    x.getResult()
 
     if destination.exists():
-        destination.securityCheck(request, (davxml.WriteContent(), davxml.WriteProperties()), recurse=True)
+        x = waitForDeferred(destination.securityCheck(request, (davxml.WriteContent(), davxml.WriteProperties()), recurse=True))
+        yield x
+        x.getResult()
     else:
-        destparent = self.locateParent(request, destination_uri)
-        destparent.securityCheck(request, (davxml.Bind(),))
+        destparent = waitForDeferred(request.locateResource(parentForURL(destination_uri)))
+        yield destparent
+        destparent = destparent.getResult()
+ 
+        x = waitForDeferred(destparent.securityCheck(request, (davxml.Bind(),)))
+        yield x
+        x.getResult()
 
     # Check for existing destination resource
     overwrite = request.headers.getHeader("overwrite", True)
     if destination.exists() and not overwrite:
         log.err("Attempt to copy onto existing file without overwrite flag enabled: %s"
                 % (destination.fp.path,))
-        return StatusResponse(
+        raise HTTPError(StatusResponse(
             responsecode.PRECONDITION_FAILED,
-            "Destination %s already exists." % (destination_uri,)
+            "Destination %s already exists." % (destination_uri,))
         )
 
     # Checks for copying a calendar collection
     if self.isCalendarCollection():
         log.err("Attempt to copy a calendar collection into another calendar collection %s" % destination)
-        return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "calendar-collection-location-ok"))
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "calendar-collection-location-ok")))
 
     # We also do not allow regular collections in calendar collections
     if self.isCollection():
         log.err("Attempt to copy a collection into a calendar collection")
-        return StatusResponse(
+        raise HTTPError(StatusResponse(
             responsecode.NOT_ALLOWED,
-            "Cannot create collection within special collection %s" % (destination,)
+            "Cannot create collection within special collection %s" % (destination,))
         )
 
     # May need to add a location header
     addlocation(request, destination_uri)
 
-    return storeCalendarObjectResource(
+    x = waitForDeferred(storeCalendarObjectResource(
         request = request,
         source = self,
         source_uri = request.uri,
-        sourceparent = self.locateParent(request, request.uri),
+        sourceparent = sourceparent,
         sourcecal = sourcecal,
         destination = destination,
         destination_uri = destination_uri,
-        destinationparent = destination.locateParent(request, destination_uri),
+        destinationparent = destinationparent,
         destinationcal = destinationcal,
-   )
+    ))
+    yield x
+    yield x.getResult()
 
+http_COPY = deferredGenerator(http_COPY)
+
 def http_MOVE(self, request):
     """
     Special handling of MOVE request if parent is a calendar collection.
@@ -103,93 +124,121 @@
     since its effectively being deleted. We do need to do an index update for
     the destination if its a calendar collection
     """
-    result, sourcecal, destination_uri, destination, destinationcal = checkForCalendarAction(self, request)
+    r = waitForDeferred(checkForCalendarAction(self, request))
+    yield r
+    result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = r.getResult()
     if not result:
         # Do default WebDAV action
-        return super(CalDAVFile, self).http_MOVE(request)
+        d = waitForDeferred(super(CalDAVFile, self).http_MOVE(request))
+        yield d
+        yield d.getResult()
+        return
         
     #
     # Check authentication and access controls
     #
-    parent = self.locateParent(request, request.uri)
-    parent.securityCheck(request, (davxml.Unbind(),))
+    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
+    yield parent
+    parent = parent.getResult()
 
+    x = waitForDeferred(parent.securityCheck(request, (davxml.Unbind(),)))
+    yield x
+    x.getResult()
+
     if destination.exists():
-        destination.securityCheck(request, (davxml.Bind(), davxml.Unbind()), recurse=True)
+        x = waitForDeferred(destination.securityCheck(request, (davxml.Bind(), davxml.Unbind()), recurse=True))
+        yield x
+        x.getResult()
     else:
-        destparent = self.locateParent(request, destination_uri)
-        destparent.securityCheck(request, (davxml.Bind(),))
+        destparent = waitForDeferred(request.locateResource(parentForURL(destination_uri)))
+        yield destparent
+        destparent = destparent.getResult()
 
+        x = waitForDeferred(destparent.securityCheck(request, (davxml.Bind(),)))
+        yield x
+        x.getResult()
+
     # Check for existing destination resource
     overwrite = request.headers.getHeader("overwrite", True)
     if destination.exists() and not overwrite:
         log.err("Attempt to copy onto existing file without overwrite flag enabled: %s"
                 % (destination.fp.path,))
-        return StatusResponse(
+        raise HTTPError(StatusResponse(
             responsecode.PRECONDITION_FAILED,
             "Destination %s already exists." % (destination_uri,)
-        )
+        ))
 
     if destinationcal:
         # Checks for copying a calendar collection
         if self.isCalendarCollection():
             log.err("Attempt to copy a calendar collection into another calendar collection %s" % destination)
-            return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "calendar-collection-location-ok"))
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "calendar-collection-location-ok")))
     
         # We also do not allow regular collections in calendar collections
         if self.isCollection():
             log.err("Attempt to copy a collection into a calendar collection")
-            return StatusResponse(
+            raise HTTPError(StatusResponse(
                 responsecode.NOT_ALLOWED,
                 "Cannot create collection within special collection %s" % (destination,)
-            )
+            ))
 
     # May need to add a location header
     addlocation(request, destination_uri)
 
-    return storeCalendarObjectResource(
+    x = waitForDeferred(storeCalendarObjectResource(
         request = request,
         source = self,
         source_uri = request.uri,
-        sourceparent = self.locateParent(request, request.uri),
+        sourceparent = sourceparent,
         sourcecal = sourcecal,
         destination = destination,
         destination_uri = destination_uri,
-        destinationparent = destination.locateParent(request, destination_uri),
+        destinationparent = destinationparent,
         destinationcal = destinationcal,
         deletesource = True,
-   )
+    ))
+    yield x
+    yield x.getResult()
 
+http_MOVE = deferredGenerator(http_MOVE)
+
 def checkForCalendarAction(self, request):
     """
     Check to see whether the source or destination of the copy/move
     is a calendar collection, since we need to do special processing
     if that is the case.
     @return: tuple::
-        result:      True if special CalDAV processing required, False otherwise
-                     NB If there is any type of error with the request, return False
-                     and allow normal COPY/MOVE processing to return the error.
-        sourcecal:   True if source is in a calendar collection, False otherwise
-        destination_uri: The URI of the destination resource
-        destination: CalDAVFile of destination if special proccesing required,
+        result:           True if special CalDAV processing required, False otherwise
+                          NB If there is any type of error with the request, return False
+                          and allow normal COPY/MOVE processing to return the error.
+        sourcecal:        True if source is in a calendar collection, False otherwise
+        sourceparent:     The parent resource for the source
+        destination_uri:  The URI of the destination resource
+        destination:      CalDAVFile of destination if special proccesing required,
         None otherwise
-        destinationcal: True if the destination is in a calendar collection,
-                        False otherwise
+        destinationcal:   True if the destination is in a calendar collection,
+                          False otherwise
+        destinationparent:The parent resource for the destination
         
     """
     
     result = False
     sourcecal = False
-    destination = None
     destinationcal = False
     
     # Check the source path first
     if not self.fp.exists():
-        return False, False, None, None, False
+        log.err("File not found: %s" % (self.fp.path,))
+        raise HTTPError(StatusResponse(
+            responsecode.NOT_FOUND,
+            "Source resource %s not found." % (request.uri,)
+        ))
 
     # Check for parent calendar collection
-    parent = self.locateParent(request, request.uri)
-    if isCalendarCollectionResource(parent):
+    sourceparent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
+    yield sourceparent
+    sourceparent = sourceparent.getResult()
+    if isCalendarCollectionResource(sourceparent):
         result = True
         sourcecal = True
     
@@ -199,18 +248,23 @@
     destination_uri = request.headers.getHeader("destination")
 
     if not destination_uri:
-        return False, False, None, None, False
+        msg = "No destination header in %s request." % (request.method,)
+        log.err(msg)
+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
     
-    try:
-        destination = self.locateSiblingResource(request, destination_uri)
-    except ValueError:
-        return False, False, None, None, False
+    destination = waitForDeferred(request.locateResource(destination_uri))
+    yield destination
+    destination = destination.getResult()
 
     # Check for parent calendar collection
     destination_uri = urlsplit(destination_uri)[2]
-    parent = destination.locateParent(request, destination_uri)
-    if isCalendarCollectionResource(parent):
+    destinationparent = waitForDeferred(request.locateResource(parentForURL(destination_uri)))
+    yield destinationparent
+    destinationparent = destinationparent.getResult()
+    if isCalendarCollectionResource(destinationparent):
         result = True
         destinationcal = True
 
-    return result, sourcecal, destination_uri, destination, destinationcal
+    yield (result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent)
+
+checkForCalendarAction = deferredGenerator(checkForCalendarAction)

Modified: CalendarServer/trunk/twistedcaldav/method/mkcalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/mkcalendar.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/mkcalendar.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,17 +24,16 @@
 
 __all__ = ["http_MKCALENDAR"]
 
-import os
-
-from twisted.internet.defer import maybeDeferred
+from twisted.internet.defer import deferredGenerator
+from twisted.internet.defer import waitForDeferred
 from twisted.python import log
 from twisted.python.failure import Failure
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.dav.http import ErrorResponse, MultiStatusResponse, PropertyStatusResponseQueue
 from twisted.web2.dav.util import davXMLFromStream
+from twisted.web2.dav.util import parentForURL
 from twisted.web2.http import HTTPError, StatusResponse
-from twisted.web2.iweb import IResponse
 
 from twistedcaldav import caldavxml
 
@@ -43,89 +42,86 @@
     Respond to a MKCALENDAR request.
     (CalDAV-access-09, section 5.3.1)
     """
-    self.fp.restat(False)
 
+    #
+    # Check authentication and access controls
+    #
+    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
+    yield parent
+    parent = parent.getResult()
+
+    x = waitForDeferred(parent.securityCheck(request, (davxml.Bind(),)))
+    yield x
+    x.getResult()
+
     if self.exists():
-        return ErrorResponse(
+        log.err("Attempt to create collection where file exists: %s"
+                % (self.fp.path,))
+        raise HTTPError(ErrorResponse(
             responsecode.FORBIDDEN,
-            (davxml.dav_namespace, "resource-must-be-null")
+            (davxml.dav_namespace, "resource-must-be-null"))
         )
 
-    if not os.path.isdir(self.fp.dirname()):
-        return ErrorResponse(
+    if not parent.isCollection():
+        log.err("Attempt to create collection with non-collection parent: %s"
+                % (self.fp.path,))
+        raise HTTPError(ErrorResponse(
             responsecode.CONFLICT,
-            (caldavxml.caldav_namespace, "calendar-collection-location-ok")
+            (caldavxml.caldav_namespace, "calendar-collection-location-ok"))
         )
 
     #
-    # Check authentication and access controls
-    #
-    parent = self.locateParent(request, request.uri)
-    parent.securityCheck(request, (davxml.Bind(),))
-
-    #
     # Read request body
     #
-    d = davXMLFromStream(request.stream)
+    try:
+        doc = waitForDeferred(davXMLFromStream(request.stream))
+        yield doc
+        doc = doc.getResult()
 
-    def gotError(f):
-        log.err("Error while handling MKCALENDAR: %s" % (f,))
+        result = waitForDeferred(self.createCalendar(request))
+        yield result
+        result = result.getResult()
+    except ValueError, e:
+        log.err("Error while handling MKCALENDAR: %s" % (e,))
+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
 
-        # Clean up
-        if self.fp.exists(): self.fp.remove()
+    if doc is not None:
+        makecalendar = doc.root_element
+        if not isinstance(makecalendar, caldavxml.MakeCalendar):
+            # Clean up
+            if self.fp.exists(): self.fp.remove()
 
-        if f.check(ValueError):
-            return StatusResponse(responsecode.BAD_REQUEST, str(f))
-        elif f.check(HTTPError):
-            return f.value.response
-        else:
-            return f
+            error = ("Non-%s element in MKCALENDAR request body: %s"
+                     % (caldavxml.MakeCalendar.name, makecalendar))
+            log.err(error)
+            raise HTTPError(StatusResponse(responsecode.UNSUPPORTED_MEDIA_TYPE, error))
 
-    def gotXML(doc):
-        # FIXME: if we get any errors, we need to delete the calendar
-
-        d = maybeDeferred(self.createCalendar, request)
-
-        if doc:
-            makecalendar = doc.root_element
-            if not isinstance(makecalendar, caldavxml.MakeCalendar):
-                error = ("Non-%s element in MKCALENDAR request body: %s"
-                         % (caldavxml.MakeCalendar.name, makecalendar))
-                log.err(error)
-                raise HTTPError(StatusResponse(responsecode.UNSUPPORTED_MEDIA_TYPE, error))
-
-            def finish(response):
-                errors = PropertyStatusResponseQueue("PROPPATCH", request.uri, responsecode.NO_CONTENT)
-                got_an_error = False
-
-                if makecalendar.children:
-                    # mkcalendar -> set -> prop -> property*
-                    for property in makecalendar.children[0].children[0].children:
-                        try:
-                            if property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"):
-                                self.writeDeadProperty(property)
-                            else:
-                                self.writeProperty(property, request)
-                        except:
-                            errors.add(Failure(), property)
-                            got_an_error = True
-                        else:
-                            errors.add(responsecode.OK, property)
-
-                if got_an_error:
-                    errors.error()
-                    raise HTTPError(MultiStatusResponse([errors.response()]))
+        errors = PropertyStatusResponseQueue("PROPPATCH", request.uri, responsecode.NO_CONTENT)
+        got_an_error = False
+    
+        if makecalendar.children:
+            # mkcalendar -> set -> prop -> property*
+            for property in makecalendar.children[0].children[0].children:
+                try:
+                    if property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"):
+                        self.writeDeadProperty(property)
+                    else:
+                        p = waitForDeferred(self.writeProperty(property, request))
+                        yield p
+                        p.getResult()
+                except:
+                    errors.add(Failure(), property)
+                    got_an_error = True
                 else:
-                    response = IResponse(response)
-                    response.headers.setHeader("cache-control", { "no-cache": None })
-                    return response
+                    errors.add(responsecode.OK, property)
+    
+        if got_an_error:
+            # Clean up
+            if self.fp.exists(): self.fp.remove()
 
-            d.addCallback(finish)
-            d.addErrback(gotError)
+            errors.error()
+            raise HTTPError(MultiStatusResponse([errors.response()]))
 
-        return d
+    yield responsecode.CREATED
 
-    d.addCallback(gotXML)
-    d.addErrback(gotError)
-
-    return d
+http_MKCALENDAR = deferredGenerator(http_MKCALENDAR)

Modified: CalendarServer/trunk/twistedcaldav/method/mkcol.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/mkcol.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/mkcol.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,8 +24,9 @@
 
 __all__ = ["http_MKCOL"]
 
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.web2 import responsecode
-from twisted.web2.http import StatusResponse
+from twisted.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.resource import isPseudoCalendarCollectionResource
 
@@ -33,11 +34,17 @@
     #
     # Don't allow DAV collections in a calendar collection for now
     #
-    parent = self._checkParents(request, isPseudoCalendarCollectionResource)
+    parent = waitForDeferred(self._checkParents(request, isPseudoCalendarCollectionResource))
+    yield parent
+    parent = parent.getResult()
     if parent is not None:
-        return StatusResponse(
+        raise HTTPError(StatusResponse(
             responsecode.FORBIDDEN,
-            "Cannot create collection within special collection %s" % (parent,)
+            "Cannot create collection within special collection %s" % (parent,))
         )
 
-    return super(CalDAVFile, self).http_MKCOL(request)
+    d = waitForDeferred(super(CalDAVFile, self).http_MKCOL(request))
+    yield d
+    yield d.getResult()
+
+http_MKCOL = deferredGenerator(http_MKCOL)
\ No newline at end of file

Modified: CalendarServer/trunk/twistedcaldav/method/post.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/post.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/post.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,7 +24,8 @@
 
 __all__ = ["http_POST"]
 
-from twisted.internet.defer import deferredGenerator, maybeDeferred
+from twisted.internet.defer import deferredGenerator, waitForDeferred
+from twisted.web2.dav.util import parentForURL
 
 from twistedcaldav import caldavxml
 from twistedcaldav.method.schedule_common import processScheduleRequest
@@ -43,8 +44,17 @@
     #
     # Check authentication and access controls
     #
-    parent = self.locateParent(request, request.uri)
-    parent.securityCheck(request, (caldavxml.Schedule(),))
+    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
+    yield parent
+    parent = parent.getResult()
+
+    d = waitForDeferred(parent.securityCheck(request, (caldavxml.Schedule(),)))
+    yield d
+    d.getResult()
         
     # Initiate deferred generator
-    return maybeDeferred(deferredGenerator(processScheduleRequest), self, "POST", request)
+    d = waitForDeferred(processScheduleRequest(self, "POST", request))
+    yield d
+    yield d.getResult()
+
+http_POST = deferredGenerator(http_POST)

Modified: CalendarServer/trunk/twistedcaldav/method/put.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/put.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,20 +24,23 @@
 
 __all__ = ["http_PUT"]
 
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.python import log
 from twisted.web2 import responsecode
-from twisted.web2.dav import davxml
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.util import allDataFromStream, parentForURL
-from twisted.web2.http import HTTPError
+from twisted.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.method.put_common import storeCalendarObjectResource
 from twistedcaldav.resource import isPseudoCalendarCollectionResource
 
 def http_PUT(self, request):
-    parent = self.locateParent(request, request.uri)
 
+    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
+    yield parent
+    parent = parent.getResult()
+
     if isPseudoCalendarCollectionResource(parent):
         self.fp.restat(False)
 
@@ -45,41 +48,38 @@
         content_type = request.headers.getHeader("content-type")
         if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "calendar"):
             log.err("MIME type %s not allowed in calendar collection" % (content_type,))
-            return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data"))
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
             
         # Read the calendar component from the stream
-        d = allDataFromStream(request.stream)
+        try:
+            d = waitForDeferred(allDataFromStream(request.stream))
+            yield d
+            calendardata = d.getResult()
 
-        def gotCalendarData(calendardata):
-
             # We must have some data at this point
             if calendardata is None:
                 # Use correct DAV:error response
-                return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
+                raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
 
-            return storeCalendarObjectResource(
+            d = waitForDeferred(storeCalendarObjectResource(
                 request = request,
                 sourcecal = False,
                 calendardata = calendardata,
                 destination = self,
                 destination_uri = request.uri,
                 destinationcal = True,
-                destinationparent = parent,
+                destinationparent = parent,)
             )
-        
-        def gotError(f):
-            log.err("Error while handling (calendar) PUT: %s" % (f,))
-    
-            # ValueError is raised on a bad request.  Re-raise others.
-            f.trap(ValueError)
-    
-            # Use correct DAV:error response
-            return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-    
-        d.addCallback(gotCalendarData)
-        d.addErrback(gotError)
+            yield d
+            yield d.getResult()
+            return
+        except ValueError, e:
+            log.err("Error while handling (calendar) PUT: %s" % (e,))
+            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
 
-        return d
+    else:
+        d = waitForDeferred(super(CalDAVFile, self).http_PUT(request))
+        yield d
+        yield d.getResult()
 
-    else:
-        return super(CalDAVFile, self).http_PUT(request)
+http_PUT = deferredGenerator(http_PUT)

Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -15,7 +15,6 @@
 #
 # DRI: Cyrus Daboo, cdaboo at apple.com
 ##
-from twisted.python import failure
 
 """
 PUT/COPY/MOVE common behavior.
@@ -26,6 +25,7 @@
 __all__ = ["storeCalendarObjectResource"]
 
 from twisted.internet.defer import maybeDeferred
+from twisted.python import failure
 from twisted.python import log
 from twisted.python.filepath import FilePath
 from twisted.web2 import responsecode
@@ -36,6 +36,7 @@
 from twisted.web2.dav.fileop import put
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.util import joinURL, parentForURL
+from twisted.web2.http import HTTPError
 from twisted.web2.iweb import IResponse
 from twisted.web2.stream import MemoryStream
 
@@ -296,24 +297,28 @@
                     result, message = validContentType()
                     if not result:
                         log.err(message)
-                        return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data"))
+                        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
                 
                     # At this point we need the calendar data to do more tests
                     calendar = source.iCalendar()
                 else:
-                    calendar = Component.fromString(calendardata)
+                    try:
+                        calendar = Component.fromString(calendardata)
+                    except ValueError, e:
+                        log.err(e)
+                        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
                         
                 # Valid calendar data check
                 result, message = validCalendarDataCheck()
                 if not result:
                     log.err(message)
-                    return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
                     
                 # Valid calendar data for CalDAV check
                 result, message = validCalDAVDataCheck()
                 if not result:
                     log.err(message)
-                    return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-object-resource"))
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-object-resource")))
 
                 # Must have a valid UID at this point
                 uid = calendar.resourceUID()
@@ -323,7 +328,7 @@
                 uid = source_index.resourceUIDForName(source.fp.basename())
                 if uid is None:
                     log.err("Source calendar does not have a UID: %s" % source.fp.basename())
-                    return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-object-resource"))
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-object-resource")))
 
                 # FIXME: We need this here because we have to re-index the destination. Ideally it
                 # would be better to copy the index entries from the source and add to the destination.
@@ -334,9 +339,9 @@
                 result, message, rname = noUIDConflict(uid)
                 if not result:
                     log.err(message)
-                    return ErrorResponse(responsecode.FORBIDDEN,
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN,
                         NoUIDConflict(davxml.HRef.fromString(joinURL(parentForURL(destination_uri), rname)))
-                    )
+                    ))
             
             # Reserve UID
             # FIXME: A race-condition could exist here if a deferred action were to be inserted between this statement
@@ -405,10 +410,10 @@
                 logging.debug("Destination indexed %s" % (destination.fp.path,), system="Store Resource")
             except TooManyInstancesError, ex:
                 log.err("Cannot index calendar resource as there are too many recurrence instances %s" % destination)
-                return ErrorResponse(
+                raise HTTPError(ErrorResponse(
                     responsecode.FORBIDDEN,
                     NumberOfRecurrencesWithinLimits(PCDATAElement(str(ex.max_allowed)))
-                )
+                ))
 
             destination.writeProperty(davxml.GETContentType.fromString("text/calendar"), request)
             return None
@@ -448,10 +453,10 @@
             try:
                 source_index.addResource(source.fp.basename(), calendar)
             except TooManyInstancesError, ex:
-                return ErrorResponse(
+                raise HTTPError(ErrorResponse(
                     responsecode.FORBIDDEN,
                     NumberOfRecurrencesWithinLimits(PCDATAElement(str(ex.max_allowed)))
-                )
+                ))
 
             source.writeProperty(davxml.GETContentType.fromString("text/calendar"), request)
             return None

Modified: CalendarServer/trunk/twistedcaldav/method/report_calquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_calquery.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/report_calquery.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,6 +24,7 @@
 
 __all__ = ["report_urn_ietf_params_xml_ns_caldav_calendar_query"]
 
+from twisted.internet.defer import deferredGenerator, succeed, waitForDeferred
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
@@ -32,6 +33,7 @@
 from twisted.web2.dav.method.report import NumberOfMatchesWithinLimits
 from twisted.web2.dav.method.report import max_number_of_matches
 from twisted.web2.dav.util import joinURL
+from twisted.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.method import report_common
@@ -41,13 +43,19 @@
     Generate a calendar-query REPORT.
     (CalDAV-access-09, section 7.6)
     """
-    if not self.isCollection() and not self.locateParent(request, request.uri).isPseudoCalendarCollection():
-        log.err("calendar-query report is not allowed on a resource outside of a calendar collection %s" % (self,))
-        return responsecode.FORBIDDEN
 
+    # Verify root element
     if calendar_query.qname() != (caldav_namespace, "calendar-query"):
         raise ValueError("{CalDAV:}calendar-query expected as root element, not %s." % (calendar_query.sname(),))
 
+    if not self.isCollection():
+        parent = waitForDeferred(self.locateParent(request, request.uri))
+        yield parent
+        parent = parent.getResult()
+        if not parent.isPseudoCalendarCollection():
+            log.err("calendar-query report is not allowed on a resource outside of a calendar collection %s" % (self,))
+            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Must be calendar collection or calendar resource"))
+
     responses = []
 
     filter = calendar_query.filter
@@ -59,7 +67,7 @@
     query_tz = calendar_query.timezone
     if query_tz is not None and not query_tz.valid():
         log.err("CalDAV:timezone must contain one VTIMEZONE component only: %s" % (query_tz,))
-        return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
     if query_tz:
         filter.settimezone(query_tz)
 
@@ -76,7 +84,7 @@
         result, message = report_common.validPropertyListCalendarDataTypeVersion(query)
         if not result:
             log.err(message)
-            return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data"))
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
         
     else:
         raise AssertionError("We shouldn't be here")
@@ -84,7 +92,7 @@
     # Verify that the filter element is valid
     if (filter is None) or not filter.valid():
         log.err("Invalid filter element: %r" % (filter,))
-        return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-filter"))
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-filter")))
 
     matchcount = [0]
     def doQuery(calresource, uri):
@@ -115,52 +123,87 @@
                 else:
                     href = davxml.HRef.fromString(uri)
             
-                report_common.responseForHref(request, responses, href, resource, calendar, propertiesForResource, query)
+                return report_common.responseForHref(request, responses, href, resource, calendar, propertiesForResource, query)
+            else:
+                return succeed(None)
     
         # Check whether supplied resource is a calendar or a calendar object resource
         if calresource.isPseudoCalendarCollection():
             # Get the timezone property from the collection if one was not set in the query,
             # and store in the query filter for later use
-            if query_tz is None and calresource.hasProperty((caldav_namespace, "calendar-timezone"), request):
-                tz = calresource.readProperty((caldav_namespace, "calendar-timezone"), request)
+            has_prop = waitForDeferred(calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
+            yield has_prop
+            has_prop = has_prop.getResult()
+            if query_tz is None and has_prop:
+                tz = waitForDeferred(calresource.readProperty((caldav_namespace, "calendar-timezone"), request))
+                yield tz
+                tz = tz.getResult()
                 filter.settimezone(tz)
 
             # Do some optimisation of access control calculation by determining any inherited ACLs outside of
             # the child resource loop and supply those to the checkAccess on each child.
-            filteredaces = calresource.inheritedACEsforChildren(request)
+            filteredaces = waitForDeferred(calresource.inheritedACEsforChildren(request))
+            yield filteredaces
+            filteredaces = filteredaces.getResult()
         
             # Check for disabled access
             if filteredaces is not None:
                 for name, uid, type in calresource.index().search(filter): #@UnusedVariable
                     # Check privileges - must have at least DAV:read
-                    child = calresource.getChild(name)
-                    error = child.checkAccess(request, (davxml.Read(),), inheritedaces=filteredaces)
-                    if error:
+                    child_url = joinURL(uri, name)
+                    child = waitForDeferred(request.locateResource(child_url))
+                    yield child
+                    child = child.getResult()
+
+                    try:
+                        d = waitForDeferred(child.checkAccess(request, (davxml.Read(),), inheritedaces=filteredaces))
+                        yield d
+                        d.getResult()
+                    except:
                         continue
     
                     calendar = calresource.iCalendar(name)
                     assert calendar is not None, "Calendar %s is missing from calendar collection %r" % (name, self)
                     
-                    queryCalendarObjectResource(calresource.getChild(name), uri, name, calendar)
+                    d = waitForDeferred(queryCalendarObjectResource(child, uri, name, calendar))
+                    yield d
+                    d.getResult()
         else:
             # Get the timezone property from the collection if one was not set in the query,
             # and store in the query object for later use
             if query_tz is None:
-                parent = calresource.locateParent(request, uri)
+
+                parent = waitForDeferred(calresource.locateParent(request, uri))
+                yield parent
+                parent = parent.getResult()
                 assert parent is not None and parent.isPseudoCalendarCollection()
-                if parent.hasProperty((caldav_namespace, "calendar-timezone"), request):
-                    tz = parent.readProperty((caldav_namespace, "calendar-timezone"), request)
+
+                has_prop = waitForDeferred(parent.hasProperty((caldav_namespace, "calendar-timezone"), request))
+                yield has_prop
+                has_prop = has_prop.getResult()
+                if has_prop:
+                    tz = waitForDeferred(parent.readProperty((caldav_namespace, "calendar-timezone"), request))
+                    yield tz
+                    tz = tz.getResult()
                     filter.settimezone(tz)
 
             calendar = calresource.iCalendar()
-            queryCalendarObjectResource(calresource, uri, None, calendar)
+            d = waitForDeferred(queryCalendarObjectResource(calresource, uri, None, calendar))
+            yield d
+            d.getResult()
 
+    doQuery = deferredGenerator(doQuery)
+
     # Run report taking depth into account
     try:
         depth = request.headers.getHeader("depth", "0")
-        report_common.applyToCalendarCollections(self, request, request.uri, depth, doQuery, (davxml.Read(),))
+        d = waitForDeferred(report_common.applyToCalendarCollections(self, request, request.uri, depth, doQuery, (davxml.Read(),)))
+        yield d
+        d.getResult()
     except NumberOfMatchesWithinLimits:
         log.err("Too many matching components in calendar-query report")
-        return ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "number-of-matches-within-limits"))
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "number-of-matches-within-limits")))
     
-    return MultiStatusResponse(responses)
+    yield MultiStatusResponse(responses)
+
+report_urn_ietf_params_xml_ns_caldav_calendar_query = deferredGenerator(report_urn_ietf_params_xml_ns_caldav_calendar_query)

Modified: CalendarServer/trunk/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_common.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/report_common.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -28,6 +28,7 @@
     "buildFreeBusyResult",
 ]
 
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.python import log
 from twisted.python.failure import Failure
 from twisted.web2 import responsecode
@@ -68,27 +69,32 @@
     """
 
     # First check the privilege on this resource
-    error = resource.checkAccess(request, privileges)
-    if error:
+    try:
+        d = waitForDeferred(resource.checkAccess(request, privileges))
+        yield d
+        d.getResult()
+    except:
+        yield None
         return
 
     # When scanning we only go down as far as a calendar collection - not into one
     if resource.isPseudoCalendarCollection():
         resources = [(resource, request_uri)]
-        doJoin = False
     elif not resource.isCollection():
         resources = [(resource, request_uri)]
-        doJoin = False
     else:
         resources = []
-        resources.extend(resource.findCalendarCollectionsWithPrivileges(depth, privileges, request))
-        doJoin = True
-        
+        d = waitForDeferred(resource.findCalendarCollections(depth, request, lambda x, y: resources.append((x, y)), privileges = privileges))
+        yield d
+        d.getResult()
+         
     for calresource, uri in resources:
-        if doJoin:
-            uri = joinURL(request_uri, uri)
-        apply(calresource, uri)
+        d = waitForDeferred(apply(calresource, uri))
+        yield d
+        d.getResult()
 
+applyToCalendarCollections = deferredGenerator(applyToCalendarCollections)
+
 def responseForHref(request, responses, href, resource, calendar, propertiesForResource, propertyreq):
     """
     Create an appropriate property status response for the given resource.
@@ -104,21 +110,24 @@
     @param propertyreq: the L{PropertyContainer} element for the properties of interest.
     """
 
-    properties_by_status = propertiesForResource(request, propertyreq, resource, calendar)
-    
-    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)
+    def _defer(properties_by_status):
+        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)
+                        )
                     )
                 )
-            )
 
+    d = propertiesForResource(request, propertyreq, resource, calendar)
+    d.addCallback(_defer)
+    return d
+
 def allPropertiesForResource(request, prop, resource, calendar=None): #@UnusedVariable
     """
     Return all (non-hidden) properties for the specified resource.
@@ -130,10 +139,14 @@
                      will be used to get the calendar if needed.
     @return: a map of OK and NOT FOUND property values.
     """
-    props = resource.listAllprop(request)
 
-    return _namedPropertiesForResource(request, props, resource, calendar)
+    def _defer(props):
+        return _namedPropertiesForResource(request, props, resource, calendar)
 
+    d = resource.listAllprop(request)
+    d.addCallback(_defer)
+    return d
+
 def propertyNamesForResource(request, prop, resource, calendar=None): #@UnusedVariable
     """
     Return property names for all properties on the specified resource.
@@ -145,11 +158,16 @@
                      will be used to get the calendar if needed.
     @return: a map of OK and NOT FOUND property values.
     """
-    properties_by_status = {
-        responsecode.OK: [propertyName(p) for p in resource.listProperties(request)]
-    }
+
+    def _defer(props):
+        properties_by_status = {
+            responsecode.OK: [propertyName(p) for p in props]
+        }
+        return properties_by_status
     
-    return properties_by_status
+    d = resource.listProperties(request)
+    d.addCallback(_defer)
+    return d
 
 def propertyListForResource(request, prop, resource, calendar=None):
     """
@@ -218,9 +236,16 @@
         else:
             qname = property
     
-        if qname in resource.listProperties(request):
+        props = waitForDeferred(resource.listProperties(request))
+        yield props
+        props = props.getResult()
+
+        if qname in props:
             try:
-                properties_by_status[responsecode.OK].append(resource.readProperty(qname, request))
+                prop = waitForDeferred(resource.readProperty(qname, request))
+                yield prop
+                prop = prop.getResult()
+                properties_by_status[responsecode.OK].append(prop)
             except:
                 f = Failure()
     
@@ -233,7 +258,9 @@
             log.err("Can't find property %r for resource %s" % (qname, request.uri))
             properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname))
     
-    return properties_by_status
+    yield properties_by_status
+
+_namedPropertiesForResource = deferredGenerator(_namedPropertiesForResource)
     
 def generateFreeBusyInfo(request, calresource, fbinfo, timerange, matchtotal, excludeuid=None):
     """
@@ -249,9 +276,13 @@
     """
     
     # First check the privilege on this collection
-    error = calresource.checkAccess(request, (caldavxml.ReadFreeBusy(),))
-    if error:
-        return matchtotal
+    try:
+        d = waitForDeferred(calresource.checkAccess(request, (caldavxml.ReadFreeBusy(),)))
+        yield d
+        d.getResult()
+    except:
+        yield matchtotal
+        return
 
     #
     # What we do is a fake calendar-query for VEVENT/VFREEBUSYs in the specified time-range.
@@ -276,16 +307,24 @@
 
     # Get the timezone property from the collection, and store in the query filter
     # for use during the query itself.
-    if calresource.hasProperty((caldav_namespace, "calendar-timezone"), request):
-        tz = calresource.readProperty((caldav_namespace, "calendar-timezone"), request)
+    has_prop = waitForDeferred(calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
+    yield has_prop
+    has_prop = has_prop.getResult()
+    if has_prop:
+        tz = waitForDeferred(calresource.readProperty((caldav_namespace, "calendar-timezone"), request))
+        yield tz
+        tz = tz.getResult()
     else:
         tz = None
     tzinfo = filter.settimezone(tz)
 
     # Do some optimisation of access control calculation by determining any inherited ACLs outside of
     # the child resource loop and supply those to the checkAccess on each child.
-    filteredaces = calresource.inheritedACEsforChildren(request)
+    filteredaces = waitForDeferred(calresource.inheritedACEsforChildren(request))
+    yield filteredaces
+    filteredaces = filteredaces.getResult()
 
+    uri = request.urlForResource(calresource)
     for name, uid, type in calresource.index().search(filter): #@UnusedVariable
         
         # Ignore ones of this UID
@@ -293,9 +332,16 @@
             continue
 
         # Check privileges - must have at least CalDAV:read-free-busy
-        child = calresource.getChild(name)
-        error = child.checkAccess(request, (caldavxml.ReadFreeBusy(),), inheritedaces=filteredaces)
-        if error:
+        child_url = joinURL(uri, name)
+        child = waitForDeferred(request.locateResource(child_url))
+        yield child
+        child = child.getResult()
+
+        try:
+            d = waitForDeferred(child.checkAccess(request, (caldavxml.ReadFreeBusy(),), inheritedaces=filteredaces))
+            yield d
+            d.getResult()
+        except:
             continue
 
         calendar = calresource.iCalendar(name)
@@ -314,8 +360,10 @@
             else:
                 assert "Free-busy query returned unwanted component: %s in %r", (name, calresource,)
     
-    return matchtotal
+    yield matchtotal
 
+generateFreeBusyInfo = deferredGenerator(generateFreeBusyInfo)
+
 def processEventFreeBusy(calendar, fbinfo, timerange, tzinfo):
     """
     Extract free busy data from a VEVENT component.

Modified: CalendarServer/trunk/twistedcaldav/method/report_freebusy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_freebusy.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/report_freebusy.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,12 +24,13 @@
 
 __all__ = ["report_urn_ietf_params_xml_ns_caldav_free_busy_query"]
 
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav.element.base import dav_namespace
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.method.report import NumberOfMatchesWithinLimits
-from twisted.web2.http import Response
+from twisted.web2.http import HTTPError, Response, StatusResponse
 from twisted.web2.http_headers import MimeType
 from twisted.web2.stream import MemoryStream
 
@@ -43,14 +44,14 @@
     """
     if not self.isCollection():
         log.err("freebusy report is only allowed on collection resources %s" % (self,))
-        return responsecode.FORBIDDEN
+        raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Not a calendar collection"))
 
     if freebusy.qname() != (caldavxml.caldav_namespace, "free-busy-query"):
         raise ValueError("{CalDAV:}free-busy-query expected as root element, not %s." % (freebusy.sname(),))
 
     timerange = freebusy.timerange
     if not timerange.valid():
-        return responsecode.BAD_REQUEST
+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid time-range specified"))
 
     # First list is BUSY, second BUSY-TENTATIVE, third BUSY-UNAVAILABLE
     fbinfo = ([], [], [])
@@ -63,15 +64,21 @@
         @param calresource: the L{CalDAVFile} for a calendar collection.
         @param uri: the uri for the calendar collecton resource.
         """
-        matchcount[0] = report_common.generateFreeBusyInfo(request, calresource, fbinfo, timerange, matchcount[0])
+        d = waitForDeferred(report_common.generateFreeBusyInfo(request, calresource, fbinfo, timerange, matchcount[0]))
+        yield d
+        matchcount[0] = d.getResult()
     
+    generateFreeBusyInfo = deferredGenerator(generateFreeBusyInfo)
+
     # Run report taking depth into account
     try:
         depth = request.headers.getHeader("depth", "0")
-        report_common.applyToCalendarCollections(self, request, request.uri, depth, generateFreeBusyInfo, (caldavxml.ReadFreeBusy(),))
+        d = waitForDeferred(report_common.applyToCalendarCollections(self, request, request.uri, depth, generateFreeBusyInfo, (caldavxml.ReadFreeBusy(),)))
+        yield d
+        d.getResult()
     except NumberOfMatchesWithinLimits:
         log.err("Too many matching components in free-busy report")
-        return ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "number-of-matches-within-limits"))
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "number-of-matches-within-limits")))
     
     # Now build a new calendar object with the free busy info we have
     fbcalendar = report_common.buildFreeBusyResult(fbinfo, timerange)
@@ -80,4 +87,6 @@
     response.stream = MemoryStream(str(fbcalendar))
     response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
 
-    return response
+    yield response
+
+report_urn_ietf_params_xml_ns_caldav_free_busy_query = deferredGenerator(report_urn_ietf_params_xml_ns_caldav_free_busy_query)
\ No newline at end of file

Modified: CalendarServer/trunk/twistedcaldav/method/report_multiget.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_multiget.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/report_multiget.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,12 +24,14 @@
 
 __all__ = ["report_urn_ietf_params_xml_ns_caldav_calendar_multiget"]
 
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.dav.element.base import dav_namespace
 from twisted.web2.dav.http import ErrorResponse, MultiStatusResponse
 from twisted.web2.dav.method.report import max_number_of_matches
+from twisted.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.method import report_common
@@ -39,13 +41,20 @@
     Generate a multiget REPORT.
     (CalDAV-access-09, section 7.7)
     """
-    if not self.isCollection() and not self.locateParent(request, request.uri).isPseudoCalendarCollection():
-        log.err("calendar-multiget report is not allowed on a resource outside of a calendar collection %s" % (self,))
-        return responsecode.FORBIDDEN
 
+    # Verify root element
     if multiget.qname() != (caldav_namespace, "calendar-multiget"):
         raise ValueError("{CalDAV:}calendar-multiget expected as root element, not %s." % (multiget.sname(),))
 
+    # Make sure target resource is of the right type
+    if not self.isCollection():
+        parent = waitForDeferred(self.locateParent(request, request.uri))
+        yield parent
+        parent = parent.getResult()
+        if not parent.isPseudoCalendarCollection():
+            log.err("calendar-multiget report is not allowed on a resource outside of a calendar collection %s" % (self,))
+            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Must be calendar resource"))
+
     responses = []
 
     propertyreq = multiget.property
@@ -64,14 +73,14 @@
         result, message = report_common.validPropertyListCalendarDataTypeVersion(propertyreq)
         if not result:
             log.err(message)
-            return ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data"))
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
     else:
         raise AssertionError("We shouldn't be here")
 
     # Check size of results is within limit
     if len(resources) > max_number_of_matches:
         log.err("Too many results in multiget report: %d" % len(resources))
-        return ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "number-of-matches-within-limits"))
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "number-of-matches-within-limits")))
 
     """
     Three possibilities exist:
@@ -94,7 +103,9 @@
 
         # Do some optimisation of access control calculation by determining any inherited ACLs outside of
         # the child resource loop and supply those to the checkAccess on each child.
-        filteredaces = self.inheritedACEsforChildren(request)
+        filteredaces = waitForDeferred(self.inheritedACEsforChildren(request))
+        yield filteredaces
+        filteredaces = filteredaces.getResult()
     
         # Check for disabled access
         if filteredaces is None:
@@ -113,9 +124,9 @@
             # Do href checks
             if requestURIis == "calendar":
                 # Verify that href is an immediate child of the request URI and that resource exists.
-                resource = str(href)
-                name = resource[resource.rfind("/") + 1:]
-                if not self._isChildURI(request, resource) or self.getChild(name) is None:
+                resource_uri = str(href)
+                name = resource_uri[resource_uri.rfind("/") + 1:]
+                if not self._isChildURI(request, resource_uri) or self.getChild(name) is None:
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
                     continue
                 
@@ -124,44 +135,63 @@
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_ALLOWED)))
                     continue
                 
-                child = self.getChild(name)
+                child = waitForDeferred(request.locateResource(resource_uri))
+                yield child
+                child = child.getResult()
     
             elif requestURIis == "collection":
-                resource = str(href)
-                name = resource[resource.rfind("/") + 1:]
-                if not self._isChildURI(request, resource, False):
+                resource_uri = str(href)
+                name = resource_uri[resource_uri.rfind("/") + 1:]
+                if not self._isChildURI(request, resource_uri, False):
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
                     continue
-                child = self.locateSiblingResource(request, resource)
+ 
+                child = waitForDeferred(request.locateResource(resource_uri))
+                yield child
+                child = child.getResult()
+
                 if not child or not child.exists():
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
                     continue
-                parent = child.locateParent(request, resource)
+
+                parent = waitForDeferred(child.locateParent(request, resource_uri))
+                yield parent
+                parent = parent.getResult()
+
                 if not parent.isCalendarCollection() or not parent.index().resourceExists(name):
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_ALLOWED)))
                     continue
                 
                 # Check privileges on parent - must have at least DAV:read
-                error = parent.checkAccess(request, (davxml.Read(),))
-                if error:
+                try:
+                    d = waitForDeferred(parent.checkAccess(request, (davxml.Read(),)))
+                    yield d
+                    d.getResult()
+                except:
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_ALLOWED)))
                     continue
                 
-                # Cache the last parents inherited aces for checkAccess optimization
+                # Cache the last parent's inherited aces for checkAccess optimization
                 if lastParent != parent:
                     lastParent = parent
             
                     # Do some optimisation of access control calculation by determining any inherited ACLs outside of
                     # the child resource loop and supply those to the checkAccess on each child.
-                    filteredaces = parent.inheritedACEsforChildren(request)
+                    filteredaces = waitForDeferred(parent.inheritedACEsforChildren(request))
+                    yield filteredaces
+                    filteredaces = filteredaces.getResult()
     
             else:
-                resource = str(href)
-                name = resource[resource.rfind("/") + 1:]
-                if (resource != request.uri) or not self.exists():
+                resource_uri = str(href)
+                name = resource_uri[resource_uri.rfind("/") + 1:]
+                if (resource_uri != request.uri) or not self.exists():
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
                     continue
-                parent = self.locateParent(request, resource)
+
+                parent = waitForDeferred(self.locateParent(request, resource_uri))
+                yield parent
+                parent = parent.getResult()
+
                 if not parent.isPseudoCalendarCollection() or not parent.index().resourceExists(name):
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_ALLOWED)))
                     continue
@@ -169,14 +199,23 @@
         
                 # Do some optimisation of access control calculation by determining any inherited ACLs outside of
                 # the child resource loop and supply those to the checkAccess on each child.
-                filteredaces = parent.inheritedACEsforChildren(request)
+                filteredaces = waitForDeferred(parent.inheritedACEsforChildren(request))
+                yield filteredaces
+                filteredaces = filteredaces.getResult()
     
             # Check privileges - must have at least DAV:read
-            error = child.checkAccess(request, (davxml.Read(),), inheritedaces=filteredaces)
-            if error:
+            try:
+                d = waitForDeferred(child.checkAccess(request, (davxml.Read(),), inheritedaces=filteredaces))
+                yield d
+                d.getResult()
+            except:
                 responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_ALLOWED)))
                 continue
     
-            report_common.responseForHref(request, responses, href, child, None, propertiesForResource, propertyreq)
+            d = waitForDeferred(report_common.responseForHref(request, responses, href, child, None, propertiesForResource, propertyreq))
+            yield d
+            d.getResult()
 
-    return MultiStatusResponse(responses)
+    yield MultiStatusResponse(responses)
+
+report_urn_ietf_params_xml_ns_caldav_calendar_multiget = deferredGenerator(report_urn_ietf_params_xml_ns_caldav_calendar_multiget)

Modified: CalendarServer/trunk/twistedcaldav/method/schedule.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/schedule.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/schedule.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -15,7 +15,6 @@
 #
 # DRI: Cyrus Daboo, cdaboo at apple.com
 ##
-from twistedcaldav.method.schedule_common import processScheduleRequest
 
 """
 CalDAV SCHEDULE method.
@@ -25,9 +24,11 @@
 
 __all__ = ["http_SCHEDULE"]
 
-from twisted.internet.defer import deferredGenerator, maybeDeferred
+from twisted.internet.defer import deferredGenerator, waitForDeferred
+from twisted.web2.dav.util import parentForURL
 
 from twistedcaldav import caldavxml
+from twistedcaldav.method.schedule_common import processScheduleRequest
 
 def http_SCHEDULE(self, request):
 
@@ -43,8 +44,17 @@
     #
     # Check authentication and access controls
     #
-    parent = self.locateParent(request, request.uri)
-    parent.securityCheck(request, (caldavxml.Schedule(),))
+    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
+    yield parent
+    parent = parent.getResult()
+
+    d = waitForDeferred(parent.securityCheck(request, (caldavxml.Schedule(),)))
+    yield d
+    d.getResult()
         
     # Initiate deferred generator
-    return maybeDeferred(deferredGenerator(processScheduleRequest), self, "SCHEDULE", request)
+    d = waitForDeferred(processScheduleRequest(self, "SCHEDULE", request))
+    yield d
+    yield d.getResult()
+
+http_SCHEDULE = deferredGenerator(http_SCHEDULE)

Modified: CalendarServer/trunk/twistedcaldav/method/schedule_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/schedule_common.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/method/schedule_common.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -25,13 +25,14 @@
 __all__ = ["processScheduleRequest"]
 
 from twisted.internet import reactor
-from twisted.internet.defer import maybeDeferred, waitForDeferred
+from twisted.internet.defer import deferredGenerator, maybeDeferred, waitForDeferred
 from twisted.python import failure, log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.util import joinURL
 from twisted.web2.http import HTTPError
+from twistedcaldav.resource import findAnyCalendarUser
 
 from twistedcaldav import caldavxml
 from twistedcaldav import customxml
@@ -60,38 +61,35 @@
     # Must be targetting an OUTBOX
     if not isScheduleOutboxResource(self):
         log.err("%s must target an schedule Outbox collection: %s" % (method, self,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-collection"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-collection")))
 
     # Must be content-type text/calendar
     content_type = request.headers.getHeader("content-type")
     if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "calendar"):
         log.err("MIME type %s not allowed in calendar collection" % (content_type,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
     
     # Must have Originator header
     originator = request.headers.getRawHeaders("originator")
     if originator is None or (len(originator) != 1):
         log.err("%s request must have Originator header" % (method,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-specified"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-specified")))
     else:
         originator = originator[0]
     
     # Verify that Originator is a valid calendar user (has an INBOX)
-    inboxURL = CalendarPrincipalCollectionResource.inboxForCalendarUser(request, originator)
+    inboxURL = waitForDeferred(CalendarPrincipalCollectionResource.inboxForCalendarUser(request, originator))
+    yield inboxURL
+    inboxURL = inboxURL.getResult()
     if inboxURL is None:
         log.err("Could not find Inbox for originator: %s" % (originator,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-allowed"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-allowed")))
     
     # Get list of Recipient headers
     rawrecipients = request.headers.getRawHeaders("recipient")
     if rawrecipients is None or (len(rawrecipients) == 0):
         log.err("%s request must have at least one Recipient header" % (method,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "recipient-specified"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "recipient-specified")))
 
     # Recipient header may be comma separated list
     recipients = []
@@ -105,49 +103,48 @@
     recipients_state = {"OK":0, "BAD":0}
 
     # Parse the calendar object from the HTTP request stream
-    d = waitForDeferred(Component.fromIStream(request.stream))
-    yield d
     try:
+        d = waitForDeferred(Component.fromIStream(request.stream))
+        yield d
         calendar = d.getResult()
     except:
         log.err("Error while handling %s: %s" % (method, failure.Failure(),))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-        return
-
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
+ 
     # Must be a valid calendar
     try:
         calendar.validCalendarForCalDAV()
     except ValueError:
         log.err("%s request calendar component is not valid: %s" % (method, calendar,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
 
     # Must have a METHOD
     if not calendar.isValidMethod():
         log.err("%s request must have valid METHOD property in calendar component: %s" % (method, calendar,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
         
     # Verify iTIP behaviour
     if not calendar.isValidITIP():
         log.err("%s request must have a calendar component that satisfies iTIP requirements: %s" % (method, calendar,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
     
     # Verify that the ORGANIZER's cu address maps to the request.uri
     organizer = calendar.getOrganizer()
     if organizer:
-        outboxURL = CalendarPrincipalCollectionResource.outboxForCalendarUser(request, organizer)
+        outboxURL = waitForDeferred(CalendarPrincipalCollectionResource.outboxForCalendarUser(request, organizer))
+        yield outboxURL
+        outboxURL = outboxURL.getResult()
     if (organizer is None) or (outboxURL is None):
         log.err("ORGANIZER in calendar data is not valid: %s" % (calendar,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed"))
-        return
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
+
     # Prevent spoofing of ORGANIZER with specific METHODs
     if (calendar.propertyValue("METHOD") in ("PUBLISH", "REQUEST", "ADD", "CANCEL", "DECLINECOUNTER")) and (outboxURL != request.uri):
         log.err("ORGANIZER in calendar data does not match owner of Outbox: %s" % (calendar,))
-        yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed"))
-        return
-    oprincipal = CalendarPrincipalCollectionResource.findAnyCalendarUser(request, organizer)        
+        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
+    oprincipal = waitForDeferred(findAnyCalendarUser(request, organizer))
+    yield oprincipal
+    oprincipal = oprincipal.getResult()
 
     # Prevent spoofing when doing reply-like METHODs
     if calendar.propertyValue("METHOD") in ("REPLY", "COUNTER", "REFRESH"):
@@ -158,15 +155,15 @@
         # Must have only one
         if len(attendees) != 1:
             log.err("ATTENDEE list in calendar data is wrong: %s" % (calendar,))
-            yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed"))
-            return
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed")))
             
         # Attendee's Outbox MUST be the request URI
-        aoutboxURL = CalendarPrincipalCollectionResource.outboxForCalendarUser(request, attendees[0])
+        aoutboxURL = waitForDeferred(CalendarPrincipalCollectionResource.outboxForCalendarUser(request, attendees[0]))
+        yield aoutboxURL
+        aoutboxURL = aoutboxURL.getResult()
         if (aoutboxURL is None) or (aoutboxURL != request.uri):
             log.err("ATTENDEE in calendar data does not match owner of Outbox: %s" % (calendar,))
-            yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed"))
-            return
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed")))
 
     # For free-busy do immediate determination of iTIP result rather than fan-out
     if (calendar.propertyValue("METHOD") == "REQUEST") and (calendar.mainType() == "VFREEBUSY"):
@@ -174,14 +171,12 @@
         vfreebusies = [v for v in calendar.subcomponents() if v.name() == "VFREEBUSY"]
         if len(vfreebusies) != 1:
             log.err("iTIP data is not valid for a VFREEBUSY request: %s" % (calendar,))
-            yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-            return
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
         dtstart = vfreebusies[0].getStartDateUTC()
         dtend = vfreebusies[0].getEndDateUTC()
         if dtstart is None or dtend is None:
             log.err("VFREEBUSY start/end not valid: %s" % (calendar,))
-            yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"))
-            return
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
         timerange.start = dtstart
         timerange.end = dtend
 
@@ -223,8 +218,7 @@
             d.getResult()
         except:
             log.err("Error while handling %s: %s" % (method, failure.Failure(),))
-            yield ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "outbox-copy"))
-            return
+            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "outbox-copy")))
         
         # Store CALDAV:originator property
         child.writeDeadProperty(caldavxml.Originator(davxml.HRef.fromString(originator)))
@@ -240,13 +234,17 @@
     autoresponses = []
     for recipient in recipients:
         # Get the principal resource for this recipient
-        principal = CalendarPrincipalCollectionResource.findAnyCalendarUser(request, recipient)
+        principal = waitForDeferred(findAnyCalendarUser(request, recipient))
+        yield principal
+        principal = principal.getResult()
 
         # Map recipient to their inbox
         if principal is not None:
             inboxURL = principal.scheduleInboxURL()
             if inboxURL:
-                inbox = self.locateSiblingResource(request, inboxURL)
+                inbox = waitForDeferred(request.locateResource(inboxURL))
+                yield inbox
+                inbox = inbox.getResult()
         if principal is None or inboxURL is None or inbox is None:
             log.err("Could not find Inbox for recipient: %s" % (recipient,))
             err = HTTPError(ErrorResponse(responsecode.NOT_FOUND, (caldav_namespace, "recipient-exists")))
@@ -260,8 +258,11 @@
             #
             # Check access controls
             #
-            errors = inbox.checkAccess(request, (caldavxml.Schedule(),), principal=davxml.Principal(davxml.HRef.fromString(oprincipal)))
-            if errors:
+            try:
+                d = waitForDeferred(inbox.checkAccess(request, (caldavxml.Schedule(),), principal=davxml.Principal(davxml.HRef.fromString(oprincipal))))
+                yield d
+                d.getResult()
+            except:
                 log.err("Could not access Inbox for recipient: %s" % (recipient,))
                 err = HTTPError(ErrorResponse(responsecode.NOT_FOUND, (caldav_namespace, "recipient-permisions")))
                 responses.add(recipient, failure.Failure(exc_value=err), reqstatus="3.8;No authority")
@@ -277,7 +278,9 @@
                 attendeeProp = calendar.getAttendeeProperty(cuas)
             
                 # Find the current recipients calendar-free-busy-set
-                fbset = principal.calendarFreeBusySet(request)
+                fbset = waitForDeferred(principal.calendarFreeBusySet(request))
+                yield fbset
+                fbset = fbset.getResult()
 
                 # First list is BUSY, second BUSY-TENTATIVE, third BUSY-UNAVAILABLE
                 fbinfo = ([], [], [])
@@ -286,14 +289,18 @@
                     matchtotal = 0
                     for href in fbset.children:
                         calURL = str(href)
-                        cal = self.locateSiblingResource(request, calURL)
+                        cal = waitForDeferred(request.locateResource(calURL))
+                        yield cal
+                        cal = cal.getResult()
                         if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
                             # We will ignore missing calendars. If the recipient has failed to
                             # properly manage the free busy set that should not prevent us from working.
                             continue
                          
                         # TODO: make this a waitForDeferred and yield it
-                        matchtotal = report_common.generateFreeBusyInfo(request, cal, fbinfo, timerange, matchtotal)
+                        matchtotal = waitForDeferred(report_common.generateFreeBusyInfo(request, cal, fbinfo, timerange, matchtotal))
+                        yield matchtotal
+                        matchtotal = matchtotal.getResult()
                     
                     # Build VFREEBUSY iTIP reply for this recipient
                     fbresult = report_common.buildFreeBusyResult(fbinfo, timerange, organizer=organizerProp, attendee=attendeeProp, uid=uid)
@@ -367,4 +374,6 @@
 
     # Return with final response if we are done
     yield responses.response()
-    return
+
+processScheduleRequest = deferredGenerator(processScheduleRequest)
+

Modified: CalendarServer/trunk/twistedcaldav/repository.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/repository.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/repository.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -641,7 +641,10 @@
             child = CalDAVFile(os.path.join(home.fp.path, calendar))
             child_exists = child.exists()
             if not child_exists:
-                child.createCalendarCollection()
+                c = child.createCalendarCollection()
+                assert c.called
+                c = c.result
+                
             calendars.append(childURL)
             if (resetACLs or not child_exists):
                 child.setAccessControlList(

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -36,24 +36,24 @@
 from weakref import WeakValueDictionary
 
 from zope.interface import implements
-import twisted.web2.server
-from twisted.internet.defer import maybeDeferred
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, maybeDeferred, succeed
+from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.web2 import responsecode
-from twisted.web2.iweb import IResponse
+from twisted.web2.dav import auth, davxml
+from twisted.web2.dav.acl import DAVPrincipalResource
+from twisted.web2.dav.davxml import dav_namespace
+from twisted.web2.dav.http import ErrorResponse
+from twisted.web2.dav.resource import DAVResource, TwistedACLInheritable
+from twisted.web2.dav.util import joinURL, parentForURL, unimplemented
 from twisted.web2.http import HTTPError, RedirectResponse, StatusResponse, Response
 from twisted.web2.http_headers import MimeType
+from twisted.web2.iweb import IResponse
 from twisted.web2.stream import MemoryStream
-from twisted.web2.dav import auth
-from twisted.web2.dav import davxml
-from twisted.web2.dav.acl import DAVPrincipalResource
-from twisted.web2.dav.davxml import dav_namespace
-from twisted.web2.dav.http import ErrorResponse
-from twisted.web2.dav.resource import DAVResource
-from twisted.web2.dav.resource import TwistedACLInheritable
-from twisted.web2.dav.util import parentForURL
+import twisted.web2.server
 
 import twistedcaldav
-from twistedcaldav import authkerb
 from twistedcaldav import caldavxml
 from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource, ICalendarSchedulingCollectionResource
 from twistedcaldav.caldavxml import caldav_namespace
@@ -64,30 +64,6 @@
 else:
     serverVersion = twisted.web2.server.VERSION + " TwistedCalDAV/?"
 
-# Need to replace global DAVResource authenticator with the one we want for our server
-def getAuthenticator(self, request):
-    """
-    See L{DAVResource.getAuthenticator}.
-    
-    This implementation picks a suitable authorizer from a list of available auth mechanisms.
-    
-    TODO: We need some way to input a 'realm' to the authorizer. Right now it is empty.
-    """
-
-    validAuths = [auth.BasicAuthorizer]
-    #validAuths = [auth.DigestAuthorizer]
-    #validAuths = [auth.BasicAuthorizer, auth.DigestAuthorizer]
-    #validAuths = [authkerb.BasicKerberosAuthorizer]
-    #validAuths = [authkerb.NegotiateAuthorizer]
-    for authert in validAuths:
-        auther = authert("")
-        if auther.validForRequest(request):
-            return auther
-    else:
-        return None
-
-DAVResource.getAuthenticator = getAuthenticator
-
 class CalDAVResource (DAVResource):
     """
     CalDAV resource.
@@ -107,11 +83,15 @@
                 # Redirect to include trailing '/' in URI
                 return RedirectResponse(request.unparseURL(path=request.path+'/'))
 
-            response = Response()
-            response.stream = MemoryStream(str(self.iCalendar(request=request)))
-            response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
+            def _defer(data):
+                response = Response()
+                response.stream = MemoryStream(str(data))
+                response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
+                return response
 
-            return response
+            d = self.iCalendarRolledup(request)
+            d.addCallback(_defer)
+            return d
         else:
             return super(CalDAVResource, self).render(request)
 
@@ -151,10 +131,8 @@
     def readProperty(self, property, request):
         if type(property) is tuple:
             qname = property
-            sname = "{%s}%s" % property
         else:
             qname = property.qname()
-            sname = property.sname()
 
         namespace, name = qname
 
@@ -162,16 +140,16 @@
             if name == "supported-calendar-component-set":
                 # CalDAV-access-09, section 5.2.3
                 if self.deadProperties().contains(qname):
-                    return self.deadProperties().get(qname)
-                return self.supportedCalendarComponentSet
+                    return succeed(self.deadProperties().get(qname))
+                return succeed(self.supportedCalendarComponentSet)
             elif name == "supported-calendar-data":
                 # CalDAV-access-09, section 5.2.4
-                return caldavxml.SupportedCalendarData(
+                return succeed(caldavxml.SupportedCalendarData(
                     caldavxml.CalendarData(**{
                         "content-type": "text/calendar",
                         "version"     : "2.0",
                     }),
-                )
+                ))
 
         return super(CalDAVResource, self).readProperty(property, request)
 
@@ -232,31 +210,70 @@
         """
         return self.isCalendarCollection()
 
-    def findCalendarCollections(self, depth):
+    def findCalendarCollections(self, depth, request, callback, privileges=None):
         """
-        See L{ICalDAVResource.findCalendarCollections}.
-        This implementation raises L{NotImplementedError}; a subclass must
-        override it.
-        """
-        # FIXME: Can this be implemented genericly by using findChildren()?
-        return NotImplementedError("Subclass must implement findCalendarCollections()")
+        See L{IDAVResource.findChildren}.
 
-    def findCalendarCollectionsWithPrivileges(self, depth, privileges, request):
+        This implementation works for C{depth} values of C{"0"}, C{"1"}, 
+        and C{"infinity"}.  As long as C{self.listChildren} is implemented
         """
-        See L{ICalDAVResource.findCalendarCollectionsWithPrivileges}.
-        This implementation raises L{NotImplementedError}; a subclass must
-        override it.
-        """
-        # FIXME: Can this be implemented genericly by using findChildren()?
-        return NotImplementedError("Subclass must implement findCalendarCollectionsWithPrivileges()")
+        assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
 
+        def _checkAccessEb(failure):
+            from twisted.web2.dav.acl import AccessDeniedError
+            failure.trap(AccessDeniedError)
+            
+            reactor.callLater(0, _getChild)
+
+        def _checkAccess(child):
+            if privileges is None:
+                return child
+   
+            ca = child.checkAccess(request, privileges)
+            ca.addCallback(lambda ign: child)
+            return ca
+
+        def _gotChild(child, childpath):
+            if child.isCalendarCollection():
+                callback(child, childpath)
+            elif child.isCollection():
+                if depth == 'infinity': 
+                    fc = child.findCalendarCollections(depth, request, callback, privileges)
+                    fc.addCallback(lambda x: reactor.callLater(0, _getChild))
+                    return fc
+
+            reactor.callLater(0, _getChild)
+
+        def _getChild():
+            try:
+                childname = children.pop()
+            except IndexError:
+                completionDeferred.callback(None)
+            else:
+                childpath = joinURL(basepath, childname)
+                child = request.locateResource(childpath)
+                child.addCallback(_checkAccess)
+                child.addCallbacks(_gotChild, _checkAccessEb, (childpath,))
+                child.addErrback(completionDeferred.errback)
+
+        completionDeferred = Deferred()
+
+        if depth != "0" and self.isCollection():
+            basepath = request.urlForResource(self)
+            children = self.listChildren()
+            _getChild()
+        else:
+            completionDeferred.callback(None)
+
+        return completionDeferred
+
     def createCalendar(self, request):
         """
         See L{ICalDAVResource.createCalendar}.
         This implementation raises L{NotImplementedError}; a subclass must
         override it.
         """
-        return NotImplementedError("Subclass must implement createCalendar()")
+        unimplemented(self)
 
     def iCalendar(self, name=None):
         """
@@ -278,6 +295,15 @@
         except ValueError:
             return None
 
+    def iCalendarRolledup(self, request):
+        """
+        See L{ICalDAVResource.iCalendarRolledup}.
+
+        This implementation raises L{NotImplementedError}; a subclass must
+        override it.
+        """
+        unimplemented(self)
+
     def iCalendarText(self, name=None):
         """
         See L{ICalDAVResource.iCalendarText}.
@@ -340,7 +366,7 @@
         @param request: an L{IRequest} object for the request being processed.
         @param uri: the URI whose parent resource is desired.
         """
-        return self.locateSiblingResource(request, parentForURL(uri))
+        return request.locateResource(parentForURL(uri))
 
 class CalendarPrincipalCollectionResource (CalDAVResource):
     """
@@ -360,12 +386,17 @@
         @return: the URI of the calendar outbox, or C{None} if no outbox for
             exists for the user.
         """
-        principal = clazz.findAnyCalendarUser(request, address)
-        if principal:
-            return principal.scheduleOutboxURL()
-        else:
-            return None
+        
+        def _defer(principal):
+            if principal:
+                return principal.scheduleOutboxURL()
+            else:
+                return None
 
+        d = findAnyCalendarUser(request, address)
+        d.addCallback(_defer)
+        return d
+
     @classmethod
     def inboxForCalendarUser(clazz, request, address):
         """
@@ -376,35 +407,17 @@
         @return: the URI of the calendar inbox, or C{None} if no inbox exists
             for the user
         """
-        principal = clazz.findAnyCalendarUser(request, address)
-        if principal:
-            return principal.scheduleInboxURL()
-        else:
-            return None
+        
+        def _defer(principal):
+            if principal:
+                return principal.scheduleInboxURL()
+            else:
+                return None
 
-    @classmethod
-    def findAnyCalendarUser(clazz, request, address):
-        """
-        Find the calendar user principal associated with the specified calendar
-        user address in any of the currently defined principal collections.
-        @param request: an L{IRequest} object for the request being processed.
-        @param address: the calendar user address to look up.
-        @return: the L{CalendarPrincipalResource} for the specified calendar
-            user, or C{None} if the user is not found.
-        """
-        for url in clazz._principleCollectionSet.keys():
-            try:
-                pcollection = clazz._principleCollectionSet[url]
-                if isinstance(pcollection, CalendarPrincipalCollectionResource):
-                    principal = pcollection.findCalendarUser(request, address)
-                    if principal is not None:
-                        return principal
-            except ReferenceError:
-                pass
+        d = findAnyCalendarUser(request, address)
+        d.addCallback(_defer)
+        return d
 
-        return None
-
-
     def __init__(self, url):
         self._url = url
 
@@ -423,14 +436,20 @@
         
         # Look at cuaddress property on each child and do attempt a match
         for childname in self.listChildren():
-            child = self.getChild(childname)
+            child_url = joinURL(self._url, childname)
+            child = waitForDeferred(request.locateResource(child_url))
+            yield child
+            child = child.getResult()
             if not isinstance(child, CalendarPrincipalResource):
                 continue
             if child.matchesCalendarUserAddress(request, address):
-                return child
+                yield child
+                return
         
-        return None
+        yield None
 
+    findCalendarUser = deferredGenerator(findCalendarUser)
+
     def principalCollectionURL(self):
         return self._url
 
@@ -443,6 +462,32 @@
         result.append(davxml.Report(davxml.PrincipalSearchPropertySet(),))
         return result
 
+def findAnyCalendarUser(request, address):
+    """
+    Find the calendar user principal associated with the specified calendar
+    user address in any of the currently defined principal collections.
+    @param request: an L{IRequest} object for the request being processed.
+    @param address: the calendar user address to look up.
+    @return: the L{CalendarPrincipalResource} for the specified calendar
+        user, or C{None} if the user is not found.
+    """
+    for url in CalendarPrincipalCollectionResource._principleCollectionSet.keys():
+        try:
+            pcollection = CalendarPrincipalCollectionResource._principleCollectionSet[url]
+            if isinstance(pcollection, CalendarPrincipalCollectionResource):
+                principal = waitForDeferred(pcollection.findCalendarUser(request, address))
+                yield principal
+                principal = principal.getResult()
+                if principal is not None:
+                    yield principal
+                    return
+        except ReferenceError:
+            pass
+
+    yield None
+
+findAnyCalendarUser = deferredGenerator(findAnyCalendarUser)
+
 class CalendarPrincipalResource (DAVPrincipalResource):
     """
     CalDAV principal resource.
@@ -455,14 +500,14 @@
         """
         @return: a list of calendar user home URLs for this principal.
         """
-        return self.readDeadProperty((caldavxml.caldav_namespace, "calendar-home-set"))
+        return self.readDeadProperty((caldav_namespace, "calendar-home-set"))
 
     def scheduleInboxURL(self):
         """
         @return: the schedule INBOX URL for this principal.
         """
-        if self.hasDeadProperty((caldavxml.caldav_namespace, "schedule-inbox-URL")):
-            inbox = self.readDeadProperty((caldavxml.caldav_namespace, "schedule-inbox-URL"))
+        if self.hasDeadProperty((caldav_namespace, "schedule-inbox-URL")):
+            inbox = self.readDeadProperty((caldav_namespace, "schedule-inbox-URL"))
             assert isinstance(inbox, caldavxml.ScheduleInboxURL)
             inbox.removeWhitespaceNodes()
             if len(inbox.children) == 1:
@@ -474,8 +519,8 @@
         """
         @return: the schedule OUTBOX URL for this principal.
         """
-        if self.hasDeadProperty((caldavxml.caldav_namespace, "schedule-outbox-URL")):
-            outbox = self.readDeadProperty((caldavxml.caldav_namespace, "schedule-outbox-URL"))
+        if self.hasDeadProperty((caldav_namespace, "schedule-outbox-URL")):
+            outbox = self.readDeadProperty((caldav_namespace, "schedule-outbox-URL"))
             assert isinstance(outbox, caldavxml.ScheduleOutboxURL)
             outbox.removeWhitespaceNodes()
             if len(outbox.children) == 1:
@@ -487,8 +532,8 @@
         """
         @return: a list of calendar user addresses for this principal.
         """
-        if self.hasDeadProperty((caldavxml.caldav_namespace, "calendar-user-address-set")):
-            return self.readDeadProperty((caldavxml.caldav_namespace, "calendar-user-address-set"))
+        if self.hasDeadProperty((caldav_namespace, "calendar-user-address-set")):
+            return self.readDeadProperty((caldav_namespace, "calendar-user-address-set"))
             
         # Must have a valid address of some kind so use the principal uri
         return caldavxml.CalendarUserAddressSet(davxml.HRef().fromString(self._url))
@@ -514,25 +559,28 @@
             return True
 
         # Look at the property if URI lookup does not work
-        if self.hasProperty((caldav_namespace, "calendar-user-address-set"), request):
-            addresses = self.readProperty((caldav_namespace, "calendar-user-address-set"), request)
-            for cua in addresses.childrenOfType(davxml.HRef):
-                if str(cua) == address:
-                    return True
+        for cua in self.calendarUserAddressSet().children:
+            if str(cua) == address:
+                return True
         
         return False
 
     def calendarFreeBusySet(self, request):
         """
-        @return: a list of calendars that contribute to free-busy for this
+        @return: L{Deferred} whose result is a list of calendars that contribute to free-busy for this
             principal's calendar user.
         """
-        inbox = self.scheduleInboxURL()
-        resource = self.locateSiblingResource(request, inbox)
-        if resource and resource.hasDeadProperty((caldavxml.caldav_namespace, "calendar-free-busy-set")):
-            return resource.readDeadProperty((caldavxml.caldav_namespace, "calendar-free-busy-set"))
-        return caldavxml.CalendarFreeBusySet()
+        
+        def _defer(inbox):
+            if inbox and inbox.hasDeadProperty((caldav_namespace, "calendar-free-busy-set")):
+                return inbox.readDeadProperty((caldav_namespace, "calendar-free-busy-set"))
+            return caldavxml.CalendarFreeBusySet()
 
+        inbox_url = self.scheduleInboxURL()
+        d = request.locateResource(inbox_url)
+        d.addCallback(_defer)
+        return d
+
 class CalendarSchedulingCollectionResource (CalDAVResource):
     """
     CalDAV principal resource.
@@ -572,7 +620,7 @@
                 if self.isScheduleInbox(): types.append(caldavxml.ScheduleInbox())
                 if self.isScheduleOutbox(): types.append(caldavxml.ScheduleOutbox())
 
-                return davxml.ResourceType(*types)
+                return succeed(davxml.ResourceType(*types))
 
         return super(CalendarSchedulingCollectionResource, self).readProperty(property, request)
 

Modified: CalendarServer/trunk/twistedcaldav/static.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/static.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/static.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -37,6 +37,7 @@
 import errno
 from urlparse import urlsplit
 
+from twisted.internet.defer import deferredGenerator, fail, succeed, waitForDeferred
 from twisted.python import log
 from twisted.python.failure import Failure
 from twisted.python.filepath import FilePath
@@ -77,58 +78,6 @@
     # CalDAV
     ##
 
-    def findCalendarCollections(self, depth):
-        """
-        Find all calendar collections within this collection down to the specified depth.
-        Note that we only want 'regular' calendar collections returned - not schedule-inbox or schedule-outbox.
-        
-        @param depth:    string with the appropriate depth -on ly "0", "1" or "infinity" allowed.
-        """
-        
-        assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
-        if depth != "0" and self.isCollection():
-            for name in self.listChildren():
-                try:
-                    child = ICalDAVResource(self.getChild(name))
-                except TypeError:
-                    child = None
-
-                if child is not None:
-                    if child.isCalendarCollection():
-                        yield (child, name)
-                    elif child.isCollection():
-                        if depth == "infinity":
-                            for grandchild in child.findCalendarCollections(depth):
-                                yield (grandchild[0], name + "/" + grandchild[1])
-
-    def findCalendarCollectionsWithPrivileges(self, depth, privileges, request):
-        """
-        Find all calendar collections within this collection down to the specified depth.
-        Note that we only want 'regular' calendar collections returned - not schedule-inbox or schedule-outbox.
-        
-        @param depth:    string with the appropriate depth -on ly "0", "1" or "infinity" allowed.
-        """
-        
-        assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
-        if depth != "0" and self.isCollection():
-            for name in self.listChildren():
-                try:
-                    child = ICalDAVResource(self.getChild(name))
-                except TypeError:
-                    child = None
-
-                if child is not None:
-                    # Check privileges of child - skip if access denied
-                    if child.checkAccess(request, privileges):
-                        continue
-
-                    if child.isCalendarCollection():
-                        yield (child, name)
-                    elif child.isCollection():
-                        if depth == "infinity":
-                            for grandchild in child.findCalendarCollections(depth):
-                                yield (grandchild[0], name + "/" + grandchild[1])
-
     def createCalendar(self, request):
         #
         # request object is required because we need to validate against parent
@@ -137,84 +86,102 @@
 
         if self.fp.exists():
             log.err("Attempt to create collection where file exists: %s" % (self.fp.path,))
-            return responsecode.NOT_ALLOWED
+            raise HTTPError(StatusResponse(responsecode.NOT_ALLOWED, "File exists"))
 
         if not os.path.isdir(os.path.dirname(self.fp.path)):
             log.err("Attempt to create collection with no parent: %s" % (self.fp.path,))
-            return StatusResponse(responsecode.CONFLICT, "No parent collection")
+            raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection"))
 
         #
         # Verify that no parent collection is a calendar also
         #
         log.msg("Creating calendar collection %s" % (self,))
 
+        def _defer(parent):
+            if parent is not None:
+                log.err("Cannot create a calendar collection within a calendar collection %s" % (parent,))
+                raise HTTPError(ErrorResponse(
+                    responsecode.FORBIDDEN,
+                    (caldavxml.caldav_namespace, "calendar-collection-location-ok")
+                ))
+    
+            return self.createCalendarCollection()
+            
         parent = self._checkParents(request, isPseudoCalendarCollectionResource)
-        if parent is not None:
-            log.err("Cannot create a calendar collection within a calendar collection %s" % (parent,))
-            return ErrorResponse(
-                responsecode.FORBIDDEN,
-                (caldavxml.caldav_namespace, "calendar-collection-location-ok")
-            )
+        parent.addCallback(_defer)
+        return parent
 
-        return self.createCalendarCollection()
-
     def createCalendarCollection(self):
         #
         # Create the collection once we know it is safe to do so
         #
-        try:
-            r = mkcollection(self.fp)
-            if r != responsecode.CREATED: raise HTTPError(r)
+        def _deferOK(result):
+            if result != responsecode.CREATED:
+                raise HTTPError(result)
     
             self.writeDeadProperty(davxml.ResourceType.calendar)
-
-        except:
-            f = Failure()
-    
+            return responsecode.CREATED
+        
+        def _deferErr(f):
             try:
                 rmdir(self.fp)
             except Exception, e:
                 log.err("Unable to clean up after failed MKCALENDAR: %s" % e)
     
-            if isinstance(f.value, HTTPError): return f.value.response
+            if isinstance(f.value, HTTPError):
+                return f.value.response
     
             f.raiseException()
-    
-        return responsecode.CREATED
-
-    def iCalendar(self, name=None, request=None):
+            
+        d = mkcollection(self.fp)
+        d.addCallback(_deferOK)
+        d.addErrback(_deferErr)
+        return d
+ 
+    def iCalendarRolledup(self, request):
         if self.isPseudoCalendarCollection():
-            if name is None:
-                # Generate a monolithic calendar
-                calendar = iComponent("VCALENDAR")
-                calendar.addProperty(iProperty("VERSION", "2.0"))
+            # Generate a monolithic calendar
+            calendar = iComponent("VCALENDAR")
+            calendar.addProperty(iProperty("VERSION", "2.0"))
 
-                # Must verify ACLs which means we need a request object at this point
-                if request is not None:
-                    # Do some optimisation of access control calculation by determining any inherited ACLs outside of
-                    # the child resource loop and supply those to the checkAccess on each child.
-                    filteredaces = self.inheritedACEsforChildren(request)
+            # Do some optimisation of access control calculation by determining any inherited ACLs outside of
+            # the child resource loop and supply those to the checkAccess on each child.
+            filteredaces = waitForDeferred(self.inheritedACEsforChildren(request))
+            yield filteredaces
+            filteredaces = filteredaces.getResult()
 
-                    for name, uid, type in self.index().search(None): #@UnusedVariable
-                        try:
-                            child = IDAVResource(self.getChild(name))
-                        except TypeError:
-                            child = None
-            
-                        if child is not None:
-                            # Check privileges of child - skip if access denied
-                            if child.checkAccess(request, (davxml.Read(),), inheritedaces=filteredaces):
-                                continue
-                            subcalendar = self.iCalendar(name)
-                            assert subcalendar.name() == "VCALENDAR"
-        
-                            for component in subcalendar.subcomponents():
-                                calendar.addComponent(component)
+            # Must verify ACLs which means we need a request object at this point
+            for name, uid, type in self.index().search(None): #@UnusedVariable
+                try:
+                    child_url = joinURL(request.uri, str(name))
+                    child = waitForDeferred(request.locateResource(child_url))
+                    yield child
+                    child = child.getResult()
+                    child = IDAVResource(child)
+                except TypeError:
+                    child = None
+    
+                if child is not None:
+                    # Check privileges of child - skip if access denied
+                    try:
+                        d = waitForDeferred(child.checkAccess(request, (davxml.Read(),), inheritedaces=filteredaces))
+                        yield d
+                        d.getResult()
+                    except:
+                        continue
+                    subcalendar = self.iCalendar(name)
+                    assert subcalendar.name() == "VCALENDAR"
 
-                return calendar
+                    for component in subcalendar.subcomponents():
+                        calendar.addComponent(component)
+                        
+            yield calendar
+            return
 
-        return super(CalDAVFile, self).iCalendar(name)
+        yield fail(HTTPError((ErrorResponse(responsecode.BAD_REQUEST))))
 
+    iCalendarRolledup = deferredGenerator(iCalendarRolledup)
+
     def iCalendarText(self, name=None):
         if self.isPseudoCalendarCollection():
             if name is None:
@@ -301,11 +268,17 @@
             
         # read-free-busy support on calendar collection and calendar object resources
         if self.isCollection():
-            return CalDAVFile._supportedCalendarPrivilegeSet
+            return succeed(CalDAVFile._supportedCalendarPrivilegeSet)
         else:
-            parent = self.locateParent(request, self.getURI(request))
-            if parent and isCalendarCollectionResource(parent):
-                return CalDAVFile._supportedCalendarPrivilegeSet
+            def _callback(parent):
+                if parent and isCalendarCollectionResource(parent):
+                    return succeed(CalDAVFile._supportedCalendarPrivilegeSet)
+                else:
+                    return super(CalDAVFile, self).supportedPrivileges(request)
+
+            d = self.locateParent(request, request.urlForResource(self))
+            d.addCallback(_callback)
+            return d
         
         return super(CalDAVFile, self).supportedPrivileges(request)
 
@@ -381,14 +354,18 @@
             parent_uri = parentForURL(parent_uri)
             if not parent_uri: break
 
-            parent = self.locateSiblingResource(request, parent_uri)
+            parent = waitForDeferred(request.locateResource(parent_uri))
+            yield parent
+            parent = parent.getResult()
 
-            log.msg("Testing parent: %s" % (parent,))
+            if test(parent):
+                yield parent
+                return
 
-            if test(parent): return parent
+        yield None
+    
+    _checkParents = deferredGenerator(_checkParents)
 
-        return None
-
 class ScheduleInboxFile (ScheduleInboxResource, CalDAVFile):
     """
     L{CalDAVFile} calendar inbox collection resource.
@@ -470,7 +447,7 @@
                 ),
             )
             
-        return ScheduleInboxFile._supportedSchedulePrivilegeSet
+        return succeed(ScheduleInboxFile._supportedSchedulePrivilegeSet)
 
 class ScheduleOutboxFile (ScheduleOutboxResource, CalDAVFile):
     """
@@ -553,7 +530,7 @@
                 ),
             )
             
-        return ScheduleOutboxFile._supportedSchedulePrivilegeSet
+        return succeed(ScheduleOutboxFile._supportedSchedulePrivilegeSet)
 
 class CalendarHomeFile (CalDAVFile):
     """
@@ -769,7 +746,9 @@
             child = CalDAVFile(os.path.join(home.fp.path, calendar))
             child_exists = child.exists()
             if not child_exists:
-                child.createCalendarCollection()
+                c = child.createCalendarCollection()
+                assert c.called
+                c = c.result
             calendars.append(childURL)
             if (resetacl or not child_exists):
                 child.setAccessControlList(
@@ -895,9 +874,10 @@
         assert self.exists(), "%s should exist" % (self,)
         assert self.isCollection(), "%s should be a collection" % (self,)
 
+        # FIXME: I don't think we need this anymore nwo that we have static & OD repository builders.
         # Create children
         for name, clazz in (
-            ("users" , CalendarUserPrincipalProvisioningResource),
+            ("users/" , CalendarUserPrincipalProvisioningResource),
         ):
             child_fp = self.fp.child(name)
             if not child_fp.exists(): child_fp.makedirs()

Modified: CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,8 +24,9 @@
 from twisted.web2.iweb import IResponse
 from twisted.web2.stream import MemoryStream
 from twisted.web2.dav import davxml
-from twisted.web2.dav.test.util import SimpleRequest
+from twisted.web2.dav.fileop import rmdir
 from twisted.web2.dav.util import davXMLFromStream
+from twisted.web2.test.test_server import SimpleRequest
 
 import twistedcaldav.test.util
 from twistedcaldav import caldavxml
@@ -93,7 +94,7 @@
 
         def got_xml(doc):
             if not isinstance(doc.root_element, davxml.MultiStatus):
-                self.fail("REPORT response XML root element is not multistatus: %r" % (multistatus,))
+                self.fail("REPORT response XML root element is not multistatus: %r" % (doc.root_element,))
 
             for response in doc.root_element.childrenOfType(davxml.PropertyStatusResponse):
                 properties_to_find = [p.qname() for p in calendar_properties]
@@ -156,7 +157,6 @@
         Event by UID.
         (CalDAV-access-09, section 7.6.6)
         """
-        cal_uri = "/calendar_query_uid/"
         uid = "C3189A88-1ED0-11D9-A5E0-000A958A3252"
 
         return self.simple_event_query(
@@ -203,7 +203,7 @@
 
         def got_xml(doc):
             if not isinstance(doc.root_element, davxml.MultiStatus):
-                self.fail("REPORT response XML root element is not multistatus: %r" % (multistatus,))
+                self.fail("REPORT response XML root element is not multistatus: %r" % (doc.root_element,))
 
             for response in doc.root_element.childrenOfType(davxml.PropertyStatusResponse):
                 for propstat in response.childrenOfType(davxml.PropertyStatus):
@@ -211,10 +211,8 @@
 
                     if status.code != responsecode.OK:
                         self.fail("REPORT failed (status %s) to locate properties: %r"
-                                  % (status.code, properties))
+                                  % (status.code, propstat))
 
-                    caldata = propstat.childOfType(davxml.PropertyContainer).childOfType(caldavxml.CalendarData)
-
                     properties = propstat.childOfType(davxml.PropertyContainer).children
 
                     for property in properties:
@@ -275,8 +273,8 @@
 
                 return davXMLFromStream(response.stream).addCallback(got_xml)
 
-            return self.send(request, do_test, calendar_path)
+            return self.send(request, do_test)
 
         request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
 
-        return self.send(request, do_report, calendar_path)
+        return self.send(request, do_report)

Modified: CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,7 +24,7 @@
 from twisted.web2.stream import MemoryStream, FileStream
 from twisted.web2.http_headers import MimeType
 from twisted.web2.dav.fileop import rmdir
-from twisted.web2.dav.test.util import SimpleRequest
+from twisted.web2.test.test_server import SimpleRequest
 
 from twistedcaldav.ical import Component
 from twistedcaldav.static import CalDAVFile
@@ -56,13 +56,12 @@
                     self.fail("Incorrect response to nested MKCOL: %s" % (response.code,))
 
             nested_uri  = os.path.join(calendar_uri, "nested")
-            nested_path = os.path.join(self.docroot, nested_uri[1:])
 
             request = SimpleRequest(self.site, "MKCOL", nested_uri)
-            self.send(request, mkcol_cb, nested_path)
+            self.send(request, mkcol_cb)
 
         request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
-        return self.send(request, mkcalendar_cb, calendar_path)
+        return self.send(request, mkcalendar_cb)
 
     def test_bogus_file(self):
         """
@@ -70,9 +69,11 @@
         """
         # FIXME: Should FileStream be OK here?
         dst_file = file(__file__)
-        try: stream = FileStream(dst_file)
-        finally: dst_file.close()
-        return self._test_file_in_calendar("bogus file in calendar", (stream, responsecode.FORBIDDEN))
+        try:
+            stream = FileStream(dst_file)
+            return self._test_file_in_calendar("bogus file in calendar", (stream, responsecode.FORBIDDEN))
+        finally:
+            dst_file.close()
 
     def test_monolithic_ical(self):
         """
@@ -80,9 +81,11 @@
         """
         # FIXME: Should FileStream be OK here?
         dst_file = file(os.path.join(self.data_dir, "Holidays.ics"))
-        try: stream = FileStream(dst_file)
-        finally: dst_file.close()
-        return self._test_file_in_calendar("monolithic iCalendar file in calendar", (stream, responsecode.FORBIDDEN))
+        try:
+            stream = FileStream(dst_file)
+            return self._test_file_in_calendar("monolithic iCalendar file in calendar", (stream, responsecode.FORBIDDEN))
+        finally:
+            dst_file.close()
 
     def test_single_events(self):
         """
@@ -146,17 +149,16 @@
                         self.fail("Incorrect response to %s: %s (!= %s)" % (what, response.code, response_code))
 
                 dst_uri  = os.path.join(calendar_uri, "dst%d.ics" % (c,))
-                dst_path = os.path.join(self.docroot, dst_uri[1:])
     
                 request = SimpleRequest(self.site, "PUT", dst_uri)
                 request.headers.setHeader("if-none-match", "*")
                 request.headers.setHeader("content-type", MimeType("text", "calendar"))
                 request.stream = stream
-                ds.append(self.send(request, put_cb, dst_path))
+                ds.append(self.send(request, put_cb))
 
                 c += 1
 
             return DeferredList(ds)
 
         request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
-        return self.send(request, mkcalendar_cb, calendar_path)
+        return self.send(request, mkcalendar_cb)

Modified: CalendarServer/trunk/twistedcaldav/test/test_freebusyquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_freebusyquery.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_freebusyquery.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -27,8 +27,8 @@
 from twisted.web2.stream import MemoryStream
 from twisted.web2.dav import davxml
 from twisted.web2.dav.fileop import rmdir
-from twisted.web2.dav.test.util import SimpleRequest
 from twisted.web2.dav.util import davXMLFromStream
+from twisted.web2.test.test_server import SimpleRequest
 
 import twistedcaldav.test.util
 from twistedcaldav import caldavxml

Modified: CalendarServer/trunk/twistedcaldav/test/test_mkcalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_mkcalendar.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_mkcalendar.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -15,6 +15,8 @@
 #
 # DRI: Wilfredo Sanchez, wsanchez at apple.com
 ##
+from twisted.internet.defer import waitForDeferred
+from twisted.internet.defer import deferredGenerator
 
 import os
 
@@ -23,7 +25,7 @@
 from twisted.web2.stream import MemoryStream
 from twisted.web2.dav import davxml
 from twisted.web2.dav.fileop import rmdir
-from twisted.web2.dav.test.util import SimpleRequest
+from twisted.web2.test.test_server import SimpleRequest
 
 import twistedcaldav.test.util
 from twistedcaldav import caldavxml
@@ -61,7 +63,7 @@
                           % (response.code,))
 
         request = SimpleRequest(self.site, "MKCALENDAR", uri)
-        return self.send(request, do_test, path)
+        return self.send(request, do_test)
 
     def test_make_calendar_with_props(self):
         """
@@ -86,12 +88,18 @@
                 (davxml.DisplayName.qname(), "Lisa's Events"),
                 (caldavxml.CalendarDescription.qname(), "Calendar restricted to events."),
             ):
-                stored = str(resource.readProperty(qname, None))
+                stored = waitForDeferred(resource.readProperty(qname, None))
+                yield stored
+                stored = stored.getResult()
+                stored = str(stored)
                 if stored != value:
                     self.fail("MKCALENDAR failed to set property %s: %s != %s"
                               % (qname, stored, value))
 
-            supported_components = resource.readProperty(caldavxml.SupportedCalendarComponentSet, None).children
+            supported_components = waitForDeferred(resource.readProperty(caldavxml.SupportedCalendarComponentSet, None))
+            yield supported_components
+            supported_components = supported_components.getResult()
+            supported_components = supported_components.children
             if len(supported_components) != 1:
                 self.fail("MKCALENDAR failed to set property %s: len(%s) != 1"
                           % (caldavxml.SupportedCalendarComponentSet.qname(), supported_components))
@@ -100,9 +108,14 @@
                           % (caldavxml.SupportedCalendarComponentSet.qname(),
                              supported_components[0].toxml(), caldavxml.CalendarComponent(name="VEVENT").toxml()))
 
-            tz = resource.readProperty(caldavxml.CalendarTimeZone, None).calendar()
+            tz = waitForDeferred(resource.readProperty(caldavxml.CalendarTimeZone, None))
+            yield tz
+            tz = tz.getResult()
+            tz = tz.calendar()
             self.failUnless(tz.resourceType() == "VTIMEZONE")
             self.failUnless(tuple(tz.subcomponents())[0].propertyValue("TZID") == "US-Eastern")
+        
+        do_test = deferredGenerator(do_test)
 
         mk = caldavxml.MakeCalendar(
             davxml.Set(
@@ -141,7 +154,7 @@
 
         request = SimpleRequest(self.site, "MKCALENDAR", uri)
         request.stream = MemoryStream(mk.toxml())
-        return self.send(request, do_test, path)
+        return self.send(request, do_test)
 
     def test_make_calendar_no_parent(self):
         """
@@ -159,7 +172,7 @@
             # FIXME: Check for CalDAV:calendar-collection-location-ok element
 
         request = SimpleRequest(self.site, "MKCALENDAR", uri)
-        return self.send(request, do_test, path)
+        return self.send(request, do_test)
 
     def test_make_calendar_on_resource(self):
         """
@@ -181,7 +194,7 @@
             # FIXME: Check for DAV:resource-must-be-null element
 
         request = SimpleRequest(self.site, "MKCALENDAR", uri)
-        return self.send(request, do_test, path)
+        return self.send(request, do_test)
 
     def test_make_calendar_in_calendar(self):
         """
@@ -208,7 +221,7 @@
             nested_path = os.path.join(self.docroot, nested_uri[1:])
 
             request = SimpleRequest(self.site, "MKCALENDAR", nested_uri)
-            self.send(request, do_test, nested_path)
+            self.send(request, do_test)
 
         request = SimpleRequest(self.site, "MKCALENDAR", first_uri)
-        return self.send(request, next, first_path)
+        return self.send(request, next)

Modified: CalendarServer/trunk/twistedcaldav/test/test_multiget.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_multiget.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_multiget.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -24,8 +24,8 @@
 from twisted.web2.stream import MemoryStream
 from twisted.web2.dav import davxml
 from twisted.web2.dav.fileop import rmdir
-from twisted.web2.dav.test.util import SimpleRequest
 from twisted.web2.dav.util import davXMLFromStream
+from twisted.web2.test.test_server import SimpleRequest
 
 import twistedcaldav.test.util
 from twistedcaldav import caldavxml
@@ -115,7 +115,11 @@
             
             for response in doc.root_element.childrenOfType(davxml.StatusResponse):
                 href = str(response.childOfType(davxml.HRef))
-                status = response.childOfType(davxml.Status)
+                propstatus = response.childOfType(davxml.PropertyStatus)
+                if propstatus is not None:
+                    status = propstatus.childOfType(davxml.Status)
+                else:
+                    status = response.childOfType(davxml.Status)
                 if status.code != responsecode.OK:
                     if href in okhrefs:
                         self.fail("REPORT failed (status %s) to locate properties: %r"
@@ -165,8 +169,8 @@
 
                 return davXMLFromStream(response.stream).addCallback(got_xml)
 
-            return self.send(request, do_test, calendar_path)
+            return self.send(request, do_test)
 
         request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
 
-        return self.send(request, do_report, calendar_path)
+        return self.send(request, do_report)

Modified: CalendarServer/trunk/twistedcaldav/test/test_options.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_options.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_options.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -17,7 +17,7 @@
 ##
 
 from twisted.web2.iweb import IResponse
-from twisted.web2.dav.test.util import SimpleRequest
+from twisted.web2.test.test_server import SimpleRequest
 
 import twistedcaldav.test.util
 

Modified: CalendarServer/trunk/twistedcaldav/test/test_props.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_props.py	2006-09-11 19:53:54 UTC (rev 117)
+++ CalendarServer/trunk/twistedcaldav/test/test_props.py	2006-09-11 20:51:22 UTC (rev 118)
@@ -23,8 +23,8 @@
 from twisted.web2 import responsecode
 from twisted.web2.iweb import IResponse
 from twisted.web2.dav import davxml
-from twisted.web2.dav.test.util import SimpleRequest
 from twisted.web2.dav.util import davXMLFromStream
+from twisted.web2.test.test_server import SimpleRequest
 from twistedcaldav import caldavxml
 
 import twistedcaldav.test.util
@@ -139,10 +139,10 @@
 
             request = SimpleRequest(self.site, "PROPFIND", calendar_uri)
             request.stream = MemoryStream(query.toxml())
-            return self.send(request, propfind_cb, calendar_path)
+            return self.send(request, propfind_cb)
 
         request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
-        return self.send(request, mkcalendar_cb, calendar_path)
+        return self.send(request, mkcalendar_cb)
 
     def test_all_props(self):
         """
@@ -205,8 +205,8 @@
 
             request = SimpleRequest(self.site, "PROPFIND", calendar_uri)
             request.stream = MemoryStream(query.toxml())
-            return self.send(request, propfind_cb, calendar_path)
+            return self.send(request, propfind_cb)
 
         request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
-        return self.send(request, mkcalendar_cb, calendar_path)
+        return self.send(request, mkcalendar_cb)
         

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20060911/ae8bc1a1/attachment.html


More information about the calendarserver-changes mailing list