[CalendarServer-changes] [4861] CalendarServer/branches/users/glyph/more-deferreds-5

source_changes at macosforge.org source_changes at macosforge.org
Mon Dec 14 23:22:11 PST 2009


Revision: 4861
          http://trac.macosforge.org/projects/calendarserver/changeset/4861
Author:   glyph at apple.com
Date:     2009-12-14 23:22:08 -0800 (Mon, 14 Dec 2009)
Log Message:
-----------
Merge the more-deferreds branch forward yet again (with the tests passing)

Modified Paths:
--------------
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/__init__.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/provision/root.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/sidecar/task.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/caldav.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/test/test_caldav.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/export.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/principals.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/util.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/warmup.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webadmin/resource.py
    CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webcal/resource.py
    CalendarServer/branches/users/glyph/more-deferreds-5/setup.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twext/__init__.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/__init__.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/accesslog.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/caldavxml.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/__init__.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/aggregate.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/cachingdirectory.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendar.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/directory.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/idirectory.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/resource.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sudo.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_aggregate.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_cachedirectory.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_calendar.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_guidchange.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_opendirectory.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_principal.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_proxyprincipalmembers.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_sudo.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/util.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/wiki.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/xmlfile.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/dropbox.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/extensions.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/fileops.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/freebusyurl.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/ical.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/icaldav.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/index.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/log.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/mail.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/memcacheprops.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/copymove.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/delete_common.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/get.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/mkcalendar.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/propfind.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put_common.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_calquery.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_common.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_multiget.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/resource.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/schedule.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/caldav.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/implicit.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/processing.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/scheduler.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/utils.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/static.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_collectioncontents.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_extensions.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_index.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_memcacheprops.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_mkcalendar.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_resource.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_sql.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_static.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_upgrade.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/timezoneservice.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/upgrade.py

Added Paths:
-----------
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.auth.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.noneprops.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.static.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.util.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.static.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.test.test_static.patch
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/accounts2.xml

Removed Paths:
-------------
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/apache.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sqldb.py
    CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_apache.py

Property Changed:
----------------
    CalendarServer/branches/users/glyph/more-deferreds-5/


Property changes on: CalendarServer/branches/users/glyph/more-deferreds-5
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/more-deferreds:4446,4462
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/more-deferreds:4446,4462
/CalendarServer/branches/more-deferreds-3:4490-4666
/CalendarServer/branches/more-deferreds-4:4672-4858
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/__init__.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/__init__.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/__init__.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,3 +1,4 @@
+# -*- test-case-name: calendarserver -*-
 ##
 # Copyright (c) 2005-2007 Apple Inc. All rights reserved.
 #

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/provision/root.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/provision/root.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/provision/root.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -19,7 +19,7 @@
     "RootResource",
 ]
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.cred.error import LoginFailed, UnauthorizedLogin
 
 from twisted.web2 import responsecode
@@ -95,7 +95,7 @@
         return self._dead_properties
 
     def defaultAccessControlList(self):
-        return config.RootResourceACL
+        return succeed(config.RootResourceACL)
 
     @inlineCallbacks
     def checkSacl(self, request):
@@ -135,7 +135,7 @@
         request.checkingSACL = True
 
         for collection in self.principalCollections():
-            principal = collection._principalForURI(authzUser.children[0].children[0].data)
+            principal = (yield collection._principalForURI(authzUser.children[0].children[0].data))
             if principal is None:
                 response = (yield UnauthorizedResponse.makeResponse(
                     request.credentialFactories,
@@ -189,7 +189,7 @@
                     log.debug("Wiki lookup returned user: %s" % (username,))
                     principal = None
                     directory = request.site.resource.getDirectory()
-                    record = directory.recordWithShortName("users", username)
+                    record = (yield directory.recordWithShortName("users", username))
                     log.debug("Wiki user record for user %s : %s" % (username, record))
                     if record:
                         # Note: record will be None if it's a /Local/Default user
@@ -197,6 +197,21 @@
                             principal = collection.principalForRecord(record)
                             if principal is not None:
                                 break
+                    if record is None:
+                        raise HTTPError(StatusResponse(
+                            responsecode.FORBIDDEN,
+                            "The username (%s) corresponding to your sessionID was not found by calendar server." % (username,)
+                        ))
+                    for collection in self.principalCollections():
+                        principal = (yield collection.principalForRecord(record))
+                        if principal is not None:
+                            break
+                    else:
+                        # Can't find principal
+                        raise HTTPError(StatusResponse(
+                            responsecode.FORBIDDEN,
+                            "The principal corresponding to your username (%s) was not found by calendar server." % (username,)
+                        ))
 
                     if principal:
                         log.debug("Found wiki principal and setting authnuser and authzuser")
@@ -269,8 +284,7 @@
             except KeyError:
                 pass
 
-        child = (yield super(RootResource, self).locateChild(request, segments))
-        returnValue(child)
+        returnValue((yield super(RootResource, self).locateChild(request, segments)))
 
     def http_COPY       (self, request): return responsecode.FORBIDDEN
     def http_MOVE       (self, request): return responsecode.FORBIDDEN

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/sidecar/task.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/sidecar/task.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/sidecar/task.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -16,21 +16,20 @@
 from __future__ import with_statement
 
 __all__ = [
-    "CalDAVService",
-    "CalDAVOptions",
-    "CalDAVServiceMaker",
+    "CalDAVTaskServiceMaker",
 ]
 
 from calendarserver.provision.root import RootResource
 from time import sleep
 from twisted.application.service import Service, IServiceMaker
 from twisted.internet.address import IPv4Address
-from twisted.internet.defer import DeferredList, inlineCallbacks, returnValue
+from twisted.internet.defer import DeferredList, returnValue, inlineCallbacks
 from twisted.internet.reactor import callLater
 from twisted.plugin import IPlugin
 from twisted.python.reflect import namedClass
 from twisted.python.usage import Options, UsageError
 from twisted.web2.http_headers import Headers
+from twisted.web2.server import NoURLForResourceError
 from twistedcaldav import memcachepool
 from twistedcaldav.config import config
 from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
@@ -91,8 +90,8 @@
 def processInboxItem(rootResource, directory, inboxFile, inboxItemFile, uuid):
     log.debug("Processing inbox item %s" % (inboxItemFile,))
 
-    principals = rootResource.getChild("principals")
-    ownerPrincipal = principals.principalForUID(uuid)
+    principals = (yield rootResource.getChild("principals"))
+    ownerPrincipal = (yield principals.principalForUID(uuid))
     cua = "urn:uuid:%s" % (uuid,)
     owner = LocalCalendarUser(cua, ownerPrincipal,
         inboxFile, ownerPrincipal.scheduleInboxURL())
@@ -111,18 +110,20 @@
         # originator is the organizer
         originator = calendar.getOrganizer()
 
-    originatorPrincipal = principals.principalForCalendarUserAddress(originator)
+    originatorPrincipal = (yield principals.principalForCalendarUserAddress(originator))
     originator = LocalCalendarUser(originator, originatorPrincipal)
     recipients = (owner,)
     scheduler = DirectScheduler(FakeRequest(rootResource, "PUT"), inboxItemFile)
-    result = (yield scheduler.doSchedulingViaPUT(originator, recipients,
-        calendar, internal_request=False))
+    yield scheduler.doSchedulingViaPUT(
+        originator, recipients, calendar, internal_request=False
+    )
 
     if os.path.exists(inboxItemFile.fp.path):
         os.remove(inboxItemFile.fp.path)
 
 
 
+
 class Task(object):
 
     def __init__(self, service, fileName):
@@ -149,11 +150,11 @@
             returnValue(None)
 
     @inlineCallbacks
+    @inlineCallbacks
     def task_scheduleinboxes(self):
+        calendars = (yield self.service.root.getChild("calendars"))
+        uidDir = (yield calendars.getChild("__uids__"))
 
-        calendars = self.service.root.getChild("calendars")
-        uidDir = calendars.getChild("__uids__")
-
         inboxItems = set()
         with open(self.taskFile) as input:
             for inboxItem in input:
@@ -164,15 +165,15 @@
             log.info("Processing inbox item: %s" % (inboxItem,))
             ignore, uuid, ignore, fileName = inboxItem.rsplit("/", 3)
 
-            homeFile = uidDir.getChild(uuid)
+            homeFile = (yield uidDir.getChild(uuid))
             if not homeFile:
                 continue
 
-            inboxFile = homeFile.getChild("inbox")
+            inboxFile = (yield homeFile.getChild("inbox"))
             if not inboxFile:
                 continue
 
-            inboxItemFile = inboxFile.getChild(fileName)
+            inboxItemFile = (yield inboxFile.getChild(fileName))
 
             yield processInboxItem(
                 self.service.root,
@@ -363,8 +364,6 @@
         #
         # Setup the Directory
         #
-        directories = []
-
         directoryClass = namedClass(config.DirectoryService.type)
 
         self.log_info("Configuring directory service of type: %s"

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/caldav.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/caldav.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -621,14 +621,6 @@
             directory,
         )
 
-        self.log_info("Setting up calendar collection: %r"
-                      % (self.calendarResourceClass,))
-
-        calendarCollection = self.calendarResourceClass(
-            os.path.join(config.DocumentRoot, "calendars"),
-            directory, "/calendars/",
-        )
-
         self.log_info("Setting up root resource: %r"
                       % (self.rootResourceClass,))
 
@@ -636,10 +628,18 @@
             config.DocumentRoot,
             principalCollections=(principalCollection,),
         )
-
         root.putChild("principals", principalCollection)
-        root.putChild("calendars", calendarCollection)
 
+        d = self.calendarResourceClass.fetch(None,
+            os.path.join(config.DocumentRoot, "calendars"),
+            directory, "/calendars/")
+
+        def _installCalendars(calendarCollection):
+            self.log_info("Setting up calendar collection: %r"
+                          % (self.calendarResourceClass,))
+            root.putChild("calendars", calendarCollection)
+        d.addCallback(_installCalendars)
+
         for name, info in config.Aliases.iteritems():
             if os.path.sep in name or not info.get("path", None):
                 self.log_error("Invalid alias: %s" % (name,))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/test/test_caldav.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/test/test_caldav.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tap/test/test_caldav.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -29,6 +29,7 @@
 
 from twisted.application.service import IService
 from twisted.application import internet
+from twisted.internet.defer import inlineCallbacks
 
 from twisted.web2.dav import auth
 from twisted.web2.log import LogWrapperResource
@@ -595,6 +596,7 @@
 
         self.failUnless(isinstance(root, CalDAVServiceMaker.rootResourceClass))
 
+    @inlineCallbacks
     def test_principalResource(self):
         """
         Test the principal resource
@@ -603,10 +605,11 @@
         root = site.resource.resource.resource
 
         self.failUnless(isinstance(
-            root.getChild("principals"),
+            (yield root.getChild("principals")),
             CalDAVServiceMaker.principalResourceClass
         ))
 
+    @inlineCallbacks
     def test_calendarResource(self):
         """
         Test the calendar resource
@@ -615,7 +618,7 @@
         root = site.resource.resource.resource
 
         self.failUnless(isinstance(
-            root.getChild("calendars"),
+            (yield root.getChild("calendars")),
             CalDAVServiceMaker.calendarResourceClass
         ))
 
@@ -644,28 +647,31 @@
 
     configOptions = {"HTTPPort": 8008}
 
+    @inlineCallbacks
     def test_sameDirectory(self):
         """
         Test that the principal hierarchy has a reference
         to the same DirectoryService as the calendar hierarchy
         """
         site = self.getSite()
-        principals = site.resource.resource.resource.getChild("principals")
-        calendars = site.resource.resource.resource.getChild("calendars")
+        principals = (yield site.resource.resource.resource.getChild("principals"))
+        calendars = (yield site.resource.resource.resource.getChild("calendars"))
 
         self.assertEquals(principals.directory, calendars.directory)
 
+    @inlineCallbacks
     def test_aggregateDirectory(self):
         """
         Assert that the base directory service is actually
         an AggregateDirectoryService
         """
         site = self.getSite()
-        principals = site.resource.resource.resource.getChild("principals")
+        principals = (yield site.resource.resource.resource.getChild("principals"))
         directory = principals.directory
 
         self.failUnless(isinstance(directory, AggregateDirectoryService))
 
+    @inlineCallbacks
     def test_sudoDirectoryService(self):
         """
         Test that a sudo directory service is available if the
@@ -678,7 +684,7 @@
         open(self.config.SudoersFile, "w").write(sudoersFile)
 
         site = self.getSite()
-        principals = site.resource.resource.resource.getChild("principals")
+        principals = (yield site.resource.resource.resource.getChild("principals"))
         directory = principals.directory
 
         self.failUnless(self.config.SudoersFile)
@@ -694,6 +700,7 @@
             in directory.userRecordTypes
         )
 
+    @inlineCallbacks
     def test_sudoDirectoryServiceNoFile(self):
         """
         Test that there is no SudoDirectoryService if
@@ -703,7 +710,7 @@
 
         self.writeConfig()
         site = self.getSite()
-        principals = site.resource.resource.resource.getChild("principals")
+        principals = (yield site.resource.resource.resource.getChild("principals"))
         directory = principals.directory
 
         self.failUnless(self.config.SudoersFile)
@@ -714,13 +721,14 @@
             SudoDirectoryService.recordType_sudoers
         )
 
+    @inlineCallbacks
     def test_sudoDirectoryServiceNotConfigured(self):
         """
         Test that there is no SudoDirectoryService if
         the SudoersFile is not configured
         """
         site = self.getSite()
-        principals = site.resource.resource.resource.getChild("principals")
+        principals = (yield site.resource.resource.resource.getChild("principals"))
         directory = principals.directory
 
         self.failIf(self.config.SudoersFile)
@@ -731,13 +739,14 @@
             SudoDirectoryService.recordType_sudoers
         )
 
+    @inlineCallbacks
     def test_configuredDirectoryService(self):
         """
         Test that the real directory service is the directory service
         set in the configuration file.
         """
         site = self.getSite()
-        principals = site.resource.resource.resource.getChild("principals")
+        principals = (yield site.resource.resource.resource.getChild("principals"))
         directory = principals.directory
 
         realDirectory = directory.serviceForRecordType("users")

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/export.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/export.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/export.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -37,6 +37,8 @@
 from os.path import dirname, abspath
 
 from twistedcaldav.config import ConfigurationError
+from twisted.internet.defer import gatherResults
+
 from twistedcaldav.ical import Component as iComponent, Property as iProperty
 from twistedcaldav.ical import iCalendarProductID
 from twistedcaldav.resource import isCalendarCollectionResource
@@ -153,6 +155,12 @@
             sys.stdout.write("%s\n" % (e,))
             sys.exit(1)
 
+    def _gotChildren(children):
+                for childName in children:
+                    child = calendarHome.getChild(childName)
+                    if isCalendarCollectionResource(child):
+                        collections.add(child)
+
     for record in records:
         recordType, shortName = record
         calendarHome = config.directory.calendarHomeForShortName(recordType, shortName)
@@ -161,63 +169,65 @@
             sys.exit(1)
         calendarHomes.add(calendarHome)
 
-    for calendarHome in calendarHomes:
-        for childName in calendarHome.listChildren():
-            child = calendarHome.getChild(childName)
-            if isCalendarCollectionResource(child):
-                collections.add(child)
+        d = gatherResults([calendarHome.listChildren().addCallback(_gotChildren) for calendarHome in calendarHomes])
+    def _finish(_):
+        try:
+            calendar = iComponent("VCALENDAR")
+            calendar.addProperty(iProperty("VERSION", "2.0"))
+            calendar.addProperty(iProperty("PRODID", iCalendarProductID))
 
-    try:
-        calendar = iComponent("VCALENDAR")
-        calendar.addProperty(iProperty("VERSION", "2.0"))
-        calendar.addProperty(iProperty("PRODID", iCalendarProductID))
+            uids  = set()
+            tzids = set()
 
-        uids  = set()
-        tzids = set()
+            ds = []
+            for collection in collections:
+                d = collection.index().indexedSearch(None)
+                def _gotResults(results):
+                    for name, uid, type in results:
+                        child = collection.getChild(name)
+                        childData = child.iCalendarText()
 
-        for collection in collections:
-            for name, uid, type in collection.index().indexedSearch(None):
-                child = collection.getChild(name)
-                childData = child.iCalendarText()
+                        try:
+                            childCalendar = iComponent.fromString(childData)
+                        except ValueError:
+                            continue
+                        assert childCalendar.name() == "VCALENDAR"
 
-                try:
-                    childCalendar = iComponent.fromString(childData)
-                except ValueError:
-                    continue
-                assert childCalendar.name() == "VCALENDAR"
-
-                if uid in uids:
-                    sys.stderr.write("Skipping duplicate event UID %r from %s\n" % (uid, collection.fp.path))
-                    continue
-                else:
-                    uids.add(uid)
-
-                for component in childCalendar.subcomponents():
-                    # Only insert VTIMEZONEs once
-                    if component.name() == "VTIMEZONE":
-                        tzid = component.propertyValue("TZID")
-                        if tzid in tzids:
+                        if uid in uids:
+                            sys.stderr.write("Skipping duplicate event UID %r from %s\n" % (uid, collection.fp.path))
                             continue
                         else:
-                            tzids.add(tzid)
+                            uids.add(uid)
 
-                    calendar.addComponent(component)
+                        for component in childCalendar.subcomponents():
+                            # Only insert VTIMEZONEs once
+                            if component.name() == "VTIMEZONE":
+                                tzid = component.propertyValue("TZID")
+                                if tzid in tzids:
+                                    continue
+                                else:
+                                    tzids.add(tzid)
 
-        calendarData = str(calendar)
+                            calendar.addComponent(component)
+                ds.append(d)
+            def _done(_):
+                calendarData = str(calendar)
 
-        if outputFileName:
-            try:
-                output = open(outputFileName, "w")
-            except IOError, e:
-                sys.stderr.write("Unable to open output file for writing %s: %s\n" % (outputFileName, e))
-                sys.exit(1)
-        else:
-            output = sys.stdout
+                if outputFileName:
+                    try:
+                        output = open(outputFileName, "w")
+                    except IOError, e:
+                        sys.stderr.write("Unable to open output file for writing %s: %s\n" % (outputFileName, e))
+                        sys.exit(1)
+                else:
+                    output = sys.stdout
 
-        output.write(calendarData)
+                output.write(calendarData)
+            return gatherResults(ds).addCallback(_gotResults).addCallback(_done)
 
-    except UsageError, e:
-        usage(e)
+        except UsageError, e:
+            usage(e)
+    return d.addCallback(_finish)
 
 if __name__ == "__main__":
     main()

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/principals.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/principals.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -27,7 +27,7 @@
 from twisted.python.util import switchUID
 from twisted.internet import reactor
 from twisted.internet.address import IPv4Address
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web2.dav import davxml
 
 from twext.python.log import StandardIOObserver
@@ -157,6 +157,7 @@
                 raise AssertionError("Unknown proxy type")
 
             try:
+                # MOR: what to do here?
                 principalForPrincipalID(arg, checkOnly=True)
             except ValueError, e:
                 abort(e)
@@ -165,6 +166,7 @@
 
         elif opt in ("", "--remove-proxy"):
             try:
+                # MOR: what to do here?
                 principalForPrincipalID(arg, checkOnly=True)
             except ValueError, e:
                 abort(e)
@@ -233,6 +235,7 @@
 
     for arg in args:
         try:
+            # MOR: what to do here?
             principalForPrincipalID(arg, checkOnly=True)
         except ValueError, e:
             abort(e)
@@ -288,7 +291,7 @@
         for principalID in principalIDs:
             # Resolve the given principal IDs to principals
             try:
-                principal = principalForPrincipalID(principalID)
+                principal = (yield principalForPrincipalID(principalID))
             except ValueError:
                 principal = None
 
@@ -307,6 +310,7 @@
         #
         reactor.stop()
 
+ at inlineCallbacks
 def principalForPrincipalID(principalID, checkOnly=False, directory=None):
     
     # Allow a directory parameter to be passed in, but default to config.directory
@@ -319,14 +323,14 @@
         raise ValueError("Can't resolve paths yet")
 
         if checkOnly:
-            return None
+            returnValue(None)
 
     if principalID.startswith("("):
         try:
             i = principalID.index(")")
 
             if checkOnly:
-                return None
+                returnValue(None)
 
             recordType = principalID[1:i]
             shortName = principalID[i+1:]
@@ -334,26 +338,26 @@
             if not recordType or not shortName or "(" in recordType:
                 raise ValueError()
 
-            return directory.principalCollection.principalForShortName(recordType, shortName)
+            returnValue((yield directory.principalCollection.principalForShortName(recordType, shortName)))
 
         except ValueError:
             pass
 
     if ":" in principalID:
         if checkOnly:
-            return None
+            returnValue(None)
 
         recordType, shortName = principalID.split(":", 1)
 
-        return directory.principalCollection.principalForShortName(recordType, shortName)
+        returnValue((yield directory.principalCollection.principalForShortName(recordType, shortName)))
 
     try:
         guid = UUID(principalID)
 
         if checkOnly:
-            return None
+            returnValue(None)
 
-        return directory.principalCollection.principalForUID(guid)
+        returnValue((yield directory.principalCollection.principalForUID(guid)))
     except ValueError:
         pass
 
@@ -372,7 +376,7 @@
 @inlineCallbacks
 def action_listProxies(principal, *proxyTypes):
     for proxyType in proxyTypes:
-        subPrincipal = proxySubprincipal(principal, proxyType)
+        subPrincipal = (yield proxySubprincipal(principal, proxyType))
         if subPrincipal is None:
             print "No %s proxies for %s" % (proxyType, principal)
             continue
@@ -392,7 +396,7 @@
 @inlineCallbacks
 def action_addProxy(principal, proxyType, *proxyIDs):
     for proxyID in proxyIDs:
-        proxyPrincipal = principalForPrincipalID(proxyID)
+        proxyPrincipal = (yield principalForPrincipalID(proxyID))
         (yield action_addProxyPrincipal(principal, proxyType, proxyPrincipal))
 
 @inlineCallbacks
@@ -425,7 +429,7 @@
 @inlineCallbacks
 def action_removeProxy(principal, *proxyIDs, **kwargs):
     for proxyID in proxyIDs:
-        proxyPrincipal = principalForPrincipalID(proxyID)
+        proxyPrincipal = (yield principalForPrincipalID(proxyID))
         (yield action_removeProxyPrincipal(principal, proxyPrincipal, **kwargs))
 
 @inlineCallbacks
@@ -434,7 +438,7 @@
     for proxyType in proxyTypes:
         proxyURL = proxyPrincipal.url()
 
-        subPrincipal = proxySubprincipal(principal, proxyType)
+        subPrincipal = (yield proxySubprincipal(principal, proxyType))
         if subPrincipal is None:
             sys.stderr.write("Unable to edit %s proxies for %s\n" % (proxyType, principal))
             continue
@@ -478,8 +482,6 @@
 
     print ""
 
-    resource = None
-
     for opt, arg in optargs:
 
         if opt in ("-s", "--search",):

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/util.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/util.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -25,6 +25,7 @@
 import os
 from time import sleep
 
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.python.reflect import namedClass
 
 import socket
@@ -69,21 +70,24 @@
 
         principalCollection = property(getPrincipalCollection, setPrincipalCollection)
 
+        @inlineCallbacks
         def calendarHomeForRecord(self, record):
-            principal = self.principalCollection.principalForRecord(record)
+            principal = (yield self.principalCollection.principalForRecord(record))
             if principal:
                 try:
-                    return principal.calendarHome()
+                    returnValue(principal.calendarHome())
                 except AttributeError:
                     pass
-            return None
+            returnValue(None)
 
+        @inlineCallbacks
         def calendarHomeForShortName(self, recordType, shortName):
-            principal = self.principalCollection.principalForShortName(recordType, shortName)
+            principal = (yield self.principalCollection.principalForShortName(recordType, shortName))
             if principal:
-                return principal.calendarHome()
-            return None
+                returnValue(principal.calendarHome())
+            returnValue(None)
 
+        # Deferred
         def principalForCalendarUserAddress(self, cua):
             return self.principalCollection.principalForCalendarUserAddress(cua)
 
@@ -100,7 +104,7 @@
     baseGUID = "51856FD4-5023-4890-94FE-4356C4AAC3E4"
     def recordTypes(self): return ()
     def listRecords(self): return ()
-    def recordWithShortName(self): return None
+    def recordWithShortName(self): return succeed(None)
 
 dummyDirectoryRecord = DirectoryRecord(
     service = DummyDirectoryService(),

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/warmup.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/warmup.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/tools/warmup.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -33,6 +33,8 @@
 from os.path import dirname, abspath
 
 from twistedcaldav.config import ConfigurationError
+from twisted.internet.defer import gatherResults
+
 from twistedcaldav.resource import isPseudoCalendarCollectionResource
 from twistedcaldav.static import CalDAVFile, CalendarHomeFile
 from twistedcaldav.directory.directory import DirectoryService
@@ -152,31 +154,38 @@
 
     calendarCollections = set()
 
+    def _gotChildren(children):
+        for childName in children:
+            child = calendarHome.getChild(childName)
+            if isPseudoCalendarCollectionResource(child):
+                calendarCollections.add(child)
+    ds = []
     for calendarHome in calendarHomes:
         #print calendarHome
         #sys.stdout.write("*")
         readProperties(calendarHome)
 
-        for childName in calendarHome.listChildren():
-            child = calendarHome.getChild(childName)
-            if isPseudoCalendarCollectionResource(child):
-                calendarCollections.add(child)
+        ds.append(calendarHome.listChildren().addCallback(_gotChildren))
+    d = gatherResults(ds)
 
-    for calendarCollection in calendarCollections:
-        try:
-            for name, uid, type in calendarCollection.index().indexedSearch(None):
-                child = calendarCollection.getChild(name)
+    def _finish(_):
+        for calendarCollection in calendarCollections:
+            try:
+                for name, uid, type in calendarCollection.index().indexedSearch(None):
+                    child = calendarCollection.getChild(name)
 
-                #sys.stdout.write("+")
-                childCalendar = child.iCalendarText()
+                    #sys.stdout.write("+")
+                    d = child.iCalendarText()
 
-                readProperties(child)
+                    readProperties(child)
 
-        except sqlite3.OperationalError:
-            # Outbox doesn't live on disk
-            if calendarCollection.fp.basename() != "outbox":
-                raise
+            except sqlite3.OperationalError:
+                # Outbox doesn't live on disk
+                if calendarCollection.fp.basename() != "outbox":
+                    raise
 
+    return d.addCallback(_finish)
+
 def readProperties(resource):
     #sys.stdout.write("-")
     for qname in resource.deadProperties().list():

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webadmin/resource.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webadmin/resource.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webadmin/resource.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -31,7 +31,7 @@
 from twistedcaldav.config import config
 from twistedcaldav.extensions import DAVFile, ReadOnlyResourceMixIn
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.web2.http import Response
 from twisted.web2.http_headers import MimeType
 from twisted.web2.stream import MemoryStream
@@ -48,11 +48,11 @@
 
     # Only allow administrators to access
     def defaultAccessControlList(self):
-        return davxml.ACL(*config.AdminACEs)
+        return succeed(davxml.ACL(*config.AdminACEs))
     
     def etag(self):
         # Can't be calculated here
-        return None
+        return succeed(None)
 
     def contentLength(self):
         # Can't be calculated here
@@ -65,7 +65,7 @@
         return True
 
     def displayName(self):
-        return "Web Admin"
+        return succeed("Web Admin")
 
     def contentType(self):
         return MimeType.fromString("text/html; charset=utf-8");
@@ -74,7 +74,7 @@
         return None
 
     def createSimilarFile(self, path):
-        return DAVFile(path, principalCollections=self.principalCollections())
+        return succeed(DAVFile(path, principalCollections=self.principalCollections()))
 
     def directoryStyleSheet(self):
         return (
@@ -149,24 +149,24 @@
         # Add details if a resource has been selected.
         if resourceId:
         
-            principal = self.getResourceById(request, resourceId)
+            principal = (yield self.getResourceById(request, resourceId))
     
             # Update the auto-schedule value if specified.
             if autoSchedule is not None and (autoSchedule == "true" or autoSchedule == "false"):
                 if principal.record.recordType != "users" and principal.record.recordType != "groups":
-                    result = (yield principal.setAutoSchedule(autoSchedule == "true"))
+                    (yield principal.setAutoSchedule(autoSchedule == "true"))
 
             # Update the proxies if specified.
             for proxyId in removeProxies:
-                proxy = self.getResourceById(request, proxyId)
+                proxy = (yield self.getResourceById(request, proxyId))
                 (yield action_removeProxyPrincipal(principal, proxy, proxyTypes=["read", "write"]))
 
             for proxyId in makeReadProxies:
-                proxy = self.getResourceById(request, proxyId)
+                proxy = (yield self.getResourceById(request, proxyId))
                 (yield action_addProxyPrincipal(principal, "read", proxy))
 
             for proxyId in makeWriteProxies:
-                proxy = self.getResourceById(request, proxyId)
+                proxy = (yield self.getResourceById(request, proxyId))
                 (yield action_addProxyPrincipal(principal, "write", proxy))
                 
             # Add the detailed content
@@ -263,7 +263,7 @@
         if davPropertyName:
             try:
                 namespace, name = davPropertyName.split("#")
-            except Exception, e:
+            except Exception:
                 propertyHtml += "<div>Unable to parse property to read: <b>%s</b></div>" % davPropertyName
 
             result = (yield resource.readProperty((namespace, name), None))
@@ -435,6 +435,7 @@
         htmlContent.addCallback(_defer)
         return htmlContent
 
+    # Deferred
     def getResourceById(self, request, resourceId):
         if resourceId.startswith("/"):
             return request.locateResource(resourceId)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webcal/resource.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webcal/resource.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/calendarserver/webcal/resource.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -28,6 +28,7 @@
 from urlparse import urlparse
 from cgi import parse_qs
 
+from twisted.internet.defer import succeed
 from twisted.web2 import responsecode
 from twisted.web2.http import Response
 from twisted.web2.http_headers import MimeType
@@ -40,20 +41,22 @@
 
 class WebCalendarResource (ReadOnlyResourceMixIn, DAVFile):
     def defaultAccessControlList(self):
-        return davxml.ACL(
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
+        return succeed(
+            davxml.ACL(
+                davxml.ACE(
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                    ),
+                    davxml.Protected(),
+                    TwistedACLInheritable(),
                 ),
-                davxml.Protected(),
-                TwistedACLInheritable(),
-            ),
+            )
         )
 
     def etag(self):
         # Can't be calculated here
-        return None
+        return succeed(None)
 
     def contentLength(self):
         # Can't be calculated here
@@ -66,7 +69,7 @@
         return True
 
     def displayName(self):
-        return "Web Calendar"
+        return succeed("Web Calendar")
 
     def contentType(self):
         return MimeType.fromString("text/html; charset=utf-8");
@@ -75,7 +78,7 @@
         return None
 
     def createSimilarFile(self, path):
-        return DAVFile(path, principalCollections=self.principalCollections())
+        return succeed(DAVFile(path, principalCollections=self.principalCollections()))
 
     _htmlContent_lastCheck      = 0
     _htmlContent_statInfo       = 0

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.auth.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.auth.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.auth.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,27 @@
+Index: twisted/web2/dav/auth.py
+===================================================================
+--- twisted/web2/dav/auth.py	(revision 27622)
++++ twisted/web2/dav/auth.py	(working copy)
+@@ -91,14 +91,18 @@
+         else:
+             raise error.UnauthorizedLogin("Bad credentials for: %s" % (principalURIs[0],))
+ 
+-    def requestAvatarId(self, credentials):
+-        pcreds = IPrincipalCredentials(credentials)
+-        pswd = str(pcreds.authnPrincipal.readDeadProperty(TwistedPasswordProperty))
+-
++    def _cbReadDeadProperty(self, result, credentials, pcreds):
++        pswd = str(result)
+         d = defer.maybeDeferred(credentials.checkPassword, pswd)
+         d.addCallback(self._cbPasswordMatch, (pcreds.authnPrincipal.principalURL(), pcreds.authzPrincipal.principalURL()))
+         return d
+ 
++    def requestAvatarId(self, credentials):
++        pcreds = IPrincipalCredentials(credentials)
++        d = pcreds.authnPrincipal.readDeadProperty(TwistedPasswordProperty)
++        d.addCallback(self._cbReadDeadProperty, credentials, pcreds)
++        return d
++
+ ##
+ # Utilities
+ ##

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,10 @@
+Index: twisted/web2/dav/method/propfind.py
+===================================================================
+--- twisted/web2/dav/method/propfind.py	(revision 27622)
++++ twisted/web2/dav/method/propfind.py	(working copy)
+@@ -1,4 +1,4 @@
+-# -*- test-case-name: twisted.web2.dav.test.test_prop.PROP.test_PROPFIND -*-
++# -*- test-case-name: twisted.web2.dav.test.test_prop.PROP -*-
+ ##
+ # Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ #

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,50 @@
+Index: twisted/web2/dav/method/put_common.py
+===================================================================
+--- twisted/web2/dav/method/put_common.py	(revision 27622)
++++ twisted/web2/dav/method/put_common.py	(working copy)
+@@ -22,7 +22,7 @@
+ 
+ __version__ = "0.0"
+ 
+-__all__ = ["storeCalendarObjectResource"]
++__all__ = ["storeResource"]
+ 
+ from twisted.internet.defer import deferredGenerator, maybeDeferred, waitForDeferred
+ from twisted.python import failure, log
+@@ -208,20 +208,31 @@
+         # Update the MD5 value on the resource
+         if source is not None:
+             # Copy MD5 value from source to destination
+-            if source.hasDeadProperty(TwistedGETContentMD5):
+-                md5 = source.readDeadProperty(TwistedGETContentMD5)
+-                destination.writeDeadProperty(md5)
++            hasDeadProperty = waitForDeferred(source.hasDeadProperty(TwistedGETContentMD5))
++            yield hasDeadProperty
++            hasDeadProperty = hasDeadProperty.getResult()
++            if hasDeadProperty:
++                md5 = waitForDeferred(source.readDeadProperty(TwistedGETContentMD5))
++                yield md5
++                md5 = md5.getResult()
++                ignore = waitForDeferred(destination.writeDeadProperty(md5))
++                yield ignore
++                ignore = ignore.getResult( )
+         else:
+             # Finish MD5 calc and write dead property
+             md5.close()
+             md5 = md5.getMD5()
+-            destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
++            ignore = waitForDeferred(destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5)))
++            yield ignore
++            ignore = ignore.getResult( )
+ 
+         # Update the content-type value on the resource if it is not been copied or moved
+         if source is None:
+             content_type = request.headers.getHeader("content-type")
+             if content_type is not None:
+-                destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
++                ignore = waitForDeferred(destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type))))
++                yield ignore
++                ignore = ignore.getResult( )
+ 
+         response = IResponse(response)
+         

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.noneprops.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.noneprops.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.noneprops.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,37 @@
+Index: twisted/web2/dav/noneprops.py
+===================================================================
+--- twisted/web2/dav/noneprops.py	(revision 27622)
++++ twisted/web2/dav/noneprops.py	(working copy)
+@@ -33,6 +33,8 @@
+ 
+ from twisted.web2 import responsecode
+ from twisted.web2.http import HTTPError, StatusResponse
++from twisted.python.failure import Failure
++from twisted.internet.defer import succeed, fail
+ 
+ class NonePropertyStore (object):
+     """
+@@ -50,18 +52,18 @@
+         pass
+ 
+     def get(self, qname):
+-        raise HTTPError(StatusResponse(responsecode.NOT_FOUND, "No such property: {%s}%s" % qname))
++        return fail(Failure(HTTPError(StatusResponse(responsecode.NOT_FOUND, "No such property: {%s}%s" % qname))))
+ 
+     def set(self, property):
+-        raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Permission denied for setting property: %s" % (property,)))
++        return fail(Failure(HTTPError(StatusResponse(responsecode.FORBIDDEN, "Permission denied for setting property: %s" % (property,)))))
+ 
+     def delete(self, qname):
+         # RFC 2518 Section 12.13.1 says that removal of
+         # non-existing property is not an error.
+-        pass
++        return succeed(None)
+ 
+     def contains(self, qname):
+-        return False
++        return succeed(False)
+ 
+     def list(self):
+-        return ()
++        return succeed( () )

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.resource.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.resource.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,413 @@
+Index: twisted/web2/dav/resource.py
+===================================================================
+--- twisted/web2/dav/resource.py	(revision 27622)
++++ twisted/web2/dav/resource.py	(working copy)
+@@ -57,7 +57,7 @@
+ from twisted.python import log
+ from twisted.python.failure import Failure
+ from twisted.internet.defer import Deferred, maybeDeferred, succeed, inlineCallbacks
+-from twisted.internet.defer import waitForDeferred, deferredGenerator
++from twisted.internet.defer import waitForDeferred, deferredGenerator, returnValue
+ from twisted.internet import reactor
+ from twisted.web2 import responsecode
+ from twisted.web2.http import HTTPError, RedirectResponse, StatusResponse
+@@ -186,11 +186,12 @@
+         namespace, name = qname
+         if namespace == dav_namespace:
+             if name in ("quota-available-bytes", "quota-used-bytes"):
+-                d = self.hasQuota(request)
+-                d.addCallback(lambda result: result)
+-                return d
++                return self.hasQuota(request)
+         
+-        return succeed(qname in self.liveProperties or self.deadProperties().contains(qname))
++        if qname in self.liveProperties:
++            return succeed(True)
++        else:
++            return self.deadProperties().contains(qname)
+ 
+     def readProperty(self, property, request):
+         """
+@@ -209,24 +210,28 @@
+             if namespace == dav_namespace:
+                 if name == "resourcetype":
+                     # Allow live property to be overriden by dead property
+-                    if self.deadProperties().contains(qname):
+-                        return self.deadProperties().get(qname)
+-                    if self.isCollection():
+-                        return davxml.ResourceType.collection
+-                    return davxml.ResourceType.empty
++                    def restype(hasDead):
++                        if hasDead:
++                            return self.deadProperties().get(qname)
++                        if self.isCollection():
++                            return davxml.ResourceType.collection
++                        return davxml.ResourceType.empty
++                    return self.deadProperties().contains(qname).addCallback(restype)
+ 
+                 if name == "getetag":
+-                    etag = self.etag()
+-                    if etag is None:
+-                        return None
+-                    return davxml.GETETag(etag.generate())
++                    detag = self.etag()
++                    def procetag(etag):
++                        if etag is None:
++                            return None
++                        return davxml.GETETag(etag.generate())
++                    return detag.addCallback(procetag)
+ 
+                 if name == "getcontenttype":
+-                    mimeType = self.contentType()
+-                    if mimeType is None:
+-                        return None
+-                    return davxml.GETContentType(generateContentType(mimeType))
+-
++                    def mimetype(mimeType):
++                        if mimeType is None:
++                            return None
++                        return davxml.GETContentType(generateContentType(mimeType))
++                    return self.contentType().addCallback(mimetype)
+                 if name == "getcontentlength":
+                     length = self.contentLength()
+                     if length is None:
+@@ -250,10 +255,11 @@
+                     return davxml.CreationDate.fromDate(creationDate)
+ 
+                 if name == "displayname":
+-                    displayName = self.displayName()
+-                    if displayName is None:
+-                        return None
+-                    return davxml.DisplayName(displayName)
++                    def dispname(displayName):
++                        if displayName is None:
++                            return None
++                        return davxml.DisplayName(displayName)
++                    return self.displayName().addCallback(dispname)
+ 
+                 if name == "supportedlock":
+                     return davxml.SupportedLock(
+@@ -429,7 +435,10 @@
+             if not has:
+                 qnames.remove(dqname)
+ 
+-        for qname in self.deadProperties().list():
++        wfd = waitForDeferred(self.deadProperties().list())
++        yield wfd
++        deadPropNames = wfd.getResult()
++        for qname in deadPropNames:
+             if (qname not in qnames) and (qname[0] != twisted_private_namespace):
+                 qnames.add(qname)
+ 
+@@ -498,7 +507,7 @@
+         in the dead property store may or may not be ignored when reading the
+         property with L{readProperty}.
+         """
+-        self.deadProperties().set(property)
++        return self.deadProperties().set(property)
+ 
+     def removeDeadProperty(self, property):
+         """
+@@ -517,18 +526,23 @@
+     # Overrides some methods in MetaDataMixin in order to allow DAV properties
+     # to override the values of some HTTP metadata.
+     #
++    @inlineCallbacks
+     def contentType(self):
+-        if self.hasDeadProperty((davxml.dav_namespace, "getcontenttype")):
+-            return self.readDeadProperty((davxml.dav_namespace, "getcontenttype")).mimeType()
++        if (yield self.hasDeadProperty((davxml.dav_namespace, "getcontenttype"))):
++            returnValue((yield self.readDeadProperty((davxml.dav_namespace, "getcontenttype")).mimeType()))
+         else:
+-            return super(DAVPropertyMixIn, self).contentType()
++            returnValue((yield super(DAVPropertyMixIn, self).contentType()))
+ 
++    @inlineCallbacks
+     def displayName(self):
+-        if self.hasDeadProperty((davxml.dav_namespace, "displayname")):
+-            return str(self.readDeadProperty((davxml.dav_namespace, "displayname")))
++        if (yield self.hasDeadProperty((davxml.dav_namespace, "displayname"))):
++            returnValue(str((yield self.readDeadProperty((
++                                davxml.dav_namespace, "displayname")))))
+         else:
+-            return super(DAVPropertyMixIn, self).displayName()
++            returnValue((yield super(DAVPropertyMixIn, self).displayName()))
+ 
++
++
+ class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
+     """
+     WebDAV resource.
+@@ -567,6 +581,7 @@
+         """
+         unimplemented(self)
+ 
++
+     def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
+         """
+         See L{IDAVResource.findChildren}.
+@@ -581,7 +596,7 @@
+ 
+         completionDeferred = Deferred()
+         basepath = request.urlForResource(self)
+-        children = list(self.listChildren())
++        children = []
+ 
+         def checkPrivilegesError(failure):
+             failure.trap(AccessDeniedError)
+@@ -613,6 +628,11 @@
+ 
+             reactor.callLater(0, getChild)
+ 
++        def gotChildren(childs):
++            children[:] = childs
++            getChild()
++            return completionDeferred
++
+         def getChild():
+             try:
+                 childname = children.pop()
+@@ -624,10 +644,10 @@
+                 d.addCallback(checkPrivileges)
+                 d.addCallbacks(gotChild, checkPrivilegesError, (childpath,))
+                 d.addErrback(completionDeferred.errback)
++                return d
+ 
+-        getChild()
++        return self.listChildren().addCallback(gotChildren)
+ 
+-        return completionDeferred
+ 
+     def supportedReports(self):
+         """
+@@ -706,7 +726,7 @@
+                 hasattr(request, 'loginInterfaces')):
+             request.authnUser = davxml.Principal(davxml.Unauthenticated())
+             request.authzUser = davxml.Principal(davxml.Unauthenticated())
+-            return (request.authnUser, request.authzUser)
++            return succeed((request.authnUser, request.authzUser))
+ 
+         authHeader = request.headers.getHeader('authorization')
+ 
+@@ -754,7 +774,7 @@
+         else:
+             request.authnUser = davxml.Principal(davxml.Unauthenticated())
+             request.authzUser = davxml.Principal(davxml.Unauthenticated())
+-            return (request.authnUser, request.authzUser)
++            return succeed((request.authnUser, request.authzUser))
+ 
+     ##
+     # ACL
+@@ -789,7 +809,7 @@
+         # and deny any type of write access (PUT, DELETE, etc.) to
+         # everything.
+         #
+-        return readonlyACL
++        return succeed(readonlyACL)
+ 
+     def defaultAccessControlList(self):
+         """
+@@ -800,7 +820,7 @@
+         # The default behaviour is no ACL; we should inherrit from the parent
+         # collection.
+         #
+-        return davxml.ACL()
++        return succeed(davxml.ACL())
+ 
+     def setAccessControlList(self, acl):
+         """
+@@ -809,7 +829,7 @@
+         This implementation stores the ACL in the private property
+         C{(L{twisted_private_namespace}, "acl")}.
+         """
+-        self.writeDeadProperty(acl)
++        return self.writeDeadProperty(acl)
+ 
+     def mergeAccessControlList(self, new_acl, request):
+         """
+@@ -967,7 +987,7 @@
+         via the ACL command.
+         @param new_aces: C{list} of L{ACE} for ACL being set.
+         """
+-        self.setAccessControlList(davxml.ACL(*new_aces))
++        return self.setAccessControlList(davxml.ACL(*new_aces))
+ 
+     def matchPrivilege(self, privilege, ace_privileges, supportedPrivileges):
+         for ace_privilege in ace_privileges:
+@@ -1117,7 +1137,9 @@
+             return url
+ 
+         try:
+-            acl = self.readDeadProperty(davxml.ACL)
++            dacl = waitForDeferred(self.readDeadProperty(davxml.ACL))
++            yield dacl
++            acl = dacl.getResult()
+         except HTTPError, e:
+             assert e.response.code == responsecode.NOT_FOUND, (
+                 "Expected %s response from readDeadProperty() exception, not %s"
+@@ -1130,9 +1152,13 @@
+ 
+             if myURL == "/":
+                 # If we get to the root without any ACLs, then use the default.
+-                acl = self.defaultRootAccessControlList()
++                acl = waitForDeferred(self.defaultRootAccessControlList())
++                yield acl
++                acl = acl.getResult()
+             else:
+-                acl = self.defaultAccessControlList()
++                acl = waitForDeferred(self.defaultAccessControlList())
++                yield acl
++                acl = acl.getResult()
+ 
+         # Dynamically update privileges for those ace's that are inherited.
+         if inheritance:
+@@ -1251,16 +1277,17 @@
+             It will errback with an HTTPError(responsecode.FORBIDDEN) if
+             the principal isn't found.
+         """
+-        authnPrincipal = self.findPrincipalForAuthID(authid)
++        d = self.findPrincipalForAuthID(authid)
++        def principalFound(authnPrincipal):
++            if authnPrincipal is None:
++                log.msg("Could not find the principal resource for user id: %s" % (authid,))
++                raise HTTPError(responsecode.FORBIDDEN)
+ 
+-        if authnPrincipal is None:
+-            log.msg("Could not find the principal resource for user id: %s" % (authid,))
+-            raise HTTPError(responsecode.FORBIDDEN)
++            d2 = self.authorizationPrincipal(request, authid, authnPrincipal)
++            d2.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
++            return d2
++        return d.addCallback(principalFound)
+ 
+-        d = self.authorizationPrincipal(request, authid, authnPrincipal)
+-        d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
+-        return d
+-
+     def findPrincipalForAuthID(self, authid):
+         """
+         Return authentication and authorization principal identifiers for the
+@@ -1663,7 +1690,9 @@
+ 
+         # Check this resource first
+         if self.isCollection():
+-            qroot = self.quotaRoot(request)
++            qroot = waitForDeferred(self.quotaRoot(request))
++            yield qroot
++            qroot = qroot.getResult()
+             if qroot is not None:
+                 used = waitForDeferred(self.currentQuotaUse(request))
+                 yield used
+@@ -1701,7 +1730,10 @@
+         """
+         
+         # Check this one first
+-        if self.hasQuotaRoot(request):
++        hqr = waitForDeferred(self.hasQuotaRoot(request))
++        yield hqr
++        hqr = hqr.getResult()
++        if hqr:
+             yield True
+             return
+         
+@@ -1714,7 +1746,8 @@
+                 parent = parent.getResult()
+                 d = waitForDeferred(parent.hasQuota(request))
+                 yield d
+-                yield d.getResult()
++                res = d.getResult()
++                yield res
+             else:
+                 yield False
+         except NoURLForResourceError:
+@@ -1727,16 +1760,17 @@
+         @return: a C{True} if this resource has quota root, C{False} otherwise.
+         """
+         return self.hasDeadProperty(TwistedQuotaRootProperty)
+-    
++
++    @inlineCallbacks
+     def quotaRoot(self, request):
+         """
+         @return: a C{int} containing the maximum allowed bytes if this collection
+             is quota-controlled, or C{None} if not quota controlled.
+         """
+-        if self.hasDeadProperty(TwistedQuotaRootProperty):
+-            return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
++        if (yield self.hasDeadProperty(TwistedQuotaRootProperty)):
++            returnValue(int(str((yield self.readDeadProperty(TwistedQuotaRootProperty)))))
+         else:
+-            return None
++            returnValue(None)
+     
+     def quotaRootParent(self, request):
+         """
+@@ -1759,7 +1793,8 @@
+         yield None
+     
+     quotaRootParent = deferredGenerator(quotaRootParent)
+-        
++
++    @inlineCallbacks
+     def setQuotaRoot(self, request, maxsize):
+         """
+         @param maxsize: a C{int} containing the maximum allowed bytes for the contents
+@@ -1769,11 +1804,11 @@
+         assert maxsize is None or isinstance(maxsize, int), "maxsize must be an int or None"
+         
+         if maxsize is not None:
+-            self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
++            yield self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
+         else:
+             # Remove both the root and the cached used value
+-            self.removeDeadProperty(TwistedQuotaRootProperty)
+-            self.removeDeadProperty(TwistedQuotaUsedProperty)
++            yield self.removeDeadProperty(TwistedQuotaRootProperty)
++            yield self.removeDeadProperty(TwistedQuotaUsedProperty)
+     
+     def quotaSize(self, request):
+         """
+@@ -1798,7 +1833,9 @@
+         quotaroot = self
+         while(quotaroot is not None):
+             # Check quota on this root (if it has one)
+-            quota = quotaroot.quotaRoot(request)
++            quota = waitForDeferred(quotaroot.quotaRoot(request))
++            yield quota
++            quota = quota.getResult()
+             if quota is not None:
+                 if available > quota[0]:
+                     yield False
+@@ -1844,6 +1881,8 @@
+ 
+     quotaSizeAdjust = deferredGenerator(quotaSizeAdjust)
+ 
++
++    @inlineCallbacks
+     def currentQuotaUse(self, request):
+         """
+         Get the cached quota use value, or if not present (or invalid) determine
+@@ -1856,17 +1895,15 @@
+         assert self.hasQuotaRoot(request), "Quota use only on quota root collection"
+         
+         # Try to get the cached value property
+-        if self.hasDeadProperty(TwistedQuotaUsedProperty):
+-            return succeed(int(str(self.readDeadProperty(TwistedQuotaUsedProperty))))
++        if (yield self.hasDeadProperty(TwistedQuotaUsedProperty)):
++            returnValue(int(str((yield self.readDeadProperty(TwistedQuotaUsedProperty)))))
+         else:
+             # Do brute force size determination and cache the result in the private property
+-            def _defer(result):
+-                self.writeDeadProperty(TwistedQuotaUsedProperty(str(result)))
+-                return result
+-            d = self.quotaSize(request)
+-            d.addCallback(_defer)
+-            return d
++            result = yield self.quotaSize(request)
++            yield self.writeDeadProperty(TwistedQuotaUsedProperty(str(result)))
++            returnValue(result)
+ 
++
+     def updateQuotaUse(self, request, adjust):
+         """
+         Update the quota used value on this resource.

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.static.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.static.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,105 @@
+Index: twisted/web2/dav/static.py
+===================================================================
+--- twisted/web2/dav/static.py	(revision 27622)
++++ twisted/web2/dav/static.py	(working copy)
+@@ -28,7 +28,7 @@
+ 
+ __all__ = ["DAVFile"]
+ 
+-from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
++from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred, maybeDeferred
+ from twisted.python.filepath import InsecurePath
+ from twisted.python import log
+ from twisted.web2 import http_headers
+@@ -53,6 +53,14 @@
+ 
+     Extends twisted.web2.static.File to handle WebDAV methods.
+     """
++    @classmethod
++    def fetch(cls, request, path, *args, **kwargs):
++        """
++        stuff etc
++        """
++        return succeed(cls(path, *args, **kwargs))
++
++
+     def __init__(
+         self, path,
+         defaultType="text/plain", indexNames=None,
+@@ -82,12 +90,26 @@
+     ##
+ 
+     def etag(self):
+-        if not self.fp.exists(): return None
+-        if self.hasDeadProperty(TwistedGETContentMD5):
+-            return http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5)))
++        if not self.fp.exists():
++            yield None
++            return
++
++        hasProp = waitForDeferred(self.hasDeadProperty(TwistedGETContentMD5))
++        yield hasProp
++        hasProp = hasProp.getResult()
++        if hasProp:
++            propValue = waitForDeferred(self.readDeadProperty(TwistedGETContentMD5))
++            yield propValue
++            propValue = propValue.getResult()
++            yield http_headers.ETag(str(propValue))
+         else:
+-            return super(DAVFile, self).etag()
++            d = waitForDeferred(super(DAVFile, self).etag())
++            yield d
++            d = d.getResult()
++            yield d
+ 
++    etag = deferredGenerator(etag)
++
+     def davComplianceClasses(self):
+         return ("1", "access-control") # Add "2" when we have locking
+ 
+@@ -169,9 +191,12 @@
+         """
+         # If getChild() finds a child resource, return it
+         try:
+-            child = self.getChild(segments[0])
++            child = waitForDeferred(maybeDeferred(self.getChild, segments[0]))
++            yield child
++            child = child.getResult()
+             if child is not None:
+-                return (child, segments[1:])
++                yield (child, segments[1:])
++                return
+         except InsecurePath:
+             raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Invalid URL path"))
+         
+@@ -180,19 +205,26 @@
+         # that the request wants created.
+         self.fp.restat(False)
+         if self.fp.exists() and not self.fp.isdir():
+-            return (None, ())
++            yield (None, ())
++            return
+ 
+         # OK, we need to return a child corresponding to the first segment
+         path = segments[0]
+         
+         if path == "":
+             # Request is for a directory (collection) resource
+-            return (self, ())
++            yield (self, ())
++            return
+ 
+-        return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
++        result = waitForDeferred(self.createSimilarFile(self.fp.child(path).path))
++        yield result
++        result = result.getResult()
++        yield (result, segments[1:])
+ 
++    locateChild = deferredGenerator(locateChild)
++
+     def createSimilarFile(self, path):
+-        return self.__class__(
++        return self.__class__.fetch(None, 
+             path, defaultType=self.defaultType, indexNames=self.indexNames[:],
+             principalCollections=self.principalCollections())
+ 

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,378 @@
+Index: twisted/web2/dav/test/test_acl.py
+===================================================================
+--- twisted/web2/dav/test/test_acl.py	(revision 27622)
++++ twisted/web2/dav/test/test_acl.py	(working copy)
+@@ -26,6 +26,8 @@
+ 
+ from twisted.cred.portal import Portal
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
++
+ from twisted.web2 import responsecode
+ from twisted.web2.auth import basic
+ from twisted.web2.stream import MemoryStream
+@@ -50,7 +52,9 @@
+         os.mkdir(docroot)
+ 
+         userResource = TestDAVPrincipalResource("/principals/users/user01")
+-        userResource.writeDeadProperty(TwistedPasswordProperty("user01"))
++        d = waitForDeferred(userResource.writeDeadProperty(TwistedPasswordProperty("user01")))
++        yield d
++        d = d.getResult()
+ 
+         principalCollection = TestPrincipalsCollection(
+             "/principals/",
+@@ -75,7 +79,9 @@
+             loginInterfaces
+         ))
+ 
+-        rootResource.setAccessControlList(self.grant(davxml.All()))
++        d = waitForDeferred(rootResource.setAccessControlList(self.grant(davxml.All())))
++        yield d
++        d.getResult( )
+ 
+         for name, acl in (
+             ("none"       , self.grant()),
+@@ -88,7 +94,9 @@
+             if not os.path.isfile(filename):
+                 file(filename, "w").close()
+             resource = self.resource_class(filename)
+-            resource.setAccessControlList(acl)
++            d2 = waitForDeferred(resource.setAccessControlList(acl))
++            yield d2
++            d2.getResult()
+ 
+         for name, acl in (
+             ("nobind" , self.grant()),
+@@ -99,9 +107,12 @@
+             if not os.path.isdir(dirname):
+                 os.mkdir(dirname)
+             resource = self.resource_class(dirname)
+-            resource.setAccessControlList(acl)
+-        return docroot
++            d3 = waitForDeferred(resource.setAccessControlList(acl))
++            yield d3
++            d3.getResult()
++        yield docroot
+ 
++    createDocumentRoot = deferredGenerator(createDocumentRoot)
+ 
+     def restore(self):
+         # Get rid of whatever messed up state the test has now so that we'll
+@@ -119,6 +130,7 @@
+             dst_path = os.path.join(self.docroot, "copy_dst")
+             dst_uri = "/" + os.path.basename(dst_path)
+ 
++            results = []
+             for src, status in (
+                 ("nobind", responsecode.FORBIDDEN),
+                 ("bind",   responsecode.FORBIDDEN),
+@@ -129,11 +141,15 @@
+                 if not os.path.isdir(src_path):
+                     os.mkdir(src_path)
+                 src_resource = self.resource_class(src_path)
+-                src_resource.setAccessControlList({
++
++                d = waitForDeferred(src_resource.setAccessControlList({
+                     "nobind": self.grant(),
+                     "bind"  : self.grant(davxml.Bind()),
+                     "unbind": self.grant(davxml.Bind(), davxml.Unbind())
+-                }[src])
++                }[src]))
++                yield d
++                d.getResult()
++
+                 for name, acl in (
+                     ("none"       , self.grant()),
+                     ("read"       , self.grant(davxml.Read())),
+@@ -144,8 +160,11 @@
+                     filename = os.path.join(src_path, name)
+                     if not os.path.isfile(filename):
+                         file(filename, "w").close()
+-                    self.resource_class(filename).setAccessControlList(acl)
+ 
++                    d = waitForDeferred(self.resource_class(filename).setAccessControlList(acl))
++                    yield d
++                    d.getResult()
++
+                 for method in ("COPY", "MOVE"):
+                     for name, code in (
+                         ("none"       , {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]),
+@@ -166,12 +185,30 @@
+                                 os.remove(dst_path)
+     
+                             if response.code != code:
+-                                return self.oops(request, response, code, method, name)
++                                d = waitForDeferred(self.oops(request, response, code, method, name))
++                                yield d
++                                d = d.getResult()
++                                yield d
++                                return
+     
+-                        yield (request, test)
++                        results.append( (request, test) )
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_MOVE_source = deferredGenerator(test_COPY_MOVE_source)
++
++
+     def test_COPY_MOVE_dest(self):
+         """
+         Verify destination access controls during COPY and MOVE.
+@@ -180,6 +217,7 @@
+             src_path = os.path.join(self.docroot, "read")
+             uri = "/" + os.path.basename(src_path)
+ 
++            results = []
+             for method in ("COPY", "MOVE"):
+                 for name, code in (
+                     ("nobind" , responsecode.FORBIDDEN),
+@@ -198,18 +236,41 @@
+                             os.remove(dst_path)
+ 
+                         if response.code != code:
+-                            return self.oops(request, response, code, method, name)
++                            d = waitForDeferred(self.oops(request, response, code, method, name))
++                            yield d
++                            d = d.getResult()
++                            yield d
++                            return
+ 
+-                    yield (request, test)
++                    results.append((request, test))
+                     self.restore()
++                    # restore( ) blows away _docroot explicitly;
++                    # need to repopulate it
++                    d = waitForDeferred(self._getDocumentRoot())
++                    yield d
++                    d.getResult()
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_MOVE_dest = deferredGenerator(test_COPY_MOVE_dest)
++
+     def test_DELETE(self):
+         """
+         Verify access controls during DELETE.
+         """
+         def work():
++            results = []
+             for name, code in (
+                 ("nobind" , responsecode.FORBIDDEN),
+                 ("bind"   , responsecode.FORBIDDEN),
+@@ -225,12 +286,28 @@
+ 
+                 def test(response, code=code, path=path):
+                     if response.code != code:
+-                        return self.oops(request, response, code, "DELETE", name)
++                        d = waitForDeferred(self.oops(request, response, code, "DELETE", name))
++                        yield d
++                        d = d.getResult()
++                        yield d
++                        return
+ 
+-                yield (request, test)
++                results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def test_UNLOCK(self):
+         """
+         Verify access controls during UNLOCK of unowned lock.
+@@ -243,8 +320,10 @@
+         """
+         Verify access controls during MKCOL.
+         """
+-        for method in ("MKCOL", "PUT"):
+-            def work():
++        def work():
++            results = []
++
++            for method in ("MKCOL", "PUT"):
+                 for name, code in (
+                     ("nobind" , responsecode.FORBIDDEN),
+                     ("bind"   , responsecode.CREATED),
+@@ -263,17 +342,34 @@
+ 
+                     def test(response, code=code, path=path):
+                         if response.code != code:
+-                            return self.oops(request, response, code, method, name)
++                            d = waitForDeferred(self.oops(request, response, code, "DELETE", name))
++                            yield d
++                            d = d.getResult()
++                            yield d
++                            return
+ 
+-                    yield (request, test)
++                    results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def test_PUT_exists(self):
+         """
+         Verify access controls during PUT of existing file.
+         """
+         def work():
++            results = []
+             for name, code in (
+                 ("none"       , responsecode.FORBIDDEN),
+                 ("read"       , responsecode.FORBIDDEN),
+@@ -288,12 +384,28 @@
+ 
+                 def test(response, code=code, path=path):
+                     if response.code != code:
+-                        return self.oops(request, response, code, "PUT", name)
++                        d = waitForDeferred(self.oops(request, response, code, "PUT", name))
++                        yield d
++                        d = d.getResult()
++                        yield d
++                        return
+ 
+-                yield (request, test)
++                results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def test_PROPFIND(self):
+         """
+         Verify access controls during PROPFIND.
+@@ -307,6 +419,7 @@
+         Verify access controls during PROPPATCH.
+         """
+         def work():
++            results = []
+             for name, code in (
+                 ("none"       , responsecode.FORBIDDEN),
+                 ("read"       , responsecode.FORBIDDEN),
+@@ -324,17 +437,33 @@
+ 
+                 def test(response, code=code, path=path):
+                     if response.code != code:
+-                        return self.oops(request, response, code, "PROPPATCH", name)
++                        d = waitForDeferred(self.oops(request, response, code, "PROPPATCH", name))
++                        yield d
++                        d = d.getResult()
++                        yield d
++                        return
+ 
+-                yield (request, test)
++                results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
+     def test_GET_REPORT(self):
+         """
+         Verify access controls during GET and REPORT.
+         """
+         def work():
++            results = []
+             for method in ("GET", "REPORT"):
+                 if method == "GET":
+                     ok = responsecode.OK
+@@ -360,12 +489,27 @@
+ 
+                     def test(response, code=code, path=path):
+                         if response.code != code:
+-                            return self.oops(request, response, code, method, name)
++                            d = waitForDeferred(self.oops(request, response, code, method, name))
++                            yield d
++                            d = d.getResult()
++                            yield d
+ 
+-                    yield (request, test)
++                    results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def oops(self, request, response, code, method, name):
+         def gotResponseData(doc):
+             if doc is None:

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,106 @@
+Index: twisted/web2/dav/test/test_copy.py
+===================================================================
+--- twisted/web2/dav/test/test_copy.py	(revision 27622)
++++ twisted/web2/dav/test/test_copy.py	(working copy)
+@@ -26,6 +26,7 @@
+ import os
+ import urllib
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ import twisted.web2.dav.test.util
+ from twisted.web2 import responsecode
+ from twisted.web2.test.test_server import SimpleRequest
+@@ -78,8 +79,17 @@
+                 self.fail("Source %s is neither a file nor a directory"
+                           % (path,))
+ 
+-        return serialize(self.send, work(self, test))
++        d = waitForDeferred(work(self, test))
++        yield d
++        d = d.getResult( )
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_create = deferredGenerator(test_COPY_create)
++
+     def test_COPY_exists(self):
+         """
+         COPY to existing resource.
+@@ -92,8 +102,18 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, overwrite=False))
++        d = waitForDeferred(work(self, test, overwrite=False))
++        yield d
++        d = d.getResult( )
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_exists = deferredGenerator(test_COPY_exists)
++
++
+     def test_COPY_overwrite(self):
+         """
+         COPY to existing resource with overwrite header.
+@@ -108,8 +128,17 @@
+ 
+             self.failUnless(os.path.exists(dst_path), "COPY didn't produce file: %s" % (dst_path,))
+ 
+-        return serialize(self.send, work(self, test, overwrite=True))
++        d = waitForDeferred(work(self, test, overwrite=True))
++        yield d
++        d = d.getResult( )
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_overwrite = deferredGenerator(test_COPY_overwrite)
++
+     def test_COPY_no_parent(self):
+         """
+         COPY to resource with no parent.
+@@ -122,9 +151,19 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
++        d = waitForDeferred(work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
++        yield d
++        results = d.getResult()
+ 
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_no_parent = deferredGenerator(test_COPY_no_parent)
++
+ def work(self, test, overwrite=None, dst=None, depths=("0", "infinity", None)):
++    results = []
+     if dst is None:
+         dst = os.path.join(self.docroot, "dst")
+         os.mkdir(dst)
+@@ -158,8 +197,13 @@
+             if overwrite is not None:
+                 request.headers.setHeader("overwrite", overwrite)
+ 
+-            yield (request, do_test)
++            results.append((request, do_test))
+ 
++    yield results
++
++work = deferredGenerator(work)
++
++
+ def sumFile(path):
+     m = md5()
+ 

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,51 @@
+Index: twisted/web2/dav/test/test_delete.py
+===================================================================
+--- twisted/web2/dav/test/test_delete.py	(revision 27622)
++++ twisted/web2/dav/test/test_delete.py	(working copy)
+@@ -26,6 +26,7 @@
+ import urllib
+ import random
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.test.test_server import SimpleRequest
+@@ -54,6 +55,8 @@
+                 self.fail("DELETE did not remove path %s" % (path,))
+ 
+         def work():
++            results = []
++
+             for filename in os.listdir(self.docroot):
+                 path = os.path.join(self.docroot, filename)
+                 uri = urllib.quote("/" + filename)
+@@ -63,12 +66,26 @@
+                 def do_test(response, path=path):
+                     return check_result(response, path)
+ 
+-                request = SimpleRequest(self.site, "DELETE", uri)
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++                request = SimpleRequest(site, "DELETE", uri)
+ 
+                 depth = random.choice(("infinity", None))
+                 if depth is not None:
+                     request.headers.setHeader("depth", depth)
+ 
+-                yield (request, do_test)
++                results.append((request, do_test))
+ 
+-        return serialize(self.send, work())
++            yield results
++
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        d = d.getResult( )
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,46 @@
+Index: twisted/web2/dav/test/test_mkcol.py
+===================================================================
+--- twisted/web2/dav/test/test_mkcol.py	(revision 27622)
++++ twisted/web2/dav/test/test_mkcol.py	(working copy)
+@@ -24,6 +24,7 @@
+ 
+ import os
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import MemoryStream
+@@ -35,6 +36,7 @@
+     """
+     MKCOL request
+     """
++
+     # FIXME:
+     # Try in nonexistant parent collection.
+     # Try on existing resource.
+@@ -58,8 +60,13 @@
+ 
+         request = SimpleRequest(self.site, "MKCOL", uri)
+ 
+-        return self.send(request, check_result)
++        d = waitForDeferred(self.send(request, check_result))
++        yield d
++        d = d.getResult()
++        yield d
+ 
++    test_MKCOL = deferredGenerator(test_MKCOL)
++
+     def test_MKCOL_invalid_body(self):
+         """
+         MKCOL request with invalid request body
+@@ -82,4 +89,9 @@
+         request = SimpleRequest(self.site, "MKCOL", uri)
+         request.stream = MemoryStream("This is not a valid MKCOL request body.")
+ 
+-        return self.send(request, check_result)
++        d = waitForDeferred(self.send(request, check_result))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_MKCOL_invalid_body = deferredGenerator(test_MKCOL_invalid_body)

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,89 @@
+Index: twisted/web2/dav/test/test_move.py
+===================================================================
+--- twisted/web2/dav/test/test_move.py	(revision 27622)
++++ twisted/web2/dav/test/test_move.py	(working copy)
+@@ -24,6 +24,7 @@
+ 
+ import os
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ import twisted.web2.dav.test.util
+ import twisted.web2.dav.test.test_copy
+ from twisted.web2 import responsecode
+@@ -60,8 +61,18 @@
+                 if sum != sumFile(dst_path):
+                     self.fail("isdir %s produced different directory" % (uri,))
+ 
+-        return serialize(self.send, work(self, test))
+ 
++        d = waitForDeferred(work(self, test))
++        yield d
++        d = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_create = deferredGenerator(test_MOVE_create)
++
+     def test_MOVE_exists(self):
+         """
+         MOVE to existing resource.
+@@ -74,8 +85,17 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, overwrite=False))
++        d = waitForDeferred(work(self, test, overwrite=False))
++        yield d
++        d = d.getResult()
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_exists = deferredGenerator(test_MOVE_exists)
++
+     def test_MOVE_overwrite(self):
+         """
+         MOVE to existing resource with overwrite header.
+@@ -88,8 +108,17 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, overwrite=True))
++        d = waitForDeferred(work(self, test, overwrite=True))
++        yield d
++        d = d.getResult()
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_overwrite = deferredGenerator(test_MOVE_overwrite)
++
+     def test_MOVE_no_parent(self):
+         """
+         MOVE to resource with no parent.
+@@ -102,7 +131,17 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
+ 
++        d = waitForDeferred(work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
++        yield d
++        d = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_no_parent = deferredGenerator(test_MOVE_no_parent)
++
+ def work(self, test, overwrite=None, dst=None):
+     return twisted.web2.dav.test.test_copy.work(self, test, overwrite, dst, depths=(None,))

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,21 @@
+Index: twisted/web2/dav/test/test_options.py
+===================================================================
+--- twisted/web2/dav/test/test_options.py	(revision 27622)
++++ twisted/web2/dav/test/test_options.py	(working copy)
+@@ -31,6 +31,16 @@
+     """
+     OPTIONS request
+     """
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++        self._getSite()
++
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def test_DAV1(self):
+         """
+         DAV level 1

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,65 @@
+Index: twisted/web2/dav/test/test_prop.py
+===================================================================
+--- twisted/web2/dav/test/test_prop.py	(revision 27622)
++++ twisted/web2/dav/test/test_prop.py	(working copy)
+@@ -21,18 +21,15 @@
+ #
+ # DRI: Wilfredo Sanchez, wsanchez at apple.com
+ ##
+-from twisted.web2.dav.element.rfc4331 import QuotaUsedBytes
+-from twisted.web2.dav.element.rfc4331 import QuotaAvailableBytes
+ 
+-import random
++from twisted.internet.defer import inlineCallbacks
+ 
+-from twisted.trial.unittest import SkipTest
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import MemoryStream
+ from twisted.web2 import http_headers
+ from twisted.web2.dav import davxml
+-from twisted.web2.dav.resource import DAVResource
++
+ from twisted.web2.dav.davxml import dav_namespace, lookupElement
+ from twisted.web2.dav.util import davXMLFromStream
+ from twisted.web2.test.test_server import SimpleRequest
+@@ -60,6 +57,7 @@
+     def liveProperties(self):
+         return [lookupElement(qname)() for qname in self.resource_class.liveProperties if (qname[0] == dav_namespace) and qname not in dynamicLiveProperties]
+ 
++    @inlineCallbacks
+     def test_PROPFIND_basic(self):
+         """
+         PROPFIND request
+@@ -115,16 +113,17 @@
+ 
+         query = davxml.PropertyFind(davxml.PropertyContainer(*self.liveProperties()))
+ 
+-        request = SimpleRequest(self.site, "PROPFIND", "/")
++        for depth in ("0", "1", "infinity", None):
++            request = SimpleRequest(self.site, "PROPFIND", "/")
+ 
+-        depth = random.choice(("0", "1", "infinity", None))
+-        if depth is not None:
+-            request.headers.setHeader("depth", depth)
++            if depth is not None:
++                request.headers.setHeader("depth", depth)
+ 
+-        request.stream = MemoryStream(query.toxml())
++            request.stream = MemoryStream(query.toxml())
++            yield self.send(request, check_result)
+ 
+-        return self.send(request, check_result)
+ 
++
+     def test_PROPFIND_list(self):
+         """
+         PROPFIND with allprop, propname
+@@ -142,7 +141,6 @@
+ 
+         def check_xml(doc, which):
+             response = doc.root_element.childOfType(davxml.PropertyStatusResponse)
+-
+             self.failUnless(
+                 response.childOfType(davxml.HRef) == "/",
+                 "Incorrect response URI: %s != /" % (response.childOfType(davxml.HRef),)

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,169 @@
+Index: twisted/web2/dav/test/test_put.py
+===================================================================
+--- twisted/web2/dav/test/test_put.py	(revision 27622)
++++ twisted/web2/dav/test/test_put.py	(working copy)
+@@ -25,6 +25,7 @@
+ import os
+ import filecmp
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import FileStream
+@@ -44,25 +45,31 @@
+         """
+         dst_path = os.path.join(self.docroot, "dst")
+ 
+-        def checkResult(response, path):
+-            response = IResponse(response)
++        def makeClosure(path):
+ 
+-            if response.code not in (
+-                responsecode.CREATED,
+-                responsecode.NO_CONTENT
+-            ):
+-                self.fail("PUT failed: %s" % (response.code,))
++            # Return a function with 'path' closed
+ 
+-            if not os.path.isfile(dst_path):
+-                self.fail("PUT failed to create file %s." % (dst_path,))
++            def checkResult(response):
++                response = IResponse(response)
+ 
+-            if not filecmp.cmp(path, dst_path):
+-                self.fail("PUT failed to preserve data for file %s in file %s." % (path, dst_path))
++                if response.code not in (
++                    responsecode.CREATED,
++                    responsecode.NO_CONTENT
++                ):
++                    self.fail("PUT failed: %s" % (response.code,))
+ 
+-            etag = response.headers.getHeader("etag")
+-            if not etag:
+-                self.fail("No etag header in PUT response %r." % (response,))
++                if not os.path.isfile(dst_path):
++                    self.fail("PUT failed to create file %s." % (dst_path,))
+ 
++                if not filecmp.cmp(path, dst_path):
++
++                    self.fail("PUT failed to preserve data for file %s in file %s." % (path, dst_path))
++
++                etag = response.headers.getHeader("etag")
++                if not etag:
++                    self.fail("No etag header in PUT response %r." % (response,))
++            return checkResult
++
+         #
+         # We need to serialize these request & test iterations because they can
+         # interfere with each other.
+@@ -70,6 +77,7 @@
+         def work():
+             dst_uri = "/dst"
+ 
++            results = []
+             for name in os.listdir(self.docroot):
+                 if name == "dst":
+                     continue
+@@ -78,38 +86,53 @@
+ 
+                 # Can't really PUT something you can't read
+                 if not os.path.isfile(path): continue
+-    
+-                def do_test(response): checkResult(response, path)
+-    
++
+                 request = SimpleRequest(self.site, "PUT", dst_uri)
+                 request.stream = FileStream(file(path, "rb"))
+-    
+-                yield (request, do_test)
+ 
+-        return serialize(self.send, work())
++                results.append((request, makeClosure(path)))
+ 
++            yield results
++
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_PUT_simple = deferredGenerator(test_PUT_simple)
++
++
+     def test_PUT_again(self):
+         """
+         PUT on existing resource with If-None-Match header
+         """
++
+         dst_path = os.path.join(self.docroot, "dst")
+         dst_uri = "/dst"
+ 
+         def work():
++            results = []
+             for code in (
+                 responsecode.CREATED,
+                 responsecode.PRECONDITION_FAILED,
+                 responsecode.NO_CONTENT,
+                 responsecode.PRECONDITION_FAILED,
+                 responsecode.NO_CONTENT,
+-                responsecode.CREATED,
+             ):
+-                def checkResult(response, code=code):
+-                    response = IResponse(response)
++                def makeClosure(code):
++                    def checkResult(response, code=code):
++                        response = IResponse(response)
+ 
+-                    if response.code != code:
+-                        self.fail("Incorrect response code for PUT (%s != %s)"
+-                                  % (response.code, code))
++                        if response.code != code:
++                            self.fail("Incorrect response code for PUT (%s != %s)"
++                                      % (response.code, code))
++                    return checkResult
+ 
+                 def onError(f):
+                     f.trap(HTTPError)
+@@ -125,10 +148,23 @@
+                 elif code == responsecode.PRECONDITION_FAILED:
+                     request.headers.setHeader("if-none-match", ("*",))
+     
+-                yield (request, (checkResult, onError))
++                results.append((request, (makeClosure(code), onError)))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_PUT_again = deferredGenerator(test_PUT_again)
++
+     def test_PUT_no_parent(self):
+         """
+         PUT with no parent
+@@ -145,4 +181,9 @@
+         request = SimpleRequest(self.site, "PUT", dst_uri)
+         request.stream = FileStream(file(__file__, "rb"))
+ 
+-        return self.send(request, checkResult)
++        result = waitForDeferred(self.send(request, checkResult))
++        yield result
++        result = result.getResult()
++        yield result
++
++    test_PUT_no_parent = deferredGenerator(test_PUT_no_parent)

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,49 @@
+Index: twisted/web2/dav/test/test_quota.py
+===================================================================
+--- twisted/web2/dav/test/test_quota.py	(revision 27622)
++++ twisted/web2/dav/test/test_quota.py	(working copy)
+@@ -22,6 +22,7 @@
+ # DRI: Wilfredo Sanchez, wsanchez at apple.com
+ ##
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import FileStream
+@@ -34,16 +35,31 @@
+ 
+ class QuotaBase(twisted.web2.dav.test.util.TestCase):
+ 
+-    def createDocumentRoot(self):
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++
+         docroot = self.mktemp()
+         os.mkdir(docroot)
+         rootresource = self.resource_class(docroot)
+-        rootresource.setAccessControlList(self.grantInherit(davxml.All()))
+-        self.site = Site(rootresource)
+-        self.site.resource.setQuotaRoot(None, 100000)
+-        return docroot
++        d = waitForDeferred(rootresource.setAccessControlList(self.grantInherit(davxml.All())))
++        yield d
++        d = d.getResult()
+ 
++        self._site = Site(rootresource)
++        d = waitForDeferred(self._site.resource.setQuotaRoot(None, 100000))
++        yield d
++        d = d.getResult()
++        yield d
+ 
++    setUp = deferredGenerator(setUp)
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
++
+     def checkQuota(self, value):
+         def _defer(quota):
+             self.assertEqual(quota, value)

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,21 @@
+Index: twisted/web2/dav/test/test_report.py
+===================================================================
+--- twisted/web2/dav/test/test_report.py	(revision 27622)
++++ twisted/web2/dav/test/test_report.py	(working copy)
+@@ -34,6 +34,16 @@
+     """
+     REPORT request
+     """
++
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++        self._getSite()
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def test_REPORT_no_body(self):
+         """
+         REPORT request with no body

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,102 @@
+Index: twisted/web2/dav/test/test_resource.py
+===================================================================
+--- twisted/web2/dav/test/test_resource.py	(revision 27622)
++++ twisted/web2/dav/test/test_resource.py	(working copy)
+@@ -40,7 +40,13 @@
+     def setUp(self):
+         twisted.web2.dav.test.util.TestCase.setUp(self)
+         TestResource._cachedPropertyStores = {}
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        self._getSite()
+ 
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+ class GenericDAVResource(TestCase):
+     def setUp(self):
+         TestCase.setUp(self)
+@@ -61,7 +67,7 @@
+             })
+         })
+ 
+-        self.site = Site(rootresource)
++        self._site = Site(rootresource)
+ 
+     def test_findChildren(self):
+         """
+@@ -200,6 +206,7 @@
+         return d
+ 
+ class AccessTests(TestCase):
++    @deferredGenerator
+     def setUp(self):
+         TestCase.setUp(self)
+ 
+@@ -218,13 +225,15 @@
+         protected = TestResource(
+             "/protected", principalCollections=[rootresource])
+ 
+-        protected.setAccessControlList(davxml.ACL(
++        d = waitForDeferred(protected.setAccessControlList(davxml.ACL(
+             davxml.ACE(
+                 davxml.Principal(davxml.HRef("/users/gooduser")),
+                 davxml.Grant(davxml.Privilege(davxml.All())),
+                 davxml.Protected()
+             )
+-        ))
++        )))
++        yield d
++        d.getResult()
+ 
+         rootresource.children["protected"] = protected
+ 
+@@ -236,7 +245,7 @@
+         loginInterfaces = (IPrincipal,)
+ 
+         self.rootresource = rootresource
+-        self.site = Site(AuthenticationWrapper(
++        self._site = Site(AuthenticationWrapper(
+             self.rootresource,
+             portal,
+             credentialFactories,
+@@ -385,7 +394,7 @@
+         return self.children is not None
+ 
+     def listChildren(self):
+-        return self.children.keys()
++        return succeed(self.children.keys())
+ 
+     def supportedPrivileges(self, request):
+         return succeed(davPrivilegeSet)
+@@ -407,6 +416,7 @@
+ 
+     def setAccessControlList(self, acl):
+         self.acl = acl
++        return succeed(self.acl)
+ 
+     def accessControlList(self, request, **kwargs):
+         return succeed(self.acl)
+@@ -468,10 +478,10 @@
+             dictionary, which itself is a L{TestResource} with
+             L{IDAVPrincipalCollectionResource} children.
+ 
+-        @return: a DAV principal resource of the given type with the given
+-            name.
++        @return: a L{Deferred} firing with a DAV principal resource of the
++            given type with the given name.
+ 
+-        @rtype: L{IDAVPrincipalResource} or C{NoneType}
++        @rtype: L{Deferred} firing L{IDAVPrincipalResource} or C{NoneType}
+         """
+         # XXX either move this to CalendarServer entirely or document it on
+         # IDAVPrincipalCollectionResource
+@@ -480,7 +490,7 @@
+         if typeResource:
+             user = typeResource.children.get(shortName, None)
+ 
+-        return user
++        return succeed(user)
+ 
+ 
+ 

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,33 @@
+Index: twisted/web2/dav/test/test_static.py
+===================================================================
+--- twisted/web2/dav/test/test_static.py	(revision 27622)
++++ twisted/web2/dav/test/test_static.py	(working copy)
+@@ -28,6 +28,16 @@
+ from twisted.web2.test.test_server import SimpleRequest
+ 
+ class DAVFileTest(util.TestCase):
++
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        util.TestCase.setUp(self)
++        self._getSite()
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def test_renderPrivileges(self):
+         """
+         Verify that a directory listing includes children which you
+@@ -36,8 +46,9 @@
+         request = SimpleRequest(self.site, "GET", "/")
+ 
+         def setEmptyACL(resource):
+-            resource.setAccessControlList(davxml.ACL()) # Empty ACL = no access
+-            return resource
++            # Empty ACL = no access
++            return resource.setAccessControlList(davxml.ACL()).addCallback(
++                lambda nothing: resource)
+ 
+         def renderRoot(_):
+             d = request.locateResource("/")

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,170 @@
+Index: twisted/web2/dav/test/test_xattrprops.py
+===================================================================
+--- twisted/web2/dav/test/test_xattrprops.py	(revision 27622)
++++ twisted/web2/dav/test/test_xattrprops.py	(working copy)
+@@ -5,10 +5,13 @@
+ Tests for L{twisted.web2.dav.xattrprops}.
+ """
+ 
++import sys
+ from zlib import compress, decompress
+ from pickle import dumps
+ from cPickle import UnpicklingError
+ 
++from twisted.internet.defer import inlineCallbacks, returnValue
++from twisted.python import failure
+ from twisted.python.filepath import FilePath
+ from twisted.trial.unittest import TestCase
+ from twisted.web2.responsecode import NOT_FOUND, INTERNAL_SERVER_ERROR
+@@ -42,16 +45,18 @@
+         self.propertyStore = xattrPropertyStore(self.resource)
+ 
+ 
++    @inlineCallbacks
+     def test_getAbsent(self):
+         """
+         L{xattrPropertyStore.get} raises L{HTTPError} with a I{NOT FOUND}
+         response code if passed the name of an attribute for which there is no
+         corresponding value.
+         """
+-        error = self.assertRaises(HTTPError, self.propertyStore.get, ("foo", "bar"))
++        error = (yield self.assertRaisesDeferred(HTTPError, self.propertyStore.get, ("foo", "bar")))
+         self.assertEquals(error.response.code, NOT_FOUND)
+ 
+ 
++    @inlineCallbacks
+     def _forbiddenTest(self, method):
+         # Remove access to the directory containing the file so that getting
+         # extended attributes from it fails with EPERM.
+@@ -62,10 +67,10 @@
+ 
+         # Try to get a property from it - and fail.
+         document = self._makeValue()
+-        error = self.assertRaises(
++        error = (yield self.assertRaisesDeferred(
+             HTTPError,
+             getattr(self.propertyStore, method),
+-            document.root_element.qname())
++            document.root_element.qname()))
+ 
+         # Make sure that the status is FORBIDDEN, a roughly reasonable mapping
+         # of the EPERM failure.
+@@ -106,11 +111,12 @@
+         return self.attrs[attribute]
+ 
+ 
++    @inlineCallbacks
+     def _checkValue(self, originalDocument):
+         property = originalDocument.root_element.qname()
+ 
+         # Try to load it via xattrPropertyStore.get
+-        loadedDocument = self.propertyStore.get(property)
++        loadedDocument = (yield self.propertyStore.get(property))
+ 
+         # XXX Why isn't this a WebDAVDocument?
+         self.assertIsInstance(loadedDocument, Depth)
+@@ -178,6 +184,7 @@
+             decompress(self._getValue(document)), document.toxml())
+ 
+ 
++    @inlineCallbacks
+     def test_getInvalid(self):
+         """
+         If the value associated with the property name passed to
+@@ -190,7 +197,7 @@
+             "random garbage goes here! \0 that nul is definitely garbage")
+ 
+         property = document.root_element.qname()
+-        error = self.assertRaises(HTTPError, self.propertyStore.get, property)
++        error = (yield self.assertRaisesDeferred(HTTPError, self.propertyStore.get, property))
+         self.assertEquals(error.response.code, INTERNAL_SERVER_ERROR)
+         self.assertEquals(
+             len(self.flushLoggedErrors(UnpicklingError)), 1)
+@@ -227,6 +234,7 @@
+         self.assertRaises(KeyError, self._getValue, document)
+ 
+ 
++    @inlineCallbacks
+     def test_deleteErrors(self):
+         """
+         If there is a problem deleting the specified property (aside from the
+@@ -240,15 +248,16 @@
+ 
+         # Try to delete a property from it - and fail.
+         document = self._makeValue()
+-        error = self.assertRaises(
++        error = (yield self.assertRaisesDeferred(
+             HTTPError,
+-            self.propertyStore.delete, document.root_element.qname())
++            self.propertyStore.delete, document.root_element.qname()))
+ 
+         # Make sure that the status is NOT FOUND, a roughly reasonable mapping
+         # of the EEXIST failure.
+         self.assertEquals(error.response.code, NOT_FOUND)
+ 
+ 
++    @inlineCallbacks
+     def test_contains(self):
+         """
+         L{xattrPropertyStore.contains} returns C{True} if the given property
+@@ -256,10 +265,10 @@
+         """
+         document = self._makeValue()
+         self.assertFalse(
+-            self.propertyStore.contains(document.root_element.qname()))
++            (yield self.propertyStore.contains(document.root_element.qname())))
+         self._setValue(document, document.toxml())
+         self.assertTrue(
+-            self.propertyStore.contains(document.root_element.qname()))
++            (yield self.propertyStore.contains(document.root_element.qname())))
+ 
+ 
+     def test_containsError(self):
+@@ -272,6 +281,7 @@
+         self._forbiddenTest('contains')
+ 
+ 
++    @inlineCallbacks
+     def test_list(self):
+         """
+         L{xattrPropertyStore.list} returns a C{list} of property names
+@@ -281,10 +291,11 @@
+         self.attrs[prefix + '{foo}bar'] = 'baz'
+         self.attrs[prefix + '{bar}baz'] = 'quux'
+         self.assertEquals(
+-            set(self.propertyStore.list()),
++            set((yield self.propertyStore.list())),
+             set([(u'foo', u'bar'), (u'bar', u'baz')]))
+ 
+ 
++    @inlineCallbacks
+     def test_listError(self):
+         """
+         If there is a problem checking if the specified property exists (aside
+@@ -301,8 +312,25 @@
+ 
+         # Try to get a property from it - and fail.
+         document = self._makeValue()
+-        error = self.assertRaises(HTTPError, self.propertyStore.list)
++        error = (yield self.assertRaisesDeferred(HTTPError, self.propertyStore.list))
+ 
+         # Make sure that the status is FORBIDDEN, a roughly reasonable mapping
+         # of the EPERM failure.
+         self.assertEquals(error.response.code, FORBIDDEN)
++
++
++    @inlineCallbacks
++    def assertRaisesDeferred(self, exception, f, *args, **kwargs):
++        try:
++            result = (yield f(*args, **kwargs))
++        except exception, inst:
++            returnValue(inst)
++        except:
++            raise self.failureException('%s raised instead of %s:\n %s'
++                                        % (sys.exc_info()[0],
++                                           exception.__name__,
++                                           failure.Failure().getTraceback()))
++        else:
++            raise self.failureException('%s not raised (%r returned)'
++                                        % (exception.__name__, result))
++

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.util.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.util.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.test.util.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,114 @@
+Index: twisted/web2/dav/test/util.py
+===================================================================
+--- twisted/web2/dav/test/util.py	(revision 27622)
++++ twisted/web2/dav/test/util.py	(working copy)
+@@ -31,7 +31,7 @@
+ 
+ from twisted.python import log
+ from twisted.trial import unittest
+-from twisted.internet.defer import Deferred
++from twisted.internet.defer import Deferred, waitForDeferred, deferredGenerator, succeed
+ 
+ from twisted.web2.http import HTTPError, StatusResponse
+ from twisted.web2 import responsecode
+@@ -60,22 +60,24 @@
+             ))
+ 
+         doc = davxml.WebDAVDocument.fromString(property)
+-        return doc.root_element
++        return succeed(doc.root_element)
+ 
+     def set(self, property):
+         self._dict[property.qname()] = property.toxml()
++        return succeed(None)
+ 
+     def delete(self, qname):
+         try:
+             del(self._dict[qname])
+         except KeyError:
+             pass
++        return succeed(None)
+ 
+     def contains(self, qname):
+-        return qname in self._dict
++        return succeed(qname in self._dict)
+ 
+     def list(self):
+-        return self._dict.keys()
++        return succeed(self._dict.keys())
+ 
+ class TestFile (DAVFile):
+     _cachedPropertyStores = {}
+@@ -121,7 +123,9 @@
+         docroot = self.mktemp()
+         os.mkdir(docroot)
+         rootresource = self.resource_class(docroot)
+-        rootresource.setAccessControlList(self.grantInherit(davxml.All()))
++        d = waitForDeferred(rootresource.setAccessControlList(self.grantInherit(davxml.All())))
++        yield d
++        d = d.getResult()
+ 
+         dirnames = (
+             os.path.join(docroot, "dir1"),                          # 0
+@@ -149,37 +153,56 @@
+         for dirname in (docroot,) + dirnames[3:8+1]:
+             for filename in filenames[:5]:
+                 copy(filename, dirname)
+-        return docroot
++        yield docroot
+ 
++    createDocumentRoot = deferredGenerator(createDocumentRoot)
+ 
++
+     def _getDocumentRoot(self):
+         if not hasattr(self, "_docroot"):
+             log.msg("Setting up docroot for %s" % (self.__class__,))
+ 
+-            self._docroot = self.createDocumentRoot()
++            d = waitForDeferred(self.createDocumentRoot())
++            yield d
++            self._docroot = d.getResult()
+ 
++        yield self._docroot
++
++    _getDocumentRoot = deferredGenerator(_getDocumentRoot)
++
++    def _getStoredDocumentRoot(self):
+         return self._docroot
+ 
+     def _setDocumentRoot(self, value):
+         self._docroot = value
+ 
+-    docroot = property(_getDocumentRoot, _setDocumentRoot)
++    docroot = property(_getStoredDocumentRoot, _setDocumentRoot)
+ 
+     def _getSite(self):
+         if not hasattr(self, "_site"):
+             rootresource = self.resource_class(self.docroot)
+-            rootresource.setAccessControlList(self.grantInherit(davxml.All()))
++            d = waitForDeferred(rootresource.setAccessControlList(self.grantInherit(davxml.All())))
++            yield d
++            d.getResult()
+             self._site = Site(rootresource)
++        yield self._site
++    _getSite = deferredGenerator(_getSite)
++
++    def _getStoredSite(self):
+         return self._site
+ 
+     def _setSite(self, site):
+         self._site = site
+ 
+-    site = property(_getSite, _setSite)
++    site = property(_getStoredSite, _setSite)
+ 
+     def setUp(self):
+         unittest.TestCase.setUp(self)
+         TestFile._cachedPropertyStores = {}
++        d = self._getDocumentRoot()
++        d.addCallback(lambda _: self._getSite())
++        return d
++        # self._getSite()
+ 
+     def tearDown(self):
+         unittest.TestCase.tearDown(self)

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,120 @@
+Index: twisted/web2/dav/xattrprops.py
+===================================================================
+--- twisted/web2/dav/xattrprops.py	(revision 27622)
++++ twisted/web2/dav/xattrprops.py	(working copy)
+@@ -47,6 +47,8 @@
+ if getattr(xattr, 'xattr', None) is None:
+     raise ImportError("wrong xattr package imported")
+ 
++from twisted.internet.defer import succeed, fail
++
+ from twisted.python.util import untilConcludes
+ from twisted.python.failure import Failure
+ from twisted.python import log
+@@ -124,18 +126,18 @@
+         try:
+             data = self.attrs.get(self._encode(qname))
+         except KeyError:
+-            raise HTTPError(StatusResponse(
++            return fail(HTTPError(StatusResponse(
+                     responsecode.NOT_FOUND,
+-                    "No such property: {%s}%s" % qname))
++                    "No such property: {%s}%s" % qname)))
+         except IOError, e:
+             if e.errno in _ATTR_MISSING:
+-                raise HTTPError(StatusResponse(
++                return fail(HTTPError(StatusResponse(
+                         responsecode.NOT_FOUND,
+-                        "No such property: {%s}%s" % qname))
++                        "No such property: {%s}%s" % qname)))
+             else:
+-                raise HTTPError(StatusResponse(
++                return fail(HTTPError(StatusResponse(
+                         statusForFailure(Failure()),
+-                        "Unable to read property: {%s}%s" % qname))
++                        "Unable to read property: {%s}%s" % qname)))
+ 
+         #
+         # Unserialize XML data from an xattr.  The storage format has changed
+@@ -165,15 +167,15 @@
+                 format = "Invalid property value stored on server: {%s}%s %s"
+                 msg = format % (qname[0], qname[1], data)
+                 log.err(None, msg)
+-                raise HTTPError(
+-                    StatusResponse(responsecode.INTERNAL_SERVER_ERROR, msg))
++                return fail(HTTPError(
++                    StatusResponse(responsecode.INTERNAL_SERVER_ERROR, msg)))
+             else:
+                 legacy = True
+ 
+         if legacy:
+             self.set(doc.root_element)
+ 
+-        return doc.root_element
++        return succeed(doc.root_element)
+ 
+ 
+     def set(self, property):
+@@ -188,6 +190,7 @@
+ 
+         # Update the resource because we've modified it
+         self.resource.fp.restat()
++        return succeed(None)
+ 
+ 
+     def delete(self, qname):
+@@ -208,10 +211,11 @@
+                 if e.errno not in _ATTR_MISSING:
+                     raise
+         except:
+-            raise HTTPError(
++            return Failure(HTTPError(
+                 StatusResponse(
+                     statusForFailure(Failure()),
+-                    "Unable to delete property: " + key))
++                    "Unable to delete property: " + key)))
++        return succeed(None)
+ 
+ 
+     def contains(self, qname):
+@@ -228,16 +232,16 @@
+         try:
+             self.attrs.get(key)
+         except KeyError:
+-            return False
++            return succeed(False)
+         except IOError, e:
+             if e.errno in _ATTR_MISSING or e.errno == errno.ENOENT:
+-                return False
+-            raise HTTPError(
++                return succeed(False)
++            return Failure(HTTPError(
+                 StatusResponse(
+                     statusForFailure(Failure()),
+-                    "Unable to read property: " + key))
++                    "Unable to read property: " + key)))
+         else:
+-            return True
++            return succeed(True)
+ 
+ 
+     def list(self):
+@@ -252,13 +256,14 @@
+         try:
+             attrs = iter(self.attrs)
+         except IOError, e:
+-            raise HTTPError(
++            return Failure(HTTPError(
+                 StatusResponse(
+                     statusForFailure(Failure()),
+-                    "Unable to list properties: " + self.resource.fp.path))
++                    "Unable to list properties: " + self.resource.fp.path)))
+         else:
+-            return [
+-                self._decode(name)
++            return succeed(
++                [self._decode(name)
+                 for name
+                 in attrs
+                 if name.startswith(prefix)]
++            )

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.static.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.static.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.static.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,290 @@
+Index: twisted/web2/static.py
+===================================================================
+--- twisted/web2/static.py	(revision 27622)
++++ twisted/web2/static.py	(working copy)
+@@ -7,7 +7,7 @@
+ """
+ 
+ # System Imports
+-import os, time, stat
++import os, time
+ import tempfile
+ 
+ # Sibling Imports
+@@ -16,7 +16,7 @@
+ 
+ # Twisted Imports
+ from twisted.python import filepath
+-from twisted.internet.defer import maybeDeferred
++from twisted.internet.defer import maybeDeferred, succeed, deferredGenerator, waitForDeferred, returnValue, inlineCallbacks
+ from zope.interface import implements
+ 
+ class MetaDataMixin(object):
+@@ -28,7 +28,7 @@
+         """
+         @return: The current etag for the resource if available, None otherwise.
+         """
+-        return None
++        return succeed(None)
+ 
+     def lastModified(self):
+         """
+@@ -64,7 +64,7 @@
+         """
+         @return: The display name of the resource if available, None otherwise.
+         """
+-        return None
++        return succeed(None)
+ 
+     def exists(self):
+         """
+@@ -76,18 +76,26 @@
+     def checkPreconditions(self, request):
+         # This code replaces the code in resource.RenderMixin
+         if request.method not in ("GET", "HEAD"):
++            etag = waitForDeferred(self.etag())
++            yield etag
++            etag = etag.getResult()
+             http.checkPreconditions(
+                 request,
+                 entityExists = self.exists(),
+-                etag = self.etag(),
++                etag = etag,
+                 lastModified = self.lastModified(),
+             )
+ 
+         # Check per-method preconditions
+         method = getattr(self, "preconditions_" + request.method, None)
+         if method:
+-            return method(request)
++            result = waitForDeferred(method(request))
++            yield result
++            result = result.getResult()
++            yield result
+ 
++    checkPreconditions = deferredGenerator(checkPreconditions)
++
+     def renderHTTP(self, request):
+         """
+         See L{resource.RenderMixIn.renderHTTP}.
+@@ -100,16 +108,23 @@
+ 
+             # Don't provide additional resource information to error responses
+             if response.code < 400:
+-                # Content-* headers refer to the response content, not
+-                # (necessarily) to the resource content, so they depend on the
+-                # request method, and therefore can't be set here.
+-                for (header, value) in (
+-                    ("etag", self.etag()),
+-                    ("last-modified", self.lastModified()),
+-                ):
+-                    if value is not None:
+-                        response.headers.setHeader(header, value)
++                d = self.etag()
++                def _gotEtag(etag):
++                    # Content-* headers refer to the response content, not
++                    # (necessarily) to the resource content, so they depend on the
++                    # request method, and therefore can't be set here.
++                    for (header, value) in (
++                        ("etag", etag),
++                        ("last-modified", self.lastModified()),
++                    ):
++                        if value is not None:
++                            response.headers.setHeader(header, value)
+ 
++                    return response
++
++                d.addCallback(_gotEtag)
++                return d
++
+             return response
+ 
+         def onError(f):
+@@ -132,8 +147,8 @@
+ 
+     def etag(self):
+         lastModified = self.lastModified()
+-        return http_headers.ETag("%X-%X" % (lastModified, hash(self.data)),
+-                                 weak=(time.time() - lastModified <= 1))
++        return succeed(http_headers.ETag("%X-%X" % (lastModified, hash(self.data)),
++                                 weak=(time.time() - lastModified <= 1)))
+ 
+     def lastModified(self):
+         return self.creationDate()
+@@ -224,7 +239,7 @@
+         return self.fp.exists()
+ 
+     def etag(self):
+-        if not self.fp.exists(): return None
++        if not self.fp.exists(): return succeed(None)
+ 
+         st = self.fp.getstatinfo()
+ 
+@@ -235,10 +250,10 @@
+         #
+         weak = (time.time() - st.st_mtime <= 1)
+ 
+-        return http_headers.ETag(
++        return succeed(http_headers.ETag(
+             "%X-%X-%X" % (st.st_ino, st.st_size, st.st_mtime),
+             weak=weak
+-        )
++        ))
+ 
+     def lastModified(self):
+         if self.fp.exists():
+@@ -285,10 +300,11 @@
+         return self._encoding
+ 
+     def displayName(self):
++        self.fp.changed()       # XXX HACK
+         if self.fp.exists():
+-            return self.fp.basename()
++            return succeed(self.fp.basename())
+         else:
+-            return None
++            return succeed(None)
+ 
+     def ignoreExt(self, ext):
+         """Ignore the given extension.
+@@ -298,12 +314,13 @@
+         self.ignoredExts.append(ext)
+ 
+     def directoryListing(self):
+-        return dirlist.DirectoryLister(self.fp.path,
+-                                       self.listChildren(),
+-                                       self.contentTypes,
+-                                       self.contentEncodings,
+-                                       self.defaultType)
+-
++        return self.listChildren().addCallback(lambda children: dirlist.DirectoryLister(
++                self.fp.path,
++                children,
++                self.contentTypes,
++                self.contentEncodings,
++                self.defaultType))
++                                        
+     def putChild(self, name, child):
+         """
+         Register a child with the given name with this resource.
+@@ -321,7 +338,7 @@
+             return self
+ 
+         child = self.putChildren.get(name, None)
+-        if child: return child
++        if child: return succeed(child)
+ 
+         child_fp = self.fp.child(name)
+         if hasattr(self, "knownChildren"):
+@@ -330,7 +347,7 @@
+         if child_fp.exists():
+             return self.createSimilarFile(child_fp)
+         else:
+-            return None
++            return succeed(None)
+ 
+     def listChildren(self):
+         """
+@@ -340,21 +357,23 @@
+         if self.fp.isdir():
+             children += [c for c in self.fp.listdir() if c not in children]
+             self.knownChildren = set(children)
+-        return children
++        return succeed(children)
+ 
++    @inlineCallbacks
+     def locateChild(self, req, segments):
+         """
+         See L{IResource}C{.locateChild}.
+         """
+         # If getChild() finds a child resource, return it
+-        child = self.getChild(segments[0])
+-        if child is not None: return (child, segments[1:])
++        child = (yield self.getChild(segments[0]))
++        if child is not None: returnValue((child, segments[1:]))
+ 
+         # If we're not backed by a directory, we have no children.
+         # But check for existance first; we might be a collection resource
+         # that the request wants created.
+         self.fp.restat(False)
+-        if self.fp.exists() and not self.fp.isdir(): return (None, ())
++        if self.fp.exists() and not self.fp.isdir():
++            returnValue((None, ()))
+ 
+         # OK, we need to return a child corresponding to the first segment
+         path = segments[0]
+@@ -363,23 +382,23 @@
+             fpath = self.fp.child(path)
+         else:
+             # Request is for a directory (collection) resource
+-            return (self, server.StopTraversal)
++            returnValue((self, server.StopTraversal))
+ 
+         # Don't run processors on directories - if someone wants their own
+         # customized directory rendering, subclass File instead.
+         if fpath.isfile():
+             processor = self.processors.get(fpath.splitext()[1].lower())
+             if processor:
+-                return (
++                returnValue((
+                     processor(fpath.path),
+-                    segments[1:])
++                    segments[1:]))
+ 
+         elif not fpath.exists():
+             sibling_fpath = fpath.siblingExtensionSearch(*self.ignoredExts)
+             if sibling_fpath is not None:
+                 fpath = sibling_fpath
+ 
+-        return self.createSimilarFile(fpath.path), segments[1:]
++        returnValue((yield self.createSimilarFile(fpath.path)), segments[1:])
+ 
+     def renderHTTP(self, req):
+         self.fp.changed()
+@@ -398,17 +417,18 @@
+                 ifp = self.fp.childSearchPreauth(*self.indexNames)
+                 if ifp:
+                     # Render from the index file
+-                    standin = self.createSimilarFile(ifp.path)
++                    # MOR: What is going on here?
++                    self.createSimilarFile(ifp.path).render(req)
+                 else:
+                     # Render from a DirectoryLister
+-                    standin = dirlist.DirectoryLister(
+-                        self.fp.path,
+-                        self.listChildren(),
+-                        self.contentTypes,
+-                        self.contentEncodings,
+-                        self.defaultType
+-                    )
+-                return standin.render(req)
++                    return self.listChildren().addCallback(
++                         lambda children: dirlist.DirectoryLister(
++                          self.fp.path,
++                          children,
++                          self.contentTypes,
++                          self.contentEncodings,
++                          self.defaultType
++                         ).render(req))
+ 
+         try:
+             f = self.fp.open()
+@@ -434,8 +454,8 @@
+         return response
+ 
+     def createSimilarFile(self, path):
+-        return self.__class__(path, self.defaultType, self.ignoredExts,
+-                              self.processors, self.indexNames[:])
++        return succeed(self.__class__(path, self.defaultType, self.ignoredExts,
++                              self.processors, self.indexNames[:]))
+ 
+ 
+ class FileSaver(resource.PostableResource):
+@@ -604,7 +624,6 @@
+ if __name__ == '__builtin__':
+     # Running from twistd -y
+     from twisted.application import service, strports
+-    from twisted.web2 import server
+     res = File('/')
+     application = service.Application("demo")
+     s = strports.service('8080', server.Site(res))

Added: CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.test.test_static.patch
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.test.test_static.patch	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/lib-patches/Twisted/twisted.web2.test.test_static.patch	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,17 @@
+Index: twisted/web2/test/test_static.py
+===================================================================
+--- twisted/web2/test/test_static.py	(revision 27622)
++++ twisted/web2/test/test_static.py	(working copy)
+@@ -34,7 +34,11 @@
+         """
+         Test that we can get an ETag
+         """
+-        self.failUnless(self.data.etag())
++        d = self.data.etag()
++        def _callback(etag):
++            self.failUnless(etag)
++        d.addCallback(_callback)
++        return d
+ 
+ 
+     def test_render(self):

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/setup.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/setup.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/setup.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-
+# -*- test-case-name: twistedcaldav,twext,calendarserver -*-
 ##
 # Copyright (c) 2006-2009 Apple Inc. All rights reserved.
 #

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twext/__init__.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twext/__init__.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twext/__init__.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,3 +1,4 @@
+# -*- test-case-name: twext -*-
 ##
 # Copyright (c) 2005-2009 Apple Inc. All rights reserved.
 #

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/__init__.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/__init__.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/__init__.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,3 +1,4 @@
+# -*- test-case-name: twistedcaldav -*-
 ##
 # Copyright (c) 2005-2007 Apple Inc. All rights reserved.
 #

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/accesslog.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/accesslog.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/accesslog.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -79,6 +79,11 @@
                         uidz = str(request.authzUser.children[0])
                         
                     def convertUIDtoShortName(uid):
+                        # MOR: recordWithUID is now deferred -- not sure what to do here, so shortcircuiting for the moment:
+                        return uid
+                        # Cyrus suggests adding the records as attributes of
+                        # the request objects when authn/authz are added
+
                         uid = uid.rstrip("/")
                         uid = uid[uid.rfind("/") + 1:]
                         record = request.site.resource.getDirectory().recordWithUID(uid)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/caldavxml.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/caldavxml.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -497,7 +497,7 @@
         @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day.
         @return: an L{CalendarData} with the (filtered) calendar data.
         """
-        return self.elementFromCalendar(resource.iCalendarText(), timezone)
+        return resource.iCalendarText().addCallback(self.elementFromCalendar, timezone)
 
     def elementFromCalendar(self, calendar, timezone=None):
         """
@@ -526,7 +526,7 @@
         @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day.
         @return: an L{CalendarData} with the (filtered) calendar data.
         """
-        return self.elementFromCalendarWithAccessRestrictions(resource.iCalendarText(), access, timezone)
+        return resource.iCalendarText().addCallback(self.elementFromCalendarWithAccessRestrictions, access, timezone)
 
     def elementFromCalendarWithAccessRestrictions(self, calendar, access, timezone=None):
         """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/__init__.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/__init__.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/__init__.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,3 +1,4 @@
+# -*- test-case-name: twistedcaldav.directory.test -*-
 ##
 # Copyright (c) 2006-2007 Apple Inc. All rights reserved.
 #

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/aggregate.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/aggregate.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -30,7 +30,7 @@
 from twistedcaldav.directory.idirectory import IDirectoryService
 from twistedcaldav.directory.directory import DirectoryService, DirectoryError
 from twistedcaldav.directory.directory import UnknownRecordTypeError
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 class AggregateDirectoryService(DirectoryService):
     """
@@ -89,22 +89,27 @@
     def recordTypes(self):
         return set(self._recordTypes)
 
+    @inlineCallbacks
     def listRecords(self, recordType):
-        records = self._query("listRecords", recordType)
+        records = (yield self._query("listRecords", recordType))
         if records is None:
-            return ()
+            returnValue( () )
         else:
-            return records
+            returnValue(records)
 
+    # Deferred
     def recordWithShortName(self, recordType, shortName):
         return self._query("recordWithShortName", recordType, shortName)
 
+    # Deferred
     def recordWithUID(self, uid):
         return self._queryAll("recordWithUID", uid)
 
+    # Deferred
     def recordWithAuthID(self, authID):
         return self._queryAll("recordWithAuthID", authID)
 
+    # Deferred
     def recordWithCalendarUserAddress(self, address):
         return self._queryAll("recordWithCalendarUserAddress", address)
 
@@ -132,24 +137,27 @@
         except KeyError:
             raise UnknownRecordTypeError(recordType)
 
+    # Deferred
     def _query(self, query, recordType, *args):
         try:
             service = self.serviceForRecordType(recordType)
         except UnknownRecordTypeError:
-            return None
+            return succeed(None)
 
+        # query is deferred
         return getattr(service, query)(
             recordType[len(service.recordTypePrefix):],
             *[a[len(service.recordTypePrefix):] for a in args]
         )
 
+    @inlineCallbacks
     def _queryAll(self, query, *args):
         for service in self._recordTypes.values():
-            record = getattr(service, query)(*args)
+            record = (yield getattr(service, query)(*args))
             if record is not None:
-                return record
+                returnValue(record)
         else:
-            return None
+            returnValue(None)
 
     userRecordTypes = [DirectoryService.recordType_users]
 
@@ -160,13 +168,14 @@
         
         raise UnauthorizedLogin("No such user: %s" % (credentials.credentials.username,))
 
+    @inlineCallbacks
     def getResourceInfo(self):
         results = []
         for service in self._recordTypes.values():
-            for result in service.getResourceInfo():
+            for result in (yield service.getResourceInfo()):
                 if result:
                     results.append(result)
-        return results
+        returnValue(results)
 
 class DuplicateRecordTypeError(DirectoryError):
     """

Deleted: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/apache.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/apache.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/apache.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,220 +0,0 @@
-##
-# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-Apache UserFile/GroupFile compatible directory service implementation.
-"""
-
-__all__ = [
-    "BasicDirectoryService",
-    "DigestDirectoryService",
-]
-
-from crypt import crypt
-
-from twisted.python.filepath import FilePath
-from twisted.cred.credentials import UsernamePassword
-
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
-from twistedcaldav.directory.directory import UnknownRecordTypeError, DirectoryConfigurationError
-
-class AbstractDirectoryService(DirectoryService):
-    """
-    Abstract Apache-compatible implementation of L{IDirectoryService}.
-    """
-    def __repr__(self):
-        return "<%s %r: %r %r>" % (self.__class__.__name__, self.realmName, self.userFile, self.groupFile)
-
-    def __init__(self, params):
-        defaults = {
-            'realmName' : '',
-            'userFile' : None,
-            'groupFile' : None,
-        }
-        ignored = None
-        params = self.getParams(params, defaults, ignored)
-
-        super(AbstractDirectoryService, self).__init__()
-
-        userFile = params["userFile"]
-        if not userFile:
-            raise DirectoryConfigurationError("Invalid Apache user file name: %r" % (userFile,))
-
-        if userFile and type(userFile) is str:
-            userFile = FilePath(userFile)
-
-        groupFile = params["groupFile"]
-        if groupFile and type(groupFile) is str:
-            groupFile = FilePath(groupFile)
-
-        self.realmName = params["realmName"]
-        self.userFile = userFile
-        self.groupFile = groupFile
-
-    def recordTypes(self):
-        recordTypes = (DirectoryService.recordType_users,)
-        if self.groupFile is not None:
-            recordTypes += (DirectoryService.recordType_groups,)
-        return recordTypes
-
-    def listRecords(self, recordType):
-        for entryShortName, entryData in self.entriesForRecordType(recordType):
-            if recordType == DirectoryService.recordType_users:
-                yield self.userRecordClass(
-                    service       = self,
-                    recordType    = recordType,
-                    shortName     = entryShortName,
-                    cryptPassword = entryData,
-                )
-
-            elif recordType == DirectoryService.recordType_groups:
-                yield GroupRecord(
-                    service    = self,
-                    recordType = recordType,
-                    shortName  = entryShortName,
-                    members    = entryData,
-                )
-
-            else:
-                # Subclass should cover the remaining record types
-                raise UnknownRecordTypeError("Unknown record type: %s" % (recordType,))
-
-    def recordWithShortName(self, recordType, shortName):
-        for entryShortName, entryData in self.entriesForRecordType(recordType):
-            if entryShortName == shortName:
-                if recordType == DirectoryService.recordType_users:
-                    return self.userRecordClass(
-                        service       = self,
-                        recordType    = recordType,
-                        shortName     = entryShortName,
-                        cryptPassword = entryData,
-                    )
-
-                if recordType == DirectoryService.recordType_groups:
-                    return GroupRecord(
-                        service    = self,
-                        recordType = recordType,
-                        shortName  = entryShortName,
-                        members    = entryData,
-                    )
-
-                # Subclass should cover the remaining record types
-                raise UnknownRecordTypeError("Unknown record type: %s" % (recordType,))
-
-        return None
-
-    def entriesForRecordType(self, recordType):
-        if recordType == DirectoryService.recordType_users:
-            recordFile = self.userFile
-        elif recordType == DirectoryService.recordType_groups:
-            recordFile = self.groupFile
-        else:
-            raise UnknownRecordTypeError("Unknown record type: %s" % (recordType,))
-
-        if recordFile is None:
-            return
-
-        try:
-            handle = recordFile.open()
-        except IOError, OSError:
-            self.log_error("Auth file (for %s) not found: %s" % (recordType, recordFile.path))
-            return
-
-        try:
-            for entry in handle:
-                if entry and entry[0] != "#":
-                    try:
-                        shortName, rest = entry.rstrip("\n").split(":", 1)
-                    except ValueError:
-                        continue
-                    yield shortName, rest
-        finally:
-            handle.close()
-
-class AbstractDirectoryRecord(DirectoryRecord):
-    """
-    Abstract Apache-compatible implementation of L{IDirectoryRecord}.
-    """
-    def __init__(self, service, recordType, shortName):
-        super(AbstractDirectoryRecord, self).__init__(
-            service               = service,
-            recordType            = recordType,
-            guid                  = None,
-            shortNames            = (shortName,),
-        )
-
-class AbstractUserRecord(AbstractDirectoryRecord):
-    def __init__(self, service, recordType, shortName, cryptPassword=None):
-        super(AbstractUserRecord, self).__init__(service, recordType, shortName)
-
-        self._cryptPassword = cryptPassword
-
-    def groups(self):
-        for group in self.service.listRecords(DirectoryService.recordType_groups):
-            for member in group.members():
-                if member == self:
-                    yield group
-                    continue
-
-class BasicUserRecord(AbstractUserRecord):
-    """
-    Apache UserFile implementation of L{IDirectoryRecord}.
-    """
-    def verifyCredentials(self, credentials):
-        if self._cryptPassword in ("", "*", "x"):
-            return False
-
-        if isinstance(credentials, UsernamePassword):
-            return crypt(credentials.password, self._cryptPassword) == self._cryptPassword
-
-        return super(BasicUserRecord, self).verifyCredentials(credentials)
-
-class BasicDirectoryService(AbstractDirectoryService):
-    """
-    Apache UserFile/GroupFile implementation of L{IDirectoryService}.
-    """
-    baseGUID = "DDF1E45C-CADE-4FCD-8AE6-B4B41D72B325"
-    userRecordClass = BasicUserRecord
-
-class DigestUserRecord(AbstractUserRecord):
-    """
-    Apache DigestUserFile implementation of L{IDirectoryRecord}.
-    """
-    def verifyCredentials(self, credentials):
-        raise NotImplementedError()
-
-class DigestDirectoryService(AbstractDirectoryService):
-    """
-    Apache DigestUserFile/GroupFile implementation of L{IDirectoryService}.
-    """
-    baseGUID = "0C719D1B-0A14-4074-8740-6D96A7D0C787"
-    userRecordClass = DigestUserRecord
-
-class GroupRecord(AbstractDirectoryRecord):
-    """
-    Apache GroupFile implementation of L{IDirectoryRecord}.
-    """
-    def __init__(self, service, recordType, shortName, members=()):
-        super(GroupRecord, self).__init__(service, recordType, shortName)
-
-        if type(members) is str:
-            members = tuple(m.strip() for m in members.split(","))
-
-        self._members = members
-
-    def members(self):
-        for shortName in self._members:
-            yield self.service.recordWithShortName(DirectoryService.recordType_users, shortName)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/appleopendirectory.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/appleopendirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -35,6 +35,7 @@
 import dsattributes
 import dsquery
 
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.internet.threads import deferToThread
 from twisted.cred.credentials import UsernamePassword
 from twisted.web2.auth.digest import DigestedCredentials
@@ -120,8 +121,12 @@
             h = (h + hash(getattr(self, attr))) & sys.maxint
         return h
 
-    def _expandGroupMembership(self, members, nestedGroups, processedGUIDs=None, returnGroups=False):
+    @inlineCallbacks
+    def _expandGroupMembership(self, members, nestedGroups, processedGUIDs=None, results=None, returnGroups=False):
 
+        if results is None:
+            results = set()
+
         if processedGUIDs is None:
             processedGUIDs = set()
 
@@ -134,7 +139,7 @@
         for memberGUID in members:
             if memberGUID not in processedGUIDs:
                 processedGUIDs.add(memberGUID)
-                yield memberGUID
+                results.add(memberGUID)
 
         for groupGUID in nestedGroups:
             if groupGUID in processedGUIDs:
@@ -149,7 +154,9 @@
                 dsattributes.kDSStdRecordTypeGroups,
                 [dsattributes.kDSNAttrGroupMembers, dsattributes.kDSNAttrNestedGroups]
             ))
-            result = opendirectory.queryRecordsWithAttribute_list(
+            # MOR: Doublecheck this
+            result = (yield deferToThread(
+                opendirectory.queryRecordsWithAttribute_list,
                 self.directory,
                 dsattributes.kDS1AttrGeneratedUID,
                 groupGUID,
@@ -157,7 +164,7 @@
                 False,
                 dsattributes.kDSStdRecordTypeGroups,
                 [dsattributes.kDSNAttrGroupMembers, dsattributes.kDSNAttrNestedGroups]
-            )
+            ))
 
             if not result:
                 self.log_error("Couldn't find group %s when trying to expand nested groups."
@@ -168,16 +175,38 @@
 
             processedGUIDs.add(groupGUID)
             if returnGroups:
-                yield groupGUID
+                results.add(groupGUID)
 
-            for GUID in self._expandGroupMembership(
+            yield self._expandGroupMembership(
                 group.get(dsattributes.kDSNAttrGroupMembers, []),
                 group.get(dsattributes.kDSNAttrNestedGroups, []),
                 processedGUIDs,
+                results,
                 returnGroups,
-            ):
-                yield GUID
+            )
 
+        returnValue(results)
+
+    def _calendarUserAddresses(self, recordType, recordData):
+        """
+        Extract specific attributes from the directory record for use as calendar user address.
+        
+        @param recordData: a C{dict} containing the attributes retrieved from the directory.
+        @return: a C{set} of C{str} for each expanded calendar user address.
+        """
+        # Now get the addresses
+        result = set()
+        
+        # Add each email address as a mailto URI
+        emails = recordData.get(dsattributes.kDSNAttrEMailAddress)
+        if emails is not None:
+            if isinstance(emails, str):
+                emails = [emails]
+            for email in emails:
+                result.add("mailto:%s" % (email.lower(),))
+                
+        return result
+
     def recordTypes(self):
         return (
             self.recordType_users,
@@ -186,6 +215,7 @@
             self.recordType_resources,
         )
 
+    @inlineCallbacks
     def groupsForGUID(self, guid):
         
         attrs = [
@@ -207,7 +237,8 @@
                 recordType,
                 attrs,
             ))
-            results = opendirectory.queryRecordsWithAttribute_list(
+            results = (yield deferToThread(
+                opendirectory.queryRecordsWithAttribute_list,
                 self.directory,
                 query.attribute,
                 query.value,
@@ -215,7 +246,7 @@
                 False,
                 recordType,
                 attrs,
-            )
+            ))
         except opendirectory.ODError, ex:
             self.log_error("OpenDirectory (node=%s) error: %s" % (self.realmName, str(ex)))
             raise
@@ -238,7 +269,8 @@
                 recordType,
                 attrs,
             ))
-            results = opendirectory.queryRecordsWithAttribute_list(
+            results = (yield deferToThread(
+                opendirectory.queryRecordsWithAttribute_list,
                 self.directory,
                 query.attribute,
                 query.value,
@@ -246,7 +278,7 @@
                 False,
                 recordType,
                 attrs,
-            )
+            ))
         except opendirectory.ODError, ex:
             self.log_error("OpenDirectory (node=%s) error: %s" % (self.realmName, str(ex)))
             raise
@@ -258,7 +290,7 @@
             if recordGUID:
                 guids.add(recordGUID)
 
-        return guids
+        returnValue(guids)
 
     def proxiesForGUID(self, recordType, guid):
         
@@ -341,24 +373,13 @@
     _fromODRecordTypes = dict([(b, a) for a, b in _toODRecordTypes.iteritems()])
 
 
+    @inlineCallbacks
     def recordsMatchingFields(self, fields, operand="or", recordType=None):
 
         # Note that OD applies case-sensitivity globally across the entire
         # query, not per expression, so the current code uses whatever is
         # specified in the last field in the fields list
 
-        def collectResults(results):
-            self.log_info("Got back %d records from OD" % (len(results),))
-            for key, val in results:
-                self.log_debug("OD result: %s %s" % (key, val))
-                try:
-                    guid = val[dsattributes.kDS1AttrGeneratedUID]
-                    record = self.recordWithGUID(guid)
-                    if record:
-                        yield record
-                except KeyError:
-                    pass
-
         def multiQuery(directory, queries, attrs, operand):
             results = []
 
@@ -406,17 +427,30 @@
 
         queries = buildQueries(recordTypes, fields, self._ODFields)
 
-        deferred = deferToThread(
+        odResults = (yield deferToThread(
             multiQuery,
             self.directory,
             queries,
             [ dsattributes.kDS1AttrGeneratedUID ],
             operand
-        )
-        deferred.addCallback(collectResults)
-        return deferred
+        ))
 
+        results = []
+        self.log_info("Got back %d records from OD" % (len(results),))
+        for key, val in odResults.iteritems():
+            self.log_debug("OD result: %s %s" % (key, val))
+            try:
+                guid = val[dsattributes.kDS1AttrGeneratedUID]
+                record = (yield self.recordWithGUID(guid))
+                if record:
+                    results.append(record)
+            except KeyError:
+                pass
 
+        returnValue(results)
+
+
+    @inlineCallbacks
     def queryDirectory(self, recordTypes, indexType, indexKey,
         lookupMethod=opendirectory.queryRecordsWithAttribute_list):
         
@@ -490,7 +524,8 @@
                 listRecordTypes,
                 attrs,
             ))
-            results = lookupMethod(
+            results = (yield deferToThread(
+                lookupMethod,
                 self.directory,
                 query.attribute,
                 query.value,
@@ -498,13 +533,13 @@
                 False,
                 listRecordTypes,
                 attrs,
-            )
+            ))
             self.log_debug("opendirectory.queryRecordsWithAttribute_list matched records: %s" % (len(results),))
 
         except opendirectory.ODError, ex:
             if ex.message[1] == -14140 or ex.message[1] == -14200:
                 # Unsupported attribute on record - don't fail
-                return
+                returnValue(None)
             else:
                 self.log_error("OpenDirectory (node=%s) error: %s" % (self.realmName, str(ex)))
                 raise
@@ -589,7 +624,8 @@
                             dsattributes.kDSStdRecordTypeGroups,
                             [dsattributes.kDSNAttrGroupMembers, dsattributes.kDSNAttrNestedGroups,],
                         ))
-                        results = lookupMethod(
+                        results = (yield deferToThread(
+                            lookupMethod,
                             self.directory,
                             attributeToMatch,
                             valueToMatch,
@@ -597,7 +633,7 @@
                             False,
                             dsattributes.kDSStdRecordTypeGroups,
                             [dsattributes.kDSNAttrGroupMembers, dsattributes.kDSNAttrNestedGroups,],
-                        )
+                        ))
 
                         if len(results) == 1:
                             members = results[0][1].get(dsattributes.kDSNAttrGroupMembers, [])
@@ -605,7 +641,7 @@
                         else:
                             members = []
                             nestedGroups = []
-                        self.restrictedGUIDs = set(self._expandGroupMembership(members, nestedGroups, returnGroups=True))
+                        self.restrictedGUIDs = (yield self._expandGroupMembership(members, nestedGroups, returnGroups=True))
                         self.log_debug("Got %d restricted group members" % (len(self.restrictedGUIDs),))
                         self.restrictedTimestamp = time.time()
 
@@ -683,9 +719,9 @@
             # Fetch the set of groups this record is a member of so we can
             # cache it, rather than have each process make the same group
             # lookup
-            record._groupMembershipGUIDs = self.groupsForGUID(record.guid)
+            record._groupMembershipGUIDs = (yield self.groupsForGUID(record.guid))
 
-            self.recordCacheForType(recordType).addRecord(record, indexType, origIndexKey)
+            yield self.recordCacheForType(recordType).addRecord(record, indexType, origIndexKey)
 
     def _parseResourceInfo(self, plist, guid, recordType, shortname):
         """
@@ -714,6 +750,7 @@
 
         return (autoaccept, proxy, read_only_proxy,)
 
+    @inlineCallbacks
     def getResourceInfo(self):
         """
         Resource information including proxy assignments for resource and
@@ -725,6 +762,7 @@
             dsattributes.kDS1AttrGeneratedUID,
             dsattributes.kDSNAttrResourceInfo,
         ]
+        results = []
 
         for recordType in (dsattributes.kDSStdRecordTypePlaces, dsattributes.kDSStdRecordTypeResources):
             try:
@@ -733,11 +771,12 @@
                     recordType,
                     attrs,
                 ))
-                results = opendirectory.listAllRecordsWithAttributes_list(
+                results = (yield deferToThread(
+                    opendirectory.listAllRecordsWithAttributes_list,
                     self.directory,
                     recordType,
                     attrs,
-                )
+                ))
             except opendirectory.ODError, ex:
                 self.log_error("OpenDirectory (node=%s) error: %s" % (self.realmName, str(ex)))
                 raise
@@ -751,7 +790,8 @@
                             recordGUID, recordType, recordShortName)
                     except ValueError:
                         continue
-                    yield recordGUID, autoSchedule, proxy, readOnlyProxy
+                    results.append((recordGUID, autoSchedule, proxy, readOnlyProxy))
+        returnValue(results)
 
 
     def isAvailable(self):
@@ -841,44 +881,56 @@
             self.fullName
         )
 
+    @inlineCallbacks
     def members(self):
         if self.recordType != self.service.recordType_groups:
-            return
+            returnValue(None)
 
+        results = []
         for guid in self._memberGUIDs:
-            userRecord = self.service.recordWithGUID(guid)
+            userRecord = (yield self.service.recordWithGUID(guid))
             if userRecord is not None:
-                yield userRecord
+                results.append(userRecord)
+        returnValue(results)
 
+    @inlineCallbacks
     def groups(self):
         if self._groupMembershipGUIDs is None:
-            self._groupMembershipGUIDs = self.service.groupsForGUID(self.guid)
+            self._groupMembershipGUIDs = (yield self.service.groupsForGUID(self.guid))
 
+        results = []
         for guid in self._groupMembershipGUIDs:
-            record = self.service.recordWithGUID(guid)
+            record = (yield self.service.recordWithGUID(guid))
             if record:
-                yield record
+                results.append(record)
+        returnValue(results)
 
+    @inlineCallbacks
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             # Check cached password
             try:
                 if credentials.password == self.password:
-                    return True
+                    returnValue(True)
             except AttributeError:
                 pass
 
             # Check with directory services
             try:
-                if opendirectory.authenticateUserBasic(self.service.directory, self.nodeName, self.shortNames[0], credentials.password):
+                if (yield deferToThread(
+                    opendirectory.authenticateUserBasic,
+                    self.service.directory,
+                    self.nodeName,
+                    self.shortNames[0],
+                    credentials.password)):
                     # Cache the password to avoid future DS queries
                     self.password = credentials.password
-                    return True
+                    returnValue(True)
             except opendirectory.ODError, e:
                 self.log_error("OpenDirectory (node=%s) error while performing basic authentication for user %s: %s"
                             % (self.service.realmName, self.shortNames[0], e))
 
-            return False
+            returnValue(False)
 
         elif isinstance(credentials, DigestedCredentials):
             #
@@ -903,23 +955,24 @@
                     "missing digest response field: %s in: %s"
                     % (self.service.realmName, self.shortNames[0], e, credentials.fields)
                 )
-                return False
+                returnValue(False)
 
             try:
                 if self.digestcache[credentials.fields["uri"]] == response:
-                    return True
+                    returnValue(True)
             except (AttributeError, KeyError):
                 pass
 
             try:
-                if opendirectory.authenticateUserDigest(
+                if (yield deferToThread(
+                    opendirectory.authenticateUserDigest,
                     self.service.directory,
                     self.nodeName,
                     self.shortNames[0],
                     challenge,
                     response,
                     credentials.originalMethod if credentials.originalMethod else credentials.method
-                ):
+                )):
                     try:
                         cache = self.digestcache
                     except AttributeError:
@@ -927,7 +980,7 @@
 
                     cache[credentials.fields["uri"]] = response
 
-                    return True
+                    returnValue(True)
                 else:
                     self.log_debug(
 """OpenDirectory digest authentication failed with:
@@ -943,11 +996,11 @@
                     "OpenDirectory (node=%s) error while performing digest authentication for user %s: %s"
                     % (self.service.realmName, self.shortNames[0], e)
                 )
-                return False
+                returnValue(False)
 
-            return False
+            returnValue(False)
 
-        return super(OpenDirectoryRecord, self).verifyCredentials(credentials)
+        returnValue((yield super(OpenDirectoryRecord, self).verifyCredentials(credentials)))
 
 class OpenDirectoryInitError(DirectoryError):
     """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/cachingdirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/cachingdirectory.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/cachingdirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -35,6 +35,7 @@
 from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError, UnknownRecordTypeError
 from twistedcaldav.log import LoggingMixIn
 from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 
 class RecordTypeCache(object):
@@ -73,6 +74,8 @@
             CachingDirectoryService.INDEX_TYPE_AUTHID   : {},
         }
 
+    # MOR: Defer this
+    @inlineCallbacks
     def addRecord(self, record, indexType, indexKey, useMemcache=True,
         neverExpire=False):
 
@@ -94,12 +97,15 @@
                 key = "dir|%s|%s" % (indexType, indexKey)
                 self.log_debug("Memcache: storing %s" % (key,))
                 try:
-                    self.directoryService.memcacheSet(key, record)
+                    yield self.directoryService.memcacheSet(key, record)
                 except DirectoryMemcacheError:
                     self.log_error("Memcache: failed to store %s" % (key,))
                     pass
+                
+        returnValue(None)
 
 
+    # MOR: Defer this
     def removeRecord(self, record):
         if record in self.records:
             self.records.remove(record)
@@ -115,6 +121,8 @@
                         del self.recordsIndexedBy[indexType][item]
                     except KeyError:
                         self.log_debug("Missing record index item; type: %s, item: %s" % (indexType, item))
+
+        return succeed(None) # MOR: Remove this when really deferred
         
     def findRecord(self, indexType, indexKey):
         return self.recordsIndexedBy[indexType].get(indexKey)
@@ -160,6 +168,8 @@
                 debug=0, pickleProtocol=2)
         return self.memcacheClient
 
+
+    @inlineCallbacks
     def memcacheSet(self, key, record):
 
         hideService = isinstance(record, DirectoryRecord)
@@ -169,12 +179,12 @@
                 record.service = None # so we don't pickle service
 
             key = base64.b64encode(key)
-            if not self._getMemcacheClient().set(key, record, time=self.cacheTimeout):
+            if not (yield self._getMemcacheClient().set(key, record, time=self.cacheTimeout)):
                 self.log_error("Could not write to memcache, retrying")
-                if not self._getMemcacheClient(refresh=True).set(
+                if not (yield self._getMemcacheClient(refresh=True).set(
                     key, record,
                     time=self.cacheTimeout
-                ):
+                )):
                     self.log_error("Could not write to memcache again, giving up")
                     del self.memcacheClient
                     raise DirectoryMemcacheError("Failed to write to memcache")
@@ -182,22 +192,23 @@
             if hideService:
                 record.service = self
 
+    @inlineCallbacks
     def memcacheGet(self, key):
 
         key = base64.b64encode(key)
         try:
-            record = self._getMemcacheClient().get(key)
+            record = (yield self._getMemcacheClient().get(key))
             if record is not None and isinstance(record, DirectoryRecord):
                 record.service = self
         except memcacheclient.MemcacheError:
             self.log_error("Could not read from memcache, retrying")
             try:
-                record = self._getMemcacheClient(refresh=True).get(key)
+                record = (yield self._getMemcacheClient(refresh=True).get(key))
             except memcacheclient.MemcacheError:
                 self.log_error("Could not read from memcache again, giving up")
                 del self.memcacheClient
                 raise DirectoryMemcacheError("Failed to read from memcache")
-        return record
+        returnValue(record)
 
     def _initCaches(self, cacheClass):
         self._recordCaches = dict([
@@ -223,21 +234,22 @@
             raise UnknownRecordTypeError(recordType)
 
     def listRecords(self, recordType):
-        return self.recordCacheForType(recordType).records
+        return succeed(self.recordCacheForType(recordType).records)
 
     def recordWithShortName(self, recordType, shortName):
         return self._lookupRecord((recordType,), CachingDirectoryService.INDEX_TYPE_SHORTNAME, shortName)
 
+    @inlineCallbacks
     def recordWithCalendarUserAddress(self, address):
         address = normalizeCUAddr(address)
         record = None
         if address.startswith("urn:uuid:"):
             guid = address[9:]
-            record = self.recordWithGUID(guid)
+            record = (yield self.recordWithGUID(guid))
         elif address.startswith("mailto:"):
-            record = self._lookupRecord(None, CachingDirectoryService.INDEX_TYPE_CUA, address)
+            record = (yield self._lookupRecord(None, CachingDirectoryService.INDEX_TYPE_CUA, address))
 
-        return record if record and record.enabledForCalendaring else None
+        returnValue(record if record and record.enabledForCalendaring else None)
 
     def recordWithAuthID(self, authID):
         return self._lookupRecord(None, CachingDirectoryService.INDEX_TYPE_AUTHID, authID)
@@ -247,6 +259,7 @@
 
     recordWithUID = recordWithGUID
 
+    @inlineCallbacks
     def _lookupRecord(self, recordTypes, indexType, indexKey, cacheOnMiss=True):
 
         if recordTypes is None:
@@ -259,9 +272,10 @@
                 if recordType in supportedRecordTypes:
                     typesToQuery.append(recordType)
             if not typesToQuery:
-                return None
+                returnValue(None)
             recordTypes = typesToQuery
 
+        @inlineCallbacks
         def lookup():
             for recordType in recordTypes:
                 record = self.recordCacheForType(recordType).findRecord(indexType, indexKey)
@@ -271,30 +285,30 @@
                         record.cachedTime != 0 and
                         time.time() - record.cachedTime > self.cacheTimeout
                     ):
-                        self.recordCacheForType(recordType).removeRecord(record)
-                        return None
+                        yield self.recordCacheForType(recordType).removeRecord(record)
+                        returnValue(None)
                     else:
-                        return record
+                        returnValue(record)
             else:
-                return None
+                returnValue(None)
 
-        record = lookup()
+        record = (yield lookup())
         if record:
-            return record
+            returnValue(record)
 
         if cacheOnMiss:
-            
             # Check negative cache (take cache entry timeout into account)
             try:
                 disabledTime = self._disabledKeys[indexType][indexKey]
                 if time.time() - disabledTime < self.cacheTimeout:
-                    return None
+                    returnValue(None)
             except KeyError:
                 pass
             
             # Check memcache
             if config.Memcached.ClientEnabled:
                 key = "dir|%s|%s" % (indexType, indexKey)
+                record = (yield self.memcacheGet(key))
                 self.log_debug("Memcache: checking %s" % (key,))
 
                 try:
@@ -307,29 +321,29 @@
                     self.log_debug("Memcache: miss %s" % (key,))
                 else:
                     self.log_debug("Memcache: hit %s" % (key,))
-                    self.recordCacheForType(record.recordType).addRecord(record, indexType, indexKey, useMemcache=False)
-                    return record
+                    yield self.recordCacheForType(record.recordType).addRecord(record, indexType, indexKey, useMemcache=False)
+                    returnValue(record)
 
                 # Check negative memcache
                 try:
-                    val = self.memcacheGet("-%s" % (key,))
+                    val = yield self.memcacheGet("-%s" % (key,))
                 except DirectoryMemcacheError:
                     self.log_error("Memcache: failed to get -%s" % (key,))
                     val = None
                 if val == 1:
                     self.log_debug("Memcache: negative %s" % (key,))
                     self._disabledKeys[indexType][indexKey] = time.time()
-                    return None
+                    returnValue(None)
 
             # Try query
             self.log_debug("Faulting record for attribute '%s' with value '%s'" % (indexType, indexKey,))
-            self.queryDirectory(recordTypes, indexType, indexKey)
+            yield self.queryDirectory(recordTypes, indexType, indexKey)
             
             # Now try again from cache
-            record = lookup()
+            record = (yield lookup())
             if record:
                 self.log_debug("Found record for attribute '%s' with value '%s'" % (indexType, indexKey,))
-                return record
+                returnValue(record)
 
 
             # Add to negative cache with timestamp
@@ -339,13 +353,13 @@
             if config.Memcached.ClientEnabled:
                 self.log_debug("Memcache: storing (negative) %s" % (key,))
                 try:
-                    self.memcacheSet("-%s" % (key,), 1)
+                    yield self.memcacheSet("-%s" % (key,), 1)
                 except DirectoryMemcacheError:
                     self.log_error("Memcache: failed to set -%s" % (key,))
                     pass
 
+        returnValue(None)
 
-        return None
 
     def queryDirectory(self, recordTypes, indexType, indexKey):
         raise NotImplementedError()

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendar.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendar.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -27,7 +27,7 @@
     "DirectoryCalendarHomeResource",
 ]
 
-from twisted.internet.defer import succeed
+from twisted.internet.defer import succeed, fail, inlineCallbacks, returnValue, gatherResults
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.http import HTTPError
@@ -58,13 +58,33 @@
     DAVResource,
 ):
     def defaultAccessControlList(self):
-        return config.ProvisioningResourceACL
+        return succeed(config.ProvisioningResourceACL)
 
 
 class DirectoryCalendarHomeProvisioningResource (DirectoryCalendarProvisioningResource):
     """
     Resource which provisions calendar home collections as needed.    
     """
+
+    @classmethod
+    @inlineCallbacks
+    def fetch(cls, *a, **kw):
+        self = (yield super(DirectoryCalendarHomeProvisioningResource, cls).fetch(*a, **kw))
+        #
+        # Create children
+        #
+
+        @inlineCallbacks
+        def _provisionChild(name):
+            provisioned = (yield self.provisionChild(name))
+            self.putChild(name, provisioned)
+
+        for recordType in self.directory.recordTypes():
+            (yield _provisionChild(recordType))
+
+        (yield _provisionChild(uidsResourceName))
+        returnValue(self)
+
     def __init__(self, directory, url):
         """
         @param directory: an L{IDirectoryService} to provision calendars from.
@@ -81,17 +101,7 @@
         # FIXME: Smells like a hack
         directory.calendarHomesCollection = self
 
-        #
-        # Create children
-        #
-        def provisionChild(name):
-            self.putChild(name, self.provisionChild(name))
 
-        for recordType in self.directory.recordTypes():
-            provisionChild(recordType)
-
-        provisionChild(uidsResourceName)
-
     def provisionChild(self, recordType):
         raise NotImplementedError("Subclass must implement provisionChild()")
 
@@ -99,27 +109,29 @@
         return self._url
 
     def getChild(self, name):
-        return self.putChildren.get(name, None)
+        return succeed(self.putChildren.get(name, None))
 
     def listChildren(self):
-        return self.directory.recordTypes()
+        return succeed(self.directory.recordTypes())
 
     def principalCollections(self):
         # FIXME: directory.principalCollection smells like a hack
         # See DirectoryPrincipalProvisioningResource.__init__()
         return self.directory.principalCollection.principalCollections()
 
+    # Deferred
     def principalForRecord(self, record):
         # FIXME: directory.principalCollection smells like a hack
         # See DirectoryPrincipalProvisioningResource.__init__()
         return self.directory.principalCollection.principalForRecord(record)
 
+    @inlineCallbacks
     def homeForDirectoryRecord(self, record):
-        uidResource = self.getChild(uidsResourceName)
+        uidResource = (yield self.getChild(uidsResourceName))
         if uidResource is None:
-            return None
+            returnValue(None)
         else:
-            return uidResource.getChild(record.uid)
+            returnValue((yield uidResource.getChild(record.uid)))
 
     ##
     # DAV
@@ -151,28 +163,30 @@
     def url(self):
         return joinURL(self._parent.url(), self.recordType)
 
+    @inlineCallbacks
     def getChild(self, name, record=None):
-        self.provision()
+        yield self.provision()
         if name == "":
-            return self
+            returnValue(self)
 
         if record is None:
-            record = self.directory.recordWithShortName(self.recordType, name)
+            record = (yield self.directory.recordWithShortName(self.recordType, name))
             if record is None:
-                return None
+                returnValue(None)
 
-        return self._parent.homeForDirectoryRecord(record)
+        returnValue((yield self._parent.homeForDirectoryRecord(record)))
 
+    @inlineCallbacks
     def listChildren(self):
         if config.EnablePrincipalListings:
 
-            def _recordShortnameExpand():
-                for record in self.directory.listRecords(self.recordType):
-                    if record.enabledForCalendaring:
-                        for shortName in record.shortNames:
-                            yield shortName
+            results = []
+            for record in (yield self.directory.listRecords(self.recordType)):
+                if record.enabledForCalendaring:
+                    for shortName in record.shortNames:
+                        results.append(shortName)
 
-            return _recordShortnameExpand()
+            returnValue(results)
         else:
             # Not a listable collection
             raise HTTPError(responsecode.FORBIDDEN)
@@ -194,6 +208,7 @@
     def principalCollections(self):
         return self._parent.principalCollections()
 
+    # Deferred
     def principalForRecord(self, record):
         return self._parent.principalForRecord(record)
 
@@ -213,21 +228,22 @@
     def url(self):
         return joinURL(self.parent.url(), uidsResourceName)
 
+    @inlineCallbacks
     def getChild(self, name, record=None):
-        self.provision()
+        yield self.provision()
         if name == "":
-            return self
+            returnValue(self)
 
         if record is None:
-            record = self.directory.recordWithUID(name)
+            record = (yield self.directory.recordWithUID(name))
             if record is None:
-                return None
+                returnValue(None)
 
-        return self.provisionChild(name)
+        returnValue((yield self.provisionChild(name)))
 
     def listChildren(self):
         # Not a listable collection
-        raise HTTPError(responsecode.FORBIDDEN)
+        return fail(HTTPError(responsecode.FORBIDDEN))
 
     ##
     # DAV
@@ -243,6 +259,7 @@
     def principalCollections(self):
         return self.parent.principalCollections()
 
+    # Deferred
     def principalForRecord(self, record):
         return self.parent.principalForRecord(record)
 
@@ -251,6 +268,35 @@
     """
     Calendar home collection resource.
     """
+    @classmethod
+    def fetch(cls, *a, **kw):
+        d = super(DirectoryCalendarHomeResource, cls).fetch(*a, **kw)
+        def _populateChildren(self):
+            # Cache children which must be of a specific type
+            childlist = (
+                ("inbox" , ScheduleInboxResource ),
+                ("outbox", ScheduleOutboxResource),
+            )
+            if config.EnableDropBox:
+                childlist += (
+                    ("dropbox", DropBoxHomeResource),
+                )
+            if config.FreeBusyURL.Enabled:
+                childlist += (
+                    ("freebusy", FreeBusyURLResource),
+                )
+            ds = []
+            for name, cls in childlist:
+                d = self.provisionChild(name)
+                def _newChild(child):
+                    assert isinstance(child, cls), "Child %r is not a %s: %r" % (name, cls.__name__, child)
+                    self.putChild(name, child)
+                d.addCallback(_newChild)
+                ds.append(d)
+            return gatherResults(ds).addCallback(lambda _: self)
+
+        return d.addCallback(_populateChildren)
+
     def __init__(self, parent, record):
         """
         @param path: the path to the file which will back the resource.
@@ -263,73 +309,43 @@
         self.record = record
         self.parent = parent
 
-        # Cache children which must be of a specific type
-        childlist = (
-            ("inbox" , ScheduleInboxResource ),
-            ("outbox", ScheduleOutboxResource),
-        )
-        if config.EnableDropBox:
-            childlist += (
-                ("dropbox", DropBoxHomeResource),
-            )
-        if config.FreeBusyURL.Enabled:
-            childlist += (
-                ("freebusy", FreeBusyURLResource),
-            )
-        for name, cls in childlist:
-            child = self.provisionChild(name)
-            assert isinstance(child, cls), "Child %r is not a %s: %r" % (name, cls.__name__, child)
-            self.putChild(name, child)
 
+    @inlineCallbacks
     def provisionDefaultCalendars(self):
 
         # Disable notifications during provisioning
         if hasattr(self, "clientNotifier"):
             self.clientNotifier.disableNotify()
 
-        def setupFreeBusy(_):
-            # Default calendar is initially opaque to freebusy
-            child.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
+        try:
+            yield self.provision()
 
+            childName = "calendar"
+            childURL = joinURL(self.url(), childName)
+            child = (yield self.provisionChild(childName))
+            
+            assert isinstance(child, CalDAVResource), "Child %r is not a %s: %r" % (childName, CalDAVResource.__name__, child)
+            yield child.createCalendarCollection()
+            yield child.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
+
             # FIXME: Shouldn't have to call provision() on another resource
             # We cheat here because while inbox will auto-provision itself when located,
             # we need to write a dead property to it pre-emptively.
             # This will go away once we remove the free-busy-set property on inbox.
 
             # Set calendar-free-busy-set on inbox
-            inbox = self.getChild("inbox")
-            inbox.provision()
-            inbox.processFreeBusyCalendar(childURL, True)
+            inbox = (yield self.getChild("inbox"))
+            yield inbox.provision()
+            yield inbox.processFreeBusyCalendar(childURL, True)
 
             # Default calendar is marked as the default for scheduling
-            inbox.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(childURL)))
+            yield inbox.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(childURL)))
 
-            return self
-
-        try:
-            self.provision()
-
-            childName = "calendar"
-            childURL = joinURL(self.url(), childName)
-            child = self.provisionChild(childName)
-            assert isinstance(child, CalDAVResource), "Child %r is not a %s: %r" % (childName, CalDAVResource.__name__, child)
-
-            d = child.createCalendarCollection()
-            d.addCallback(setupFreeBusy)
-        except:
-            # We want to make sure to re-enable notifications, so do so
-            # if there is an immediate exception above, or via errback, below
+        finally:
             if hasattr(self, "clientNotifier"):
                 self.clientNotifier.enableNotify(None)
-            raise
 
-        # Re-enable notifications
-        if hasattr(self, "clientNotifier"):
-            d.addCallback(self.clientNotifier.enableNotify)
-            d.addErrback(self.clientNotifier.enableNotify)
 
-        return d
-
     def provisionChild(self, name):
         raise NotImplementedError("Subclass must implement provisionChild()")
 
@@ -353,14 +369,18 @@
     # ACL
     ##
 
+    @inlineCallbacks
     def owner(self, request):
-        return succeed(davxml.HRef(self.principalForRecord().principalURL()))
+        principal = (yield self.principalForRecord())
+        returnValue(davxml.HRef(principal.principalURL()))
 
+    # Deferred
     def ownerPrincipal(self, request):
-        return succeed(self.principalForRecord())
+        return self.principalForRecord()
 
+    @inlineCallbacks
     def defaultAccessControlList(self):
-        myPrincipal = self.principalForRecord()
+        myPrincipal = (yield self.principalForRecord())
 
         aces = (
             # Inheritable DAV:all access for the resource's associated principal.
@@ -409,7 +429,7 @@
                 ),
             )
 
-        return davxml.ACL(*aces)
+        returnValue(davxml.ACL(*aces))
 
     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
         def gotACL(wikiACL):
@@ -422,13 +442,20 @@
                 # inheritance rules, etc.
                 return succeed(self.defaultAccessControlList())
 
-        d = getWikiACL(self, request)
-        d.addCallback(gotACL)
-        return d
+        wikiACL = (yield getWikiACL(self, request))
+        if wikiACL is not None:
+            # ACL depends on wiki server...
+            log.debug("Wiki ACL: %s" % (wikiACL.toxml(),))
+            returnValue(wikiACL)
+        else:
+            # ...otherwise permissions are fixed, and are not subject to
+            # inheritance rules, etc.
+            returnValue((yield self.defaultAccessControlList()))
 
     def principalCollections(self):
         return self.parent.principalCollections()
 
+    # Deferred
     def principalForRecord(self):
         return self.parent.principalForRecord(self.record)
 
@@ -442,7 +469,7 @@
 
         @return: a C{True} if this resource has quota root, C{False} otherwise.
         """
-        return config.UserQuota != 0
+        return succeed(config.UserQuota != 0)
     
     def quotaRoot(self, request):
         """
@@ -451,4 +478,4 @@
         @return: a C{int} containing the maximum allowed bytes if this collection
             is quota-controlled, or C{None} if not quota controlled.
         """
-        return config.UserQuota if config.UserQuota != 0 else None
+        return succeed(config.UserQuota if config.UserQuota != 0 else None)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendaruserproxy.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/calendaruserproxy.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -70,11 +70,11 @@
             for principal in config.AdminPrincipals
         ))
 
-        return davxml.ACL(*aces)
+        return succeed(davxml.ACL(*aces))
 
     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
         # Permissions here are fixed, and are not subject to inheritance rules, etc.
-        return succeed(self.defaultAccessControlList())
+        return self.defaultAccessControlList()
 
 class CalendarUserProxyPrincipalResource (CalDAVComplianceMixIn, PermissionsMixIn, DAVPrincipalResource, DAVFile):
     """
@@ -130,9 +130,9 @@
 
     def resourceType(self):
         if self.proxyType == "calendar-proxy-read":
-            return davxml.ResourceType.calendarproxyread
+            return succeed(davxml.ResourceType.calendarproxyread)
         elif self.proxyType == "calendar-proxy-write":
-            return davxml.ResourceType.calendarproxywrite
+            return succeed(davxml.ResourceType.calendarproxywrite)
         else:
             return super(CalendarUserProxyPrincipalResource, self).resourceType()
 
@@ -149,13 +149,14 @@
         return True
 
     def etag(self):
-        return None
+        return succeed(None)
 
     def deadProperties(self):
         if not hasattr(self, "_dead_properties"):
             self._dead_properties = NonePropertyStore(self)
         return self._dead_properties
 
+    # Deferred
     def writeProperty(self, property, request):
         assert isinstance(property, davxml.WebDAVElement)
 
@@ -182,7 +183,7 @@
         principals = []
         newUIDs = set()
         for uri in members:
-            principal = self.pcollection._principalForURI(uri)
+            principal = (yield self.pcollection._principalForURI(uri))
             # Invalid principals MUST result in an error.
             if principal is None or principal.principalURL() != uri:
                 raise HTTPError(StatusResponse(
@@ -204,7 +205,7 @@
         
         changedUIDs = newUIDs.symmetric_difference(oldUIDs)
         for uid in changedUIDs:
-            principal = self.pcollection.principalForUID(uid)
+            principal = (yield self.pcollection.principalForUID(uid))
             if principal:
                 yield principal.cacheNotifier.changed()
             
@@ -275,7 +276,7 @@
     ##
 
     def displayName(self):
-        return self.proxyType
+        return succeed(self.proxyType)
 
     ##
     # ACL
@@ -306,7 +307,7 @@
         if uid not in uids:
             from twistedcaldav.directory.principal import DirectoryPrincipalResource
             uids.add(uid)
-            principal = self.pcollection.principalForUID(uid)
+            principal = (yield self.pcollection.principalForUID(uid))
             if isinstance(principal, CalendarUserProxyPrincipalResource):
                 members = yield self._directGroupMembers()
                 for member in members:
@@ -330,7 +331,7 @@
         found = []
         missing = []
         for uid in members:
-            p = self.pcollection.principalForUID(uid)
+            p = (yield self.pcollection.principalForUID(uid))
             if p:
                 found.append(p)
                 # Make sure any outstanding deletion timer entries for
@@ -343,8 +344,7 @@
         for uid in missing:
             cacheTimeout = config.DirectoryService.params.get("cacheTimeout", 30) * 60 # in seconds
 
-            yield self._index().removePrincipal(uid,
-                delay=cacheTimeout*2)
+            yield self._index().removePrincipal(uid, delay=cacheTimeout*2)
 
         returnValue(found)
 
@@ -354,16 +354,17 @@
     def expandedGroupMembers(self):
         return self._expandMemberUIDs(infinity=True)
 
+    @inlineCallbacks
     def groupMemberships(self):
         # Get membership UIDs and map to principal resources
-        d = self._index().getMemberships(self.uid)
-        d.addCallback(lambda memberships: [
-            p for p
-            in [self.pcollection.principalForUID(uid) for uid in memberships]
-            if p
-        ])
-        return d
+        memberships = yield self._index().getMemberships(self.uid)
+        returnValue([p for p in
+                     [(yield self.pcollection.principalForUID(uid))
+                      for uid in memberships]
+                     if p])
 
+
+
 class CalendarUserProxyDatabase(AbstractSQLDatabase, LoggingMixIn):
     """
     A database to maintain calendar user proxy group memberships.

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/directory.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/directory.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -35,7 +35,7 @@
 from twisted.cred.error import UnauthorizedLogin
 from twisted.cred.checkers import ICredentialsChecker
 from twisted.web2.dav.auth import IPrincipalCredentials
-from twisted.internet.defer import succeed
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed, maybeDeferred
 
 from twistedcaldav.log import LoggingMixIn
 from twistedcaldav.directory.idirectory import IDirectoryService, IDirectoryRecord
@@ -82,6 +82,7 @@
     # For ICredentialsChecker
     credentialInterfaces = (IPrincipalCredentials,)
 
+    # called using maybeDeferred...
     def requestAvatarId(self, credentials):
         credentials = IPrincipalCredentials(credentials)
 
@@ -107,13 +108,16 @@
                 credentials.authzPrincipal.principalURL(),
             )
         else:
-            if credentials.authnPrincipal.record.verifyCredentials(credentials.credentials):
-                return (
-                    credentials.authnPrincipal.principalURL(),
-                    credentials.authzPrincipal.principalURL(),
-                )
-            else:
-                raise UnauthorizedLogin("Incorrect credentials for %s" % (credentials.credentials.username,)) 
+            d = maybeDeferred(credentials.authnPrincipal.record.verifyCredentials, credentials.credentials)
+            def _verify(authed):
+                if authed:
+                    return (
+                        credentials.authnPrincipal.principalURL(),
+                        credentials.authzPrincipal.principalURL(),
+                        )
+                else:
+                    raise UnauthorizedLogin("Incorrect credentials for %s" % (credentials.credentials.username,)) 
+            return d.addCallback(_verify)
 
     def recordTypes(self):
         raise NotImplementedError("Subclass must implement recordTypes()")
@@ -127,35 +131,36 @@
     def recordWithUID(self, uid):
         for record in self.allRecords():
             if record.uid == uid:
-                return record
-        return None
+                return succeed(record)
+        return succeed(None)
 
     def recordWithGUID(self, guid):
         for record in self.allRecords():
             if record.guid == guid:
-                return record
-        return None
+                return succeed(record)
+        return succeed(None)
 
     def recordWithAuthID(self, authID):
         for record in self.allRecords():
             if authID in record.authIDs:
-                return record
-        return None
+                return succeed(record)
+        return succeed(None)
 
+    @inlineCallbacks
     def recordWithCalendarUserAddress(self, address):
         address = normalizeCUAddr(address)
         record = None
         if address.startswith("urn:uuid:"):
             guid = address[9:]
-            record = self.recordWithGUID(guid)
+            record = (yield self.recordWithGUID(guid))
         elif address.startswith("mailto:"):
             for record in self.allRecords():
                 if address in record.calendarUserAddresses:
                     break
             else:
-                return None
+                returnValue(None)
 
-        return record if record and record.enabledForCalendaring else None
+        returnValue(record if record and record.enabledForCalendaring else None)
 
     def allRecords(self):
         for recordType in self.recordTypes():
@@ -248,7 +253,7 @@
         return succeed(yieldMatches(recordType))
 
     def getResourceInfo(self):
-        return ()
+        return succeed(())
 
     def isAvailable(self):
         return True
@@ -370,13 +375,13 @@
         return h
 
     def members(self):
-        return ()
+        return succeed(())
 
     def groups(self):
-        return ()
+        return succeed(())
 
     def verifyCredentials(self, credentials):
-        return False
+        return succeed(False)
 
     # Mapping from directory record.recordType to RFC2445 CUTYPE values
     _cuTypes = {

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/idirectory.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/idirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -42,7 +42,7 @@
     def listRecords(recordType):
         """
         @param type: the type of records to retrieve.
-        @return: an iterable of records of the given type.
+        @return: a L{Deferred} firing an iterable of records of the given type.
         """
 
     def recordWithShortName(recordType, shortName):

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/principal.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/principal.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -38,7 +38,7 @@
 from twisted.cred.credentials import UsernamePassword
 from twisted.python.failure import Failure
 from twisted.internet.defer import inlineCallbacks, returnValue
-from twisted.internet.defer import succeed
+from twisted.internet.defer import succeed, fail
 from twisted.web2.auth.digest import DigestedCredentials
 from twisted.web2 import responsecode
 from twisted.web2.http import HTTPError
@@ -75,7 +75,7 @@
 
 class PermissionsMixIn (ReadOnlyResourceMixIn):
     def defaultAccessControlList(self):
-        return authReadACL
+        return succeed(authReadACL)
 
     @inlineCallbacks
     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
@@ -88,7 +88,7 @@
         else:
             # ...otherwise permissions are fixed, and are not subject to
             # inheritance rules, etc.
-            returnValue(self.defaultAccessControlList())
+            returnValue((yield self.defaultAccessControlList()))
 
 
 
@@ -147,11 +147,12 @@
     def __repr__(self):
         return "<%s: %s %s>" % (self.__class__.__name__, self.directory, self._url)
 
+    @inlineCallbacks
     def locateChild(self, req, segments):
-        child = self.getChild(segments[0])
+        child = (yield self.getChild(segments[0]))
         if child is not None:
-            return (child, segments[1:])
-        return (None, ())
+            returnValue( (child, segments[1:]) )
+        returnValue( (None, ()) )
 
     def deadProperties(self):
         if not hasattr(self, "_dead_properties"):
@@ -159,34 +160,38 @@
         return self._dead_properties
 
     def etag(self):
-        return None
+        return succeed(None)
 
+    @inlineCallbacks
     def principalForShortName(self, recordType, name):
-        return self.principalForRecord(self.directory.recordWithShortName(recordType, name))
+        returnValue((yield self.principalForRecord((yield self.directory.recordWithShortName(recordType, name)))))
 
+    # Deferred
     def principalForUser(self, user):
         return self.principalForShortName(DirectoryService.recordType_users, user)
 
+    @inlineCallbacks
     def principalForAuthID(self, user):
         # Basic/Digest creds -> just lookup user name
         if isinstance(user, UsernamePassword) or isinstance(user, DigestedCredentials):
-            return self.principalForUser(user.username)
+            returnValue((yield self.principalForUser(user.username)))
         elif isinstance(user, NegotiateCredentials):
             authID = "Kerberos:%s" % (user.principal,)
-            principal = self.principalForRecord(self.directory.recordWithAuthID(authID))
+            principal = (yield self.principalForRecord((yield self.directory.recordWithAuthID(authID))))
             if principal:
-                return principal
+                returnValue(principal)
             elif user.username:
-                return self.principalForUser(user.username)
+                returnValue((yield self.principalForUser(user.username)))
         
-        return None
+        returnValue(None)
 
     def principalForUID(self, uid):
         raise NotImplementedError("Subclass must implement principalForUID()")
 
+    # Deferred
     def principalForRecord(self, record):
         if record is None:
-            return None
+            return succeed(None)
         return self.principalForUID(record.uid)
 
     def principalForCalendarUserAddress(self, address):
@@ -264,9 +269,13 @@
 
         self.putChild(uidsResourceName, DirectoryPrincipalUIDProvisioningResource(self))
 
+    @inlineCallbacks
     def principalForUID(self, uid):
-        return self.getChild(uidsResourceName).getChild(uid)
+        child = (yield self.getChild(uidsResourceName))
+        child = (yield child.getChild(uid))
+        returnValue(child)
 
+    @inlineCallbacks
     def _principalForURI(self, uri):
         scheme, netloc, path, _ignore_params, _ignore_query, _ignore_fragment = urlparse(uri)
 
@@ -287,51 +296,52 @@
                 port = int(netloc[1])
 
             if host != config.ServerHostName:
-                return None
+                returnValue(None)
 
             if port != {
                 "http" : config.HTTPPort,
                 "https": config.SSLPort,
             }[scheme]:
-                return None
+                returnValue(None)
 
         elif scheme == "urn":
             if path.startswith("uuid:"):
-                return self.principalForUID(path[5:])
+                returnValue((yield self.principalForUID(path[5:])))
             else:
-                return None
+                returnValue(None)
         else:
-            return None
+            returnValue(None)
 
         if not path.startswith(self._url):
-            return None
+            returnValue(None)
 
         path = path[len(self._url) - 1:]
 
         segments = [unquote(s) for s in path.rstrip("/").split("/")]
         if segments[0] == "" and len(segments) == 3:
-            typeResource = self.getChild(segments[1])
+            typeResource = (yield self.getChild(segments[1]))
             if typeResource is not None:
-                principalResource = typeResource.getChild(segments[2])
+                principalResource = (yield typeResource.getChild(segments[2]))
                 if principalResource:
-                    return principalResource
+                    returnValue(principalResource)
 
-        return None
+        returnValue(None)
 
+    @inlineCallbacks
     def principalForCalendarUserAddress(self, address):
         # First see if the address is a principal URI
-        principal = self._principalForURI(address)
+        principal = (yield self._principalForURI(address))
         if principal:
             if isinstance(principal, DirectoryCalendarPrincipalResource):
-                return principal
+                returnValue(principal)
         else:
             # Next try looking it up in the directory
-            record = self.directory.recordWithCalendarUserAddress(address)
+            record = (yield self.directory.recordWithCalendarUserAddress(address))
             if record is not None:
-                return self.principalForRecord(record)
+                returnValue((yield self.principalForRecord(record)))
 
         log.debug("No principal for calendar user address: %r" % (address,))
-        return None
+        returnValue(None)
 
 
     ##
@@ -344,12 +354,12 @@
 
     def getChild(self, name):
         if name == "":
-            return self
+            return succeed(self)
         else:
-            return self.putChildren.get(name, None)
+            return succeed(self.putChildren.get(name, None))
 
     def listChildren(self):
-        return self.directory.recordTypes()
+        return succeed(self.directory.recordTypes())
 
     ##
     # ACL
@@ -378,9 +388,11 @@
         self.recordType = recordType
         self.parent = parent
 
+    # Deferred
     def principalForUID(self, uid):
         return self.parent.principalForUID(uid)
 
+    # Deferred
     def principalForCalendarUserAddress(self, address):
         return self.parent.principalForCalendarUserAddress(address)
 
@@ -392,21 +404,23 @@
         log.err("Attempt to create clone %r of resource %r" % (path, self))
         raise HTTPError(responsecode.NOT_FOUND)
 
+    # Deferred
     def getChild(self, name):
         if name == "":
-            return self
+            return succeed(self)
         else:
             return self.principalForShortName(self.recordType, name)
 
+    @inlineCallbacks
     def listChildren(self):
         if config.EnablePrincipalListings:
+            results = []
+            for record in (yield self.directory.listRecords(self.recordType)):
+                for shortName in record.shortNames:
+                    results.append(shortName)
+            returnValue(results)
 
-            def _recordShortnameExpand():
-                for record in self.directory.listRecords(self.recordType):
-                    for shortName in record.shortNames:
-                        yield shortName
-
-            return _recordShortnameExpand()
+            # return succeed(_recordShortnameExpand())
         else:
             # Not a listable collection
             raise HTTPError(responsecode.FORBIDDEN)
@@ -437,9 +451,11 @@
 
         self.parent = parent
 
+    # Deferred
     def principalForUID(self, uid):
         return self.parent.principalForUID(uid)
 
+    # Deferred
     def principalForCalendarUserAddress(self, address):
         return self.parent.principalForCalendarUserAddress(address)
 
@@ -451,9 +467,10 @@
         log.err("Attempt to create clone %r of resource %r" % (path, self))
         raise HTTPError(responsecode.NOT_FOUND)
 
+    @inlineCallbacks
     def getChild(self, name):
         if name == "":
-            return self
+            returnValue(self)
 
         if "#" in name:
             # This UID belongs to a sub-principal
@@ -462,11 +479,11 @@
             primaryUID = name
             subType = None
 
-        record = self.directory.recordWithUID(primaryUID)
+        record = (yield self.directory.recordWithUID(primaryUID))
 
         if record is None:
             log.err("No principal found for UID: %s" % (name,))
-            return None
+            returnValue(None)
 
         if record.enabledForCalendaring:
             primaryPrincipal = DirectoryCalendarPrincipalResource(self, record)
@@ -474,13 +491,13 @@
             primaryPrincipal = DirectoryPrincipalResource(self, record)
 
         if subType is None:
-            return primaryPrincipal
+            returnValue(primaryPrincipal)
         else:
-            return primaryPrincipal.getChild(subType)
+            returnValue((yield primaryPrincipal.getChild(subType)))
 
     def listChildren(self):
         # Not a listable collection
-        raise HTTPError(responsecode.FORBIDDEN)
+        return fail(HTTPError(responsecode.FORBIDDEN))
 
     ##
     # ACL
@@ -569,7 +586,7 @@
         return self._dead_properties
 
     def etag(self):
-        return None
+        return succeed(None)
 
     ##
     # HTTP
@@ -630,9 +647,9 @@
 
     def displayName(self):
         if self.record.fullName:
-            return self.record.fullName
+            return succeed(self.record.fullName)
         else:
-            return self.record.shortNames[0]
+            return succeed(self.record.shortNames[0])
 
     ##
     # ACL
@@ -668,7 +685,7 @@
         proxyFors = set()
 
         if resolve_memberships:
-            memberships = self._getRelatives("groups", infinity=True)
+            memberships = (yield self._getRelatives("groups", infinity=True))
             for membership in memberships:
                 results = (yield membership.proxyFor(read_write, False))
                 proxyFors.update(results)
@@ -678,7 +695,7 @@
             proxies = []
             memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
             for uid in memberships:
-                subprincipal = self.parent.principalForUID(uid)
+                subprincipal = (yield self.parent.principalForUID(uid))
                 if subprincipal:
                     if subprincipal.isProxyType(read_write):
                         proxies.append(subprincipal.parent)
@@ -696,6 +713,7 @@
 
         returnValue(proxyFors)
 
+    @inlineCallbacks
     def _getRelatives(self, method, record=None, relatives=None, records=None, proxy=None, infinity=False):
         if record is None:
             record = self.record
@@ -706,41 +724,43 @@
 
         if record not in records:
             records.add(record)
-            for relative in getattr(record, method)():
+            for relative in (yield getattr(record, method)()):
                 if relative not in records:
-                    found = self.parent.principalForRecord(relative)
+                    found = (yield self.parent.principalForRecord(relative))
                     if found is None:
                         log.err("No principal found for directory record: %r" % (relative,))
                     else:
                         if proxy:
                             if proxy == "read-write":
-                                found = found.getChild("calendar-proxy-write")
+                                found = (yield found.getChild("calendar-proxy-write"))
                             else:
-                                found = found.getChild("calendar-proxy-read")
+                                found = (yield found.getChild("calendar-proxy-read"))
                         relatives.add(found)
 
                     if infinity:
-                        self._getRelatives(method, relative, relatives, records,
+                        yield self._getRelatives(method, relative, relatives, records,
                             infinity=infinity)
 
-        return relatives
+        returnValue(relatives)
 
+    # Deferred
     def groupMembers(self):
-        return succeed(self._getRelatives("members"))
+        return self._getRelatives("members")
 
+    # Deferred
     def expandedGroupMembers(self):
-        return succeed(self._getRelatives("members", infinity=True))
+        return self._getRelatives("members", infinity=True)
 
     @inlineCallbacks
     def groupMemberships(self, infinity=False):
-        groups = self._getRelatives("groups", infinity=infinity)
+        groups = (yield self._getRelatives("groups", infinity=infinity))
 
         if config.EnableProxyPrincipals:
             # Get proxy group UIDs and map to principal resources
             proxies = []
             memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
             for uid in memberships:
-                subprincipal = self.parent.principalForUID(uid)
+                subprincipal = (yield self.parent.principalForUID(uid))
                 if subprincipal:
                     proxies.append(subprincipal)
                 else:
@@ -765,7 +785,7 @@
     ##
 
     def setAutoSchedule(self, autoSchedule):
-        self._resource_info_index().setAutoSchedule(self.record.guid, autoSchedule)
+        return self._resource_info_index().setAutoSchedule(self.record.guid, autoSchedule)
 
     @inlineCallbacks
     def getAutoSchedule(self):
@@ -806,20 +826,21 @@
         log.err("Attempt to create clone %r of resource %r" % (path, self))
         raise HTTPError(responsecode.NOT_FOUND)
 
+    @inlineCallbacks
     def locateChild(self, req, segments):
-        child = self.getChild(segments[0])
+        child = (yield self.getChild(segments[0]))
         if child is not None:
-            return (child, segments[1:])
-        return (None, ())
+            returnValue((child, segments[1:]))
+        returnValue((None, ()))
 
     def getChild(self, name):
         if name == "":
-            return self
+            return succeed(self)
 
-        return None
+        return succeed(None)
 
     def listChildren(self):
-        return ()
+        return succeed(())
 
 
 class DirectoryCalendarPrincipalResource (DirectoryPrincipalResource, CalendarPrincipalResource):
@@ -845,11 +866,14 @@
             result = (yield CalendarPrincipalResource.readProperty(self, property, request))
         returnValue(result)
 
+    @inlineCallbacks
     def extraDirectoryBodyItems(self, request):
-        return "".join((
-            """\nCalendar homes:\n"""          , format_list(format_link(u) for u in self.calendarHomeURLs()),
-            """\nCalendar user addresses:\n""" , format_list(format_link(a) for a in self.calendarUserAddresses()),
-        ))
+        homeURLs = (yield self.calendarHomeURLs())
+        cuas = (yield self.calendarUserAddresses())
+        returnValue( "".join((
+            """\nCalendar homes:\n"""          , format_list(format_link(u) for u in homeURLs),
+            """\nCalendar user addresses:\n""" , format_list(format_link(a) for a in cuas),
+        )))
 
     ##
     # CalDAV
@@ -867,7 +891,7 @@
             if config.SSLPort:
                 addresses.add("https://%s:%s%s" % (config.ServerHostName, config.SSLPort, uri))
 
-        return addresses
+        return succeed(addresses)
 
     def enabledAsOrganizer(self):
         if self.record.recordType == DirectoryService.recordType_users:
@@ -882,19 +906,18 @@
             return False
 
     def scheduleInbox(self, request):
-        home = self.calendarHome()
-        if home is None:
-            return succeed(None)
+        d = self.calendarHome()
+        def _gotHome(home):
+            if home is None:
+                return None
+            return home.getChild("inbox")
 
-        inbox = home.getChild("inbox")
-        if inbox is None:
-            return succeed(None)
+        return d.addCallback(_gotHome)
 
-        return succeed(inbox)
-
+    @inlineCallbacks
     def calendarHomeURLs(self):
-        homeURL = self._homeChildURL(None)
-        return (homeURL,) if homeURL else ()
+        homeURL = yield self._homeChildURL(None)
+        returnValue((homeURL,) if homeURL else ())
 
     def scheduleInboxURL(self):
         return self._homeChildURL("inbox/")
@@ -906,22 +929,23 @@
         if config.EnableDropBox:
             return self._homeChildURL("dropbox/")
         else:
-            return None
+            return succeed(None)
 
+    @inlineCallbacks
     def _homeChildURL(self, name):
         if not hasattr(self, "calendarHomeURL"):
-            home = self.calendarHome()
+            home = yield self.calendarHome()
             if home is None:
                 self.calendarHomeURL = None
-                return None
+                returnValue(None)
             else:
                 self.calendarHomeURL = home.url()
             
         url = self.calendarHomeURL
         if url is None:
-            return None
+            returnValue(None)
         else:
-            return joinURL(url, name) if name else url
+            returnValue(joinURL(url, name) if name else url)
 
     def calendarHome(self):
         # FIXME: self.record.service.calendarHomesCollection smells like a hack
@@ -930,7 +954,7 @@
         if hasattr(service, "calendarHomesCollection"):
             return service.calendarHomesCollection.homeForDirectoryRecord(self.record)
         else:
-            return None
+            return succeed(None)
 
 
     ##
@@ -939,19 +963,18 @@
 
     def getChild(self, name):
         if name == "":
-            return self
+            return succeed(self)
 
         if config.EnableProxyPrincipals and name in ("calendar-proxy-read", "calendar-proxy-write"):
-            return CalendarUserProxyPrincipalResource(self, name)
+            return succeed(CalendarUserProxyPrincipalResource(self, name))
         else:
-            return None
+            return succeed(None)
 
     def listChildren(self):
         if config.EnableProxyPrincipals:
-            return ("calendar-proxy-read", "calendar-proxy-write")
+            return succeed(("calendar-proxy-read", "calendar-proxy-write"))
         else:
-            return ()
-
+            return succeed(())
 ##
 # Utilities
 ##

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/resource.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/resource.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -20,7 +20,8 @@
 
 __all__ = ["AutoProvisioningResourceMixIn"]
 
-from twisted.internet.defer import maybeDeferred, inlineCallbacks, returnValue
+from twisted.internet.defer import (
+    maybeDeferred, inlineCallbacks, returnValue, succeed)
 
 class AutoProvisioningResourceMixIn (object):
     """
@@ -36,7 +37,7 @@
         already provisioned (eg. the backing store exists).
         @return: a deferred or None.
         """
-        return None
+        return succeed(None)
 
     def provisionChild(self, name):
         """
@@ -48,7 +49,7 @@
         @return: the newly created (optionally deferred) child, or None of no resource
             is bound as a child of this resource with the given C{name}.
         """
-        return None
+        return succeed(None)
 
     @inlineCallbacks
     def locateChild(self, request, segments):
@@ -60,9 +61,9 @@
 
         name = segments[0]
         if name != "":
-            child = self.provisionChild(name)
+            child = yield self.provisionChild(name)
             if child:
                 returnValue((child, segments[1:],))
-        
+
         result = (yield super(AutoProvisioningResourceMixIn, self).locateChild(request, segments))
         returnValue(result)

Deleted: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sqldb.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sqldb.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sqldb.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,368 +0,0 @@
-##
-# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-SQL (sqlite) based user/group/resource directory service implementation.
-"""
-
-"""
-SCHEMA:
-
-User Database:
-
-ROW: RECORD_TYPE, SHORT_NAME (unique), PASSWORD, NAME
-
-Group Database:
-
-ROW: SHORT_NAME, MEMBER_SHORT_NAME
-
-CUAddress database:
-
-ROW: ADDRESS (unqiue), SHORT_NAME
-
-"""
-
-__all__ = [
-    "SQLDirectoryService",
-]
-
-from twisted.cred.credentials import UsernamePassword
-from twisted.python.filepath import FilePath
-
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
-from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
-from twistedcaldav.sql import AbstractSQLDatabase
-from twistedcaldav.sql import db_prefix
-
-import os
-
-class SQLDirectoryManager(AbstractSQLDatabase):
-    """
-    House keeping operations on the SQL DB, including loading from XML file,
-    and record dumping. This can be used as a standalong DB management tool.
-    """
-    dbType = "DIRECTORYSERVICE"
-    dbFilename = db_prefix + "accounts"
-    dbFormatVersion = "3"
-
-    def __init__(self, path):
-        path = os.path.join(path, SQLDirectoryManager.dbFilename)
-        super(SQLDirectoryManager, self).__init__(path, True)
-
-    def loadFromXML(self, xmlFile):
-        parser = XMLAccountsParser(xmlFile)
-       
-        # Totally wipe existing DB and start from scratch
-        if os.path.exists(self.dbpath):
-            os.remove(self.dbpath)
-
-        self._db_execute("insert into SERVICE (REALM) values (:1)", parser.realm)
-
-        # Now add records to db
-        for item in parser.items.values():
-            for entry in item.itervalues():
-                self._add_to_db(entry)
-        self._db_commit()
-
-    def getRealm(self):
-        for realm in self._db_execute("select REALM from SERVICE"):
-            return realm[0].decode("utf-8")
-        else:
-            return ""
-
-    def listRecords(self, recordType):
-        # Get each account record
-        for shortName, guid, password, name in self._db_execute(
-            """
-            select SHORT_NAME, GUID, PASSWORD, NAME
-              from ACCOUNTS
-             where RECORD_TYPE = :1
-            """, recordType
-        ):
-            # See if we have members
-            members = self.members(shortName)
-                
-            # See if we are a member of any groups
-            groups = self.groups(shortName)
-                
-            # Get calendar user addresses
-            calendarUserAddresses = self.calendarUserAddresses(shortName)
-            
-            yield shortName, guid, password, name, members, groups, calendarUserAddresses
-
-    def getRecord(self, recordType, shortName):
-        # Get individual account record
-        for shortName, guid, password, name in self._db_execute(
-            """
-            select SHORT_NAME, GUID, PASSWORD, NAME
-              from ACCOUNTS
-             where RECORD_TYPE = :1
-               and SHORT_NAME  = :2
-            """, recordType, shortName
-        ):
-            break
-        else:
-            return None
-        
-        # See if we have members
-        members = self.members(shortName)
-            
-        # See if we are a member of any groups
-        groups = self.groups(shortName)
-            
-        # Get calendar user addresses
-        calendarUserAddresses = self.calendarUserAddresses(shortName)
-        
-        return shortName, guid, password, name, members, groups, calendarUserAddresses
-            
-    def members(self, shortName):
-        members = set()
-        for member in self._db_execute(
-            """
-            select MEMBER_RECORD_TYPE, MEMBER_SHORT_NAME
-              from GROUPS
-             where SHORT_NAME = :1
-            """, shortName
-        ):
-            members.add(tuple(member))
-        return members
-
-    def groups(self, shortName):
-        groups = set()
-        for (name,) in self._db_execute(
-            """
-            select SHORT_NAME
-              from GROUPS
-             where MEMBER_SHORT_NAME = :1
-            """, shortName
-        ):
-            groups.add(name)
-        return groups
-
-    def calendarUserAddresses(self, shortName):
-        calendarUserAddresses = set()
-        for (address,) in self._db_execute(
-            """
-            select ADDRESS
-              from ADDRESSES
-             where SHORT_NAME = :1
-            """, shortName
-        ):
-            calendarUserAddresses.add(address)
-        return calendarUserAddresses
-
-    def _add_to_db(self, record):
-        # Do regular account entry
-        recordType = record.recordType
-        shortName = record.shortNames[0]
-        guid = record.guid
-        password = record.password
-        name = record.fullName
-
-        self._db_execute(
-            """
-            insert into ACCOUNTS (RECORD_TYPE, SHORT_NAME, GUID, PASSWORD, NAME)
-            values (:1, :2, :3, :4, :5)
-            """, recordType, shortName, guid, password, name
-        )
-        
-        # Check for members
-        for memberRecordType, memberShortName in record.members:
-            self._db_execute(
-                """
-                insert into GROUPS (SHORT_NAME, MEMBER_RECORD_TYPE, MEMBER_SHORT_NAME)
-                values (:1, :2, :3)
-                """, shortName, memberRecordType, memberShortName
-            )
-                
-        # CUAddress
-        for cuaddr in record.calendarUserAddresses:
-            self._db_execute(
-                """
-                insert into ADDRESSES (ADDRESS, SHORT_NAME)
-                values (:1, :2)
-                """, cuaddr, shortName
-            )
-       
-    def _delete_from_db(self, shortName):
-        """
-        Deletes the specified entry from all dbs.
-        @param name: the name of the resource to delete.
-        @param shortName: the short name of the resource to delete.
-        """
-        self._db_execute("delete from ACCOUNTS  where SHORT_NAME        = :1", shortName)
-        self._db_execute("delete from GROUPS    where SHORT_NAME        = :1", shortName)
-        self._db_execute("delete from GROUPS    where MEMBER_SHORT_NAME = :1", shortName)
-        self._db_execute("delete from ADDRESSES where SHORT_NAME        = :1", shortName)
-    
-    def _db_version(self):
-        """
-        @return: the schema version assigned to this index.
-        """
-        return SQLDirectoryManager.dbFormatVersion
-        
-    def _db_type(self):
-        """
-        @return: the collection type assigned to this index.
-        """
-        return SQLDirectoryManager.dbType
-        
-    def _db_init_data_tables(self, q):
-        """
-        Initialise the underlying database tables.
-        @param q:           a database cursor to use.
-        """
-        #
-        # SERVICE table
-        #
-        q.execute("create table SERVICE (REALM text)")
-
-        #
-        # ACCOUNTS table
-        #
-        q.execute(
-            """
-            create table ACCOUNTS (
-                RECORD_TYPE  text,
-                SHORT_NAME   text,
-                GUID         text,
-                PASSWORD     text,
-                NAME         text
-            )
-            """
-        )
-
-        #
-        # GROUPS table
-        #
-        q.execute(
-            """
-            create table GROUPS (
-                SHORT_NAME          text,
-                MEMBER_RECORD_TYPE  text,
-                MEMBER_SHORT_NAME   text
-            )
-            """
-        )
-
-        #
-        # ADDRESSES table
-        #
-        q.execute(
-            """
-            create table ADDRESSES (
-                ADDRESS     text unique,
-                SHORT_NAME  text
-            )
-            """
-        )
-
-class SQLDirectoryService(DirectoryService):
-    """
-    XML based implementation of L{IDirectoryService}.
-    """
-    baseGUID = "8256E464-35E0-4DBB-A99C-F0E30C231675"
-    realmName = None
-
-    def __repr__(self):
-        return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.manager.dbpath)
-
-    def __init__(self, dbParentPath, xmlFile=None):
-        super(SQLDirectoryService, self).__init__()
-
-        if type(dbParentPath) is str:
-            dbParentPath = FilePath(dbParentPath)
-            
-        self.manager = SQLDirectoryManager(dbParentPath.path)
-        if xmlFile:
-            self.manager.loadFromXML(xmlFile)
-        self.realmName = self.manager.getRealm()
-
-    def recordTypes(self):
-        recordTypes = (
-            DirectoryService.recordType_users,
-            DirectoryService.recordType_groups,
-            DirectoryService.recordType_locations,
-            DirectoryService.recordType_resources,
-        )
-        return recordTypes
-
-    def listRecords(self, recordType):
-        for result in self.manager.listRecords(recordType):
-            yield SQLDirectoryRecord(
-                service               = self,
-                recordType            = recordType,
-                shortName             = result[0],
-                guid                  = result[1],
-                password              = result[2],
-                name                  = result[3],
-                members               = result[4],
-                groups                = result[5],
-                calendarUserAddresses = result[6],
-            )
-
-    def recordWithShortName(self, recordType, shortName):
-        result = self.manager.getRecord(recordType, shortName)
-        if result:
-            return SQLDirectoryRecord(
-                service               = self,
-                recordType            = recordType,
-                shortName             = result[0],
-                guid                  = result[1],
-                password              = result[2],
-                name                  = result[3],
-                members               = result[4],
-                groups                = result[5],
-                calendarUserAddresses = result[6],
-            )
-
-        return None
-
-class SQLDirectoryRecord(DirectoryRecord):
-    """
-    XML based implementation implementation of L{IDirectoryRecord}.
-    """
-    def __init__(self, service, recordType, shortName, guid, password, name, members, groups, calendarUserAddresses):
-        super(SQLDirectoryRecord, self).__init__(
-            service               = service,
-            recordType            = recordType,
-            guid                  = guid,
-            shortNames            = (shortName,),
-            fullName              = name,
-            calendarUserAddresses = calendarUserAddresses,
-        )
-
-        self.password = password
-        self._members = members
-        self._groups  = groups
-
-    def members(self):
-        for recordType, shortName in self._members:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def groups(self):
-        for shortName in self._groups:
-            yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
-
-    def verifyCredentials(self, credentials):
-        if isinstance(credentials, UsernamePassword):
-            return credentials.password == self.password
-
-        return super(SQLDirectoryRecord, self).verifyCredentials(credentials)
-
-if __name__ == '__main__':
-    mgr = SQLDirectoryManager("./")
-    mgr.loadFromXML("test/accounts.xml")

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sudo.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sudo.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/sudo.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -26,6 +26,7 @@
 from twisted.python.filepath import FilePath
 from twisted.cred.credentials import IUsernamePassword, IUsernameHashedPassword
 from twisted.cred.error import UnauthorizedLogin
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 from twext.python.plistlib import readPlist
 
@@ -80,21 +81,26 @@
             entry=entry)
 
 
-    def listRecords(self, recordType):
+    def _listRecords(self, recordType):
         if recordType != SudoDirectoryService.recordType_sudoers:
             raise UnknownRecordTypeError(recordType)
 
         for entry in self._accounts()['users']:
             yield self._recordForEntry(entry)
 
+
+    def listRecords(self, recordType):
+        return succeed(self._listRecords(recordType))
+
     def recordWithShortName(self, recordType, shortName):
         if recordType != SudoDirectoryService.recordType_sudoers:
             raise UnknownRecordTypeError(recordType)
 
         for entry in self._accounts()['users']:
             if entry['username'] == shortName:
-                return self._recordForEntry(entry)
+                return succeed(self._recordForEntry(entry))
 
+    @inlineCallbacks
     def requestAvatarId(self, credentials):
         # FIXME: ?
         # We were checking if principal is enabled; seems unnecessary in current
@@ -105,11 +111,11 @@
             raise UnauthorizedLogin("No such user: %s" % (credentials.credentials.username,))
         sudouser = credentials.authnPrincipal.record
 
-        if credentials.authnPrincipal.record.verifyCredentials(credentials.credentials):
-            return (
+        if (yield credentials.authnPrincipal.record.verifyCredentials(credentials.credentials)):
+            returnValue( (
                 credentials.authnPrincipal.principalURL(),
                 credentials.authzPrincipal.principalURL(),
-                )
+                ) )
         else:
             raise UnauthorizedLogin(
                 "Incorrect credentials for %s" % (sudouser,))
@@ -134,8 +140,8 @@
 
     def verifyCredentials(self, credentials):
         if IUsernamePassword.providedBy(credentials):
-            return credentials.checkPassword(self.password)
+            return succeed(credentials.checkPassword(self.password))
         elif IUsernameHashedPassword.providedBy(credentials):
-            return credentials.checkPassword(self.password)
+            return succeed(credentials.checkPassword(self.password))
 
         return super(SudoDirectoryRecord, self).verifyCredentials(credentials)

Copied: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/accounts2.xml (from rev 4858, CalendarServer/branches/more-deferreds-4/twistedcaldav/directory/test/accounts2.xml)
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/accounts2.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/accounts2.xml	2009-12-15 07:22:08 UTC (rev 4861)
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<!DOCTYPE accounts SYSTEM "../../../conf/accounts.dtd">
+
+<accounts realm="Test" />

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_aggregate.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_aggregate.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_aggregate.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -14,24 +14,61 @@
 # limitations under the License.
 ##
 
-from twistedcaldav.directory.apache import BasicDirectoryService
+from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.directory.xmlfile import XMLDirectoryService
 from twistedcaldav.directory.aggregate import AggregateDirectoryService
 
-from twistedcaldav.directory.test.test_apache import digestRealm, basicUserFile, groupFile
 from twistedcaldav.directory.test.test_xmlfile import xmlFile
 
-import twistedcaldav.directory.test.util
+from twistedcaldav.directory.test.util import DirectoryTestCase
+from twistedcaldav.directory.test.test_xmlfile import XMLFile
 
-apache_prefix = "apache:"
-xml_prefix = "xml:"
+node1_prefix = "node1:"
+node2_prefix = "node2:"
 
+
+class XMLFile2(object):
+    """
+    Dummy values for accounts2.xml
+    """
+    recordTypes = set((
+        DirectoryService.recordType_users,
+        DirectoryService.recordType_groups,
+        DirectoryService.recordType_locations,
+        DirectoryService.recordType_resources
+    ))
+
+    users = {
+        "wsanchez": { "password": "foo",  "guid": None, "addresses": () },
+        "cdaboo"  : { "password": "bar",  "guid": None, "addresses": () },
+        "dreid"   : { "password": "baz",  "guid": None, "addresses": () },
+        "lecroy"  : { "password": "quux", "guid": None, "addresses": () },
+    }
+    users = {}    # XXX: fix accounts2.xml to match the above values
+
+
+    groups = {
+        "managers"   : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "lecroy"),)                                        },
+        "grunts"     : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "wsanchez"),
+                                                                    (DirectoryService.recordType_users, "cdaboo"),
+                                                                    (DirectoryService.recordType_users, "dreid")) },
+        "right_coast": { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "cdaboo"),)                                        },
+        "left_coast" : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "wsanchez"),
+                                                                    (DirectoryService.recordType_users, "dreid"),
+                                                                    (DirectoryService.recordType_users, "lecroy")) },
+    }
+    groups = {}   # XXX: fix accounts2.xml to match the above values
+
+    locations = {}
+    resources = {}
+
+
 testServices = (
-    (apache_prefix, twistedcaldav.directory.test.test_apache.Apache  ),
-    (xml_prefix   , twistedcaldav.directory.test.test_xmlfile.XMLFile),
+    (node1_prefix, XMLFile),
+    (node2_prefix, XMLFile2)
 )
 
-class AggregatedDirectories (twistedcaldav.directory.test.util.DirectoryTestCase):
+class AggregatedDirectories(DirectoryTestCase):
     def _recordTypes(self):
         recordTypes = set()
         for prefix, testClass in testServices:
@@ -65,16 +102,17 @@
         """
         Returns an IDirectoryService.
         """
-        apacheService = BasicDirectoryService(
-            {
-                'realmName' : digestRealm,
-                'userFile' : basicUserFile,
-                'groupFile' : groupFile,
-            }
-        )
-        apacheService.recordTypePrefix = apache_prefix
 
-        xmlService = XMLDirectoryService({'xmlFile' : xmlFile})
-        xmlService.recordTypePrefix = xml_prefix
+        node1Service = XMLDirectoryService({'xmlFile' : xmlFile})
+        node1Service.recordTypePrefix = node1_prefix
 
-        return AggregateDirectoryService((apacheService, xmlService))
+        fn, ext = xmlFile.basename().split(".")
+        otherFile = xmlFile.sibling(fn+'2.'+ext)
+        node2Service = XMLDirectoryService({'xmlFile': otherFile})
+        node2Service.recordTypePrefix = node2_prefix
+
+        return AggregateDirectoryService((node1Service, node2Service))
+
+del DirectoryTestCase           # DirectoryTestCase is a bad test-citizen and
+                                # subclasses TestCase even though it does not
+                                # want to be discovered as such.

Deleted: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_apache.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_apache.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_apache.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,128 +0,0 @@
-##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-import os
-
-from twisted.python.filepath import FilePath
-
-import twistedcaldav.directory.test.util
-from twistedcaldav.directory.apache import BasicDirectoryService, DigestDirectoryService
-from twistedcaldav.directory.directory import DirectoryService
-
-digestRealm = "Test"
-
-basicUserFile  = FilePath(os.path.join(os.path.dirname(__file__), "basic"))
-digestUserFile = FilePath(os.path.join(os.path.dirname(__file__), "digest"))
-groupFile      = FilePath(os.path.join(os.path.dirname(__file__), "groups"))
-
-# FIXME: Add tests for GUID hooey, once we figure out what that means here
-
-class Apache (object):
-    recordTypes = set((
-        DirectoryService.recordType_users,
-        DirectoryService.recordType_groups
-    ))
-
-    users = {
-        "wsanchez": { "password": "foo",  "guid": None, "addresses": () },
-        "cdaboo"  : { "password": "bar",  "guid": None, "addresses": () },
-        "dreid"   : { "password": "baz",  "guid": None, "addresses": () },
-        "lecroy"  : { "password": "quux", "guid": None, "addresses": () },
-    }
-
-    groups = {
-        "managers"   : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "lecroy"),)                                         },
-        "grunts"     : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "wsanchez"),
-                                                                    (DirectoryService.recordType_users, "cdaboo"),
-                                                                    (DirectoryService.recordType_users, "dreid")) },
-        "right_coast": { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "cdaboo"),)                                         },
-        "left_coast" : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "wsanchez"),
-                                                                    (DirectoryService.recordType_users, "dreid"),
-                                                                    (DirectoryService.recordType_users, "lecroy")) },
-    }
-
-    locations = {
-    }
-
-    resources = {
-    }
-
-    def service(self):
-        return self.serviceClass(
-            {
-                'realmName' : digestRealm,
-                'userFile' : self.userFile(),
-                'groupFile' : self.groupFile(),
-            }
-        )
-
-    userFileName = None
-
-    def userFile(self):
-        if not hasattr(self, "_userFile"):
-            if self.userFileName is None:
-                raise NotImplementedError("Test subclass needs to specify userFileName.")
-            self._userFile = FilePath(self.mktemp())
-            basicUserFile.copyTo(self._userFile)
-        return self._userFile
-
-    def groupFile(self):
-        if not hasattr(self, "_groupFile"):
-            self._groupFile = FilePath(self.mktemp())
-            groupFile.copyTo(self._groupFile)
-        return self._groupFile
-
-    def test_changedGroupFile(self):
-        self.groupFile().open("w").write("grunts: wsanchez\n")
-        self.assertEquals(self.recordNames(DirectoryService.recordType_groups), set(("grunts",)))
-
-    def test_recordTypes_user(self):
-        """
-        IDirectoryService.recordTypes(userFile)
-        """
-        self.assertEquals(set(self.serviceClass({'realmName':digestRealm, 'userFile':self.userFile()}).recordTypes()), set((DirectoryService.recordType_users,)))
-
-    userEntry = None
-
-    def test_changedUserFile(self):
-        if self.userEntry is None:
-            raise NotImplementedError("Test subclass needs to specify userEntry.")
-        self.userFile().open("w").write(self.userEntry[1])
-        self.assertEquals(self.recordNames(DirectoryService.recordType_users), set((self.userEntry[0],)))
-
-class Basic (Apache, twistedcaldav.directory.test.util.BasicTestCase,
-    twistedcaldav.directory.test.util.NonCachingTestCase):
-    """
-    Test Apache-Compatible UserFile/GroupFile directory implementation.
-    """
-    serviceClass = BasicDirectoryService
-
-    userFileName = basicUserFile
-    userEntry = ("wsanchez", "wsanchez:Cytm0Bwm7CPJs\n")
-
-class Digest (Apache, twistedcaldav.directory.test.util.DigestTestCase,
-    twistedcaldav.directory.test.util.NonCachingTestCase):
-    """
-    Test Apache-Compatible DigestFile/GroupFile directory implementation.
-    """
-    serviceClass = DigestDirectoryService
-
-    userFileName = digestUserFile
-    userEntry = ("wsanchez", "wsanchez:Test:decbe233ab3d997cacc2fc058b19db8c\n")
-
-    def test_verifyCredentials_digest(self):
-        raise NotImplementedError() # Use super's implementation
-    test_verifyCredentials_digest.todo = "unimplemented"

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_cachedirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_cachedirectory.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_cachedirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -14,6 +14,7 @@
 # limitations under the License.
 ##
 
+from twisted.internet.defer import inlineCallbacks
 from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
     CachingDirectoryRecord, DictRecordTypeCache
 from twistedcaldav.test.util import TestCase
@@ -144,100 +145,105 @@
             ],
         })
 
+    @inlineCallbacks
     def verifyRecords(self, recordType, expectedGUIDs):
-        
-        records = self.service.listRecords(recordType)
-        recordGUIDs = set([record.guid for record in records])
+        records = yield self.service.listRecords(recordType)
+        recordGUIDs = set([(yield record).guid for record in records])
         self.assertEqual(recordGUIDs, expectedGUIDs)
 
 class GUIDLookups(CachingDirectoryTest):
-    
+
+    @inlineCallbacks
     def test_emptylist(self):
         self.dummyRecords()
 
-        self.verifyRecords(DirectoryService.recordType_users, set())
-        self.verifyRecords(DirectoryService.recordType_groups, set())
-        self.verifyRecords(DirectoryService.recordType_resources, set())
-        self.verifyRecords(DirectoryService.recordType_locations, set())
+        yield self.verifyRecords(DirectoryService.recordType_users, set())
+        yield self.verifyRecords(DirectoryService.recordType_groups, set())
+        yield self.verifyRecords(DirectoryService.recordType_resources, set())
+        yield self.verifyRecords(DirectoryService.recordType_locations, set())
     
+    @inlineCallbacks
     def test_cacheoneguid(self):
         self.dummyRecords()
 
-        self.assertTrue(self.service.recordWithGUID(self.guidForShortName("user01")) is not None)
+        self.assertTrue((yield self.service.recordWithGUID(self.guidForShortName("user01"))) is not None)
         self.assertTrue(self.service.queried)
-        self.verifyRecords(DirectoryService.recordType_users, set((
+        yield self.verifyRecords(DirectoryService.recordType_users, set((
             self.guidForShortName("user01"),
         )))
-        self.verifyRecords(DirectoryService.recordType_groups, set())
-        self.verifyRecords(DirectoryService.recordType_resources, set())
-        self.verifyRecords(DirectoryService.recordType_locations, set())
+        yield self.verifyRecords(DirectoryService.recordType_groups, set())
+        yield self.verifyRecords(DirectoryService.recordType_resources, set())
+        yield self.verifyRecords(DirectoryService.recordType_locations, set())
 
         # Make sure it really is cached and won't cause another query
         self.service.queried = False
-        self.assertTrue(self.service.recordWithGUID(self.guidForShortName("user01")) is not None)
+        self.assertTrue((yield self.service.recordWithGUID(self.guidForShortName("user01"))) is not None)
         self.assertFalse(self.service.queried)
         
+    @inlineCallbacks
     def test_cacheoneshortname(self):
         self.dummyRecords()
 
-        self.assertTrue(self.service.recordWithShortName(
+        self.assertTrue((yield self.service.recordWithShortName(
             DirectoryService.recordType_users,
             "user02"
-        ) is not None)
+        )) is not None)
         self.assertTrue(self.service.queried)
-        self.verifyRecords(DirectoryService.recordType_users, set((
+        yield self.verifyRecords(DirectoryService.recordType_users, set((
             self.guidForShortName("user02"),
         )))
-        self.verifyRecords(DirectoryService.recordType_groups, set())
-        self.verifyRecords(DirectoryService.recordType_resources, set())
-        self.verifyRecords(DirectoryService.recordType_locations, set())
+        yield self.verifyRecords(DirectoryService.recordType_groups, set())
+        yield self.verifyRecords(DirectoryService.recordType_resources, set())
+        yield self.verifyRecords(DirectoryService.recordType_locations, set())
 
         # Make sure it really is cached and won't cause another query
         self.service.queried = False
-        self.assertTrue(self.service.recordWithShortName(
+        self.assertTrue((yield self.service.recordWithShortName(
             DirectoryService.recordType_users,
             "user02"
-        ) is not None)
+        )) is not None)
         self.assertFalse(self.service.queried)
 
+    @inlineCallbacks
     def test_cacheoneemail(self):
         self.dummyRecords()
 
-        self.assertTrue(self.service.recordWithCalendarUserAddress(
+        self.assertTrue((yield self.service.recordWithCalendarUserAddress(
             "mailto:user03 at example.com"
-        ) is not None)
+        )) is not None)
         self.assertTrue(self.service.queried)
-        self.verifyRecords(DirectoryService.recordType_users, set((
+        yield self.verifyRecords(DirectoryService.recordType_users, set((
             self.guidForShortName("user03"),
         )))
-        self.verifyRecords(DirectoryService.recordType_groups, set())
-        self.verifyRecords(DirectoryService.recordType_resources, set())
-        self.verifyRecords(DirectoryService.recordType_locations, set())
+        yield self.verifyRecords(DirectoryService.recordType_groups, set())
+        yield self.verifyRecords(DirectoryService.recordType_resources, set())
+        yield self.verifyRecords(DirectoryService.recordType_locations, set())
 
         # Make sure it really is cached and won't cause another query
         self.service.queried = False
-        self.assertTrue(self.service.recordWithCalendarUserAddress(
+        self.assertTrue((yield self.service.recordWithCalendarUserAddress(
             "mailto:user03 at example.com"
-        ) is not None)
+        )) is not None)
         self.assertFalse(self.service.queried)
 
+    @inlineCallbacks
     def test_cacheoneauthid(self):
         self.dummyRecords()
 
-        self.assertTrue(self.service.recordWithAuthID(
+        self.assertTrue((yield self.service.recordWithAuthID(
             "Kerberos:user03 at example.com"
-        ) is not None)
+        )) is not None)
         self.assertTrue(self.service.queried)
-        self.verifyRecords(DirectoryService.recordType_users, set((
+        yield self.verifyRecords(DirectoryService.recordType_users, set((
             self.guidForShortName("user03"),
         )))
-        self.verifyRecords(DirectoryService.recordType_groups, set())
-        self.verifyRecords(DirectoryService.recordType_resources, set())
-        self.verifyRecords(DirectoryService.recordType_locations, set())
+        yield self.verifyRecords(DirectoryService.recordType_groups, set())
+        yield self.verifyRecords(DirectoryService.recordType_resources, set())
+        yield self.verifyRecords(DirectoryService.recordType_locations, set())
 
         # Make sure it really is cached and won't cause another query
         self.service.queried = False
-        self.assertTrue(self.service.recordWithAuthID(
+        self.assertTrue((yield self.service.recordWithAuthID(
             "Kerberos:user03 at example.com"
-        ) is not None)
+        )) is not None)
         self.assertFalse(self.service.queried)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_calendar.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_calendar.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_calendar.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -32,6 +32,7 @@
     """
     Directory service provisioned principals.
     """
+    @inlineCallbacks
     def setUp(self):
         super(ProvisionedCalendars, self).setUp()
         
@@ -48,18 +49,20 @@
 
         provisioningResource = DirectoryPrincipalProvisioningResource(url, self.directoryService)
 
-        self.site.resource.putChild("principals", provisioningResource)
+        yield self.site.resource.putChild("principals", provisioningResource)
 
-        self.setupCalendars()
+        yield self.setupCalendars()
 
         self.site.resource.setAccessControlList(davxml.ACL())
 
+    @inlineCallbacks
     def setupCalendars(self):
-        calendarCollection = CalendarHomeProvisioningFile(
+        calendarCollection = (yield CalendarHomeProvisioningFile.fetch(
+            None,
             os.path.join(self.docroot, "calendars"),
             self.directoryService,
             "/calendars/"
-        )
+        ))
         self.site.resource.putChild("calendars", calendarCollection)
 
     def test_NonExistentCalendarHome(self):
@@ -71,6 +74,7 @@
         request = SimpleRequest(self.site, "GET", "/calendars/users/12345/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
     def test_ExistentCalendarHome(self):
 
@@ -81,6 +85,7 @@
         request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
     def test_ExistentCalendar(self):
 
@@ -91,6 +96,7 @@
         request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/calendar/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
     def test_ExistentInbox(self):
 
@@ -101,6 +107,7 @@
         request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/inbox/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
     @inlineCallbacks
     def test_CalendarTranspProperty(self):

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_guidchange.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_guidchange.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_guidchange.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -19,6 +19,7 @@
 
 import os
 
+from twisted.internet.defer import inlineCallbacks
 from twisted.web2.dav import davxml
 from twisted.web2.dav.resource import AccessDeniedError
 from twisted.web2.test.test_server import SimpleRequest
@@ -76,6 +77,7 @@
 
         homeResource = "/calendars/users/cdaboo/"
         
+        @inlineCallbacks
         def privs1(result):
             # Change GUID in record
             fd = open(self.xmlfile, "w")
@@ -85,13 +87,13 @@
 
             # Force re-read of records (not sure why _fileInfo has to be wiped here...)
             self.directoryService._fileInfo = (0, 0)
-            self.directoryService.recordWithShortName(DirectoryService.recordType_users, "cdaboo")
+            yield self.directoryService.recordWithShortName(DirectoryService.recordType_users, "cdaboo")
 
             # Now force the calendar home resource to be reset
             self.resetCalendars()
             
             # Make sure new user cannot access old user's calendar home
-            return self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + newUID + "/"), davxml.Write, False)
+            returnValue((yield self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + newUID + "/"), davxml.Write, False)))
             
         # Make sure current user has access to their calendar home
         d = self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + oldUID + "/"), davxml.Write, True)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_opendirectory.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_opendirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -20,6 +20,7 @@
     pass
 else:
     import twisted.web2.auth.digest
+    from twisted.internet.defer import inlineCallbacks
     import twistedcaldav.directory.test.util
     from twistedcaldav.directory.directory import DirectoryService
     from twistedcaldav.directory.appleopendirectory import OpenDirectoryRecord
@@ -77,6 +78,7 @@
             )
             self.assertEquals(record.fullName, "")
 
+        @inlineCallbacks
         def test_invalidODDigest(self):
             record = OpenDirectoryRecord(
                 service               = self.service(),
@@ -96,8 +98,9 @@
             digestFields = {}
             digested = twisted.web2.auth.digest.DigestedCredentials("user", "GET", "example.com", digestFields, None)
 
-            self.assertFalse(record.verifyCredentials(digested))
+            self.assertFalse((yield record.verifyCredentials(digested)))
 
+        @inlineCallbacks
         def test_validODDigest(self):
             record = OpenDirectoryRecord(
                 service               = self.service(),
@@ -136,13 +139,14 @@
             record.digestcache["/"] = response
             digested = twisted.web2.auth.digest.DigestedCredentials("user", "GET", "example.com", digestFields, None)
 
-            self.assertTrue(record.verifyCredentials(digested))
+            self.assertTrue((yield record.verifyCredentials(digested)))
 
             # This should be defaulted
             del digestFields["algorithm"]
 
-            self.assertTrue(record.verifyCredentials(digested))
+            self.assertTrue((yield record.verifyCredentials(digested)))
 
+        @inlineCallbacks
         def test_queryDirectorySingleGUID(self):
             """ Test for lookup on existing and non-existing GUIDs """
 
@@ -162,11 +166,12 @@
                 return results
 
             recordTypes = [DirectoryService.recordType_users, DirectoryService.recordType_groups, DirectoryService.recordType_locations, DirectoryService.recordType_resources]
-            self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
-            self.assertTrue(self.service().recordWithGUID("1234567890"))
-            self.assertFalse(self.service().recordWithGUID("987654321"))
+            yield self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
+            self.assertTrue((yield self.service().recordWithGUID("1234567890")))
+            self.assertFalse((yield self.service().recordWithGUID("987654321")))
 
 
+        @inlineCallbacks
         def test_queryDirectoryDuplicateGUIDs(self):
             """ Test for lookup on duplicate GUIDs, ensuring they don't get
                 faulted in """
@@ -192,9 +197,10 @@
                 return results
 
             recordTypes = [DirectoryService.recordType_users, DirectoryService.recordType_groups, DirectoryService.recordType_locations, DirectoryService.recordType_resources]
-            self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
-            self.assertFalse(self.service().recordWithGUID("1234567890"))
+            yield self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
+            self.assertFalse((yield self.service().recordWithGUID("1234567890")))
 
+        @inlineCallbacks
         def test_queryDirectoryLocalUsers(self):
             """ Test for lookup on local users, ensuring they don't get
                 faulted in """
@@ -222,10 +228,10 @@
                 return results
 
             recordTypes = [DirectoryService.recordType_users, DirectoryService.recordType_groups, DirectoryService.recordType_locations, DirectoryService.recordType_resources]
-            self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
-            self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "987654321", lookupMethod=lookupMethod)
-            self.assertFalse(self.service().recordWithGUID("1234567890"))
-            self.assertTrue(self.service().recordWithGUID("987654321"))
+            yield self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
+            yield self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "987654321", lookupMethod=lookupMethod)
+            self.assertFalse((yield self.service().recordWithGUID("1234567890")))
+            self.assertTrue((yield self.service().recordWithGUID("987654321")))
 
         def test_queryDirectoryEmailAddresses(self):
             """ Test to ensure we only ask for users when email address is
@@ -239,4 +245,6 @@
                 return []
 
             recordTypes = [DirectoryService.recordType_users, DirectoryService.recordType_groups, DirectoryService.recordType_locations, DirectoryService.recordType_resources]
-            self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_CUA, "mailto:user1 at example.com", lookupMethod=lookupMethod)
+            yield self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_CUA, "mailto:user1 at example.com", lookupMethod=lookupMethod)
+            yield self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_GUID, "1234567890", lookupMethod=lookupMethod)
+            self.assertFalse((yield self.service().recordWithGUID("1234567890")))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_principal.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_principal.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -17,7 +17,7 @@
 import os
 
 from twisted.cred.credentials import UsernamePassword
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web2.dav import davxml
 from twisted.web2.dav.fileop import rmdir
 from twisted.web2.dav.resource import AccessDeniedError
@@ -25,11 +25,9 @@
 
 from twistedcaldav.static import CalendarHomeProvisioningFile
 from twistedcaldav.config import config
-from twistedcaldav.directory.apache import BasicDirectoryService, DigestDirectoryService
 from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.directory.test.test_apache import basicUserFile, digestUserFile, groupFile, digestRealm
 from twistedcaldav.directory.xmlfile import XMLDirectoryService
-from twistedcaldav.directory.test.test_xmlfile import xmlFile
+from twistedcaldav.directory.test.test_xmlfile import xmlFile, XMLFile
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
 from twistedcaldav.directory.principal import DirectoryPrincipalTypeProvisioningResource
 from twistedcaldav.directory.principal import DirectoryPrincipalResource
@@ -48,20 +46,8 @@
         super(ProvisionedPrincipals, self).setUp()
 
         self.directoryServices = (
-            BasicDirectoryService(
-                {
-                    'realmName' : digestRealm,
-                    'userFile' : basicUserFile,
-                    'groupFile' : groupFile,
-                }
-            ),
-            DigestDirectoryService(
-                {
-                    'realmName' : digestRealm,
-                    'userFile' : digestUserFile,
-                    'groupFile' : groupFile,
-                }
-            ),
+            # realm 'Test' with 'digest' users
+            # realm 'Test' with 'basic' users
             XMLDirectoryService(
                 {
                     'xmlFile' : xmlFile,
@@ -81,6 +67,7 @@
 
             self.principalRootResources[directory.__class__.__name__] = provisioningResource
 
+    @inlineCallbacks
     def test_hierarchy(self):
         """
         DirectoryPrincipalProvisioningResource.listChildren(),
@@ -105,12 +92,13 @@
             principalCollections = provisioningResource.principalCollections()
             self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
 
-            recordTypes = set(provisioningResource.listChildren())
+            children = (yield provisioningResource.listChildren())
+            recordTypes = set(children)
             self.assertEquals(recordTypes, set(directory.recordTypes()))
 
             for recordType in recordTypes:
                 #print "   -> %s" % (recordType,)
-                typeResource = provisioningResource.getChild(recordType)
+                typeResource = (yield provisioningResource.getChild(recordType))
                 self.failUnless(isinstance(typeResource, DirectoryPrincipalTypeProvisioningResource))
 
                 typeURL = provisioningURL + recordType + "/"
@@ -118,13 +106,12 @@
 
                 principalCollections = typeResource.principalCollections()
                 self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
+                shortNames = set((yield typeResource.listChildren()))
+                self.assertEquals(shortNames, set(r.shortNames[0] for r in (yield directory.listRecords(recordType))))
 
-                shortNames = set(typeResource.listChildren())
-                self.assertEquals(shortNames, set(r.shortNames[0] for r in directory.listRecords(recordType)))
-
                 for shortName in shortNames:
                     #print "     -> %s" % (shortName,)
-                    recordResource = typeResource.getChild(shortName)
+                    recordResource = (yield typeResource.getChild(shortName))
                     self.failUnless(isinstance(recordResource, DirectoryPrincipalResource))
 
                     recordURL = typeURL + shortName + "/"
@@ -132,27 +119,31 @@
 
                     principalCollections = recordResource.principalCollections()
                     self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
+                    
 
+    @inlineCallbacks
     def test_allRecords(self):
         """
         Test of a test routine...
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             self.assertEquals(recordResource.record, record)
 
     ##
     # DirectoryPrincipalProvisioningResource
     ##
 
+    @inlineCallbacks
     def test_principalForShortName(self):
         """
         DirectoryPrincipalProvisioningResource.principalForShortName()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            principal = provisioningResource.principalForShortName(recordType, record.shortNames[0])
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
+            principal = (yield provisioningResource.principalForShortName(recordType, record.shortNames[0]))
             self.failIf(principal is None)
             self.assertEquals(record, principal.record)
 
+    @inlineCallbacks
     def test_principalForUser(self):
         """
         DirectoryPrincipalProvisioningResource.principalForUser()
@@ -160,11 +151,12 @@
         for directory in self.directoryServices:
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
-            for user in directory.listRecords(DirectoryService.recordType_users):
-                userResource = provisioningResource.principalForUser(user.shortNames[0])
+            for user in (yield directory.listRecords(DirectoryService.recordType_users)):
+                userResource = (yield provisioningResource.principalForUser(user.shortNames[0]))
                 self.failIf(userResource is None)
                 self.assertEquals(user, userResource.record)
 
+    @inlineCallbacks
     def test_principalForAuthID(self):
         """
         DirectoryPrincipalProvisioningResource.principalForAuthID()
@@ -172,35 +164,57 @@
         for directory in self.directoryServices:
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
-            for user in directory.listRecords(DirectoryService.recordType_users):
+            for user in (yield directory.listRecords(DirectoryService.recordType_users)):
                 creds = UsernamePassword(user.shortNames[0], "bogus")
-                userResource = provisioningResource.principalForAuthID(creds)
+                userResource = (yield provisioningResource.principalForAuthID(creds))
                 self.failIf(userResource is None)
                 self.assertEquals(user, userResource.record)
 
+    @inlineCallbacks
     def test_principalForUID(self):
         """
         DirectoryPrincipalProvisioningResource.principalForUID()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            principal = provisioningResource.principalForUID(record.uid)
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
+            principal = (yield provisioningResource.principalForUID(record.uid))
             self.failIf(principal is None)
             self.assertEquals(record, principal.record)
 
+    @inlineCallbacks
     def test_principalForRecord(self):
         """
         DirectoryPrincipalProvisioningResource.principalForRecord()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            principal = provisioningResource.principalForRecord(record)
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
+            principal = (yield provisioningResource.principalForRecord(record))
             self.failIf(principal is None)
             self.assertEquals(record, principal.record)
 
+
+    @inlineCallbacks
+    def _warmCache(self):
+        """
+        Load some records into the cache so that listRecords() will actually
+        return something from an XMLDirectoryService.
+
+        XXX TODO this method shouldn't really be necessary; instead, we should
+        be able to express the appropriate 'list' functionality to the
+        IDirectoryService API and not rely on XMLDirectoryService's caching
+        behavior.
+        """
+        for svc in self.directoryServices:
+            for user in XMLFile.users:
+                record = (yield svc.recordWithShortName(DirectoryService.recordType_users, user))
+
+
+    @inlineCallbacks
     def test_principalForCalendarUserAddress(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+
+        yield self._warmCache()
+        for (provisioningResource, recordType, recordResource, record) in (yield self._allRecords()):
             principalURL = recordResource.principalURL()
             if principalURL.endswith("/"):
                 alternateURL = principalURL[:-1]
@@ -208,7 +222,7 @@
                 alternateURL = principalURL + "/"
 
             for address in tuple(record.calendarUserAddresses) + (principalURL, alternateURL):
-                principal = provisioningResource.principalForCalendarUserAddress(address)
+                principal = (yield provisioningResource.principalForCalendarUserAddress(address))
                 if record.enabledForCalendaring:
                     self.failIf(principal is None)
                     self.assertEquals(record, principal.record)
@@ -216,17 +230,18 @@
                     self.failIf(principal is not None)
 
         # Explicitly check the disabled record
-        self.failIf(provisioningResource.principalForCalendarUserAddress("mailto:nocalendar at example.com") is not None)
-        self.failIf(provisioningResource.principalForCalendarUserAddress("urn:uuid:543D28BA-F74F-4D5F-9243-B3E3A61171E5") is not None)
-        self.failIf(provisioningResource.principalForCalendarUserAddress("/principals/users/nocalendar/") is not None)
-        self.failIf(provisioningResource.principalForCalendarUserAddress("/principals/__uids__/543D28BA-F74F-4D5F-9243-B3E3A61171E5/") is not None)
+        self.failIf((yield provisioningResource.principalForCalendarUserAddress("mailto:nocalendar at example.com")) is not None)
+        self.failIf((yield provisioningResource.principalForCalendarUserAddress("urn:uuid:543D28BA-F74F-4D5F-9243-B3E3A61171E5")) is not None)
+        self.failIf((yield provisioningResource.principalForCalendarUserAddress("/principals/users/nocalendar/")) is not None)
+        self.failIf((yield provisioningResource.principalForCalendarUserAddress("/principals/__uids__/543D28BA-F74F-4D5F-9243-B3E3A61171E5/")) is not None)
 
+    @inlineCallbacks
     def test_enabledForCalendaring(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            principal = provisioningResource.principalForRecord(record)
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
+            principal = (yield provisioningResource.principalForRecord(record))
             self.failIf(principal is None)
             if record.enabledForCalendaring:
                 self.assertTrue(isinstance(principal, DirectoryCalendarPrincipalResource))
@@ -234,6 +249,7 @@
                 self.assertTrue(isinstance(principal, DirectoryPrincipalResource))
                 self.assertFalse(isinstance(principal, DirectoryCalendarPrincipalResource))
 
+    @inlineCallbacks
     def test_enabledAsOrganizer(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
@@ -242,10 +258,10 @@
         ok_types = (
             DirectoryService.recordType_users,
         )
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             
             if record.enabledForCalendaring:
-                principal = provisioningResource.principalForRecord(record)
+                principal = (yield provisioningResource.principalForRecord(record))
                 self.failIf(principal is None)
                 self.assertEqual(principal.enabledAsOrganizer(), recordType in ok_types)
 
@@ -258,10 +274,10 @@
             DirectoryService.recordType_locations,
             DirectoryService.recordType_resources,
         )
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             
             if record.enabledForCalendaring:
-                principal = provisioningResource.principalForRecord(record)
+                principal = (yield provisioningResource.principalForRecord(record))
                 self.failIf(principal is None)
                 self.assertEqual(principal.enabledAsOrganizer(), recordType in ok_types)
 
@@ -271,20 +287,22 @@
     # DirectoryPrincipalResource
     ##
 
+    @inlineCallbacks
     def test_cacheNotifier(self):
         """
         Each DirectoryPrincipalResource should have a cacheNotifier attribute
         that is an instance of XattrCacheChangeNotifier
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             self.failUnless(isinstance(recordResource.cacheNotifier,
                                        DisabledCacheNotifier))
 
+    @inlineCallbacks
     def test_displayName(self):
         """
         DirectoryPrincipalResource.displayName()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             self.failUnless(recordResource.displayName())
 
     @inlineCallbacks
@@ -292,39 +310,42 @@
         """
         DirectoryPrincipalResource.groupMembers()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             members = yield recordResource.groupMembers()
-            self.failUnless(set(record.members()).issubset(set(r.record for r in members)))
+            self.failUnless(set((yield record.members())).issubset(set(r.record for r in members)))
 
     @inlineCallbacks
     def test_groupMemberships(self):
         """
         DirectoryPrincipalResource.groupMemberships()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             memberships = yield recordResource.groupMemberships()
-            self.failUnless(set(record.groups()).issubset(set(r.record for r in memberships if hasattr(r, "record"))))
+            self.failUnless(set((yield record.groups())).issubset(set(r.record for r in memberships if hasattr(r, "record"))))
 
+    @inlineCallbacks
     def test_principalUID(self):
         """
         DirectoryPrincipalResource.principalUID()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             self.assertEquals(record.guid, recordResource.principalUID())
 
+    @inlineCallbacks
     def test_calendarUserAddresses(self):
         """
         DirectoryPrincipalResource.calendarUserAddresses()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             if record.enabledForCalendaring:
                 self.failUnless(
                     (
                         set((recordResource.principalURL(),)) |
                         set(record.calendarUserAddresses)
-                    ).issubset(set(recordResource.calendarUserAddresses()))
+                    ).issubset(set((yield recordResource.calendarUserAddresses())))
                 )
 
+    @inlineCallbacks
     def test_calendarHomeURLs(self):
         """
         DirectoryPrincipalResource.calendarHomeURLs(),
@@ -332,11 +353,12 @@
         DirectoryPrincipalResource.scheduleOutboxURL()
         """
         # No calendar home provisioner should result in no calendar homes.
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             if record.enabledForCalendaring:
-                self.failIf(tuple(recordResource.calendarHomeURLs()))
-                self.failIf(recordResource.scheduleInboxURL())
-                self.failIf(recordResource.scheduleOutboxURL())
+                urls = (yield recordResource.calendarHomeURLs())
+                self.assertEqual(len(urls), 0)
+                self.failIf((yield recordResource.scheduleInboxURL()))
+                self.failIf((yield recordResource.scheduleOutboxURL()))
 
         # Need to create a calendar home provisioner for each service.
         calendarRootResources = {}
@@ -349,20 +371,21 @@
                 rmdir(path)
             os.mkdir(path)
 
-            provisioningResource = CalendarHomeProvisioningFile(path, directory, url)
+            provisioningResource = (yield CalendarHomeProvisioningFile.fetch(
+                None, path, directory, url))
 
             calendarRootResources[directory.__class__.__name__] = provisioningResource
 
         # Calendar home provisioners should result in calendar homes.
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
             if record.enabledForCalendaring:
-                homeURLs = tuple(recordResource.calendarHomeURLs())
+                homeURLs = tuple((yield recordResource.calendarHomeURLs()))
                 self.failUnless(homeURLs)
 
                 calendarRootURL = calendarRootResources[record.service.__class__.__name__].url()
 
-                inboxURL = recordResource.scheduleInboxURL()
-                outboxURL = recordResource.scheduleOutboxURL()
+                inboxURL = (yield recordResource.scheduleInboxURL())
+                outboxURL = (yield recordResource.scheduleOutboxURL())
 
                 self.failUnless(inboxURL)
                 self.failUnless(outboxURL)
@@ -383,13 +406,15 @@
                 self.failIf(inboxURL)
                 self.failIf(outboxURL)
 
+
+
     @inlineCallbacks
     def test_defaultAccessControlList_principals(self):
         """
         Default access controls for principals.
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            for args in _authReadOnlyPrivileges(self, recordResource, recordResource.principalURL()):
+        for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
+            for args in (yield _authReadOnlyPrivileges(self, recordResource, recordResource.principalURL())):
                 yield self._checkPrivileges(*args)
 
     @inlineCallbacks
@@ -401,14 +426,14 @@
             #print "\n -> %s" % (directory.__class__.__name__,)
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
-            for args in _authReadOnlyPrivileges(self, provisioningResource, provisioningResource.principalCollectionURL()):
+            for args in (yield _authReadOnlyPrivileges(self, provisioningResource, provisioningResource.principalCollectionURL())):
                 yield self._checkPrivileges(*args)
-
-            for recordType in provisioningResource.listChildren():
+            children = yield provisioningResource.listChildren()
+            for recordType in children:
                 #print "   -> %s" % (recordType,)
-                typeResource = provisioningResource.getChild(recordType)
+                typeResource = (yield provisioningResource.getChild(recordType))
 
-                for args in _authReadOnlyPrivileges(self, typeResource, typeResource.principalCollectionURL()):
+                for args in (yield _authReadOnlyPrivileges(self, typeResource, typeResource.principalCollectionURL())):
                     yield self._checkPrivileges(*args)
 
     def test_propertyToField(self):
@@ -421,7 +446,7 @@
             def qname(self):
                 return self.ns, self.name
 
-        provisioningResource = self.principalRootResources['BasicDirectoryService']
+        provisioningResource = self.principalRootResources['XMLDirectoryService']
 
         expected = (
             ("DAV:", "displayname", "morgen", "fullName", "morgen"),
@@ -447,6 +472,7 @@
                 (field, converted)
             )
 
+    @inlineCallbacks
     def _allRecords(self):
         """
         @return: an iterable of tuples
@@ -457,12 +483,14 @@
             C{record} is the directory service record
             for each record in each directory in C{directoryServices}.
         """
+        results = []
         for directory in self.directoryServices:
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
             for recordType in directory.recordTypes():
-                for record in directory.listRecords(recordType):
-                    recordResource = provisioningResource.principalForRecord(record)
-                    yield provisioningResource, recordType, recordResource, record
+                for record in (yield directory.listRecords(recordType)):
+                    recordResource = (yield provisioningResource.principalForRecord(record))
+                    results.append((provisioningResource, recordType, recordResource, record))
+        returnValue(results)
 
     def _checkPrivileges(self, resource, url, principal, privilege, allowed):
         request = SimpleRequest(self.site, "GET", "/")
@@ -489,9 +517,10 @@
         d.addCallback(gotResource)
         return d
 
+ at inlineCallbacks
 def _authReadOnlyPrivileges(self, resource, url):
     items = []
-    for provisioningResource, recordType, recordResource, record in self._allRecords():
+    for provisioningResource, recordType, recordResource, record in (yield self._allRecords()):
         if recordResource == resource:
             items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Read()  , True ))
             items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Write() , True ))
@@ -501,5 +530,7 @@
     items.append(( davxml.Unauthenticated() , davxml.Read()  , False ))
     items.append(( davxml.Unauthenticated() , davxml.Write() , False ))
             
+    results = []
     for principal, privilege, allowed in items:
-        yield resource, url, principal, privilege, allowed
+        results.append((resource, url, principal, privilege, allowed))
+    returnValue(results)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -53,42 +53,38 @@
         provisioningResource = self.principalRootResources[self.directoryService.__class__.__name__]
         return provisioningResource.principalForShortName(type, name)
 
+    @inlineCallbacks
     def _groupMembersTest(self, recordType, recordName, subPrincipalName, expectedMembers):
-        def gotMembers(members):
-            memberNames = set([p.displayName() for p in members])
-            self.assertEquals(memberNames, set(expectedMembers))
 
-        principal = self._getPrincipalByShortName(recordType, recordName)
+        principal = (yield self._getPrincipalByShortName(recordType, recordName))
         if subPrincipalName is not None:
-            principal = principal.getChild(subPrincipalName)
+            principal = (yield principal.getChild(subPrincipalName))
 
-        d = principal.expandedGroupMembers()
-        d.addCallback(gotMembers)
-        return d
+        members = (yield principal.expandedGroupMembers())
+        memberNames = set([(yield p.displayName()) for p in members])
+        self.assertEquals(memberNames, set(expectedMembers))
 
+    @inlineCallbacks
     def _groupMembershipsTest(self, recordType, recordName, subPrincipalName, expectedMemberships):
-        def gotMemberships(memberships):
-            uids = set([p.principalUID() for p in memberships])
-            self.assertEquals(uids, set(expectedMemberships))
 
-        principal = self._getPrincipalByShortName(recordType, recordName)
+        principal = (yield self._getPrincipalByShortName(recordType, recordName))
         if subPrincipalName is not None:
-            principal = principal.getChild(subPrincipalName)
+            principal = (yield principal.getChild(subPrincipalName))
 
-        d = principal.groupMemberships()
-        d.addCallback(gotMemberships)
-        return d
+        memberships = list((yield principal.groupMemberships()))
+        uids = set([p.principalUID() for p in memberships])
+        self.assertEquals(uids, set(expectedMemberships))
     
     @inlineCallbacks
     def _addProxy(self, principal, subPrincipalName, proxyPrincipal):
 
         if isinstance(principal, tuple):
-            principal = self._getPrincipalByShortName(principal[0], principal[1])
-        principal = principal.getChild(subPrincipalName)
+            principal = (yield self._getPrincipalByShortName(principal[0], principal[1]))
+        principal = (yield principal.getChild(subPrincipalName))
         members = (yield principal.groupMembers())
 
         if isinstance(proxyPrincipal, tuple):
-            proxyPrincipal = self._getPrincipalByShortName(proxyPrincipal[0], proxyPrincipal[1])
+            proxyPrincipal = (yield self._getPrincipalByShortName(proxyPrincipal[0], proxyPrincipal[1]))
         members.add(proxyPrincipal)
         
         yield principal.setGroupMemberSetPrincipals(members)
@@ -96,11 +92,11 @@
     @inlineCallbacks
     def _removeProxy(self, recordType, recordName, subPrincipalName, proxyRecordType, proxyRecordName):
 
-        principal = self._getPrincipalByShortName(recordType, recordName)
-        principal = principal.getChild(subPrincipalName)
+        principal = (yield self._getPrincipalByShortName(recordType, recordName))
+        principal = (yield principal.getChild(subPrincipalName))
         members = (yield principal.groupMembers())
 
-        proxyPrincipal = self._getPrincipalByShortName(proxyRecordType, proxyRecordName)
+        proxyPrincipal = (yield self._getPrincipalByShortName(proxyRecordType, proxyRecordName))
         for p in members:
             if p.principalUID() == proxyPrincipal.principalUID():
                 members.remove(p)
@@ -112,16 +108,16 @@
     def _clearProxy(self, principal, subPrincipalName):
 
         if isinstance(principal, tuple):
-            principal = self._getPrincipalByShortName(principal[0], principal[1])
-        principal = principal.getChild(subPrincipalName)
+            principal = (yield self._getPrincipalByShortName(principal[0], principal[1]))
+        principal = (yield principal.getChild(subPrincipalName))
         yield principal.setGroupMemberSetPrincipals(set())
 
     @inlineCallbacks
     def _proxyForTest(self, recordType, recordName, expectedProxies, read_write):
-        principal = self._getPrincipalByShortName(recordType, recordName)
+        principal = (yield self._getPrincipalByShortName(recordType, recordName))
         proxies = (yield principal.proxyFor(read_write))
-        proxies = sorted([principal.displayName() for principal in proxies])
-        self.assertEquals(proxies, sorted(expectedProxies))
+        proxies = set([(yield principal.displayName()) for principal in proxies])
+        self.assertEquals(proxies, set(expectedProxies))
 
     def test_groupMembersRegular(self):
         """
@@ -195,53 +191,45 @@
 
         return DeferredList(ds)
 
+    @inlineCallbacks
     def test_groupMembersProxyMissingUser(self):
         """
         DirectoryPrincipalResource.expandedGroupMembers()
         """
-        proxy = self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo")
-        proxyGroup = proxy.getChild("calendar-proxy-write")
+        proxy = (yield self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo"))
+        proxyGroup = (yield proxy.getChild("calendar-proxy-write"))
 
-        def gotMembers(members):
-            members.add("12345")
-            return proxyGroup._index().setGroupMembers("%s#calendar-proxy-write" % (proxy.principalUID(),), members)
 
-        def check(_):
-            return self._groupMembersTest(
-                DirectoryService.recordType_users, "cdaboo", "calendar-proxy-write",
-                (),
-            )
 
         # Setup the fake entry in the DB
-        d = proxyGroup._index().getMembers("%s#calendar-proxy-write" % (proxy.principalUID(),))
-        d.addCallback(gotMembers)
-        d.addCallback(check)
-        return d
+        members = (yield proxyGroup._index().getMembers("%s#calendar-proxy-write" % (proxy.principalUID(),)))
+        members.add("12345")
+        proxyGroup._index().setGroupMembers("%s#calendar-proxy-write" % (proxy.principalUID(),), members)
+        self._groupMembersTest(
+            DirectoryService.recordType_users, "cdaboo", "calendar-proxy-write",
+            (),
+        )
 
+
+    @inlineCallbacks
     def test_groupMembershipsMissingUser(self):
         """
         DirectoryPrincipalResource.expandedGroupMembers()
         """
         # Setup the fake entry in the DB
         fake_uid = "12345"
-        proxy = self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo")
-        proxyGroup = proxy.getChild("calendar-proxy-write")
+        proxy = (yield self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo"))
+        proxyGroup = (yield proxy.getChild("calendar-proxy-write"))
 
-        def gotMembers(members):
-            members.add("%s#calendar-proxy-write" % (proxy.principalUID(),))
-            return proxyGroup._index().setGroupMembers("%s#calendar-proxy-write" % (fake_uid,), members)
 
-        def check(_):
-            return self._groupMembershipsTest(
-                DirectoryService.recordType_users, "cdaboo", "calendar-proxy-write",
-                (),
-            )
+        members = (yield proxyGroup._index().getMembers("%s#calendar-proxy-write" % (fake_uid,)))
+        members.add("%s#calendar-proxy-write" % (proxy.principalUID(),))
+        proxyGroup._index().setGroupMembers("%s#calendar-proxy-write" % (fake_uid,), members)
+        self._groupMembershipsTest(
+            DirectoryService.recordType_users, "cdaboo", "calendar-proxy-write",
+            (),
+        )
 
-        d = proxyGroup._index().getMembers("%s#calendar-proxy-write" % (fake_uid,))
-        d.addCallback(gotMembers)
-        d.addCallback(check)
-        return d
-
     @inlineCallbacks
     def test_setGroupMemberSet(self):
         class StubMemberDB(object):
@@ -256,10 +244,9 @@
                 return succeed(self.members)
 
 
-        user = self._getPrincipalByShortName(self.directoryService.recordType_users,
-                                           "cdaboo")
+        user = (yield self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo"))
 
-        proxyGroup = user.getChild("calendar-proxy-write")
+        proxyGroup = (yield user.getChild("calendar-proxy-write"))
 
         memberdb = StubMemberDB()
 
@@ -286,9 +273,9 @@
             def changed(self):
                 self.changedCount += 1
 
-        user = self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo")
+        user = (yield self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo"))
 
-        proxyGroup = user.getChild("calendar-proxy-write")
+        proxyGroup = (yield user.getChild("calendar-proxy-write"))
 
         notifier = StubCacheNotifier()
 
@@ -387,8 +374,8 @@
 
         # Set up the in-memory (non-null) memcacher:
         config.ProcessType = "Single"
-        principal = self._getPrincipalByShortName(
-            DirectoryService.recordType_users, "wsanchez")
+        principal = (yield self._getPrincipalByShortName(
+            DirectoryService.recordType_users, "wsanchez"))
         db = principal._calendar_user_proxy_index()
 
         # Set the clock to the epoch:
@@ -399,13 +386,13 @@
         for doMembershipFirst in (True, False):
             for proxyType in ("calendar-proxy-read", "calendar-proxy-write"):
 
-                principal = self._getPrincipalByShortName(DirectoryService.recordType_users, "wsanchez")
-                proxyGroup = principal.getChild(proxyType)
+                principal = (yield self._getPrincipalByShortName(DirectoryService.recordType_users, "wsanchez"))
+                proxyGroup = (yield principal.getChild(proxyType))
 
-                testPrincipal = self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo")
+                testPrincipal = (yield self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo"))
 
-                fakePrincipal = self._getPrincipalByShortName(DirectoryService.recordType_users, "dreid")
-                fakeProxyGroup = fakePrincipal.getChild(proxyType)
+                fakePrincipal = (yield self._getPrincipalByShortName(DirectoryService.recordType_users, "dreid"))
+                fakeProxyGroup = (yield fakePrincipal.getChild(proxyType))
 
                 yield self._addProxy(
                     principal,
@@ -440,10 +427,10 @@
 
                 # Remove the dreid user from the directory service
 
-                delRec = self.directoryService.recordWithShortName(
-                    DirectoryService.recordType_users, "dreid")
+                delRec = (yield self.directoryService.recordWithShortName(
+                    DirectoryService.recordType_users, "dreid"))
                 for cache in self.directoryService._recordCaches.itervalues():
-                    cache.removeRecord(delRec)
+                   yield cache.removeRecord(delRec)
                 del self.directoryService._accounts()[
                     DirectoryService.recordType_users]["dreid"]
 
@@ -483,7 +470,7 @@
                     # Restore removed user
                     parser = XMLAccountsParser(self.directoryService.xmlFile)
                     self.directoryService._parsedAccounts = parser.items
-                    self.directoryService.recordWithShortName(
+                    yield self.directoryService.recordWithShortName(
                         DirectoryService.recordType_users, "dreid")
 
                     # Trigger the proxy DB clean up, which will actually
@@ -496,10 +483,10 @@
                     self.assertEquals(result, None)
 
                     # Remove the dreid user from the directory service
-                    delRec = self.directoryService.recordWithShortName(
-                        DirectoryService.recordType_users, "dreid")
+                    delRec = (yield self.directoryService.recordWithShortName(
+                        DirectoryService.recordType_users, "dreid"))
                     for cache in self.directoryService._recordCaches.itervalues():
-                        cache.removeRecord(delRec)
+                       yield cache.removeRecord(delRec)
                     del self.directoryService._accounts()[
                         DirectoryService.recordType_users]["dreid"]
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_sudo.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_sudo.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_sudo.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -16,6 +16,7 @@
 import os
 
 from twisted.python.filepath import FilePath
+from twisted.internet.defer import inlineCallbacks
 
 import twistedcaldav.directory.test.util
 from twistedcaldav.directory.sudo import SudoDirectoryService
@@ -51,25 +52,28 @@
         service.realmName = "test realm"
         return service
 
+    @inlineCallbacks
     def test_listRecords(self):
-        for record in self.service().listRecords(self.recordType):
+        for record in (yield self.service().listRecords(self.recordType)):
             self.failUnless(record.shortNames[0] in self.sudoers)
             self.assertEqual(self.sudoers[record.shortNames[0]]['password'],
                              record.password)
 
+    @inlineCallbacks
     def test_recordWithShortName(self):
         service = self.service()
 
-        record = service.recordWithShortName(self.recordType, 'alice')
+        record = (yield service.recordWithShortName(self.recordType, 'alice'))
         self.assertEquals(record.password, 'alice')
 
-        record = service.recordWithShortName(self.recordType, 'bob')
+        record = (yield service.recordWithShortName(self.recordType, 'bob'))
         self.failIf(record)
 
+    @inlineCallbacks
     def test_calendaringDisabled(self):
         service = self.service()
 
-        record = service.recordWithShortName(self.recordType, 'alice')
+        record = (yield service.recordWithShortName(self.recordType, 'alice'))
 
         self.failIf(record.enabledForCalendaring,
                     "sudoers should have enabledForCalendaring=False")

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_xmlfile.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/test_xmlfile.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -99,6 +99,7 @@
     def service(self):
         return XMLDirectoryService({'xmlFile' : self.xmlFile()}, alwaysStat=True)
 
+    @inlineCallbacks
     def test_changedXML(self):
         service = self.service()
 
@@ -123,10 +124,10 @@
         ):
             # Fault records in
             for name in expectedRecords:
-                service.recordWithShortName(recordType, name)
+                yield service.recordWithShortName(recordType, name)
 
             self.assertEquals(
-                set(r.shortNames[0] for r in service.listRecords(recordType)),
+                set(r.shortNames[0] for r in (yield service.listRecords(recordType))),
                 set(expectedRecords)
             )
 
@@ -156,16 +157,17 @@
         ):
             # Fault records in
             for name in expectedRecords:
-                service.recordWithShortName(recordType, name)
+                yield service.recordWithShortName(recordType, name)
 
             self.assertEquals(
-                set(r.shortNames[0] for r in service.listRecords(recordType)),
+                set(r.shortNames[0] for r in (yield service.listRecords(recordType))),
                 set(expectedRecords)
             )
         resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
-        self.assertTrue((yield resourceInfoDatabase.getAutoSchedule(service.recordWithShortName(DirectoryService.recordType_locations, "my office").guid)))
+        self.assertTrue((yield resourceInfoDatabase.getAutoSchedule((yield service.recordWithShortName(DirectoryService.recordType_locations, "my office")).guid)))
 
 
+    @inlineCallbacks
     def test_okDisableCalendar(self):
         service = self.service()
 
@@ -195,16 +197,16 @@
         ):
             # Fault records in
             for name in expectedRecords:
-                service.recordWithShortName(recordType, name)
+                yield service.recordWithShortName(recordType, name)
 
             self.assertEquals(
-                set(r.shortNames[0] for r in service.listRecords(recordType)),
+                set(r.shortNames[0] for r in (yield service.listRecords(recordType))),
                 set(expectedRecords)
             )
 
         # All groups are disabled
-        self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "enabled").enabledForCalendaring)
-        self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "disabled").enabledForCalendaring)
+        self.assertFalse((yield service.recordWithShortName(DirectoryService.recordType_groups, "enabled")).enabledForCalendaring)
+        self.assertFalse((yield service.recordWithShortName(DirectoryService.recordType_groups, "disabled")).enabledForCalendaring)
 
     @inlineCallbacks
     def test_okProxies(self):
@@ -241,10 +243,10 @@
         ):
             # Fault records in
             for name in expectedRecords:
-                service.recordWithShortName(recordType, name)
+                yield service.recordWithShortName(recordType, name)
 
             self.assertEquals(
-                set(r.shortNames[0] for r in service.listRecords(recordType)),
+                set(r.shortNames[0] for r in (yield service.listRecords(recordType))),
                 set(expectedRecords)
             )
         calendarUserProxyDatabase = CalendarUserProxyDatabase(config.DataRoot)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/util.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/test/util.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,3 +1,4 @@
+# -*- test-case-name: twistedcaldav.directory.test -*-
 ##
 # Copyright (c) 2005-2009 Apple Inc. All rights reserved.
 #
@@ -14,10 +15,14 @@
 # limitations under the License.
 ##
 
+from zope.interface.verify import verifyObject
+
+from twisted.internet.defer import inlineCallbacks
 from twisted.trial.unittest import SkipTest
 from twisted.cred.credentials import UsernamePassword
 from twisted.web2.auth.digest import DigestedCredentials, calcResponse, calcHA1
 
+from twistedcaldav.directory.idirectory import IDirectoryService
 from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.directory.directory import UnknownRecordTypeError
 from twistedcaldav.test.util import TestCase
@@ -50,6 +55,15 @@
     # For aggregator subclasses
     recordTypePrefixes = ("",)
 
+
+    def test_isDirectoryService(self):
+        """
+        Does the directory service at least attempt to implement the methods
+        described by the interface?
+        """
+        self.failUnless(verifyObject(IDirectoryService, self.service()))
+
+
     def test_realm(self):
         """
         IDirectoryService.realm
@@ -65,6 +79,7 @@
 
         self.assertEquals(set(self.service().recordTypes()), self.recordTypes)
 
+    @inlineCallbacks
     def test_recordWithShortName(self):
         """
         IDirectoryService.recordWithShortName()
@@ -80,17 +95,18 @@
 
             service = self.service()
             for shortName, info in data.iteritems():
-                record = service.recordWithShortName(info.get("prefix", "") + recordType, shortName)
+                record = (yield service.recordWithShortName(info.get("prefix", "") + recordType, shortName))
                 self.failUnless(record, "No record (%s)%s" % (info.get("prefix", "") + recordType, shortName))
                 self.compare(record, shortName, data[shortName])
 
             for prefix in self.recordTypePrefixes:
                 try:
-                    record = service.recordWithShortName(prefix + recordType, "IDunnoWhoThisIsIReallyDont")
+                    record = (yield service.recordWithShortName(prefix + recordType, "IDunnoWhoThisIsIReallyDont"))
                 except UnknownRecordTypeError:
                     continue
                 self.assertEquals(record, None)
 
+    @inlineCallbacks
     def test_recordWithUID(self):
         service = self.service()
         record = None
@@ -98,24 +114,26 @@
         for shortName, what in self.allEntries():
             guid = what["guid"]
             if guid is not None:
-                record = service.recordWithUID(guid)
+                record = (yield service.recordWithUID(guid))
                 self.compare(record, shortName, what)
 
         if record is None:
             raise SkipTest("No GUIDs provided to test")
 
+    @inlineCallbacks
     def test_recordWithCalendarUserAddress(self):
         service = self.service()
         record = None
 
         for shortName, what in self.allEntries():
             for address in what["addresses"]:
-                record = service.recordWithCalendarUserAddress(address)
+                record = (yield service.recordWithCalendarUserAddress(address))
                 self.compare(record, shortName, what)
 
         if record is None:
             raise SkipTest("No calendar user addresses provided to test")
 
+    @inlineCallbacks
     def test_groupMembers(self):
         """
         IDirectoryRecord.members()
@@ -126,14 +144,15 @@
         service = self.service()
         for group, info in self.groups.iteritems():
             prefix = info.get("prefix", "")
-            groupRecord = service.recordWithShortName(prefix + DirectoryService.recordType_groups, group)
-            result = set((m.recordType, prefix + m.shortNames[0]) for m in groupRecord.members())
+            groupRecord = (yield service.recordWithShortName(prefix + DirectoryService.recordType_groups, group))
+            result = set((m.recordType, prefix + m.shortNames[0]) for m in (yield groupRecord.members()))
             expected = set(self.groups[group]["members"])
             self.assertEquals(
                 result, expected,
                 "Wrong membership for group %r: %s != %s" % (group, result, expected)
             )
 
+    @inlineCallbacks
     def test_groupMemberships(self):
         """
         IDirectoryRecord.groups()
@@ -150,8 +169,8 @@
             service = self.service()
             for shortName, info in data.iteritems():
                 prefix = info.get("prefix", "")
-                record = service.recordWithShortName(prefix + recordType, shortName)
-                result = set(prefix + g.shortNames[0] for g in record.groups())
+                record = (yield service.recordWithShortName(prefix + recordType, shortName))
+                result = set(prefix + g.shortNames[0] for g in (yield record.groups()))
                 expected = set(g for g in self.groups if (record.recordType, shortName) in self.groups[g]["members"])
                 self.assertEquals(
                     result, expected,
@@ -261,6 +280,7 @@
     """
     Tests a directory implementation with basic auth.
     """
+    @inlineCallbacks
     def test_verifyCredentials_basic(self):
         """
         IDirectoryRecord.verifyCredentials() with basic
@@ -270,8 +290,8 @@
 
         service = self.service()
         for user in self.users:
-            userRecord = service.recordWithShortName(DirectoryService.recordType_users, user)
-            self.failUnless(userRecord.verifyCredentials(UsernamePassword(user, self.users[user]["password"])))
+            userRecord = (yield service.recordWithShortName(DirectoryService.recordType_users, user))
+            self.failUnless((yield userRecord.verifyCredentials(UsernamePassword(user, self.users[user]["password"]))))
 
 # authRequest = {
 #    username="username",
@@ -290,6 +310,7 @@
     """
     Tests a directory implementation with digest auth.
     """
+    @inlineCallbacks
     def test_verifyCredentials_digest(self):
         """
         IDirectoryRecord.verifyCredentials() with digest
@@ -300,7 +321,7 @@
         service = self.service()
         for user in self.users:
             for good in (True, True, False, False, True):
-                userRecord = service.recordWithShortName(DirectoryService.recordType_users, user)
+                userRecord = (yield service.recordWithShortName(DirectoryService.recordType_users, user))
 
                 # I'm glad this is so simple...
                 response = calcResponse(
@@ -342,6 +363,6 @@
                 )
 
                 if good:
-                    self.failUnless(userRecord.verifyCredentials(credentials))
+                    self.failUnless((yield userRecord.verifyCredentials(credentials)))
                 else:
-                    self.failIf(userRecord.verifyCredentials(credentials))
+                    self.failIf((yield userRecord.verifyCredentials(credentials)))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/wiki.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/wiki.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/wiki.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -28,7 +28,7 @@
 from twisted.web2.http import HTTPError, StatusResponse
 from twisted.web2.auth.wrapper import UnauthorizedResponse
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 
 from twisted.web2.dav.resource import TwistedACLInheritable
@@ -65,7 +65,7 @@
         return (WikiDirectoryService.recordType_wikis,)
 
     def listRecords(self, recordType):
-        return ()
+        return succeed(())
 
     def recordWithShortName(self, recordType, shortName):
         if recordType != WikiDirectoryService.recordType_wikis:
@@ -75,10 +75,10 @@
             record = self.byShortName[shortName]
             self.log_info("Returning existing wiki record with UID %s" %
                 (record.uid,))
-            return record
+            return succeed(record)
 
         record = self._addRecord(shortName)
-        return record
+        return succeed(record)
 
     def recordWithUID(self, uid):
 
@@ -86,14 +86,14 @@
             record = self.byUID[uid]
             self.log_info("Returning existing wiki record with UID %s" %
                 (record.uid,))
-            return record
+            return succeed(record)
 
         if uid.startswith(self.UIDPrefix):
             shortName = uid[len(self.UIDPrefix):]
             record = self._addRecord(shortName)
-            return record
+            return succeed(record)
         else:
-            return None
+            return succeed(None)
 
     def _addRecord(self, shortName):
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/xmlfile.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/directory/xmlfile.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -1,3 +1,4 @@
+# -*- test-case-name: twistedcaldav.directory.test.test_xmlfile -*-
 ##
 # Copyright (c) 2006-2009 Apple Inc. All rights reserved.
 #
@@ -28,6 +29,7 @@
 from twisted.cred.credentials import UsernamePassword
 from twisted.web2.auth.digest import DigestedCredentials
 from twisted.python.filepath import FilePath
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
@@ -96,7 +98,10 @@
                 if matched:
                     self.recordCacheForType(recordType).addRecord(
                         record, indexType, indexKey)
+
+        return succeed(None)
             
+    @inlineCallbacks
     def recordsMatchingFields(self, fields, operand="or", recordType=None):
         # Default, brute force method search of underlying XML data
 
@@ -155,15 +160,17 @@
         else:
             recordTypes = (recordType,)
 
+        results = []
         for recordType in recordTypes:
             for xmlPrincipal in self._accounts()[recordType].itervalues():
                 if xmlPrincipalMatches(xmlPrincipal):
-                    
                     # Load/cache record from its GUID
-                    record = self.recordWithGUID(xmlPrincipal.guid)
+                    record = (yield self.recordWithGUID(xmlPrincipal.guid))
                     if record:
-                        yield record
+                        results.append(record)
 
+        returnValue(results)
+
     def _accounts(self):
         currentTime = time()
         if self._alwaysStat or currentTime - self._lastCheck > 60:
@@ -198,18 +205,26 @@
         self._members          = xmlPrincipal.members
         self._groups           = xmlPrincipal.groups
 
+    @inlineCallbacks
     def members(self):
+        results = []
         for recordType, shortName in self._members:
-            yield self.service.recordWithShortName(recordType, shortName)
+            record = (yield self.service.recordWithShortName(recordType, shortName))
+            results.append(record)
+        returnValue(results)
 
+    @inlineCallbacks
     def groups(self):
+        results = []
         for shortName in self._groups:
-            yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
+            record = (yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName))
+            results.append(record)
+        returnValue(results)
 
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
-            return credentials.password == self.password
+            return succeed(credentials.password == self.password)
         if isinstance(credentials, DigestedCredentials):
-            return credentials.checkPassword(self.password)
+            return succeed(credentials.checkPassword(self.password))
 
         return super(XMLDirectoryRecord, self).verifyCredentials(credentials)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/dropbox.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/dropbox.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/dropbox.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -21,9 +21,9 @@
 __all__ = [
     "DropBoxHomeResource",
     "DropBoxCollectionResource",
-    "DropBoxChildResource",
 ]
 
+from twisted.internet.defer import succeed
 from twext.web2.dav.davxml import ErrorResponse
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
@@ -39,7 +39,7 @@
     Drop box collection resource.
     """
     def resourceType(self):
-        return davxml.ResourceType.dropboxhome
+        return succeed(davxml.ResourceType.dropboxhome)
 
     def isCollection(self):
         return True
@@ -52,7 +52,7 @@
     Drop box resource.
     """
     def resourceType(self):
-        return davxml.ResourceType.dropbox
+        return succeed(davxml.ResourceType.dropbox)
 
     def isCollection(self):
         return True
@@ -78,7 +78,7 @@
                 edited_aces.append(ace)
         
         # Do inherited with possibly modified set of aces
-        super(DropBoxCollectionResource, self).writeNewACEs(edited_aces)
+        return super(DropBoxCollectionResource, self).writeNewACEs(edited_aces)
 
     def http_PUT(self, request):
         return ErrorResponse(

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/extensions.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/extensions.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -26,7 +26,6 @@
     "DAVFile",
     "ReadOnlyWritePropertiesResourceMixIn",
     "ReadOnlyResourceMixIn",
-    "CachingXattrPropertyStore",
 ]
 
 import cPickle as pickle
@@ -164,6 +163,7 @@
             request.authzUser = davxml.Principal(davxml.Unauthenticated())
             returnValue((request.authnUser, request.authzUser,))
 
+    @inlineCallbacks
     def principalsForAuthID(self, request, creds):
         """
         Return authentication and authorization prinicipal identifiers
@@ -181,17 +181,17 @@
             HTTPError(responsecode.FORBIDDEN) if the principal isn't
             found.
         """
-        authnPrincipal = self.findPrincipalForAuthID(creds)
+        authnPrincipal = (yield self.findPrincipalForAuthID(creds))
 
         if authnPrincipal is None:
             log.info("Could not find the principal resource for user id: %s"
                      % (creds.username,))
             raise HTTPError(responsecode.FORBIDDEN)
 
-        d = self.authorizationPrincipal(request, creds.username, authnPrincipal)
-        d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
-        return d
+        authzPrincipal = (yield self.authorizationPrincipal(request, creds.username, authnPrincipal))
+        returnValue((authnPrincipal, authzPrincipal))
 
+    @inlineCallbacks
     def findPrincipalForAuthID(self, creds):
         """
         Return an authentication and authorization principal
@@ -199,20 +199,20 @@
         Check for sudo users before regular users.
         """
         if type(creds) is str:
-            return super(SudoSACLMixin, self).findPrincipalForAuthID(creds)
+            returnValue((yield super(SudoSACLMixin, self).findPrincipalForAuthID(creds)))
 
         for collection in self.principalCollections():
-            principal = collection.principalForShortName(
+            principal = (yield collection.principalForShortName(
                 SudoDirectoryService.recordType_sudoers, 
-                creds.username)
+                creds.username))
             if principal is not None:
-                return principal
+                returnValue(principal)
 
         for collection in self.principalCollections():
-            principal = collection.principalForAuthID(creds)
+            principal = (yield collection.principalForAuthID(creds))
             if principal is not None:
-                return principal
-        return None
+                returnValue(principal)
+        returnValue(None)
 
     @inlineCallbacks
     def authorizationPrincipal(self, request, authID, authnPrincipal):
@@ -239,31 +239,33 @@
             # Substitute the authz value for principal look up
             authz = authz[0]
 
+        @inlineCallbacks
         def getPrincipalForType(type, name):
             for collection in self.principalCollections():
-                principal = collection.principalForShortName(type, name)
+                principal = (yield collection.principalForShortName(type, name))
                 if principal:
-                    return principal
+                    returnValue(principal)
 
+        @inlineCallbacks
         def isSudoUser(authzID):
-            if getPrincipalForType(SudoDirectoryService.recordType_sudoers, authzID):
-                return True
-            return False
+            if (yield getPrincipalForType(SudoDirectoryService.recordType_sudoers, authzID)):
+                returnValue(True)
+            returnValue(False)
 
         if (
             hasattr(authnPrincipal, "record") and
             authnPrincipal.record.recordType == SudoDirectoryService.recordType_sudoers
         ):
             if authz:
-                if isSudoUser(authz):
+                if (yield isSudoUser(authz)):
                     log.info("Cannot proxy as another proxy: user %r as user %r"
                              % (authID, authz))
                     raise HTTPError(responsecode.FORBIDDEN)
                 else:
-                    authzPrincipal = getPrincipalForType(DirectoryService.recordType_users, authz)
+                    authzPrincipal = (yield getPrincipalForType(DirectoryService.recordType_users, authz))
 
                     if not authzPrincipal:
-                        authzPrincipal = self.findPrincipalForAuthID(authz)
+                        authzPrincipal = (yield self.findPrincipalForAuthID(authz))
 
                     if authzPrincipal is not None:
                         log.info("Allow proxy: user %r as %r"
@@ -437,7 +439,7 @@
                 operand=operand, cuType=cuType))
 
             for record in records:
-                resource = principalCollection.principalForRecord(record)
+                resource = (yield principalCollection.principalForRecord(record))
                 matchingResources.append(resource)
 
                 # We've determined this is a matching resource
@@ -530,7 +532,7 @@
 
         children = []
         basepath = request.urlForResource(self)
-        childnames = list(self.listChildren())
+        childnames = yield self.listChildren()
         for childname in childnames:
             if names and childname not in names:
                 continue
@@ -701,7 +703,7 @@
 
         if namespace == dav_namespace:
             if name == "resourcetype":
-                returnValue(self.resourceType())
+                returnValue((yield self.resourceType()))
 
         elif namespace == calendarserver_namespace:
             if name == "expanded-group-member-set":
@@ -742,14 +744,15 @@
     def expandedGroupMemberships(self):
         return succeed(())
 
+    @inlineCallbacks
     def resourceType(self):
         # Allow live property to be overridden by dead property
-        if self.deadProperties().contains((dav_namespace, "resourcetype")):
-            return self.deadProperties().get((dav_namespace, "resourcetype"))
+        if (yield self.deadProperties().contains((dav_namespace, "resourcetype"))):
+            returnValue((yield self.deadProperties().get((dav_namespace, "resourcetype"))))
         if self.isCollection():
-            return davxml.ResourceType(davxml.Collection(), davxml.Principal())
+            returnValue(davxml.ResourceType(davxml.Collection(), davxml.Principal()))
         else:
-            return davxml.ResourceType(davxml.Principal())
+            returnValue(davxml.ResourceType(davxml.Principal()))
 
 
 class DAVFile (SudoSACLMixin, SuperDAVFile, LoggingMixIn):
@@ -763,42 +766,47 @@
             qname = property.qname()
 
         if qname == (dav_namespace, "resourcetype"):
-            return succeed(self.resourceType())
+            return self.resourceType()
 
         return super(DAVFile, self).readProperty(property, request)
 
+    @inlineCallbacks
     def resourceType(self):
         # Allow live property to be overridden by dead property
-        if self.deadProperties().contains((dav_namespace, "resourcetype")):
-            return self.deadProperties().get((dav_namespace, "resourcetype"))
+        if (yield self.deadProperties().contains((dav_namespace, "resourcetype"))):
+            returnValue((yield self.deadProperties().get((dav_namespace, "resourcetype"))))
         if self.isCollection():
-            return davxml.ResourceType.collection
-        return davxml.ResourceType.empty
+            returnValue(davxml.ResourceType.collection)
+        returnValue(davxml.ResourceType.empty)
 
+    @inlineCallbacks
     def render(self, request):
         if not self.fp.exists():
-            return responsecode.NOT_FOUND
+            returnValue(responsecode.NOT_FOUND)
 
         if self.fp.isdir():
             if request.path[-1] != "/":
                 # Redirect to include trailing '/' in URI
-                return RedirectResponse(request.unparseURL(path=urllib.quote(urllib.unquote(request.path), safe=':/')+'/'))
+                returnValue(RedirectResponse(request.unparseURL(path=urllib.quote(urllib.unquote(request.path), safe=':/')+'/')))
             else:
+                # MOR: Not sure what to do here -- it may be that render( )
+                # can't easily be deferred, in which case createSimilarFile( )
+                # will be a problem...
                 ifp = self.fp.childSearchPreauth(*self.indexNames)
                 if ifp:
                     # Render from the index file
-                    return self.createSimilarFile(ifp.path).render(request)
+                    returnValue((yield self.createSimilarFile(ifp.path).render(request)))
 
-                return self.renderDirectory(request)
+                returnValue((yield self.renderDirectory(request)))
 
         try:
             f = self.fp.open()
         except IOError, e:
             import errno
             if e[0] == errno.EACCES:
-                return responsecode.FORBIDDEN
+                returnValue(responsecode.FORBIDDEN)
             elif e[0] == errno.ENOENT:
-                return responsecode.NOT_FOUND
+                returnValue(responsecode.NOT_FOUND)
             else:
                 raise
 
@@ -806,13 +814,13 @@
         response.stream = FileStream(f, 0, self.fp.getsize())
 
         for (header, value) in (
-            ("content-type", self.contentType()),
+            ("content-type", (yield self.contentType())),
             ("content-encoding", self.contentEncoding()),
         ):
             if value is not None:
                 response.headers.setHeader(header, value)
 
-        return response
+        returnValue(response)
 
     def directoryStyleSheet(self):
         return (
@@ -868,6 +876,7 @@
         d.addCallback(gotBody)
         return d
 
+    # MOR: This is not working at the moment -- gotValues( ) isn't getting a sequence
     @printTracebacks
     def renderDirectoryBody(self, request):
         """
@@ -881,37 +890,41 @@
         ]
 
         even = Alternator()
-        for name in sorted(self.listChildren()):
-            child = self.getChild(name)
+        d = self.listChildren()
 
-            url, name, size, lastModified, contentType = self.getChildDirectoryEntry(child, name)
+        @inlineCallbacks
+        def _gotChildren(children):
+            for name in sorted(children):
+                child = (yield self.getChild(name))
 
-            # FIXME: gray out resources that are not readable
+                url, name, size, lastModified, contentType = self.getChildDirectoryEntry(child, name)
+
+                # FIXME: gray out resources that are not readable
+                output.append(
+                    """<tr class="%(even)s">"""
+                    """<td><a href="%(url)s">%(name)s</a></td>"""
+                    """<td align="right">%(size)s</td>"""
+                    """<td>%(lastModified)s</td>"""
+                    """<td>%(type)s</td>"""
+                    """</tr>"""
+                    % {
+                        "even": even.state() and "even" or "odd",
+                        "url": url,
+                        "name": cgi.escape(name),
+                        "size": size,
+                        "lastModified": lastModified,
+                        "type": contentType,
+                    }
+                )
+
             output.append(
-                """<tr class="%(even)s">"""
-                """<td><a href="%(url)s">%(name)s</a></td>"""
-                """<td align="right">%(size)s</td>"""
-                """<td>%(lastModified)s</td>"""
-                """<td>%(type)s</td>"""
-                """</tr>"""
-                % {
-                    "even": even.state() and "even" or "odd",
-                    "url": url,
-                    "name": cgi.escape(name),
-                    "size": size,
-                    "lastModified": lastModified,
-                    "type": contentType,
-                }
+                """</table></div>"""
+                """<div class="directory-listing">"""
+                """<h1>Properties</h1>"""
+                """<table>"""
+                """<tr><th>Name</th> <th>Value</th></tr>"""
             )
 
-        output.append(
-            """</table></div>"""
-            """<div class="directory-listing">"""
-            """<h1>Properties</h1>"""
-            """<table>"""
-            """<tr><th>Name</th> <th>Value</th></tr>"""
-        )
-
         def gotProperties(qnames):
             ds = []
 
@@ -988,9 +1001,10 @@
             d = DeferredList(ds)
             d.addCallback(gotValues)
             return d
-
-        d = self.listProperties(request)
-        d.addCallback(gotProperties)
+        d.addCallback(
+            _gotChildren).addCallback(
+            lambda _: self.listProperties(request)).addCallback(
+            gotProperties)
         return d
 
     def getChildDirectoryEntry(self, child, name):
@@ -1070,7 +1084,7 @@
     ):
         # Permissions here are fixed, and are not subject to                    
         # inheritance rules, etc.                                               
-        return succeed(self.defaultAccessControlList())
+        return self.defaultAccessControlList()
 
 class PropertyNotFoundError (HTTPError):
     def __init__(self, qname):
@@ -1090,51 +1104,55 @@
         self.propertyStore = propertyStore
         self.resource = propertyStore.resource
 
+    @inlineCallbacks
     def get(self, qname):
         #self.log_debug("Get: %r, %r" % (self.resource.fp.path, qname))
 
-        cache = self._cache()
+        cache = (yield self._cache())
 
         if qname in cache:
             property = cache.get(qname, None)
             if property is None:
                 self.log_debug("Cache miss: %r, %r, %r" % (self, self.resource.fp.path, qname))
                 try:
-                    property = self.propertyStore.get(qname)
+                    property = (yield self.propertyStore.get(qname))
                 except HTTPError:
                     del cache[qname]
                     raise PropertyNotFoundError(qname)
                 cache[qname] = property
 
-            return property
+            returnValue(property)
         else:
             raise PropertyNotFoundError(qname)
 
+    @inlineCallbacks
     def set(self, property):
         #self.log_debug("Set: %r, %r" % (self.resource.fp.path, property))
 
-        cache = self._cache()
+        cache = (yield self._cache())
 
         cache[property.qname()] = None
-        self.propertyStore.set(property)
+        yield self.propertyStore.set(property)
         cache[property.qname()] = property
+        returnValue(None)
 
+    @inlineCallbacks
     def contains(self, qname):
         #self.log_debug("Contains: %r, %r" % (self.resource.fp.path, qname))
 
         try:
-            cache = self._cache()
+            cache = (yield self._cache())
         except HTTPError, e:
             if e.response.code == responsecode.NOT_FOUND:
-                return False
+                returnValue(False)
             else:
                 raise
 
         if qname in cache:
             #self.log_debug("Contains cache hit: %r, %r, %r" % (self, self.resource.fp.path, qname))
-            return True
+            returnValue(True)
         else:
-            return False
+            returnValue(False)
 
     def delete(self, qname):
         #self.log_debug("Delete: %r, %r" % (self.resource.fp.path, qname))
@@ -1144,16 +1162,22 @@
 
         self.propertyStore.delete(qname)
 
+    @inlineCallbacks
     def list(self):
         #self.log_debug("List: %r" % (self.resource.fp.path,))
-        return self._cache().iterkeys()
+        cache = (yield self._cache())
+        returnValue(cache.iterkeys())
 
+    @inlineCallbacks
     def _cache(self):
         if not hasattr(self, "_data"):
             #self.log_debug("Cache init: %r" % (self.resource.fp.path,))
             self._data = dict(
                 (name, None)
-                for name in self.propertyStore.list()
+                for name in (yield self.propertyStore.list())
             )
-        return self._data
+        returnValue(self._data)
 
+
+    def flushCache(self):
+        del self._data

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/fileops.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/fileops.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/fileops.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -22,12 +22,15 @@
 from twisted.web2.dav.fileop import put
 from twisted.web2.dav.xattrprops import xattrPropertyStore
 
+from twisted.internet.defer import inlineCallbacks, returnValue
+
 # This class simulates a DAVFile with enough information for use with xattrPropertyStore.
 class FakeXAttrResource(object):
     
     def __init__(self, fp):
         self.fp = fp
 
+ at inlineCallbacks
 def putWithXAttrs(stream, filepath):
     """
     Write a file to a possibly existing path and preserve any xattrs at that path.
@@ -42,26 +45,22 @@
     props = []
     if filepath.exists():
         xold = xattrPropertyStore(FakeXAttrResource(filepath))
-        for item in xold.list():
-            props.append((xold.get(item)))
+        for item in (yield xold.list()):
+            props.append((yield xold.get(item)))
         xold = None
-    
-    # First do the actual file copy
-    def _gotResponse(response):
-    
-        # Restore original xattrs.
-        if props:
-            xnew = xattrPropertyStore(FakeXAttrResource(filepath))
-            for prop in props:
-                xnew.set(prop)
-            xnew = None
-    
-        return response
 
-    d = put(stream, filepath)
-    d.addCallback(_gotResponse)
-    return d
+    response = (yield put(stream, filepath))
 
+    # Restore original xattrs.
+    if props:
+        xnew = xattrPropertyStore(FakeXAttrResource(filepath))
+        for prop in props:
+            yield xnew.set(prop)
+        xnew = None
+
+    returnValue(response)
+
+
 def copyWithXAttrs(source_filepath, destination_filepath, destination_uri):
     """
     Copy a file from one path to another and also copy xattrs we care about.
@@ -102,10 +101,11 @@
     # Now copy over xattrs.
     copyXAttrs(from_fp, to_fp)
 
+ at inlineCallbacks
 def copyXAttrs(from_fp, to_fp):    
     # Create xattr stores for each file and copy over all xattrs.
     xfrom = xattrPropertyStore(FakeXAttrResource(from_fp))
     xto = xattrPropertyStore(FakeXAttrResource(to_fp))
 
-    for item in xfrom.list():
-        xto.set(xfrom.get(item))
+    for item in (yield xfrom.list()):
+        yield xto.set((yield xfrom.get(item)))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/freebusyurl.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/freebusyurl.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/freebusyurl.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -22,7 +22,7 @@
     "FreeBusyURLResource",
 ]
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
@@ -93,19 +93,19 @@
                     davxml.Protected(),
                 ),
             )
-        return davxml.ACL(*aces)
+        return succeed(davxml.ACL(*aces))
 
     def resourceType(self):
-        return davxml.ResourceType.freebusyurl
+        return succeed(davxml.ResourceType.freebusyurl)
 
     def isCollection(self):
         return False
 
     def isCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def isPseudoCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def render(self, request):
         output = """<html>
@@ -199,11 +199,11 @@
         # TODO: We should probably verify that the actual time-range is within sensible bounds (e.g. not too far in the past or future and not too long)
         
         # Now lookup the principal details for the targeted user
-        principal = self.parent.principalForRecord()
+        principal = (yield self.parent.principalForRecord())
         
         # Pick the first mailto cu address or the first other type
         cuaddr = None
-        for item in principal.calendarUserAddresses():
+        for item in (yield principal.calendarUserAddresses()):
             if cuaddr is None:
                 cuaddr = item
             if item.startswith("mailto"):
@@ -211,7 +211,7 @@
                 break
 
         # Get inbox details
-        inboxURL = principal.scheduleInboxURL()
+        inboxURL = (yield principal.scheduleInboxURL(request))
         if inboxURL is None:
             raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox URL for principal: %s" % (principal,)))
         try:

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/ical.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/ical.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -34,6 +34,7 @@
 
 from twisted.web2.dav.util import allDataFromStream
 from twisted.web2.stream import IStream
+from twisted.internet.defer import inlineCallbacks
 
 from twistedcaldav.dateops import compareDateTime, normalizeToUTC, timeRangesOverlap,\
     normalizeStartEndDuration, toString, normalizeForIndex, differenceDateTime
@@ -2075,11 +2076,12 @@
                     if dataValue.find(dropboxPrefix) != -1:
                         self.removeProperty(attachment)
 
+    @inlineCallbacks
     def normalizeCalendarUserAddresses(self, lookupFunction):
         """
         Do the ORGANIZER/ATTENDEE property normalization.
 
-        @param lookupFunction: function returning full name, guid, CUAs for a given CUA
+        @param lookupFunction: function returning full name, guid, CUAs for a given CUA (Deferred)
         @type lookupFunction: L{Function}
         """
         for component in self.subcomponents():
@@ -2093,7 +2095,7 @@
                 # Check that we can lookup this calendar user address - if not
                 # we cannot do anything with it
                 cuaddr = normalizeCUAddr(prop.value())
-                name, guid, cuaddrs = lookupFunction(cuaddr)
+                name, guid, cuaddrs = (yield lookupFunction(cuaddr))
                 if guid is None:
                     continue
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/icaldav.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/icaldav.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/icaldav.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -135,14 +135,14 @@
             free-busy for this principal's calendar user.
         """
 
-    def scheduleInboxURL():
+    def scheduleInboxURL(request=None):
         """
         Get the schedule INBOX URL for this principal's calendar user.
-        @return: a string containing the URL from the schedule-inbox-URL property.
+        @return: a Deferred that fires with a string containing the URL from the schedule-inbox-URL property.
         """
 
-    def scheduleOutboxURL():
+    def scheduleOutboxURL(request=None):
         """
         Get the schedule OUTBOX URL for this principal's calendar user.
-        @return: a string containing the URL from the schedule-outbox-URL property.
+        @return: a Deferred that fires with string containing the URL from the schedule-outbox-URL property.
         """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/index.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/index.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/index.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -42,7 +42,7 @@
 
 from vobject.icalendar import utc
 
-from twisted.internet.defer import maybeDeferred, succeed
+from twisted.internet.defer import maybeDeferred, succeed, returnValue, inlineCallbacks
 
 from twistedcaldav.ical import Component
 from twistedcaldav.query import calendarquery
@@ -162,6 +162,7 @@
         """
         raise NotImplementedError
 
+    @inlineCallbacks
     def resourceNamesForUID(self, uid):
         """
         Looks up the names of the resources with the given UID.
@@ -177,7 +178,7 @@
         resources = []
         for name in names:
             name_utf8 = name.encode("utf-8")
-            if name is not None and self.resource.getChild(name_utf8) is None:
+            if name is not None and (yield self.resource.getChild(name_utf8)) is None:
                 # Clean up
                 log.err("Stale resource record found for child %s with UID %s in %s" % (name, uid, self.resource))
                 self._delete_from_db(name, uid)
@@ -185,8 +186,9 @@
             else:
                 resources.append(name)
 
-        return resources
+        returnValue(resources)
 
+    @inlineCallbacks
     def resourceNameForUID(self, uid):
         """
         Looks up the name of the resource with the given UID.
@@ -195,11 +197,11 @@
         """
         result = None
 
-        for name in self.resourceNamesForUID(uid):
+        for name in (yield self.resourceNamesForUID(uid)):
             assert result is None, "More than one resource with UID %s in calendar collection %r" % (uid, self)
             result = name
 
-        return result
+        returnValue(result)
 
     def resourceUIDForName(self, name):
         """
@@ -265,15 +267,16 @@
         results = self._db_values_for_sql(statement, *names)
         return results
 
-
+    @inlineCallbacks
     def testAndUpdateIndex(self, minDate):
         # Find out if the index is expanded far enough
         names = self.notExpandedBeyond(minDate)
         # Actually expand recurrence max
         for name in names:
             self.log_info("Search falls outside range of index for %s %s" % (name, minDate))
-            self.reExpandResource(name, minDate)
+            yield self.reExpandResource(name, minDate)
 
+    @inlineCallbacks
     def indexedSearch(self, filter, fbtype=False):
         """
         Finds resources matching the given qualifiers.
@@ -298,7 +301,7 @@
                     maxDate = maxDate.date()
                     if isStartDate:
                         maxDate += datetime.timedelta(days=365)
-                    self.testAndUpdateIndex(maxDate)
+                    yield self.testAndUpdateIndex(maxDate)
             else:
                 # We cannot handler this filter in an indexed search
                 raise IndexedSearchException()
@@ -321,16 +324,18 @@
                 rowiter = self._db_execute("select DISTINCT RESOURCE.NAME, RESOURCE.UID, RESOURCE.TYPE" + qualifiers[0], *qualifiers[1])
 
         # Check result for missing resources
-
+        rows = []
         for row in rowiter:
             name = row[0]
-            if self.resource.getChild(name.encode("utf-8")):
-                yield row
+            if (yield self.resource.getChild(name.encode("utf-8"))):
+                rows.append(row)
             else:
                 log.err("Calendar resource %s is missing from %s. Removing from index."
                         % (name, self.resource))
                 self.deleteResource(name)
+        returnValue(rows)
 
+    @inlineCallbacks
     def bruteForceSearch(self):
         """
         List the whole index and tests for existence, updating the index
@@ -340,17 +345,17 @@
         rowiter = self._db_execute("select NAME, UID, TYPE from RESOURCE")
 
         # Check result for missing resources:
-
+        rows = []
         for row in rowiter:
             name = row[0]
-            if self.resource.getChild(name.encode("utf-8")):
-                yield row
+            if (yield self.resource.getChild(name.encode("utf-8"))):
+                rows.append(row)
             else:
                 log.err("Calendar resource %s is missing from %s. Removing from index."
                         % (name, self.resource))
                 self.deleteResource(name)
+        returnValue(rows)
 
-
     def _db_version(self):
         """
         @return: the schema version assigned to this index.
@@ -489,15 +494,17 @@
         """
         return self._db_values_for_sql("select NAME from RESOURCE where RECURRANCE_MAX < :1", minDate)
 
+    @inlineCallbacks
     def reExpandResource(self, name, expand_until):
         """
         Given a resource name, remove it from the database and re-add it
         with a longer expansion.
         """
-        calendar = self.resource.getChild(name).iCalendar()
+        child = (yield self.resource.getChild(name))
+        calendar = (yield child.iCalendar())
         self._add_to_db(name, calendar, expand_until=expand_until, reCreate=True)
         self._db_commit()
-
+        
     def _add_to_db(self, name, calendar, cursor = None, expand_until=None, reCreate=False):
         """
         Records the given calendar resource in the index with the given name.
@@ -748,6 +755,7 @@
             index. C{resource} must be a calendar collection (i.e.
             C{resource.isPseudoCalendarCollection()} returns C{True}.)
         """
+        # MOR: isCalendarCollection( ) is now deferred.  What to do here?
         assert resource.isCalendarCollection(), "non-calendar collection resource %s has no index." % (resource,)
         super(Index, self).__init__(resource)
 
@@ -785,8 +793,8 @@
         @return: True if the UID is not in the index and is not reserved,
             False otherwise.
         """
-        rname = self.resourceNameForUID(uid)
-        return (rname is None or rname in names)
+        return self.resourceNameForUID(uid).addCallback(
+            lambda rname: rname is None or rname in names)
 
     def _db_type(self):
         """
@@ -850,7 +858,8 @@
             index. C{resource} must be a calendar collection (i.e.
             C{resource.isPseudoCalendarCollection()} returns C{True}.)
         """
-        assert resource.isPseudoCalendarCollection() and not resource.isCalendarCollection(), "non-calendar collection resource %s has no index." % (resource,)
+        # MOR: isCalendarCollection( ) is now deferred.  What to do here?
+        # assert resource.isPseudoCalendarCollection() and not resource.isCalendarCollection(), "non-calendar collection resource %s has no index." % (resource,)
         super(IndexSchedule, self).__init__(resource)
 
     def reserveUID(self, uid): #@UnusedVariable
@@ -897,7 +906,7 @@
         """
 
         # iTIP does not require unique UIDs
-        return True
+        return succeed(True)
 
     def _db_type(self):
         """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/log.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/log.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/log.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -59,7 +59,7 @@
 import inspect
 import logging
 
-from twisted.python import log
+from twisted.python import log, failure
 
 from StringIO import StringIO
 
@@ -321,6 +321,30 @@
             d.addCallback(_gotData)
             return d
 
+    def err(self, _stuff=None, _why=None, **kw):
+        """
+        Compatibility layer for Twisted's log module.
+        """
+        theMessage = None
+        if isinstance(_stuff, failure.Failure):
+            theFailure = _stuff
+        elif isinstance(_stuff, Exception):
+            theFailure = failure.Failure(_stuff)
+        elif _stuff is None:
+            theFailure = failure.Failure()
+        else:
+            theFailure = None
+            theMessage = repr(_stuff)
+        if theMessage is None:
+            if _why is None:
+                theMessage = "Unhandled Error"
+            else:
+                theMessage = _why
+        if theFailure is None:
+            self.emit("error", theMessage, isError=1, why=_why, **kw)
+        else:
+            self.emit("error", theMessage, isError=1, why=_why, failure=theFailure, **kw)
+
 class LoggingMixIn (object):
     """
     Mix-in class for logging methods.
@@ -388,7 +412,6 @@
 
 # Add some compatibility with twisted's log module
 Logger.msg = Logger.info
-Logger.err = Logger.error
 
 ##
 # Errors

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/mail.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/mail.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -196,14 +196,15 @@
         self.parent = parent
 
 
+    @inlineCallbacks
     def accessControlList(self, request, inheritance=True,
         expanding=False, inherited_aces=None):
 
         if not hasattr(self, "iMIPACL"):
 
             for principalCollection in self.principalCollections():
-                principal = principalCollection.principalForShortName("users",
-                    config.Scheduling.iMIP.Username)
+                principal = (yield principalCollection.principalForShortName("users",
+                    config.Scheduling.iMIP.Username))
                 if principal is not None:
                     break
             else:
@@ -222,19 +223,19 @@
                 ),
             )
 
-        return succeed(self.iMIPACL)
+        returnValue(self.iMIPACL)
 
     def resourceType(self):
-        return davxml.ResourceType.ischeduleinbox
+        return succeed(davxml.ResourceType.ischeduleinbox)
 
     def isCollection(self):
         return False
 
     def isCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def isPseudoCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def deadProperties(self):
         if not hasattr(self, "_dead_properties"):
@@ -245,7 +246,7 @@
         return None
 
     def checkPreconditions(self, request):
-        return None
+        return succeed(None)
 
     def render(self, request):
         output = """<html>
@@ -280,13 +281,15 @@
         )
         if config.Scheduling.CalDAV.OldDraftCompatibility:
             privs += (davxml.Privilege(caldavxml.Schedule()),)
-        return davxml.ACL(
-            # DAV:Read, CalDAV:schedule-deliver for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(*privs),
-                davxml.Protected(),
-            ),
+        return succeed(
+            davxml.ACL(
+                # DAV:Read, CalDAV:schedule-deliver for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(*privs),
+                    davxml.Protected(),
+                ),
+            )
         )
 
     def supportedPrivileges(self, request):

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/memcacheprops.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/memcacheprops.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/memcacheprops.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -29,6 +29,7 @@
 
 try:
     from hashlib import md5
+    md5                         # be quiet, pyflakes
 except ImportError:
     from md5 import new as md5
 
@@ -37,6 +38,7 @@
 from twisted.python.filepath import FilePath
 from twisted.web2 import responsecode
 from twisted.web2.http import HTTPError, StatusResponse
+from twisted.internet.defer import inlineCallbacks, returnValue
 
 from twistedcaldav.config import config
 from twistedcaldav.log import LoggingMixIn, Logger
@@ -68,6 +70,7 @@
 
         return MemcachePropertyCollection._memcacheClient
 
+    @inlineCallbacks
     def propertyCache(self):
         # The property cache has this format:
         #  {
@@ -82,13 +85,14 @@
         #    ...,
         #  }
         if not hasattr(self, "_propertyCache"):
-            self._propertyCache = self._loadCache()
-        return self._propertyCache
+            self._propertyCache = (yield self._loadCache())
+        returnValue(self._propertyCache)
 
+    @inlineCallbacks
     def childCache(self, child):
         path = child.fp.path
         key = self._keyForPath(path)
-        propertyCache = self.propertyCache()
+        propertyCache = (yield self.propertyCache())
 
         try:
             childCache, token = propertyCache[key]
@@ -100,7 +104,7 @@
             #log.error(message)
             #raise AssertionError(message)
 
-        return propertyCache, key, childCache, token
+        returnValue((propertyCache, key, childCache, token))
 
     def _keyForPath(self, path):
         key = "|".join((
@@ -109,22 +113,23 @@
         ))
         return md5(key).hexdigest()
 
+    @inlineCallbacks
     def _loadCache(self, childNames=None):
         if childNames is None:
             abortIfMissing = False
-            childNames = self.collection.listChildren()
+            childNames = yield self.collection.listChildren()
         else:
             if childNames:
                 abortIfMissing = True
             else:
-                return {}
+                returnValue({})
 
         self.log_debug("Loading cache for %s" % (self.collection,))
 
         client = self.memcacheClient()
         assert client is not None, "OMG no cache!"
         if client is None:
-            return None
+            returnValue(None)
 
         keys = tuple((
             (self._keyForPath(self.collection.fp.child(childName).path), childName)
@@ -153,12 +158,12 @@
             if abortIfMissing:
                 raise MemcacheError("Unable to fully load cache for %s" % (self.collection,))
 
-            loaded = self._buildCache(childNames=missing)
-            loaded = self._loadCache(childNames=(FilePath(name).basename() for name in loaded.iterkeys()))
+            loaded = (yield self._buildCache(childNames=missing))
+            loaded = (yield self._loadCache(childNames=(FilePath(name).basename() for name in loaded.iterkeys())))
 
             result.update(loaded.iteritems())
 
-        return result
+        returnValue(result)
 
     def _storeCache(self, cache):
         self.log_debug("Storing cache for %s" % (self.collection,))
@@ -173,34 +178,33 @@
         if client is not None:
             client.set_multi(values, time=self.cacheTimeout)
 
+    @inlineCallbacks
     def _buildCache(self, childNames=None):
         if childNames is None:
-            childNames = self.collection.listChildren()
+            childNames = yield self.collection.listChildren()
         elif not childNames:
-            return {}
+            returnValue({})
 
         self.log_debug("Building cache for %s" % (self.collection,))
 
         cache = {}
 
         for childName in childNames:
-            child = self.collection.getChild(childName)
-            if child is None:
-                continue
+            child = (yield self.collection.getChild(childName))
+            if child is not None:
+                propertyStore = child.deadProperties()
+                props = {}
+                for qname in (yield propertyStore.list(cache=False)):
+                    props[qname] = (yield propertyStore.get(qname, cache=False))
 
-            propertyStore = child.deadProperties()
-            props = {}
-            for qname in propertyStore.list(cache=False):
-                props[qname] = propertyStore.get(qname, cache=False)
-
-            cache[child.fp.path] = props
-
+                cache[child.fp.path] = props
         self._storeCache(cache)
+        returnValue(cache)
 
-        return cache
 
+    @inlineCallbacks
     def setProperty(self, child, property, delete=False):
-        propertyCache, key, childCache, token = self.childCache(child)
+        propertyCache, key, childCache, token = (yield self.childCache(child))
 
         if delete:
             qname = property
@@ -228,12 +232,12 @@
 
                 finally:
                     # Re-fetch the properties for this child
-                    loaded = self._loadCache(childNames=(child.fp.basename(),))
+                    loaded = (yield self._loadCache(childNames=(child.fp.basename(),)))
                     propertyCache.update(loaded.iteritems())
 
                 retries -= 1
 
-                propertyCache, key, childCache, token = self.childCache(child)
+                propertyCache, key, childCache, token = (yield self.childCache(child))
 
                 if delete:
                     if childCache.has_key(qname):
@@ -251,10 +255,11 @@
     def deleteProperty(self, child, qname):
         return self.setProperty(child, qname, delete=True)
 
+    @inlineCallbacks
     def flushCache(self, child):
         path = child.fp.path
         key = self._keyForPath(path)
-        propertyCache = self.propertyCache()
+        propertyCache = (yield self.propertyCache())
 
         if key in propertyCache:
             del propertyCache[key]
@@ -274,20 +279,22 @@
             self.child = child
             self.childPropertyStore = childPropertyStore
 
+        @inlineCallbacks
         def propertyCache(self):
             path = self.child.fp.path
             key = self.parentPropertyCollection._keyForPath(path)
-            parentPropertyCache = self.parentPropertyCollection.propertyCache()
-            return parentPropertyCache.get(key, ({}, None))[0]
+            parentPropertyCache = (yield self.parentPropertyCollection.propertyCache())
+            returnValue((yield parentPropertyCache.get(key, ({}, None)))[0])
 
         def flushCache(self):
             self.parentPropertyCollection.flushCache(self.child)
 
+        @inlineCallbacks
         def get(self, qname, cache=True):
             if cache:
-                propertyCache = self.propertyCache()
+                propertyCache = (yield self.propertyCache())
                 if qname in propertyCache:
-                    return propertyCache[qname]
+                    returnValue(propertyCache[qname])
                 else:
                     raise HTTPError(StatusResponse(
                         responsecode.NOT_FOUND,
@@ -296,36 +303,42 @@
 
             self.log_debug("Read for %s on %s"
                            % (qname, self.childPropertyStore.resource.fp.path))
-            return self.childPropertyStore.get(qname)
+            returnValue((yield self.childPropertyStore.get(qname)))
 
+        @inlineCallbacks
         def set(self, property):
             self.log_debug("Write for %s on %s"
                            % (property.qname(), self.childPropertyStore.resource.fp.path))
 
-            self.parentPropertyCollection.setProperty(self.child, property)
-            self.childPropertyStore.set(property)
+            yield self.parentPropertyCollection.setProperty(self.child, property)
+            yield self.childPropertyStore.set(property)
+            returnValue(None)
 
+        @inlineCallbacks
         def delete(self, qname):
             self.log_debug("Delete for %s on %s"
                            % (qname, self.childPropertyStore.resource.fp.path))
 
-            self.parentPropertyCollection.deleteProperty(self.child, qname)
-            self.childPropertyStore.delete(qname)
+            yield self.parentPropertyCollection.deleteProperty(self.child, qname)
+            yield self.childPropertyStore.delete(qname)
+            returnValue(None)
 
+        @inlineCallbacks
         def contains(self, qname, cache=True):
             if cache:
-                propertyCache = self.propertyCache()
-                return qname in propertyCache
+                propertyCache = (yield self.propertyCache())
+                returnValue(qname in propertyCache)
 
             self.log_debug("Contains for %s"
                            % (self.childPropertyStore.resource.fp.path,))
-            return self.childPropertyStore.contains(qname)
+            returnValue((yield self.childPropertyStore.contains(qname)))
 
+        @inlineCallbacks
         def list(self, cache=True):
             if cache:
-                propertyCache = self.propertyCache()
-                return propertyCache.iterkeys()
+                propertyCache = (yield self.propertyCache())
+                returnValue(propertyCache.iterkeys())
 
             self.log_debug("List for %s"
                            % (self.childPropertyStore.resource.fp.path,))
-            return self.childPropertyStore.list()
+            returnValue((yield self.childPropertyStore.list()))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/copymove.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/copymove.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -48,7 +48,7 @@
     """
 
     # Copy of calendar collections isn't allowed.
-    if isPseudoCalendarCollectionResource(self):
+    if (yield isPseudoCalendarCollectionResource(self)):
         returnValue(responsecode.FORBIDDEN)
 
     result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = (yield checkForCalendarAction(self, request))
@@ -79,7 +79,7 @@
         )
 
     # Checks for copying a calendar collection
-    if self.isCalendarCollection():
+    if (yield self.isCalendarCollection()):
         log.err("Attempt to copy a calendar collection into another calendar collection %s" % destination)
         raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "calendar-collection-location-ok")))
 
@@ -118,7 +118,7 @@
     """
     result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = (yield checkForCalendarAction(self, request))
     if not result:
-        is_calendar_collection = isPseudoCalendarCollectionResource(self)
+        is_calendar_collection = (yield isPseudoCalendarCollectionResource(self))
         defaultCalendar = (yield self.isDefaultCalendar(request)) if is_calendar_collection else False
 
         # Do default WebDAV action
@@ -154,7 +154,7 @@
 
     if destinationcal:
         # Checks for copying a calendar collection
-        if self.isCalendarCollection():
+        if (yield self.isCalendarCollection()):
             log.err("Attempt to move a calendar collection into another calendar collection %s" % destination)
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "calendar-collection-location-ok")))
     
@@ -219,7 +219,7 @@
 
     # Check for parent calendar collection
     sourceparent = (yield request.locateResource(parentForURL(request.uri)))
-    if isCalendarCollectionResource(sourceparent):
+    if (yield isCalendarCollectionResource(sourceparent)):
         result = True
         sourcecal = True
     
@@ -238,7 +238,7 @@
     # Check for parent calendar collection
     destination_uri = urlsplit(destination_uri)[2]
     destinationparent = (yield request.locateResource(parentForURL(destination_uri)))
-    if isCalendarCollectionResource(destinationparent):
+    if (yield isCalendarCollectionResource(destinationparent)):
         result = True
         destinationcal = True
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/delete_common.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/delete_common.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/delete_common.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -52,6 +52,7 @@
         self.depth = depth
         self.internal_request = internal_request
 
+    @inlineCallbacks
     def validIfScheduleMatch(self):
         """
         Check for If-ScheduleTag-Match header behavior.
@@ -63,8 +64,8 @@
             if header:
                 # Do "precondition" test
                 matched = False
-                if self.resource.exists() and self.resource.hasDeadProperty(ScheduleTag):
-                    scheduletag = self.resource.readDeadProperty(ScheduleTag)
+                if self.resource.exists() and (yield self.resource.hasDeadProperty(ScheduleTag)):
+                    scheduletag = (yield self.resource.readDeadProperty(ScheduleTag))
                     matched = (scheduletag == header)
                 if not matched:
                     log.debug("If-Schedule-Tag-Match: header value '%s' does not match resource value '%s'" % (header, scheduletag,))
@@ -104,7 +105,7 @@
             yield delresource.quotaSizeAdjust(self.request, -old_size)
 
         if response == responsecode.NO_CONTENT:
-            if isPseudoCalendarCollectionResource(parent):
+            if (yield isPseudoCalendarCollectionResource(parent)):
                 index = parent.index()
                 index.deleteResource(delresource.fp.basename())
 
@@ -130,7 +131,7 @@
         # as the iTIP operation may fail and may need to prevent the delete from happening.
     
         # Do If-Schedule-Tag-Match behavior first
-        self.validIfScheduleMatch()
+        yield self.validIfScheduleMatch()
 
         # Do quota checks before we start deleting things
         myquota = (yield delresource.quota(self.request))
@@ -143,7 +144,7 @@
         lock = None
         if not self.internal_request:
             # Get data we need for implicit scheduling
-            calendar = delresource.iCalendar()
+            calendar = yield delresource.iCalendar()
             scheduler = ImplicitScheduler()
             do_implicit_action, _ignore = (yield scheduler.testImplicitSchedulingDELETE(self.request, delresource, calendar))
             if do_implicit_action:
@@ -205,8 +206,9 @@
 
         errors = ResponseQueue(deluri, "DELETE", responsecode.NO_CONTENT)
 
-        for childname in delresource.listChildren():
+        children = yield delresource.listChildren()
 
+        for childname in children:
             childurl = joinURL(deluri, childname)
             child = (yield self.request.locateChildResource(delresource, childname))
 
@@ -276,10 +278,10 @@
     @inlineCallbacks
     def run(self):
 
-        if isCalendarCollectionResource(self.parent):
+        if (yield isCalendarCollectionResource(self.parent)):
             response = (yield self.deleteCalendarResource(self.resource, self.resource_uri, self.parent))
             
-        elif isCalendarCollectionResource(self.resource):
+        elif (yield isCalendarCollectionResource(self.resource)):
             response = (yield self.deleteCalendar(self.resource, self.resource_uri, self.parent))
         
         elif self.resource.isCollection():

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/get.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/get.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/get.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -20,7 +20,7 @@
 
 __all__ = ["http_GET"]
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.web2.dav import davxml
 from twisted.web2.http import HTTPError
 from twisted.web2.http import Response
@@ -38,7 +38,7 @@
     # Look for calendar access restriction on existing resource.
     if self.exists():
         try:
-            access = self.readDeadProperty(TwistedCalendarAccessProperty)
+            access = (yield self.readDeadProperty(TwistedCalendarAccessProperty))
         except HTTPError:
             access = None
             
@@ -53,8 +53,8 @@
             if not isowner:
                 # Now "filter" the resource calendar data through the CALDAV:calendar-data element and apply
                 # access restrictions to the data.
-                caldata = caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access).calendarData()
-
+                el = yield caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access)
+                caldata = el.calendarData()
                 response = Response()
                 response.stream = MemoryStream(caldata)
                 response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
@@ -65,8 +65,8 @@
     response = (yield super(CalDAVFile, self).http_GET(request))
     
     # Add Schedule-Tag header if property is present
-    if self.exists() and self.hasDeadProperty(ScheduleTag):
-        scheduletag = self.readDeadProperty(ScheduleTag)
+    if self.exists() and (yield self.hasDeadProperty(ScheduleTag)):
+        scheduletag = (yield self.readDeadProperty(ScheduleTag))
         if scheduletag:
             response.headers.setHeader("Schedule-Tag", str(scheduletag))
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/mkcalendar.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/mkcalendar.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/mkcalendar.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -94,7 +94,7 @@
             for property in makecalendar.children[0].children[0].children:
                 try:
                     if property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"):
-                        self.writeDeadProperty(property)
+                        (yield self.writeDeadProperty(property))
                     else:
                         yield self.writeProperty(property, request)
                 except HTTPError:

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/propfind.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/propfind.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/propfind.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -24,11 +24,10 @@
 """
 WebDAV PROPFIND method
 """
-
 __all__ = ["http_PROPFIND"]
 
 from twisted.python.failure import Failure
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.web2.http import HTTPError
 from twisted.web2 import responsecode
 from twisted.web2.http import StatusResponse

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -39,7 +39,7 @@
     parentURL = parentForURL(request.uri)
     parent = (yield request.locateResource(parentURL))
 
-    if isPseudoCalendarCollectionResource(parent):
+    if (yield isPseudoCalendarCollectionResource(parent)):
 
         # Content-type check
         content_type = request.headers.getHeader("content-type")

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put_common.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/put_common.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -22,13 +22,14 @@
 __all__ = ["StoreCalendarObjectResource"]
 
 import os
+import sys
 import types
 import uuid
 
 from twext.web2.dav.davxml import ErrorResponse
 
 from twisted.internet import reactor
-from twisted.internet.defer import Deferred, inlineCallbacks, succeed
+from twisted.internet.defer import Deferred, maybeDeferred, inlineCallbacks
 from twisted.internet.defer import returnValue
 from twisted.python import failure
 from twisted.python.filepath import FilePath
@@ -87,6 +88,7 @@
             self.source_index_deleted = False
             self.destination_index_deleted = False
         
+        @inlineCallbacks
         def Rollback(self):
             """
             Rollback the server state. Do not allow this to raise another exception. If
@@ -117,7 +119,7 @@
                         self.destination_created = False
                     if self.destination_index_deleted:
                         # Must read in calendar for destination being re-indexed
-                        self.storer.doDestinationIndex(self.storer.destination.iCalendar())
+                        self.storer.doDestinationIndex((yield self.storer.destination.iCalendar()))
                         self.destination_index_deleted = False
                         log.debug("Rollback: destination re-indexed %s" % (self.storer.destination.fp.path,))
                     if self.source_index_deleted:
@@ -282,7 +284,7 @@
 
         # Basic validation
         yield self.validCopyMoveOperation()
-        self.validIfScheduleMatch()
+        yield self.validIfScheduleMatch()
 
         if self.destinationcal:
             # Valid resource name check
@@ -308,13 +310,13 @@
             if not self.sourcecal:
                 # Valid content type check on the source resource if its not in a calendar collection
                 if self.source is not None:
-                    result, message = self.validContentType()
+                    result, message = (yield self.validContentType())
                     if not result:
                         log.err(message)
                         raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
                 
                     # At this point we need the calendar data to do more tests
-                    self.calendar = self.source.iCalendar()
+                    self.calendar = (yield self.source.iCalendar())
                 else:
                     try:
                         if type(self.calendar) in (types.StringType, types.UnicodeType,):
@@ -363,7 +365,7 @@
 
                 # 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.
-                self.calendar = self.source.iCalendar()
+                self.calendar = (yield self.source.iCalendar())
 
             # Check access
             if self.destinationcal and config.EnablePrivateEvents:
@@ -374,7 +376,7 @@
 
         elif self.sourcecal:
             self.source_index = self.sourceparent.index()
-            self.calendar = self.source.iCalendar()
+            self.calendar = (yield self.source.iCalendar())
     
     @inlineCallbacks
     def validCopyMoveOperation(self):
@@ -399,6 +401,7 @@
                     log.debug(msg)
                     raise HTTPError(StatusResponse(responsecode.FORBIDDEN, msg))
 
+    @inlineCallbacks
     def validIfScheduleMatch(self):
         """
         Check for If-ScheduleTag-Match header behavior.
@@ -410,12 +413,14 @@
             header = self.request.headers.getHeader("If-Schedule-Tag-Match")
             if header:
                 # Do "precondition" test
-                
+
                 # If COPY/MOVE get Schedule-Tag on source, else use destination
-                def _getScheduleTag(resource):
-                    return resource.readDeadProperty(ScheduleTag) if resource.exists() and resource.hasDeadProperty(ScheduleTag) else None
+                resource = self.source if self.source else self.destination
+                if resource.exists() and (yield resource.hasDeadProperty(ScheduleTag)):
+                    scheduletag = (yield resource.readDeadProperty(ScheduleTag))
+                else:
+                    scheduletag = None
 
-                scheduletag = _getScheduleTag(self.source if self.source else self.destination)
                 if scheduletag != header:
                     log.debug("If-Schedule-Tag-Match: header value '%s' does not match resource value '%s'" % (header, scheduletag,))
                     raise HTTPError(responsecode.PRECONDITION_FAILED)
@@ -445,20 +450,26 @@
             message = "File name %s not allowed in calendar collection" % (filename,)
 
         return result, message
-        
+
+    @inlineCallbacks
     def validContentType(self):
         """
-        Make sure that the content-type of the source resource is text/calendar.
-        This test is only needed when the source is not in a calendar collection.
+        Make sure that the content-type of the source resource is
+        text/calendar.  This test is only needed when the source is not in a
+        calendar collection.
         """
         result = True
         message = ""
-        content_type = self.source.contentType()
-        if not ((content_type.mediaType == "text") and (content_type.mediaSubtype == "calendar")):
+        content_type = (yield maybeDeferred(self.source.contentType))
+        if (content_type is None or
+            not ((content_type.mediaType == "text") and
+                 (content_type.mediaSubtype == "calendar"))):
             result = False
-            message = "MIME type %s not allowed in calendar collection" % (content_type,)
+            message = "MIME type %s not allowed in calendar collection" % (
+                content_type,
+            )
 
-        return result, message
+        returnValue((result, message))
         
     def validContentLength(self):
         """
@@ -545,6 +556,7 @@
 
         return result, message
 
+    @inlineCallbacks
     def validAccess(self):
         """
         Make sure that the X-CALENDARSERVER-ACCESS property is properly dealt with.
@@ -559,26 +571,21 @@
                 
             # Only DAV:owner is able to set the property to other than PUBLIC
             if not self.internal_request:
-                def _callback(parent_owner):
-                    
-                    authz = self.destinationparent.currentPrincipal(self.request)
-                    if davxml.Principal(parent_owner) != authz and self.access != Component.ACCESS_PUBLIC:
-                        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (calendarserver_namespace, "valid-access-restriction-change")))
-                    
-                    return None
-    
-                d = self.destinationparent.owner(self.request)
-                d.addCallback(_callback)
-                return d
+                parentOwner = (yield self.destinationparent.owner(self.request))
+                authz = self.destinationparent.currentPrincipal(self.request)
+                if davxml.Principal(parentOwner) != authz and self.access != Component.ACCESS_PUBLIC:
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (calendarserver_namespace, "valid-access-restriction-change")))
+
         else:
             # Check whether an access property was present before and write that into the calendar data
-            if not self.source and self.destination.exists() and self.destination.hasDeadProperty(TwistedCalendarAccessProperty):
-                old_access = str(self.destination.readDeadProperty(TwistedCalendarAccessProperty))
+            if not self.source and self.destination.exists() and (yield self.destination.hasDeadProperty(TwistedCalendarAccessProperty)):
+                old_access = str((yield self.destination.readDeadProperty(TwistedCalendarAccessProperty)))
                 self.calendar.addProperty(Property(name=Component.ACCESS_PROPERTY, value=old_access))
                 self.calendardata = str(self.calendar)
                 
-        return succeed(None)
+        returnValue(None)
 
+    @inlineCallbacks
     def noUIDConflict(self, uid):
         """
         Check that the UID of the new calendar object conforms to the requirements of
@@ -600,8 +607,9 @@
 
         # UID must be unique
         index = self.destinationparent.index()
-        if not index.isAllowedUID(uid, oldname, self.destination.fp.basename()):
-            rname = index.resourceNameForUID(uid)
+        isAllowed = (yield index.isAllowedUID(uid, oldname, self.destination.fp.basename()))
+        if not isAllowed:
+            rname = (yield index.resourceNameForUID(uid))
             # This can happen if two simultaneous PUTs occur with the same UID.
             # i.e. one PUT has reserved the UID but has not yet written the resource,
             # the other PUT tries to reserve and fails but no index entry exists yet.
@@ -619,7 +627,7 @@
                     result = False
                     message = "Cannot overwrite calendar resource %s with different UID %s" % (rname, olduid)
         
-        return result, message, rname
+        returnValue((result, message, rname))
 
     @inlineCallbacks
     def checkQuota(self):
@@ -650,6 +658,7 @@
 
         returnValue(None)
 
+    @inlineCallbacks
     def setupRollback(self):
         """
         We may need to restore the original resource data if the PUT/COPY/MOVE fails,
@@ -665,7 +674,7 @@
         self.overwrite = self.destination.exists()
         if self.overwrite:
             self.rollback.destination_copy = FilePath(_createRollbackPath(self.destination.fp.path))
-            copyToWithXAttrs(self.destination.fp, self.rollback.destination_copy)
+            yield copyToWithXAttrs(self.destination.fp, self.rollback.destination_copy)
             log.debug("Rollback: backing up destination %s to %s" % (self.destination.fp.path, self.rollback.destination_copy.path))
         else:
             self.rollback.destination_created = True
@@ -673,7 +682,7 @@
 
         if self.deletesource:
             self.rollback.source_copy = FilePath(_createRollbackPath(self.source.fp.path))
-            copyToWithXAttrs(self.source.fp, self.rollback.source_copy)
+            yield copyToWithXAttrs(self.source.fp, self.rollback.source_copy)
             log.debug("Rollback: backing up source %s to %s" % (self.source.fp.path, self.rollback.source_copy.path))
 
     def truncateRecurrence(self):
@@ -691,6 +700,7 @@
         else:
             return False
 
+    @inlineCallbacks
     def preservePrivateComments(self):
         # Check for private comments on the old resource and the new resource and re-insert
         # ones that are lost.
@@ -699,7 +709,7 @@
         # the X- property is missing.
         new_has_private_comments = False
         if config.Scheduling.CalDAV.get("EnablePrivateComments", True) and self.calendar is not None:
-            old_has_private_comments = self.destination.exists() and self.destinationcal and self.destination.hasDeadProperty(TwistedCalendarHasPrivateCommentsProperty)
+            old_has_private_comments = self.destination.exists() and self.destinationcal and (yield self.destination.hasDeadProperty(TwistedCalendarHasPrivateCommentsProperty))
             new_has_private_comments = self.calendar.hasPropertyInAnyComponent((
                 "X-CALENDARSERVER-PRIVATE-COMMENT",
                 "X-CALENDARSERVER-ATTENDEE-COMMENT",
@@ -708,21 +718,23 @@
             if old_has_private_comments and not new_has_private_comments:
                 # Transfer old comments to new calendar
                 log.debug("Private Comments properties were entirely removed by the client. Restoring existing properties.")
-                old_calendar = self.destination.iCalendar()
-                self.calendar.transferProperties(old_calendar, (
-                    "X-CALENDARSERVER-PRIVATE-COMMENT",
-                    "X-CALENDARSERVER-ATTENDEE-COMMENT",
-                ))
+                old_calendar = (yield self.destination.iCalendar())
+                self.calendar.transferProperties(old_calendar,
+                    (
+                        "X-CALENDARSERVER-PRIVATE-COMMENT",
+                        "X-CALENDARSERVER-ATTENDEE-COMMENT",
+                    )
+                )
                 self.calendardata = None
         
-        return new_has_private_comments
+        returnValue(new_has_private_comments)
 
     @inlineCallbacks
     def doImplicitScheduling(self):
 
         # Get any existing schedule-tag property on the resource
-        if self.destination.exists() and self.destination.hasDeadProperty(ScheduleTag):
-            self.scheduletag = self.destination.readDeadProperty(ScheduleTag)
+        if self.destination.exists() and (yield self.destination.hasDeadProperty(ScheduleTag)):
+            self.scheduletag = (yield self.destination.readDeadProperty(ScheduleTag))
             if self.scheduletag:
                 self.scheduletag = str(self.scheduletag)
         else:
@@ -801,13 +813,13 @@
     
         # Update calendar-access property value on the resource
         if self.access:
-            self.destination.writeDeadProperty(TwistedCalendarAccessProperty(self.access))
+            (yield self.destination.writeDeadProperty(TwistedCalendarAccessProperty(self.access)))
             
         # Do not remove the property if access was not specified and we are storing in a calendar.
         # This ensure that clients that do not preserve the iCalendar property do not cause access
         # restrictions to be lost.
         elif not self.destinationcal:
-            self.destination.removeDeadProperty(TwistedCalendarAccessProperty)                
+            (yield self.destination.removeDeadProperty(TwistedCalendarAccessProperty))
 
         returnValue(IResponse(response))
 
@@ -822,7 +834,7 @@
         # Finish MD5 calculation and write dead property
         md5.close()
         md5 = md5.getMD5()
-        self.destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
+        yield self.destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
 
         returnValue(response)
 
@@ -886,6 +898,7 @@
                 ))
             return None
 
+    @inlineCallbacks
     def doDestinationIndex(self, caltoindex):
         """
         Do destination resource indexing, replacing any index previous stored.
@@ -914,10 +927,10 @@
 
         content_type = self.request.headers.getHeader("content-type")
         if not self.internal_request and content_type is not None:
-            self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
+            yield self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
         else:
-            self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "calendar", params={"charset":"utf-8"}))))
-        return None
+            yield self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "calendar", params={"charset":"utf-8"}))))
+        returnValue(None)
 
     def doRemoveDestinationIndex(self):
         """
@@ -954,7 +967,7 @@
                 # UID conflict check - note we do this after reserving the UID to avoid a race condition where two requests
                 # try to write the same calendar data to two different resource URIs.
                 if not self.isiTIP:
-                    result, message, rname = self.noUIDConflict(self.uid)
+                    result, message, rname = (yield self.noUIDConflict(self.uid))
                     if not result:
                         log.err(message)
                         raise HTTPError(ErrorResponse(responsecode.FORBIDDEN,
@@ -968,7 +981,7 @@
             rruleChanged = self.truncateRecurrence()
 
             # Preserve private comments
-            new_has_private_comments = self.preservePrivateComments()
+            new_has_private_comments = (yield self.preservePrivateComments())
     
             # Do scheduling
             implicit_result = (yield self.doImplicitScheduling())
@@ -997,7 +1010,7 @@
                 is_scheduling_resource, data_changed, did_implicit_action = implicit_result
 
             # Initialize the rollback system
-            self.setupRollback()
+            yield self.setupRollback()
 
             """
             Handle actual store operations here.
@@ -1027,7 +1040,7 @@
 
             # Check for scheduling object resource and write property
             if is_scheduling_resource:
-                self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("true"))
+                yield self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("true"))
 
                 # Need to figure out when to change the schedule tag:
                 #
@@ -1049,7 +1062,7 @@
 
                 if change_scheduletag or self.scheduletag is None:
                     self.scheduletag = str(uuid.uuid4())
-                self.destination.writeDeadProperty(ScheduleTag.fromString(self.scheduletag))
+                yield self.destination.writeDeadProperty(ScheduleTag.fromString(self.scheduletag))
 
                 # Add a response header
                 response.headers.setHeader("Schedule-Tag", self.scheduletag)                
@@ -1062,25 +1075,25 @@
                     else:
                         # Schedule-Tag did not change => add current ETag to list of those that can
                         # be used in a weak pre-condition test
-                        if self.destination.hasDeadProperty(TwistedScheduleMatchETags):
-                            etags = self.destination.readDeadProperty(TwistedScheduleMatchETags).children
+                        if (yield self.destination.hasDeadProperty(TwistedScheduleMatchETags)):
+                            etags = (yield self.destination.readDeadProperty(TwistedScheduleMatchETags)).children
                         else:
                             etags = ()
-                    etags += (davxml.GETETag.fromString(self.destination.etag().tag),)
-                    self.destination.writeDeadProperty(TwistedScheduleMatchETags(*etags))
+                    etags += (davxml.GETETag.fromString((yield self.destination.etag()).tag),)
+                    yield self.destination.writeDeadProperty(TwistedScheduleMatchETags(*etags))
                 else:
-                    self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
+                    yield self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
             else:
-                self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("false"))                
-                self.destination.removeDeadProperty(ScheduleTag)                
-                self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
+                yield self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("false"))                
+                yield self.destination.removeDeadProperty(ScheduleTag)                
+                yield self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
 
             # Check for existence of private comments and write property
             if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
                 if new_has_private_comments:
-                    self.destination.writeDeadProperty(TwistedCalendarHasPrivateCommentsProperty())
+                    yield self.destination.writeDeadProperty(TwistedCalendarHasPrivateCommentsProperty())
                 elif not self.destinationcal:
-                    self.destination.removeDeadProperty(TwistedCalendarHasPrivateCommentsProperty)                
+                    yield self.destination.removeDeadProperty(TwistedCalendarHasPrivateCommentsProperty)                
 
             # Delete the original source if needed.
             if self.deletesource:
@@ -1088,9 +1101,9 @@
     
             # Index the new resource if storing to a calendar.
             if self.destinationcal:
-                result = self.doDestinationIndex(self.calendar)
+                result = (yield self.doDestinationIndex(self.calendar))
                 if result is not None:
-                    self.rollback.Rollback()
+                    yield self.rollback.Rollback()
                     returnValue(result)
     
             # Delete the original source if needed.
@@ -1114,13 +1127,15 @@
             returnValue(response)
     
         except Exception, err:
+            excType, excValue, excTraceback = sys.exc_info()
+
             if reservation:
                 yield reservation.unreserve()
     
             # Roll back changes to original server state. Note this may do nothing
             # if the rollback has already occurred or changes already committed.
             if self.rollback:
-                self.rollback.Rollback()
+                yield self.rollback.Rollback()
 
             if isinstance(err, InvalidOverriddenInstanceError):
                 raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"), description="Invalid overridden instance"))
@@ -1130,4 +1145,7 @@
                         NumberOfRecurrencesWithinLimits(PCDATAElement(str(err.max_allowed)))
                     ))
             else:
-                raise err
+                # Important - we have to raise the original exception again, we cannot just use plain
+                # "raise" here as we have inlineCallbacks in use and those blow away the re-raised
+                # exception
+                raise excType, excValue, excTraceback

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_calquery.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_calquery.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_calquery.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -58,7 +58,7 @@
 
     if not self.isCollection():
         parent = (yield self.locateParent(request, request.uri))
-        if not parent.isPseudoCalendarCollection():
+        if not (yield 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"))
 
@@ -116,6 +116,7 @@
         @param uri: the uri for the calendar collecton resource.
         """
         
+        @inlineCallbacks
         def queryCalendarObjectResource(resource, uri, name, calendar, timezone, query_ok=False, isowner=True):
             """
             Run a query on the specified calendar.
@@ -128,7 +129,7 @@
             # Handle private events access restrictions
             if not isowner:
                 try:
-                    access = resource.readDeadProperty(TwistedCalendarAccessProperty)
+                    access = (yield resource.readDeadProperty(TwistedCalendarAccessProperty))
                 except HTTPError:
                     access = None
             else:
@@ -145,12 +146,12 @@
                 else:
                     href = davxml.HRef.fromString(uri)
             
-                return report_common.responseForHref(request, responses, href, resource, calendar, timezone, propertiesForResource, query, isowner)
+                returnValue((yield report_common.responseForHref(request, responses, href, resource, calendar, timezone, propertiesForResource, query, isowner)))
             else:
-                return succeed(None)
+                returnValue(None)
     
         # Check whether supplied resource is a calendar or a calendar object resource
-        if calresource.isPseudoCalendarCollection():
+        if (yield 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
             has_prop = (yield calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
@@ -173,11 +174,13 @@
                 try:
                     # Get list of children that match the search and have read
                     # access
+                    results = (yield calresource.index().indexedSearch(filter))
                     names = [name for name, ignore_uid, ignore_type
-                        in calresource.index().indexedSearch(filter)]
+                        in results]
                 except IndexedSearchException:
+                    results = (yield calresource.index().bruteForceSearch())
                     names = [name for name, ignore_uid, ignore_type
-                        in calresource.index().bruteForceSearch()]
+                        in results]
                     index_query_ok = False
 
                 if not names:
@@ -200,7 +203,7 @@
                     child_path_name = urllib.unquote(child_uri_name)
                     
                     if generate_calendar_data or not index_query_ok:
-                        calendar = calresource.iCalendar(child_path_name)
+                        calendar = (yield calresource.iCalendar(child_path_name))
                         assert calendar is not None, "Calendar %s is missing from calendar collection %r" % (child_uri_name, self)
                     else:
                         calendar = None
@@ -213,7 +216,7 @@
             if query_tz is None:
 
                 parent = (yield calresource.locateParent(request, uri))
-                assert parent is not None and parent.isPseudoCalendarCollection()
+                assert parent is not None and (yield parent.isPseudoCalendarCollection())
 
                 has_prop = (yield parent.hasProperty((caldav_namespace, "calendar-timezone"), request))
                 if has_prop:
@@ -224,7 +227,7 @@
             # Check private events access status
             isowner = (yield calresource.isOwner(request, adminprincipals=True, readprincipals=True))
 
-            calendar = calresource.iCalendar()
+            calendar = (yield calresource.iCalendar())
             yield queryCalendarObjectResource(calresource, uri, None, calendar, timezone)
 
         returnValue(True)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_common.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_common.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -85,7 +85,7 @@
             return
 
     # When scanning we only go down as far as a calendar collection - not into one
-    if resource.isPseudoCalendarCollection():
+    if (yield resource.isPseudoCalendarCollection()):
         resources = [(resource, request_uri)]
     elif not resource.isCollection():
         resources = [(resource, request_uri)]
@@ -246,16 +246,16 @@
             # Handle private events access restrictions
             if not isowner:
                 try:
-                    access = resource.readDeadProperty(TwistedCalendarAccessProperty)
+                    access = (yield resource.readDeadProperty(TwistedCalendarAccessProperty))
                 except HTTPError:
                     access = None
             else:
                 access = None
 
             if calendar:
-                propvalue = property.elementFromCalendarWithAccessRestrictions(calendar, access, timezone)
+                propvalue = (yield property.elementFromCalendarWithAccessRestrictions(calendar, access, timezone))
             else:
-                propvalue = property.elementFromResourceWithAccessRestrictions(resource, access, timezone)
+                propvalue = (yield property.elementFromResourceWithAccessRestrictions(resource, access, timezone))
             if propvalue is None:
                 raise ValueError("Invalid CalDAV:calendar-data for request: %r" % (property,))
             properties_by_status[responsecode.OK].append(propvalue)
@@ -318,7 +318,7 @@
             returnValue(matchtotal)
 
     # May need organizer principal
-    organizer_principal = calresource.principalForCalendarUserAddress(organizer) if organizer else None
+    organizer_principal = (yield calresource.principalForCalendarUserAddress(organizer)) if organizer else None
     organizer_uid = organizer_principal.principalUID() if organizer_principal else ""
 
     #
@@ -352,9 +352,9 @@
     filteredaces = (yield calresource.inheritedACEsforChildren(request))
 
     try:
-        resources = calresource.index().indexedSearch(filter, fbtype=True)
+        resources = (yield calresource.index().indexedSearch(filter, fbtype=True))
     except IndexedSearchException:
-        resources = calresource.index().bruteForceSearch()
+        resources = (yield calresource.index().bruteForceSearch())
 
     # We care about separate instances for VEVENTs only
     aggregated_resources = {}
@@ -367,7 +367,6 @@
 
         # Check privileges - must have at least CalDAV:read-free-busy
         child = (yield request.locateChildResource(calresource, name))
-
         # TODO: for server-to-server we bypass this right now as we have no way to authorize external users.
         if not servertoserver:
             try:
@@ -388,7 +387,7 @@
                 if excludeuid:
                     # See if we have a UID match
                     if (excludeuid == uid):
-                        test_principal = calresource.principalForCalendarUserAddress(test_organizer) if test_organizer else None
+                        test_principal = (yield calresource.principalForCalendarUserAddress(test_organizer)) if test_organizer else None
                         test_uid = test_principal.principalUID() if test_principal else ""
 
                         # Check that ORGANIZER's match (security requirement)
@@ -418,7 +417,7 @@
                     fbinfo[fbtype_index_mapper.get(fbtype, 0)].append(clipped)
                 
         else:
-            calendar = calresource.iCalendar(name)
+            calendar = (yield calresource.iCalendar(name))
             
             # The calendar may come back as None if the resource is being changed, or was deleted
             # between our initial index query and getting here. For now we will ignore this error, but in
@@ -432,7 +431,7 @@
                 # See if we have a UID match
                 if (excludeuid == uid):
                     test_organizer = calendar.getOrganizer()
-                    test_principal = calresource.principalForCalendarUserAddress(test_organizer) if test_organizer else None
+                    test_principal = (yield calresource.principalForCalendarUserAddress(test_organizer)) if test_organizer else None
                     test_uid = test_principal.principalUID() if test_principal else ""
     
                     # Check that ORGANIZER's match (security requirement)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_multiget.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_multiget.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/method/report_multiget.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -54,7 +54,7 @@
     # Make sure target resource is of the right type
     if not self.isCollection():
         parent = (yield self.locateParent(request, request.uri))
-        if not parent.isPseudoCalendarCollection():
+        if not (yield 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"))
 
@@ -105,7 +105,7 @@
     """
 
     disabled = False
-    if self.isPseudoCalendarCollection():
+    if (yield self.isPseudoCalendarCollection()):
         requestURIis = "calendar"
 
         # Do some optimisation of access control calculation by determining any inherited ACLs outside of
@@ -138,7 +138,7 @@
             for href in resources:
                 resource_uri = str(href)
                 name = unquote(resource_uri[resource_uri.rfind("/") + 1:])
-                if not self._isChildURI(request, resource_uri) or self.getChild(name) is None:
+                if not self._isChildURI(request, resource_uri) or (yield self.getChild(name)) is None:
                     responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
                 else:
                     valid_names.append(name)
@@ -216,7 +216,7 @@
     
                     parent = (yield child.locateParent(request, resource_uri))
     
-                    if not parent.isCalendarCollection() or not parent.index().resourceExists(name):
+                    if not (yield parent.isCalendarCollection()) or not parent.index().resourceExists(name):
                         responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
                         continue
                     
@@ -245,7 +245,7 @@
     
                     parent = (yield self.locateParent(request, resource_uri))
     
-                    if not parent.isPseudoCalendarCollection() or not parent.index().resourceExists(name):
+                    if not (yield parent.isPseudoCalendarCollection()) or not parent.index().resourceExists(name):
                         responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
                         continue
                     child = self

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/resource.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/resource.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -115,6 +115,7 @@
     # HTTP
     ##
 
+    @inlineCallbacks
     def render(self, request):
         if config.EnableMonolithicCalendars:
             #
@@ -139,24 +140,20 @@
         else:
             renderAsHTML = True
 
-        if not renderAsHTML and self.isPseudoCalendarCollection():
+        if not renderAsHTML and (yield self.isPseudoCalendarCollection()):
             # Render a monolithic iCalendar file
             if request.path[-1] != "/":
                 # Redirect to include trailing '/' in URI
-                return RedirectResponse(request.unparseURL(path=urllib.quote(urllib.unquote(request.path), safe=':/')+'/'))
+                returnValue(RedirectResponse(request.unparseURL(path=urllib.quote(urllib.unquote(request.path), safe=':/')+'/')))
 
-            def _defer(data):
-                response = Response()
-                response.stream = MemoryStream(str(data))
-                response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
-                return response
+            data = (yield self.iCalendarRolledup(request))
+            response = Response()
+            response.stream = MemoryStream(str(data))
+            response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
+            returnValue(response)
 
-            d = self.iCalendarRolledup(request)
-            d.addCallback(_defer)
-            return d
+        returnValue((yield super(CalDAVResource, self).render(request)))
 
-        return super(CalDAVResource, self).render(request)
-
     def renderHTTP(self, request):
         response = maybeDeferred(super(CalDAVResource, self).renderHTTP, request)
 
@@ -197,6 +194,7 @@
         *[caldavxml.CalendarComponent(name=item) for item in allowedComponents]
     )
 
+    @inlineCallbacks
     def hasProperty(self, property, request):
         """
         Need to special case schedule-calendar-transp for backwards compatability.
@@ -208,10 +206,10 @@
             qname = property.qname()
 
         # Force calendar collections to always appear to have the property
-        if qname == (caldav_namespace, "schedule-calendar-transp") and self.isCalendarCollection():
-            return succeed(True)
+        if qname == (caldav_namespace, "schedule-calendar-transp") and (yield self.isCalendarCollection()):
+            returnValue(True)
         else:
-            return super(CalDAVResource, self).hasProperty(property, request)
+            returnValue((yield super(CalDAVResource, self).hasProperty(property, request)))
 
     @inlineCallbacks
     def readProperty(self, property, request):
@@ -230,8 +228,8 @@
         elif namespace == caldav_namespace:
             if name == "supported-calendar-component-set":
                 # CalDAV-access-09, section 5.2.3
-                if self.hasDeadProperty(qname):
-                    returnValue(self.readDeadProperty(qname))
+                if (yield self.hasDeadProperty(qname)):
+                    returnValue((yield self.readDeadProperty(qname)))
                 returnValue(self.supportedCalendarComponentSet)
             elif name == "supported-calendar-data":
                 # CalDAV-access-09, section 5.2.4
@@ -258,13 +256,13 @@
             elif name == "schedule-calendar-transp":
                 # For backwards compatibility, if the property does not exist we need to create
                 # it and default to the old free-busy-set value.
-                if self.isCalendarCollection() and not self.hasDeadProperty(property):
+                if (yield self.isCalendarCollection()) and not (yield self.hasDeadProperty(property)):
                     # For backwards compatibility we need to sync this up with the calendar-free-busy-set on the inbox
                     principal = (yield self.ownerPrincipal(request))
                     fbset = (yield principal.calendarFreeBusyURIs(request))
                     url = (yield self.canonicalURL(request))
                     opaque = url in fbset
-                    self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque() if opaque else caldavxml.Transparent()))
+                    yield self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque() if opaque else caldavxml.Transparent()))
 
         result = (yield super(CalDAVResource, self).readProperty(property, request))
         returnValue(result)
@@ -276,7 +274,7 @@
         )
 
         if property.qname() == (caldav_namespace, "supported-calendar-component-set"):
-            if not self.isPseudoCalendarCollection():
+            if not (yield self.isPseudoCalendarCollection()):
                 raise HTTPError(StatusResponse(
                     responsecode.FORBIDDEN,
                     "Property %s may only be set on calendar collection." % (property,)
@@ -306,7 +304,7 @@
                 ))
 
         elif property.qname() == (caldav_namespace, "schedule-calendar-transp"):
-            if not self.isCalendarCollection():
+            if not (yield self.isCalendarCollection()):
                 raise HTTPError(StatusResponse(
                     responsecode.FORBIDDEN,
                     "Property %s may only be set on calendar collection." % (property,)
@@ -316,21 +314,16 @@
             principal = (yield self.ownerPrincipal(request))
             
             # Map owner to their inbox
-            inboxURL = principal.scheduleInboxURL()
+            inboxURL = yield principal.scheduleInboxURL()
             if inboxURL:
                 inbox = (yield request.locateResource(inboxURL))
                 myurl = (yield self.canonicalURL(request))
-                inbox.processFreeBusyCalendar(myurl, property.children[0] == caldavxml.Opaque())
+                yield inbox.processFreeBusyCalendar(myurl, property.children[0] == caldavxml.Opaque())
 
         result = (yield super(CalDAVResource, self).writeProperty(property, request))
         returnValue(result)
 
-    def writeDeadProperty(self, property):
-        val = super(CalDAVResource, self).writeDeadProperty(property)
 
-        return val
-
-
     ##
     # ACL
     ##
@@ -341,8 +334,8 @@
         acls = (yield super(CalDAVResource, self).accessControlList(request, *args, **kwargs))
 
         # Look for private events access classification
-        if self.hasDeadProperty(TwistedCalendarAccessProperty):
-            access = self.readDeadProperty(TwistedCalendarAccessProperty)
+        if (yield self.hasDeadProperty(TwistedCalendarAccessProperty)):
+            access = (yield self.readDeadProperty(TwistedCalendarAccessProperty))
             if access.getValue() in (Component.ACCESS_PRIVATE, Component.ACCESS_CONFIDENTIAL, Component.ACCESS_RESTRICTED,):
                 # Need to insert ACE to prevent non-owner principals from seeing this resource
                 owner = (yield self.owner(request))
@@ -443,9 +436,9 @@
     def displayName(self):
         if 'record' in dir(self):
             if self.record.fullName:
-                return self.record.fullName
+                return succeed(self.record.fullName)
             elif self.record.shortNames:
-                return self.record.shortNames[0]
+                return succeed(self.record.shortNames[0])
             else:
                 return super(DAVResource, self).displayName()
         else:
@@ -461,20 +454,21 @@
         """
         return self.isSpecialCollection(caldavxml.Calendar)
 
+    @inlineCallbacks
     def isSpecialCollection(self, collectiontype):
         """
         See L{ICalDAVResource.isSpecialCollection}.
         """
-        if not self.isCollection(): return False
+        if not self.isCollection(): returnValue(False)
 
         try:
-            resourcetype = self.readDeadProperty((dav_namespace, "resourcetype"))
+            resourcetype = (yield self.readDeadProperty((dav_namespace, "resourcetype")))
         except HTTPError, e:
             assert e.response.code == responsecode.NOT_FOUND, (
                 "Unexpected response code: %s" % (e.response.code,)
             )
-            return False
-        return bool(resourcetype.childrenOfType(collectiontype))
+            returnValue(False)
+        returnValue(bool(resourcetype.childrenOfType(collectiontype)))
 
     def isPseudoCalendarCollection(self):
         """
@@ -488,10 +482,10 @@
         """
         assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
 
-        def checkPrivilegesError(failure):
+        def checkPrivilegesError(failure, children):
             failure.trap(AccessDeniedError)
 
-            reactor.callLater(0, getChild)
+            reactor.callLater(0, getChild, children)
 
         def checkPrivileges(child):
             if privileges is None:
@@ -501,18 +495,23 @@
             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
+        def gotChild(child, childpath, children):
+            def isCalCallback(result):
+                if result:
+                    callback(child, childpath)
+                elif child.isCollection():
+                    if depth == "infinity":
+                        fc = child.findCalendarCollections(depth, request, callback, privileges)
+                        fc.addCallback(lambda x: reactor.callLater(0, getChild, children))
+                        return fc
+                reactor.callLater(0, getChild, children)
 
-            reactor.callLater(0, getChild)
+            d = child.isCalendarCollection()
+            d.addCallback(isCalCallback)
+            return d
 
-        def getChild():
+
+        def getChild(children):
             try:
                 childname = children.pop()
             except IndexError:
@@ -521,15 +520,14 @@
                 childpath = joinURL(basepath, childname)
                 child = request.locateResource(childpath)
                 child.addCallback(checkPrivileges)
-                child.addCallbacks(gotChild, checkPrivilegesError, (childpath,))
+                child.addCallbacks(gotChild, checkPrivilegesError, (childpath, children), None, (children,))
                 child.addErrback(completionDeferred.errback)
 
         completionDeferred = Deferred()
 
         if depth != "0" and self.isCollection():
             basepath = request.urlForResource(self)
-            children = self.listChildren()
-            getChild()
+            self.listChildren().addCallback(getChild)
         else:
             completionDeferred.callback(None)
 
@@ -554,10 +552,10 @@
         
         # For backwards compatibility we need to sync this up with the calendar-free-busy-set on the inbox
         principal = (yield self.ownerPrincipal(request))
-        inboxURL = principal.scheduleInboxURL()
+        inboxURL = yield principal.scheduleInboxURL()
         if inboxURL:
             inbox = (yield request.locateResource(inboxURL))
-            inbox.processFreeBusyCalendar(request.path, False)
+            yield inbox.processFreeBusyCalendar(request.path, False)
 
     @inlineCallbacks
     def movedCalendar(self, request, defaultCalendar, destination, destination_uri):
@@ -567,36 +565,37 @@
         
         # For backwards compatibility we need to sync this up with the calendar-free-busy-set on the inbox
         principal = (yield self.ownerPrincipal(request))
-        inboxURL = principal.scheduleInboxURL()
+        inboxURL = yield principal.scheduleInboxURL()
         if inboxURL:
             (_ignore_scheme, _ignore_host, destination_path, _ignore_query, _ignore_fragment) = urlsplit(normalizeURL(destination_uri))
 
             inbox = (yield request.locateResource(inboxURL))
-            inbox.processFreeBusyCalendar(request.path, False)
-            inbox.processFreeBusyCalendar(destination_uri, destination.isCalendarOpaque())
+            yield inbox.processFreeBusyCalendar(request.path, False)
+            yield inbox.processFreeBusyCalendar(destination_uri, (yield destination.isCalendarOpaque()))
             
             # Adjust the default calendar setting if necessary
             if defaultCalendar:
                 yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(destination_path)), request)               
 
+    @inlineCallbacks
     def isCalendarOpaque(self):
         
-        assert self.isCalendarCollection()
+        assert (yield self.isCalendarCollection())
         
-        if self.hasDeadProperty((caldav_namespace, "schedule-calendar-transp")):
-            property = self.readDeadProperty((caldav_namespace, "schedule-calendar-transp"))
-            return property.children[0] == caldavxml.Opaque()
+        if (yield self.hasDeadProperty((caldav_namespace, "schedule-calendar-transp"))):
+            property = (yield self.readDeadProperty((caldav_namespace, "schedule-calendar-transp")))
+            returnValue(property.children[0] == caldavxml.Opaque())
         else:
-            return False
+            returnValue(False)
 
     @inlineCallbacks
     def isDefaultCalendar(self, request):
         
-        assert self.isCalendarCollection()
+        assert (yield self.isCalendarCollection())
         
         # Not allowed to delete the default calendar
         principal = (yield self.ownerPrincipal(request))
-        inboxURL = principal.scheduleInboxURL()
+        inboxURL = yield principal.scheduleInboxURL()
         if inboxURL:
             inbox = (yield request.locateResource(inboxURL))
             default = (yield inbox.readProperty((caldav_namespace, "schedule-default-calendar-URL"), request))
@@ -618,14 +617,18 @@
         an infinite loop.  A subclass must override one of both of these
         methods.
         """
-        calendar_data = self.iCalendarText(name)
+        d = self.iCalendarText(name)
+        def _gotData(calendar_data):
+            if calendar_data is None:
+                return None
 
-        if calendar_data is None: return None
+            try:
+                return iComponent.fromString(calendar_data)
+            except ValueError:
+                return None
 
-        try:
-            return iComponent.fromString(calendar_data)
-        except ValueError:
-            return None
+        d.addCallback(_gotData)
+        return d
 
     def iCalendarRolledup(self, request):
         """
@@ -648,7 +651,7 @@
         an infinite loop.  A subclass must override one of both of these
         methods.
         """
-        return str(self.iCalendar(name))
+        return self.iCalendar(name).addCallback(str)
 
     def iCalendarXML(self, name=None):
         """
@@ -656,8 +659,9 @@
         This implementation returns an XML element constructed from the object
         returned by L{iCalendar} when given the same arguments.
         """
-        return caldavxml.CalendarData.fromCalendar(self.iCalendar(name))
+        return self.iCalendar(name).addCallback(caldavxml.CalendarData.fromCalendar)
 
+    # Deferred, because the lookup function has to be
     def iCalendarAddressDoNormalization(self, ical):
         """
         Normalize calendar user addresses in the supplied iCalendar object into their
@@ -667,24 +671,26 @@
         @type ical: L{Component}
         """
 
+        @inlineCallbacks
         def lookupFunction(cuaddr):
-            principal = self.principalForCalendarUserAddress(cuaddr)
+            principal = (yield self.principalForCalendarUserAddress(cuaddr))
             if principal is None:
-                return (None, None, None)
+                returnValue((None, None, None))
             else:
-                return (principal.record.fullName.decode("utf-8"),
+                returnValue((principal.record.fullName.decode("utf-8"),
                     principal.record.guid,
-                    principal.record.calendarUserAddresses)
+                    principal.record.calendarUserAddresses))
 
-        ical.normalizeCalendarUserAddresses(lookupFunction)
+        return ical.normalizeCalendarUserAddresses(lookupFunction)
 
 
+    @inlineCallbacks
     def principalForCalendarUserAddress(self, address):
         for principalCollection in self.principalCollections():
-            principal = principalCollection.principalForCalendarUserAddress(address)
+            principal = (yield principalCollection.principalForCalendarUserAddress(address))
             if principal is not None:
-                return principal
-        return None
+                returnValue(principal)
+        returnValue(None)
 
     def supportedReports(self):
         result = super(CalDAVResource, self).supportedReports()
@@ -695,6 +701,8 @@
             result.append(davxml.Report(caldavxml.FreeBusyQuery(),))
         return result
 
+
+    @inlineCallbacks
     def writeNewACEs(self, newaces):
         """
         Write a new ACL to the resource's property store. We override this for calendar collections
@@ -707,7 +715,7 @@
         """
 
         # Do this only for regular calendar collections and Inbox/Outbox
-        if self.isPseudoCalendarCollection():
+        if (yield self.isPseudoCalendarCollection()):
             edited_aces = []
             for ace in newaces:
                 if TwistedACLInheritable() not in ace.children:
@@ -720,7 +728,7 @@
             edited_aces = newaces
 
         # Do inherited with possibly modified set of aces
-        super(CalDAVResource, self).writeNewACEs(edited_aces)
+        returnValue((yield maybeDeferred(super(CalDAVResource, self).writeNewACEs, edited_aces)))
 
     ##
     # Utilities
@@ -757,13 +765,13 @@
         """
         Quota root only ever set on calendar homes.
         """
-        return False
+        return succeed(False)
     
     def quotaRoot(self, request):
         """
         Quota root only ever set on calendar homes.
         """
-        return None
+        return succeed(None)
 
 class CalendarPrincipalCollectionResource (DAVPrincipalCollectionResource, CalDAVResource):
     """
@@ -775,13 +783,13 @@
         return True
 
     def isCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def isPseudoCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def principalForCalendarUserAddress(self, address):
-        return None
+        return succeed(None)
 
     def supportedReports(self):
         """
@@ -855,24 +863,25 @@
 
         if namespace == caldav_namespace:
             if name == "calendar-home-set":
+                urls = (yield self.calendarHomeURLs())
                 returnValue(caldavxml.CalendarHomeSet(
-                    *[davxml.HRef(url) for url in self.calendarHomeURLs()]
+                    *[davxml.HRef(url) for url in urls]
                 ))
 
             elif name == "calendar-user-address-set":
                 returnValue(caldavxml.CalendarUserAddressSet(
-                    *[davxml.HRef(uri) for uri in self.calendarUserAddresses()]
+                    *[davxml.HRef(uri) for uri in (yield self.calendarUserAddresses())]
                 ))
 
             elif name == "schedule-inbox-URL":
-                url = self.scheduleInboxURL()
+                url = (yield self.scheduleInboxURL())
                 if url is None:
                     returnValue(None)
                 else:
                     returnValue(caldavxml.ScheduleInboxURL(davxml.HRef(url)))
 
             elif name == "schedule-outbox-URL":
-                url = self.scheduleOutboxURL()
+                url = (yield self.scheduleOutboxURL())
                 if url is None:
                     returnValue(None)
                 else:
@@ -883,7 +892,7 @@
 
         elif namespace == calendarserver_namespace:
             if name == "dropbox-home-URL" and config.EnableDropBox:
-                url = self.dropboxURL()
+                url = (yield self.dropboxURL())
                 if url is None:
                     returnValue(None)
                 else:
@@ -918,20 +927,22 @@
 
         return super(CalendarPrincipalResource, self).writeProperty(property, request)
 
+    @inlineCallbacks
     def calendarHomeURLs(self):
-        if self.hasDeadProperty((caldav_namespace, "calendar-home-set")):
-            home_set = self.readDeadProperty((caldav_namespace, "calendar-home-set"))
-            return [str(h) for h in home_set.children]
+        if (yield self.hasDeadProperty((caldav_namespace, "calendar-home-set"))):
+            home_set = (yield self.readDeadProperty((caldav_namespace, "calendar-home-set")))
+            returnValue([str(h) for h in home_set.children])
         else:
-            return ()
+            returnValue(())
 
+    @inlineCallbacks
     def calendarUserAddresses(self):
-        if self.hasDeadProperty((caldav_namespace, "calendar-user-address-set")):
-            addresses = self.readDeadProperty((caldav_namespace, "calendar-user-address-set"))
-            return [str(h) for h in addresses.children]
+        if (yield self.hasDeadProperty((caldav_namespace, "calendar-user-address-set"))):
+            addresses = (yield self.readDeadProperty((caldav_namespace, "calendar-user-address-set")))
+            returnValue([str(h) for h in addresses.children])
         else:
             # Must have a valid address of some kind so use the principal uri
-            return (self.principalURL(),)
+            returnValue((self.principalURL(),))
 
     def calendarFreeBusyURIs(self, request):
         def gotInbox(inbox):
@@ -961,34 +972,37 @@
         """
         @return: the deferred schedule inbox for this principal.
         """
-        return request.locateResource(self.scheduleInboxURL())
+        return self.scheduleInboxURL().addCallback(request.locateResource)
 
+    @inlineCallbacks
     def scheduleInboxURL(self):
-        if self.hasDeadProperty((caldav_namespace, "schedule-inbox-URL")):
-            inbox = self.readDeadProperty((caldav_namespace, "schedule-inbox-URL"))
-            return str(inbox.children[0])
+        if (yield self.hasDeadProperty((caldav_namespace, "schedule-inbox-URL"))):
+            inbox = (yield self.readDeadProperty((caldav_namespace, "schedule-inbox-URL")))
+            returnValue(str(inbox.children[0]))
         else:
-            return None
+            returnValue(None)
 
+    @inlineCallbacks
     def scheduleOutboxURL(self):
         """
         @return: the schedule outbox URL for this principal.
         """
-        if self.hasDeadProperty((caldav_namespace, "schedule-outbox-URL")):
-            outbox = self.readDeadProperty((caldav_namespace, "schedule-outbox-URL"))
-            return str(outbox.children[0])
+        if (yield self.hasDeadProperty((caldav_namespace, "schedule-outbox-URL"))):
+            outbox = (yield self.readDeadProperty((caldav_namespace, "schedule-outbox-URL")))
+            returnValue(str(outbox.children[0]))
         else:
-            return None
+            returnValue(None)
 
+    @inlineCallbacks
     def dropboxURL(self):
         """
         @return: the drop box home collection URL for this principal.
         """
-        if self.hasDeadProperty((calendarserver_namespace, "dropbox-home-URL")):
-            inbox = self.readDeadProperty((caldav_namespace, "dropbox-home-URL"))
-            return str(inbox.children[0])
+        if (yield self.hasDeadProperty((calendarserver_namespace, "dropbox-home-URL"))):
+            inbox = (yield self.readDeadProperty((caldav_namespace, "dropbox-home-URL")))
+            returnValue(str(inbox.children[0]))
         else:
-            return None
+            returnValue(None)
 
     ##
     # Quota
@@ -998,13 +1012,13 @@
         """
         Quota root only ever set on calendar homes.
         """
-        return False
+        return succeed(False)
     
     def quotaRoot(self, request):
         """
         Quota root only ever set on calendar homes.
         """
-        return None
+        return succeed(None)
 
 
 class AuthenticationWrapper(SuperAuthenticationWrapper):
@@ -1043,7 +1057,7 @@
     try:
         resource = ICalDAVResource(resource)
     except TypeError:
-        return False
+        return succeed(False)
     else:
         return resource.isCalendarCollection()
 
@@ -1051,7 +1065,7 @@
     try:
         resource = ICalDAVResource(resource)
     except TypeError:
-        return False
+        return succeed(False)
     else:
         return resource.isPseudoCalendarCollection()
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/schedule.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/schedule.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -26,7 +26,7 @@
 
 from twext.web2.dav.davxml import ErrorResponse
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.dav.util import joinURL, normalizeURL
@@ -63,10 +63,10 @@
         return True
 
     def isCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def isPseudoCalendarCollection(self):
-        return True
+        return succeed(True)
 
     def supportedReports(self):
         result = super(CalDAVResource, self).supportedReports()
@@ -88,7 +88,7 @@
     )
 
     def resourceType(self):
-        return davxml.ResourceType.scheduleInbox
+        return succeed(davxml.ResourceType.scheduleInbox)
 
     def defaultAccessControlList(self):
         
@@ -98,12 +98,14 @@
         if config.Scheduling.CalDAV.OldDraftCompatibility:
             privs += (davxml.Privilege(caldavxml.Schedule()),)
 
-        return davxml.ACL(
-            # CalDAV:schedule-deliver for any authenticated user
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(*privs),
-            ),
+        return succeed(
+            davxml.ACL(
+                # CalDAV:schedule-deliver for any authenticated user
+                davxml.ACE(
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(*privs),
+                ),
+            )
         )
 
     @inlineCallbacks
@@ -115,18 +117,18 @@
 
         if qname == (caldav_namespace, "calendar-free-busy-set"):
             # Always return at least an empty list
-            if not self.hasDeadProperty(property):
+            if not (yield self.hasDeadProperty(property)):
                 returnValue(caldavxml.CalendarFreeBusySet())
         elif qname == (caldav_namespace, "schedule-default-calendar-URL"):
             # Must have a valid default
             try:
-                defaultCalendarProperty = self.readDeadProperty(property)
+                defaultCalendarProperty = (yield self.readDeadProperty(property))
             except HTTPError:
                 defaultCalendarProperty = None
             if defaultCalendarProperty and len(defaultCalendarProperty.children) == 1:
                 defaultCalendar = str(defaultCalendarProperty.children[0])
                 cal = (yield request.locateResource(str(defaultCalendar)))
-                if cal is not None and cal.exists() and isCalendarCollectionResource(cal):
+                if cal is not None and cal.exists() and (yield isCalendarCollectionResource(cal)):
                     returnValue(defaultCalendarProperty) 
             
             # Default is not valid - we have to try to pick one
@@ -157,14 +159,14 @@
             # whether existing items in the property are still valid - only new ones.
             property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
             new_calendars = set([str(href) for href in property.children])
-            if not self.hasDeadProperty(property):
+            if not (yield self.hasDeadProperty(property)):
                 old_calendars = set()
             else:
-                old_calendars = set([str(href) for href in self.readDeadProperty(property).children])
+                old_calendars = set([str(href) for href in (yield self.readDeadProperty(property)).children])
             added_calendars = new_calendars.difference(old_calendars)
             for href in added_calendars:
                 cal = (yield request.locateResource(str(href)))
-                if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
+                if cal is None or not cal.exists() or not (yield isCalendarCollectionResource(cal)):
                     # Validate that href's point to a valid calendar.
                     raise HTTPError(ErrorResponse(
                         responsecode.CONFLICT,
@@ -180,7 +182,7 @@
                 calURI = str(new_calendar[0])
                 cal = (yield request.locateResource(str(new_calendar[0])))
             # TODO: check that owner of the new calendar is the same as owner of this inbox
-            if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
+            if cal is None or not cal.exists() or not (yield isCalendarCollectionResource(cal)):
                 # Validate that href's point to a valid calendar.
                 raise HTTPError(ErrorResponse(
                     responsecode.CONFLICT,
@@ -193,21 +195,22 @@
 
         yield super(ScheduleInboxResource, self).writeProperty(property, request)
 
+    @inlineCallbacks
     def processFreeBusyCalendar(self, uri, addit):
         uri = normalizeURL(uri)
 
-        if not self.hasDeadProperty((caldav_namespace, "calendar-free-busy-set")):
+        if not (yield self.hasDeadProperty((caldav_namespace, "calendar-free-busy-set"))):
             fbset = set()
         else:
-            fbset = set([normalizeURL(str(href)) for href in self.readDeadProperty((caldav_namespace, "calendar-free-busy-set")).children])
+            fbset = set([normalizeURL(str(href)) for href in (yield self.readDeadProperty((caldav_namespace, "calendar-free-busy-set"))).children])
         if addit:
             if uri not in fbset:
                 fbset.add(uri)
-                self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
+                yield self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
         else:
             if uri in fbset:
                 fbset.remove(uri)
-                self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
+                yield self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
 
     @inlineCallbacks
     def pickNewDefaultCalendar(self, request):
@@ -220,9 +223,9 @@
         defaultCalendarURL = (yield joinURL(calendarHomeURL, "calendar"))
         defaultCalendar = (yield request.locateResource(defaultCalendarURL))
         if defaultCalendar is None or not defaultCalendar.exists():
-            self.parent.provisionDefaultCalendars()
+            yield self.parent.provisionDefaultCalendars()
         else:
-            self.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(defaultCalendarURL)))
+            yield self.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(defaultCalendarURL)))
         returnValue(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(defaultCalendarURL)))
 
 class ScheduleOutboxResource (CalendarSchedulingCollectionResource):
@@ -232,9 +235,10 @@
     Extends L{DAVResource} to provide CalDAV functionality.
     """
 
+    @inlineCallbacks
     def defaultAccessControlList(self):
         if config.EnableProxyPrincipals:
-            myPrincipal = self.parent.principalForRecord()
+            myPrincipal = (yield self.parent.principalForRecord())
     
             privs = (
                 davxml.Privilege(caldavxml.ScheduleSend()),
@@ -242,19 +246,19 @@
             if config.Scheduling.CalDAV.OldDraftCompatibility:
                 privs += (davxml.Privilege(caldavxml.Schedule()),)
     
-            return davxml.ACL(
+            returnValue(davxml.ACL(
                 # CalDAV:schedule for associated write proxies
                 davxml.ACE(
                     davxml.Principal(davxml.HRef(joinURL(myPrincipal.principalURL(), "calendar-proxy-write"))),
                     davxml.Grant(*privs),
                     davxml.Protected(),
                 ),
-            )
+            ))
         else:
-            return super(ScheduleOutboxResource, self).defaultAccessControlList()
+            returnValue((yield super(ScheduleOutboxResource, self).defaultAccessControlList()))
 
     def resourceType(self):
-        return davxml.ResourceType.scheduleOutbox
+        return succeed(davxml.ResourceType.scheduleOutbox)
 
     @inlineCallbacks
     def http_POST(self, request):
@@ -301,26 +305,28 @@
         if config.Scheduling.CalDAV.OldDraftCompatibility:
             privs += (davxml.Privilege(caldavxml.Schedule()),)
 
-        return davxml.ACL(
-            # DAV:Read, CalDAV:schedule-deliver for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(*privs),
-                davxml.Protected(),
-            ),
+        return succeed(
+            davxml.ACL(
+                # DAV:Read, CalDAV:schedule-deliver for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(*privs),
+                    davxml.Protected(),
+                ),
+            )
         )
 
     def resourceType(self):
-        return davxml.ResourceType.ischeduleinbox
+        return succeed(davxml.ResourceType.ischeduleinbox)
 
     def isCollection(self):
         return False
 
     def isCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def isPseudoCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def render(self, request):
         output = """<html>

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/caldav.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/caldav.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -184,14 +184,14 @@
                 responses.add(recipient.cuaddr, responsecode.OK, reqstatus=iTIPRequestStatus.MESSAGE_DELIVERED)
     
                 # Store CALDAV:originator property
-                child.writeDeadProperty(caldavxml.Originator(davxml.HRef(self.scheduler.originator.cuaddr)))
+                yield child.writeDeadProperty(caldavxml.Originator(davxml.HRef(self.scheduler.originator.cuaddr)))
             
                 # Store CALDAV:recipient property
-                child.writeDeadProperty(caldavxml.Recipient(davxml.HRef(recipient.cuaddr)))
+                yield child.writeDeadProperty(caldavxml.Recipient(davxml.HRef(recipient.cuaddr)))
             
                 # Store CS:schedule-changes property if present
                 if changes:
-                    child.writeDeadProperty(changes)
+                    yield child.writeDeadProperty(changes)
                     
                 returnValue(True)
     
@@ -199,7 +199,7 @@
     def generateFreeBusyResponse(self, recipient, responses, organizerProp, uid):
 
         # Extract the ATTENDEE property matching current recipient from the calendar data
-        cuas = recipient.principal.calendarUserAddresses()
+        cuas = (yield recipient.principal.calendarUserAddresses())
         attendeeProp = self.scheduler.calendar.getAttendeeProperty(cuas)
 
         remote = isinstance(self.scheduler.organizer, RemoteCalendarUser)
@@ -248,7 +248,7 @@
         matchtotal = 0
         for calendarResourceURL in fbset:
             calendarResource = (yield self.scheduler.request.locateResource(calendarResourceURL))
-            if calendarResource is None or not calendarResource.exists() or not isCalendarCollectionResource(calendarResource):
+            if calendarResource is None or not calendarResource.exists() or not (yield isCalendarCollectionResource(calendarResource)):
                 # 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

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/implicit.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/implicit.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -70,7 +70,8 @@
         self.internal_request = internal_request
 
         existing_resource = resource.exists()
-        existing_type = "schedule" if self.checkSchedulingObjectResource(resource) else "calendar"
+        schedulingObject = yield self.checkSchedulingObjectResource(resource)
+        existing_type = "schedule" if schedulingObject else "calendar"
         new_type = "schedule" if (yield self.checkImplicitState()) else "calendar"
 
         if existing_type == "calendar":
@@ -87,7 +88,7 @@
             # Also make sure that we return the new calendar being written rather than the old one
             # when the implicit action is executed
             self.return_calendar = calendar
-            self.calendar = resource.iCalendar()
+            self.calendar = yield resource.iCalendar()
             yield self.checkImplicitState()
         
         # Attendees are not allowed to overwrite one type with another
@@ -107,8 +108,9 @@
         new_type = "schedule" if (yield self.checkImplicitState()) else "calendar"
 
         dest_exists = destresource.exists()
-        dest_is_implicit = self.checkSchedulingObjectResource(destresource)
-        src_is_implicit = self.checkSchedulingObjectResource(srcresource) or new_type == "schedule"
+        dest_is_implicit = yield self.checkSchedulingObjectResource(destresource)
+        src_is_implicit = yield self.checkSchedulingObjectResource(srcresource)
+        src_is_implicit = src_is_implicit or new_type == "schedule"
 
         if srccal and destcal:
             if src_is_implicit and dest_exists or dest_is_implicit:
@@ -137,8 +139,9 @@
 
         new_type = "schedule" if (yield self.checkImplicitState()) else "calendar"
 
-        dest_is_implicit = self.checkSchedulingObjectResource(destresource)
-        src_is_implicit = self.checkSchedulingObjectResource(srcresource) or new_type == "schedule"
+        dest_is_implicit = yield self.checkSchedulingObjectResource(destresource)
+        src_is_implicit = yield self.checkSchedulingObjectResource(srcresource)
+        src_is_implicit = src_is_implicit or new_type == "schedule"
 
         if srccal and destcal:
             if src_is_implicit or dest_is_implicit:
@@ -166,37 +169,39 @@
 
         yield self.checkImplicitState()
 
-        resource_type = "schedule" if self.checkSchedulingObjectResource(resource) else "calendar"
+        schedulingObject = yield self.checkSchedulingObjectResource(resource)
+        resource_type = "schedule" if schedulingObject else "calendar"
         self.action = "remove" if resource_type == "schedule" else "none"
 
         returnValue((self.action != "none", False,))
 
+    @inlineCallbacks
     def checkSchedulingObjectResource(self, resource):
         
         if resource and resource.exists():
             try:
-                implicit = resource.readDeadProperty(TwistedSchedulingObjectResource)
+                implicit = (yield resource.readDeadProperty(TwistedSchedulingObjectResource))
             except HTTPError:
                 implicit = None
             if implicit is not None:
-                return implicit != "false"
+                returnValue(implicit != "false")
             else:
-                calendar = resource.iCalendar()
+                calendar = yield resource.iCalendar()
                 # Get the ORGANIZER and verify it is the same for all components
                 try:
                     organizer = calendar.validOrganizerForScheduling()
                 except ValueError:
                     # We have different ORGANIZERs in the same iCalendar object - this is an error
-                    return False
-                organizerPrincipal = resource.principalForCalendarUserAddress(organizer) if organizer else None
-                resource.writeDeadProperty(TwistedSchedulingObjectResource("true" if organizerPrincipal != None else "false"))
+                    returnValue(False)
+                organizerPrincipal = (yield resource.principalForCalendarUserAddress(organizer)) if organizer else None
+                yield resource.writeDeadProperty(TwistedSchedulingObjectResource("true" if organizerPrincipal != None else "false"))
                 log.debug("Implicit - checked scheduling object resource state for UID: '%s', result: %s" % (
                     calendar.resourceUID(),
                     "true" if organizerPrincipal != None else "false",
                 ))
-                return organizerPrincipal != None
+                returnValue(organizerPrincipal != None)
 
-        return False
+        returnValue(False)
         
     @inlineCallbacks
     def checkImplicitState(self):
@@ -208,7 +213,7 @@
         organizer_scheduling = (yield self.isOrganizerScheduling())
         if organizer_scheduling:
             self.state = "organizer"
-        elif self.isAttendeeScheduling():
+        elif (yield self.isAttendeeScheduling()):
             self.state = "attendee"
         else:
             self.state = None
@@ -270,7 +275,7 @@
         
         # Get some useful information from the calendar
         yield self.extractCalendarData()
-        self.organizerPrincipal = self.resource.principalForCalendarUserAddress(self.organizer)
+        self.organizerPrincipal = (yield self.resource.principalForCalendarUserAddress(self.organizer))
         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
 
         # Originator is the organizer in this case
@@ -340,7 +345,7 @@
 
                 if self.originatorPrincipal:
                     # Pick the first mailto cu address or the first other type
-                    for item in self.originatorPrincipal.calendarUserAddresses():
+                    for item in (yield self.originatorPrincipal.calendarUserAddresses()):
                         if not self.originator:
                             self.originator = item
                         if item.startswith("mailto:"):
@@ -377,7 +382,7 @@
 
         # Get owner's calendar-home
         calendar_owner_principal = (yield self.resource.ownerPrincipal(self.request))
-        calendar_home = calendar_owner_principal.calendarHome()
+        calendar_home = yield calendar_owner_principal.calendarHome()
         
         check_parent_uri = parentForURL(check_uri)[:-1] if check_uri else None
 
@@ -389,12 +394,13 @@
 
         @inlineCallbacks
         def queryCalendarCollection(collection, collection_uri):
-            rname = collection.index().resourceNameForUID(self.uid)
+            rname = yield collection.index().resourceNameForUID(self.uid)
             if rname:
                 child = (yield self.request.locateResource(joinURL(collection_uri, rname)))
                 if child == check_resource:
                     returnValue(True)
-                matched_type = "schedule" if self.checkSchedulingObjectResource(child) else "calendar"
+                schedulingObject = yield self.checkSchedulingObjectResource(child)
+                matched_type = "schedule" if schedulingObject else "calendar"
                 if (
                     collection_uri != check_parent_uri and
                     (type == "schedule" or matched_type == "schedule")
@@ -422,7 +428,7 @@
             returnValue(False)
         
         # Organizer must map to a valid principal
-        self.organizerPrincipal = self.resource.principalForCalendarUserAddress(self.organizer)
+        self.organizerPrincipal = (yield self.resource.principalForCalendarUserAddress(self.organizer))
         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
         if not self.organizerPrincipal:
             returnValue(False)
@@ -433,21 +439,22 @@
 
         returnValue(True)
 
+    @inlineCallbacks
     def isAttendeeScheduling(self):
         
         # First must have organizer property
         if not self.organizer:
-            return False
+            returnValue(False)
         
         # Check to see whether any attendee is the owner
         for attendee in self.attendees:
-            attendeePrincipal = self.resource.principalForCalendarUserAddress(attendee)
+            attendeePrincipal = (yield self.resource.principalForCalendarUserAddress(attendee))
             if attendeePrincipal and attendeePrincipal.principalURL() == str(self.calendar_owner):
                 self.attendee = attendee
                 self.attendeePrincipal = attendeePrincipal
-                return True
+                returnValue(True)
         
-        return False
+        returnValue(False)
 
     @inlineCallbacks
     def doAccessControl(self, principal, is_organizer):
@@ -462,7 +469,7 @@
         """
         
         # Find outbox
-        outboxURL = principal.scheduleOutboxURL()
+        outboxURL = yield principal.scheduleOutboxURL()
         outbox = (yield self.request.locateResource(outboxURL))
         yield outbox.authorize(self.request, (caldavxml.ScheduleSend(),))
 
@@ -491,7 +498,7 @@
         elif self.action == "modify":
 
             # Read in existing data
-            self.oldcalendar = self.resource.iCalendar()
+            self.oldcalendar = yield self.resource.iCalendar()
             
             # Significant change
             no_change, self.changed_rids, reinvites, recurrence_reschedule = self.isOrganizerChangeInsignificant()
@@ -671,7 +678,7 @@
         for attendee, rids in aggregated.iteritems():
             
             # Don't send message back to the ORGANIZER
-            if attendee in self.organizerPrincipal.calendarUserAddresses():
+            if attendee in (yield self.organizerPrincipal.calendarUserAddresses()):
                 continue
 
             # Generate an iTIP CANCEL message for this attendee, cancelling
@@ -709,7 +716,7 @@
         for attendee in self.attendees:
 
             # Don't send message back to the ORGANIZER
-            if attendee in self.organizerPrincipal.calendarUserAddresses():
+            if attendee in (yield self.organizerPrincipal.calendarUserAddresses()):
                 continue
 
             # Don't send message to specified attendees
@@ -771,7 +778,7 @@
         else:
             # Make sure ORGANIZER is not changed
             if self.resource.exists():
-                self.oldcalendar = self.resource.iCalendar()
+                self.oldcalendar = yield self.resource.iCalendar()
                 oldOrganizer = self.oldcalendar.getOrganizer()
                 newOrganizer = self.calendar.getOrganizer()
                 if oldOrganizer != newOrganizer:
@@ -849,7 +856,7 @@
         self.organizer_calendar = None
         calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForPrincipals(self.request, self.organizerPrincipal, self.uid))
         if calendar_resource:
-            self.organizer_calendar = calendar_resource.iCalendar()
+            self.organizer_calendar = yield calendar_resource.iCalendar()
         
     def isAttendeeChangeInsignificant(self):
         """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/processing.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/processing.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -132,7 +132,7 @@
         self.recipient_calendar_name = None
         calendar_resource, resource_name, calendar_collection, calendar_collection_uri = (yield getCalendarObjectForPrincipals(self.request, self.recipient.principal, self.uid))
         if calendar_resource:
-            self.recipient_calendar = calendar_resource.iCalendar()
+            self.recipient_calendar = yield calendar_resource.iCalendar()
             self.recipient_calendar_collection = calendar_collection
             self.recipient_calendar_collection_uri = calendar_collection_uri
             self.recipient_calendar_name = resource_name
@@ -508,7 +508,7 @@
         all_declined = not any(instance_states.itervalues())
 
         # Do the simple case of all accepted or decline separately
-        cuas = self.recipient.principal.calendarUserAddresses()
+        cuas = (yield self.recipient.principal.calendarUserAddresses())
         if all_accepted or all_declined:
             # Extract the ATTENDEE property matching current recipient from the calendar data
             attendeeProps = calendar.getAttendeeProperties(cuas)
@@ -632,7 +632,7 @@
         """
         
         from twistedcaldav.method.delete_common import DeleteResource
-        delchild = collection.getChild(name)
+        delchild = yield collection.getChild(name)
         childURL = joinURL(collURL, name)
         self.request._rememberResource(delchild, childURL)
 
@@ -687,7 +687,7 @@
         calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForPrincipals(self.request, self.originator.principal, self.uid))
         if not calendar_resource:
             raise ImplicitProcessorException("5.1;Service unavailable")
-        originator_calendar = calendar_resource.iCalendar()
+        originator_calendar = yield calendar_resource.iCalendar()
 
         # Get attendee's view of that
         originator_calendar.attendeesView((self.recipient.cuaddr,))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/scheduler.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/scheduler.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -15,9 +15,8 @@
 ##
 
 from twext.web2.dav.davxml import ErrorResponse
+from twisted.internet.defer import inlineCallbacks, returnValue, fail, succeed
 
-from twisted.internet.defer import inlineCallbacks, returnValue
-
 from twisted.python.failure import Failure
 
 from twisted.web2 import responsecode
@@ -167,7 +166,7 @@
                 originatorPrincipal = (yield self.request.locateResource(originatorPrincipalURL))
                 if originatorPrincipal:
                     # Pick the first mailto cu address or the first other type
-                    for item in originatorPrincipal.calendarUserAddresses():
+                    for item in (yield originatorPrincipal.calendarUserAddresses()):
                         if not originator:
                             originator = item
                         if item.startswith("mailto:"):
@@ -446,20 +445,21 @@
             log.err("Unauthenticated originators not allowed: %s" % (self.originator,))
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-allowed")))
 
+    @inlineCallbacks
     def checkOriginator(self):
         """
         Check the validity of the Originator header. Extract the corresponding principal.
         """
     
         # Verify that Originator is a valid calendar user
-        originatorPrincipal = self.resource.principalForCalendarUserAddress(self.originator)
+        originatorPrincipal = (yield self.resource.principalForCalendarUserAddress(self.originator))
         if originatorPrincipal is None:
             # Local requests MUST have a principal.
             log.err("Could not find principal for originator: %s" % (self.originator,))
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-allowed")))
         else:
             # Must have a valid Inbox.
-            inboxURL = originatorPrincipal.scheduleInboxURL()
+            inboxURL = (yield originatorPrincipal.scheduleInboxURL())
             if inboxURL is None:
                 log.err("Could not find inbox for originator: %s" % (self.originator,))
                 raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "originator-allowed")))
@@ -484,7 +484,7 @@
         results = []
         for recipient in self.recipients:
             # Get the principal resource for this recipient
-            principal = self.resource.principalForCalendarUserAddress(recipient)
+            principal = (yield self.resource.principalForCalendarUserAddress(recipient))
             
             # If no principal we may have a remote recipient but we should check whether
             # the address is one that ought to be on our server and treat that as a missing
@@ -497,7 +497,7 @@
             else:
                 # Map recipient to their inbox
                 inbox = None
-                inboxURL = principal.scheduleInboxURL()
+                inboxURL = (yield principal.scheduleInboxURL())
                 if inboxURL:
                     inbox = (yield self.request.locateResource(inboxURL))
 
@@ -518,7 +518,7 @@
         # Verify that the ORGANIZER's cu address maps to a valid user
         organizer = self.calendar.getOrganizer()
         if organizer:
-            organizerPrincipal = self.resource.principalForCalendarUserAddress(organizer)
+            organizerPrincipal = (yield self.resource.principalForCalendarUserAddress(organizer))
             if organizerPrincipal:
                 outboxURL = organizerPrincipal.scheduleOutboxURL()
                 if outboxURL:
@@ -542,17 +542,22 @@
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
 
     def checkOrganizerAsOriginator(self):
-
+        def _checkoutboxurl(outboxuri):
+            if outboxuri != self.request.uri:
+                log.err("Wrong outbox for ORGANIZER in calendar data: %s" % (self.calendar,))
+                raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
+            return outboxuri
+                
         # Make sure that the ORGANIZER is local
         if not isinstance(self.organizer, LocalCalendarUser):
             log.err("ORGANIZER is not local to server in calendar data: %s" % (self.calendar,))
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
 
         # Make sure that the ORGANIZER's Outbox is the request URI
-        if self.doingPOST and self.organizer.principal.scheduleOutboxURL() != self.request.uri:
-            log.err("Wrong outbox for ORGANIZER in calendar data: %s" % (self.calendar,))
-            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
+        if self.doingPOST:
+            return self.organizer.principal.scheduleOutboxURL().addCallback(_checkoutboxurl) 
 
+    @inlineCallbacks
     def checkAttendeeAsOriginator(self):
         """
         Check the validity of the ATTENDEE value as this is the originator of the iTIP message.
@@ -569,15 +574,18 @@
         attendee = attendees[0]
     
         # Attendee's Outbox MUST be the request URI
-        attendeePrincipal = self.resource.principalForCalendarUserAddress(attendee)
+        attendeePrincipal = (yield self.resource.principalForCalendarUserAddress(attendee))
         if attendeePrincipal:
-            if self.doingPOST and attendeePrincipal.scheduleOutboxURL() != self.request.uri:
+            outboxURL = (yield attendeePrincipal.scheduleOutboxURL())
+            if self.doingPOST and outboxURL != self.request.uri:
                 log.err("ATTENDEE in calendar data does not match owner of Outbox: %s" % (self.calendar,))
                 raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed")))
+
         else:
             log.err("Unknown ATTENDEE in calendar data: %s" % (self.calendar,))
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed")))
     
+
     def securityChecks(self):
         """
         Check that the originator has the appropriate rights to send this type of iTIP message.
@@ -585,15 +593,18 @@
     
         # Prevent spoofing of ORGANIZER with specific METHODs when local
         if self.calendar.propertyValue("METHOD") in ("PUBLISH", "REQUEST", "ADD", "CANCEL", "DECLINECOUNTER"):
-            self.checkOrganizerAsOriginator()
+            return self.checkOrganizerAsOriginator()
     
         # Prevent spoofing when doing reply-like METHODs
         elif self.calendar.propertyValue("METHOD") in ("REPLY", "COUNTER", "REFRESH"):
-            self.checkAttendeeAsOriginator()
+            return self.checkAttendeeAsOriginator()
             
         else:
             log.err("Unknown iTIP METHOD for security checks: %s" % (self.calendar.propertyValue("METHOD"),))
-            raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"), description="Unknown iTIP METHOD for security checks"))
+            return fail(HTTPError(ErrorResponse(
+                       responsecode.FORBIDDEN,
+                       (caldav_namespace, "valid-calendar-data"),
+                       description="Unknown iTIP METHOD for security checks")))
 
     def finalChecks(self):
         """
@@ -633,7 +644,7 @@
         """
     
         # For remote requests we do not allow the originator to be a local user or one within our domain.
-        originatorPrincipal = self.resource.principalForCalendarUserAddress(self.originator)
+        originatorPrincipal = (yield self.resource.principalForCalendarUserAddress(self.originator))
         localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.originator))
         if originatorPrincipal or localUser:
             log.err("Cannot use originator that is on this server: %s" % (self.originator,))
@@ -694,7 +705,7 @@
         results = []
         for recipient in self.recipients:
             # Get the principal resource for this recipient
-            principal = self.resource.principalForCalendarUserAddress(recipient)
+            principal = (yield self.resource.principalForCalendarUserAddress(recipient))
             
             # If no principal we may have a remote recipient but we should check whether
             # the address is one that ought to be on our server and treat that as a missing
@@ -709,7 +720,7 @@
             else:
                 # Map recipient to their inbox
                 inbox = None
-                inboxURL = principal.scheduleInboxURL()
+                inboxURL = (yield principal.scheduleInboxURL())
                 if inboxURL:
                     inbox = (yield self.request.locateResource(inboxURL))
 
@@ -736,7 +747,7 @@
         # Verify that the ORGANIZER's cu address does not map to a valid user
         organizer = self.calendar.getOrganizer()
         if organizer:
-            organizerPrincipal = self.resource.principalForCalendarUserAddress(organizer)
+            organizerPrincipal = (yield self.resource.principalForCalendarUserAddress(organizer))
             if organizerPrincipal:
                 log.err("Invalid ORGANIZER in calendar data: %s" % (self.calendar,))
                 raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "organizer-allowed")))
@@ -768,7 +779,7 @@
         attendee = attendees[0]
     
         # Attendee cannot be local.
-        attendeePrincipal = self.resource.principalForCalendarUserAddress(attendee)
+        attendeePrincipal = (yield self.resource.principalForCalendarUserAddress(attendee))
         if attendeePrincipal:
             log.err("Invalid ATTENDEE in calendar data: %s" % (self.calendar,))
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "attendee-allowed")))
@@ -863,7 +874,7 @@
         """
     
         # For remote requests we do not allow the originator to be a local user or one within our domain.
-        originatorPrincipal = self.resource.principalForCalendarUserAddress(self.originator)
+        originatorPrincipal = (yield self.resource.principalForCalendarUserAddress(self.originator))
         localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.originator))
         if originatorPrincipal or localUser:
             log.err("Cannot use originator that is on this server: %s" % (self.originator,))
@@ -881,7 +892,7 @@
         results = []
         for recipient in self.recipients:
             # Get the principal resource for this recipient
-            principal = self.resource.principalForCalendarUserAddress(recipient)
+            principal = (yield self.resource.principalForCalendarUserAddress(recipient))
             
             # If no principal we may have a remote recipient but we should check whether
             # the address is one that ought to be on our server and treat that as a missing
@@ -896,7 +907,7 @@
             else:
                 # Map recipient to their inbox
                 inbox = None
-                inboxURL = principal.scheduleInboxURL()
+                inboxURL = (yield principal.scheduleInboxURL())
                 if inboxURL:
                     inbox = (yield self.request.locateResource(inboxURL))
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/utils.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/utils.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/scheduling/utils.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -30,27 +30,28 @@
     result["calendar_collection_uri"] = None
     if principal:
         # Get principal's calendar-home
-        calendar_home = principal.calendarHome()
+        calendar_home = yield principal.calendarHome()
         
         # FIXME: because of the URL->resource request mapping thing, we have to force the request
         # to recognize this resource
         request._rememberResource(calendar_home, calendar_home.url())
 
         # Run a UID query against the UID
-
-        def queryCalendarCollection(collection, uri):
-            rname = collection.index().resourceNameForUID(uid)
+        def queryCalendarCollection(rname, collection, uri):
             if rname:
-                result["resource"] = collection.getChild(rname)
+                def _setResource(rsrc):
+                    result["resource"] = rsrc
+                d = collection.getChild(rname).addCallback(_setResource)
                 result["resource_name"] = rname
                 result["calendar_collection"] = collection
                 result["calendar_collection_uri"] = uri
-                return succeed(False)
+                return d
             else:
                 return succeed(True)
-        
+        def getResourceName(collection, uri):
+            return collection.index().resourceNameForUID(uid).addCallback(queryCalendarCollection, collection, uri)
         # NB We are by-passing privilege checking here. That should be OK as the data found is not
         # exposed to the user.
-        yield report_common.applyToCalendarCollections(calendar_home, request, calendar_home.url(), "infinity", queryCalendarCollection, None)
+        yield report_common.applyToCalendarCollections(calendar_home, request, calendar_home.url(), "infinity", getResourceName, None)
 
     returnValue((result["resource"], result["resource_name"], result["calendar_collection"], result["calendar_collection_uri"],))

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/static.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/static.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -34,14 +34,15 @@
     "TimezoneServiceFile",
 ]
 
+import os
 import datetime
-import os
 import errno
 from urlparse import urlsplit
 
 from twext.web2.dav.davxml import ErrorResponse
+from twisted.internet.defer import (succeed, inlineCallbacks, returnValue,
+                                    maybeDeferred)
 
-from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue, maybeDeferred
 from twisted.python.failure import Failure
 from twisted.python.filepath import FilePath
 from twisted.web2 import responsecode, http, http_headers
@@ -87,8 +88,18 @@
     """
     CalDAV-accessible L{DAVFile} resource.
     """
+    propertyStore = CachingPropertyStore
+
+    @classmethod
+    def fetch(cls, request, path, *args, **kwargs):
+        """
+        stuff etc
+        """
+        return succeed(cls(path, *args, **kwargs))
+
     def __repr__(self):
-        if self.isCalendarCollection():
+        # MOR: I don't think we can defer __repr__, can we? Problem is, isCalendarCollection( ) now is deferred
+        if False and self.isCalendarCollection():
             return "<%s (calendar collection): %s>" % (self.__class__.__name__, self.fp.path)
         else:
             return super(CalDAVFile, self).__repr__()
@@ -98,6 +109,7 @@
             return False
         return self.fp.path == other.fp.path
 
+    @inlineCallbacks
     def checkPreconditions(self, request):
         """
         We override the base class to handle the special implicit scheduling weak ETag behavior
@@ -106,8 +118,8 @@
         
         if config.Scheduling.CalDAV.ScheduleTagCompatibility:
             
-            if self.exists() and self.hasDeadProperty(TwistedScheduleMatchETags):
-                etags = self.readDeadProperty(TwistedScheduleMatchETags).children
+            if self.exists() and (yield self.hasDeadProperty(TwistedScheduleMatchETags)):
+                etags = (yield self.readDeadProperty(TwistedScheduleMatchETags)).children
                 if len(etags) > 1:
                     # This is almost verbatim from twisted.web2.static.checkPreconditions
                     if request.method not in ("GET", "HEAD"):
@@ -135,13 +147,12 @@
                     # Check per-method preconditions
                     method = getattr(self, "preconditions_" + request.method, None)
                     if method:
-                        response = maybeDeferred(method, request)
-                        response.addCallback(lambda _: request)
-                        return response
+                        response = yield(method(request))
+                        returnValue(response)
                     else:
-                        return None
+                        returnValue(None)
 
-        return super(CalDAVFile, self).checkPreconditions(request)
+        returnValue((yield super(CalDAVFile, self).checkPreconditions(request)))
 
     def deadProperties(self, caching=True):
         if not hasattr(self, "_dead_properties"):
@@ -160,11 +171,12 @@
     # CalDAV
     ##
 
+    @inlineCallbacks
     def resourceType(self):
-        if self.isCalendarCollection():
-            return davxml.ResourceType.calendar
+        if (yield self.isCalendarCollection()):
+            returnValue(davxml.ResourceType.calendar)
         else:
-            return super(CalDAVFile, self).resourceType()
+            returnValue((yield super(CalDAVFile, self).resourceType()))
 
     def createCalendar(self, request):
         #
@@ -199,29 +211,25 @@
         parent.addCallback(_defer)
         return parent
 
+    @inlineCallbacks
     def createCalendarCollection(self):
         #
         # Create the collection once we know it is safe to do so
         #
-        def onCalendarCollection(status):
-            if status != responsecode.CREATED:
-                raise HTTPError(status)
+        status = (yield self.createSpecialCollection(davxml.ResourceType.calendar))
+        if status != responsecode.CREATED:
+            raise HTTPError(status)
 
-            # Initialize CTag on the calendar collection
-            d1 = self.updateCTag()
+        # Initialize CTag on the calendar collection
+        yield self.updateCTag()
 
-            # Calendar is initially transparent to freebusy
-            self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Transparent()))
+        # Calendar is initially transparent to freebusy
+        yield self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Transparent()))
 
-            # Create the index so its ready when the first PUTs come in
-            d1.addCallback(lambda _: self.index().create())
-            d1.addCallback(lambda _: status)
-            return d1
+        # Create the index so its ready when the first PUTs come in
+        self.index().create()
+        returnValue(status)
 
-        d = self.createSpecialCollection(davxml.ResourceType.calendar)
-        d.addCallback(onCalendarCollection)
-        return d
-
     def createSpecialCollection(self, resourceType=None):
         #
         # Create the collection once we know it is safe to do so
@@ -230,8 +238,9 @@
             if status != responsecode.CREATED:
                 raise HTTPError(status)
 
-            self.writeDeadProperty(resourceType)
-            return status
+            d = self.writeDeadProperty(resourceType)
+            d.addCallback(lambda _: status)
+            return d
 
         def onError(f):
             try:
@@ -248,7 +257,7 @@
 
     @inlineCallbacks
     def iCalendarRolledup(self, request):
-        if self.isPseudoCalendarCollection():
+        if (yield self.isPseudoCalendarCollection()):
             # Generate a monolithic calendar
             calendar = iComponent("VCALENDAR")
             calendar.addProperty(iProperty("VERSION", "2.0"))
@@ -259,8 +268,8 @@
 
             tzids = set()
             isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
-
-            for name, uid, type in self.index().bruteForceSearch(): #@UnusedVariable
+            searchResults = yield self.index().bruteForceSearch()
+            for name, uid, type in searchResults: #@UnusedVariable
                 try:
                     child = yield request.locateChildResource(self, name)
                     child = IDAVResource(child)
@@ -275,7 +284,7 @@
                         continue
 
                     # Get the access filtered view of the data
-                    caldata = child.iCalendarTextFiltered(isowner)
+                    caldata = yield child.iCalendarTextFiltered(isowner)
                     try:
                         subcalendar = iComponent.fromString(caldata)
                     except ValueError:
@@ -297,9 +306,10 @@
 
         raise HTTPError(ErrorResponse(responsecode.BAD_REQUEST))
 
+    @inlineCallbacks
     def iCalendarTextFiltered(self, isowner):
         try:
-            access = self.readDeadProperty(TwistedCalendarAccessProperty)
+            access = (yield self.readDeadProperty(TwistedCalendarAccessProperty))
         except HTTPError:
             access = None
 
@@ -308,23 +318,24 @@
             if not isowner:
                 # Now "filter" the resource calendar data through the CALDAV:calendar-data element and apply
                 # access restrictions to the data.
-                return caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access).calendarData()
+                el = (yield caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access))
+                returnValue(el.calendarData())
+        returnValue((yield self.iCalendarText()))
 
-        return self.iCalendarText()
-
+    @inlineCallbacks
     def iCalendarText(self, name=None):
-        if self.isPseudoCalendarCollection():
+        if (yield self.isPseudoCalendarCollection()):
             if name is None:
-                return str(self.iCalendar())
+                returnValue(self.iCalendar().addCallback(str))
 
             try:
                 calendar_file = self.fp.child(name).open()
             except IOError, e:
-                if e[0] == errno.ENOENT: return None
+                if e[0] == errno.ENOENT: returnValue(None)
                 raise
 
         elif self.isCollection():
-            return None
+            returnValue(None)
 
         else:
             if name is not None:
@@ -338,28 +349,23 @@
         finally:
             calendar_file.close()
 
-        return calendar_data
+        returnValue(calendar_data)
 
     def iCalendarXML(self, name=None):
-        return caldavxml.CalendarData.fromCalendar(self.iCalendarText(name))
+        return self.iCalendarText(name).addCallback(caldavxml.CalendarData.fromCalendar)
 
+    @inlineCallbacks
     def supportedPrivileges(self, request):
         # read-free-busy support on calendar collection and calendar object resources
-        if self.isCollection():
-            return succeed(calendarPrivilegeSet)
+        if (yield self.isCollection()):
+            returnValue(calendarPrivilegeSet)
         else:
-            def gotParent(parent):
-                if parent and isCalendarCollectionResource(parent):
-                    return succeed(calendarPrivilegeSet)
-                else:
-                    return super(CalDAVFile, self).supportedPrivileges(request)
+            parent = (yield self.locateParent(request, request.urlForResource(self)))
+            if parent and (yield isCalendarCollectionResource(parent)):
+                returnValue(calendarPrivilegeSet)
+            else:
+                returnValue((yield super(CalDAVFile, self).supportedPrivileges(request)))
 
-            d = self.locateParent(request, request.urlForResource(self))
-            d.addCallback(gotParent)
-            return d
-
-        return super(CalDAVFile, self).supportedPrivileges(request)
-
     ##
     # Public additions
     ##
@@ -378,23 +384,23 @@
     ##
 
     def listChildren(self):
-        return [
-            child for child in super(CalDAVFile, self).listChildren()
-            if not child.startswith(".")
-        ]
+        return super(CalDAVFile, self).listChildren().addCallback(
+            lambda children: [child for child in children
+                              if not child.startswith(".")])
 
     def propertyCollection(self):
         if not hasattr(self, "_propertyCollection"):
             self._propertyCollection = MemcachePropertyCollection(self)
         return self._propertyCollection
 
+    @inlineCallbacks
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            returnValue(self)
 
-        similar = super(CalDAVFile, self).createSimilarFile(path)
+        similar = yield maybeDeferred(super(CalDAVFile, self).createSimilarFile, path)
 
-        if isCalendarCollectionResource(self):
+        if (yield isCalendarCollectionResource(self)):
 
             # Short-circuit stat with information we know to be true at this point
             if isinstance(path, FilePath) and hasattr(self, "knownChildren"):
@@ -437,15 +443,17 @@
 
                 setattr(similar, method, override)
 
-        return similar
+        returnValue(similar)
 
+    @inlineCallbacks
     def updateCTag(self):
-        assert self.isCollection()
+        assert (yield self.isCollection())
         try:
-            self.writeDeadProperty(customxml.GETCTag(
+            yield self.writeDeadProperty(customxml.GETCTag(
                     str(datetime.datetime.now())))
         except:
-            return fail(Failure())
+            # MOR: was: return fail(Failure())
+            raise Failure()
 
         if hasattr(self, 'clientNotifier'):
             self.clientNotifier.notify(op="update")
@@ -454,12 +462,12 @@
                       % (self,))
 
         if hasattr(self, 'cacheNotifier'):
-            return self.cacheNotifier.changed()
+            returnValue((yield self.cacheNotifier.changed()))
         else:
             log.debug("%r does not have a cacheNotifier but the CTag changed"
                       % (self,))
 
-        return succeed(True)
+        returnValue(True)
 
     ##
     # Quota
@@ -557,40 +565,41 @@
 
             parent = yield request.locateResource(parent_uri)
 
-            if test(parent):
+            if (yield test(parent)):
                 returnValue(parent)
 
 class AutoProvisioningFileMixIn (AutoProvisioningResourceMixIn):
+
+    @inlineCallbacks
     def provision(self):
-        self.provisionFile()
-        return super(AutoProvisioningFileMixIn, self).provision()
+        yield self.provisionFile()
+        returnValue((yield super(AutoProvisioningFileMixIn, self).provision()))
 
-
-    def provisionFile(self):
+    @inlineCallbacks
+    def provisionFile(self, request=None):
         if hasattr(self, "_provisioned_file"):
-            return False
+            returnValue(False)
         else:
             self._provisioned_file = True
 
         # If the file already exists we can just exit here - there is no need to go further
         if self.fp.exists():
-            return False
+            returnValue(False)
 
         # At this point the original FilePath did not indicate an existing file, but we should
         # recheck it to see if some other request sneaked in and already created/provisioned it
 
         fp = self.fp
-
         fp.restat(False)
         if fp.exists():
-            return False
+            returnValue(False)
 
         log.msg("Provisioning file: %s" % (self,))
 
         if hasattr(self, "parent"):
             parent = self.parent
             if not parent.exists() and isinstance(parent, AutoProvisioningFileMixIn):
-                parent.provision()
+                yield parent.provision()
 
             assert parent.exists(), "Parent %s of %s does not exist" % (parent, self)
             assert parent.isCollection(), "Parent %s of %s is not a collection" % (parent, self)
@@ -608,7 +617,7 @@
             fp.open("w").close()
             fp.changed()
 
-        return True
+        returnValue(True)
 
 class CalendarHomeProvisioningFile (AutoProvisioningFileMixIn, DirectoryCalendarHomeProvisioningResource, DAVFile):
     """
@@ -654,24 +663,24 @@
         else:
             self.homeResourceClass = homeResourceClass
 
+    @inlineCallbacks
     def provisionChild(self, name):
-        record = self.directory.recordWithUID(name)
+        record = (yield self.directory.recordWithUID(name))
 
         if record is None:
             log.msg("No directory record with GUID %r" % (name,))
-            return None
+            returnValue(None)
 
         if not record.enabledForCalendaring:
             log.msg("Directory record %r is not enabled for calendaring" % (record,))
-            return None
+            returnValue(None)
 
         assert len(name) > 4, "Directory record has an invalid GUID: %r" % (name,)
         
         childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
-        child = self.homeResourceClass(childPath.path, self, record)
-
+        child = (yield self.homeResourceClass.fetch(None, childPath.path, self, record))
         if not child.exists():
-            self.provision()
+            yield self.provision()
 
             if not childPath.parent().isdir():
                 childPath.parent().makedirs()
@@ -680,7 +689,7 @@
                 # Pre 2.0: All in one directory
                 self.fp.child(name),
                 # Pre 1.2: In types hierarchy instead of the GUID hierarchy
-                self.parent.getChild(record.recordType).fp.child(record.shortNames[0]),
+                (yield (yield self.parent.getChild(record.recordType)).fp.child(record.shortNames[0])),
             ):
                 if oldPath.exists():
                     # The child exists at an old location.  Move to new location.
@@ -696,27 +705,11 @@
                     child.fp.changed()
                     break
             else:
-                #
-                # NOTE: provisionDefaultCalendars() returns a deferred, which we are ignoring.
-                # The result being that the default calendars will be present at some point
-                # in the future, not necessarily right now, and we don't have a way to wait
-                # on that to finish.
-                #
-                child.provisionDefaultCalendars()
+                yield child.provisionDefaultCalendars()
 
-                #
-                # Try to work around the above a little by telling the client that something
-                # when wrong temporarily if the child isn't provisioned right away.
-                #
-                if not child.exists():
-                    raise HTTPError(StatusResponse(
-                        responsecode.SERVICE_UNAVAILABLE,
-                        "Provisioning calendar home."
-                    ))
-
             assert child.exists()
 
-        return child
+        returnValue(child)
 
     def createSimilarFile(self, path):
         raise HTTPError(responsecode.NOT_FOUND)
@@ -761,26 +754,31 @@
         }.get(name, None)
 
         if cls is not None:
-            child = cls(self.fp.child(name).path, self)
-            child.cacheNotifier = self.cacheNotifier
-            child.clientNotifier = self.clientNotifier
-            return child
+            d = cls.fetch(None, self.fp.child(name).path, self)
+            def _gotChild(child):
+                child.cacheNotifier = self.cacheNotifier
+                child.clientNotifier = self.clientNotifier
+                return child
+            return d.addCallback(_gotChild)
 
         return self.createSimilarFile(self.fp.child(name).path)
 
+
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            similar = CalDAVFile(path, principalCollections=self.principalCollections())
-            similar.cacheNotifier = self.cacheNotifier
-            similar.clientNotifier = self.clientNotifier
-            return similar
+            d = CalDAVFile.fetch(None, path, principalCollections=self.principalCollections())
+            def _gotChild(similar):
+                similar.cacheNotifier = self.cacheNotifier
+                similar.clientNotifier = self.clientNotifier
+                return similar
+            return d.addCallback(_gotChild)
 
     def getChild(self, name):
         # This avoids finding case variants of put children on case-insensitive filesystems.
         if name not in self.putChildren and name.lower() in (x.lower() for x in self.putChildren):
-            return None
+            return succeed(None)
 
         return super(CalendarHomeFile, self).getChild(name)
 
@@ -847,9 +845,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return CalDAVFile(path, principalCollections=self.principalCollections())
+            return succeed(CalDAVFile(path, principalCollections=self.principalCollections()))
 
     def index(self):
         """
@@ -879,16 +877,17 @@
         ScheduleFile.__init__(self, path, parent)
         ScheduleInboxResource.__init__(self, parent)
 
+    @inlineCallbacks
     def provision(self):
-        if self.provisionFile():
+        if (yield self.provisionFile()):
 
             # Initialize CTag on the calendar collection
-            self.updateCTag()
+            yield self.updateCTag()
 
             # Initialize the index
             self.index().create()
 
-        return super(ScheduleInboxFile, self).provision()
+        returnValue((yield super(ScheduleInboxFile, self).provision()))
 
     def __repr__(self):
         return "<%s (calendar inbox collection): %s>" % (self.__class__.__name__, self.fp.path)
@@ -909,12 +908,13 @@
         ScheduleFile.__init__(self, path, parent)
         ScheduleOutboxResource.__init__(self, parent)
 
+    @inlineCallbacks
     def provision(self):
-        if self.provisionFile():
+        if (yield self.provisionFile()):
             # Initialize CTag on the calendar collection
-            self.updateCTag()
+            yield self.updateCTag()
 
-        return super(ScheduleOutboxFile, self).provision()
+        returnValue((yield super(ScheduleOutboxFile, self).provision()))
 
     def __repr__(self):
         return "<%s (calendar outbox collection): %s>" % (self.__class__.__name__, self.fp.path)
@@ -943,9 +943,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return responsecode.NOT_FOUND
+            return succeed(responsecode.NOT_FOUND)
 
     def http_PUT        (self, request): return responsecode.FORBIDDEN
     def http_COPY       (self, request): return responsecode.FORBIDDEN
@@ -965,10 +965,10 @@
         return self._dead_properties
 
     def etag(self):
-        return None
+        return succeed(None)
 
     def checkPreconditions(self, request):
-        return None
+        return succeed(None)
 
     ##
     # ACL
@@ -995,9 +995,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return responsecode.NOT_FOUND
+            return succeed(responsecode.NOT_FOUND)
 
     def http_PUT        (self, request): return responsecode.FORBIDDEN
     def http_COPY       (self, request): return responsecode.FORBIDDEN
@@ -1026,9 +1026,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return DropBoxCollectionFile(path, self)
+            return succeed(DropBoxCollectionFile(path, self))
 
     def __repr__(self):
         return "<%s (dropbox home collection): %s>" % (self.__class__.__name__, self.fp.path)
@@ -1040,9 +1040,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return DropBoxChildFile(path, self)
+            return succeed(DropBoxChildFile(path, self))
 
     def __repr__(self):
         return "<%s (dropbox collection): %s>" % (self.__class__.__name__, self.fp.path)
@@ -1055,9 +1055,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return responsecode.NOT_FOUND
+            return succeed(responsecode.NOT_FOUND)
 
 class TimezoneServiceFile (TimezoneServiceResource, CalDAVFile):
     def __init__(self, path, parent):
@@ -1068,9 +1068,9 @@
 
     def createSimilarFile(self, path):
         if self.comparePath(path):
-            return self
+            return succeed(self)
         else:
-            return responsecode.NOT_FOUND
+            return succeed(responsecode.NOT_FOUND)
 
     def http_PUT        (self, request): return responsecode.FORBIDDEN
     def http_COPY       (self, request): return responsecode.FORBIDDEN
@@ -1090,10 +1090,10 @@
         return self._dead_properties
 
     def etag(self):
-        return None
+        return succeed(None)
 
     def checkPreconditions(self, request):
-        return None
+        return succeed(None)
 
     def checkPrivileges(self, request, privileges, recurse=False, principal=None, inherited_aces=None):
         return succeed(None)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_collectioncontents.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_collectioncontents.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_collectioncontents.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -192,9 +192,11 @@
         fd.close()
         fd = open(os.path.join(colpath, "bogus"), "w")
         fd.close()
-        children = self.site.resource.listChildren()
-        self.assertTrue("bogus" in children)
-        self.assertFalse("._bogus" in children)
+        d = self.site.resource.listChildren()
+        def _gotChildren(children):
+            self.assertTrue("bogus" in children)
+            self.assertFalse("._bogus" in children)
+        return d.addCallback(_gotChildren)
 
     def test_fail_dot_file_put_in_calendar(self):
         """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_extensions.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_extensions.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_extensions.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -177,8 +177,8 @@
         names of both entities.
         """
         def addUnicodeChild(davFile):
-            davFile.writeProperty(UnicodeProperty(), None)
-            davFile.writeProperty(StrProperty(), None)
+            yield davFile.writeProperty(UnicodeProperty(), None)
+            yield davFile.writeProperty(StrProperty(), None)
         yield self.doDirectoryTest([nonASCIIFilename], addUnicodeChild,
                                    [nonASCIIFilename.encode("utf-8")])
 

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_index.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_index.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_index.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -16,6 +16,7 @@
 
 from twisted.internet import reactor
 from twisted.internet.task import deferLater
+from twisted.internet.defer import succeed, inlineCallbacks
 
 from twistedcaldav.ical import Component
 from twistedcaldav.index import Index, default_future_expansion_duration,\
@@ -40,7 +41,7 @@
 
     def setUp(self):
         super(SQLIndexTests, self).setUp()
-        self.site.resource.isCalendarCollection = lambda: True
+        self.site.resource.isCalendarCollection = lambda: succeed(True)
         self.db = Index(self.site.resource)
 
 
@@ -100,6 +101,7 @@
         return d
 
 
+    @inlineCallbacks
     def test_index(self):
         data = (
             (
@@ -250,7 +252,7 @@
             else:
                 self.assertFalse(self.db.resourceExists(name), msg=description)
 
-        self.db.testAndUpdateIndex(datetime.date(2020, 1, 1))
+        yield self.db.testAndUpdateIndex(datetime.date(2020, 1, 1))
         for description, name, calendar_txt, reCreate, ok in data:
             if ok:
                 self.assertTrue(self.db.resourceExists(name), msg=description)
@@ -426,13 +428,15 @@
                    )
               )
 
-            resources = self.db.indexedSearch(filter, fbtype=True)
-            index_results = set()
-            for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype in resources:
-                self.assertEqual(test_organizer, organizer, msg=description)
-                index_results.add((float, start, end, fbtype,))
+            d = self.db.indexedSearch(filter, fbtype=True)
+            def _gotSearchResults(resources):
+                index_results = set()
+                for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype in resources:
+                    self.assertEqual(test_organizer, organizer, msg=description)
+                    index_results.add((float, start, end, fbtype,))
 
-            self.assertEqual(set(instances), index_results, msg=description)
+                self.assertEqual(set(instances), index_results, msg=description)
+            return d.addCallback(_gotSearchResults)
 
 class SQLIndexUpgradeTests (twistedcaldav.test.util.TestCase):
     """

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_memcacheprops.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_memcacheprops.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_memcacheprops.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -27,6 +27,7 @@
 import os
 
 from twisted.web2.http import HTTPError
+from twisted.internet.defer import succeed, inlineCallbacks, returnValue
 
 from twistedcaldav.memcacheprops import MemcachePropertyCollection
 from twistedcaldav.test.util import InMemoryPropertyStore
@@ -48,7 +49,7 @@
         return self.children.iterkeys()
 
     def getChild(self, childName):
-        return self.children[childName]
+        return succeed(self.children[childName])
 
     def propertyCollection(self):
         if not hasattr(self, "_propertyCollection"):
@@ -98,94 +99,113 @@
 
 class MemcachePropertyCollectionTestCase(TestCase):
     """
-    Test MemcacheProprtyCollection
+    Test MemcachePropertyCollection
     """
 
+    @inlineCallbacks
+    def assertRaisesDeferred(self, exception, f, *args, **kwargs):
+        try:
+            result = (yield f(*args, **kwargs))
+        except exception, inst:
+            returnValue(inst)
+        except:
+            raise self.failureException('%s raised instead of %s:\n %s'
+                                        % (sys.exc_info()[0],
+                                           exception.__name__,
+                                           failure.Failure().getTraceback()))
+        else:
+            raise self.failureException('%s not raised (%r returned)'
+                                        % (exception.__name__, result))
+
+
     def getColl(self):
         return StubCollection("calendars", ["a", "b", "c"])
 
+    @inlineCallbacks
     def test_setget(self):
 
-        child1 = self.getColl().getChild("a")
-        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
+        child1 = (yield self.getColl().getChild("a"))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val1")))
 
-        child2 = self.getColl().getChild("a")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        child2 = (yield self.getColl().getChild("a"))
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val1")
 
-        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val2"))
+        (yield child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val2")))
 
         # force memcache to be consulted (once per collection per request)
-        child1 = self.getColl().getChild("a")
+        child1 = (yield self.getColl().getChild("a"))
 
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop1"))).value,
             "val2")
 
+    @inlineCallbacks
     def test_merge(self):
-        child1 = self.getColl().getChild("a")
-        child2 = self.getColl().getChild("a")
-        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0"))
+        child1 = (yield self.getColl().getChild("a"))
+        child2 = (yield self.getColl().getChild("a"))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0")))
 
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")
 
-        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val3"))
+        (yield child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val3")))
 
         # force memcache to be consulted (once per collection per request)
-        child2 = self.getColl().getChild("a")
+        child2 = (yield self.getColl().getChild("a"))
 
         # verify properties
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val1")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop3"))).value,
             "val3")
 
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop1"))).value,
             "val1")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val3")
 
+    @inlineCallbacks
     def test_delete(self):
-        child1 = self.getColl().getChild("a")
-        child2 = self.getColl().getChild("a")
-        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0"))
+        child1 = (yield self.getColl().getChild("a"))
+        child2 = (yield self.getColl().getChild("a"))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0")))
 
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")
 
-        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
-        child1.deadProperties().delete(("ns1:", "prop1"))
-        self.assertRaises(HTTPError, child1.deadProperties().get, ("ns1:", "prop1"))
+        (yield child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1")))
+        (yield child1.deadProperties().delete(("ns1:", "prop1")))
+        self.assertRaisesDeferred(HTTPError, child1.deadProperties().get, ("ns1:", "prop1"))
 
-        self.assertFalse(child1.deadProperties().contains(("ns1:", "prop1"))) 
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertFalse((yield child1.deadProperties().contains(("ns1:", "prop1")))) 
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")
 
         # force memcache to be consulted (once per collection per request)
-        child2 = self.getColl().getChild("a")
+        child2 = (yield self.getColl().getChild("a"))
 
         # verify properties
-        self.assertFalse(child2.deadProperties().contains(("ns1:", "prop1"))) 
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertFalse((yield child2.deadProperties().contains(("ns1:", "prop1")))) 
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_mkcalendar.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_mkcalendar.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_mkcalendar.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -92,7 +92,7 @@
                 (davxml.DisplayName.qname(), "Lisa's Events"),
                 (caldavxml.CalendarDescription.qname(), "Calendar restricted to events."),
             ):
-                stored = yield resource.readProperty(qname, None)
+                stored = (yield resource.readProperty(qname, None))
                 stored = str(stored)
                 if stored != value:
                     self.fail("MKCALENDAR failed to set property %s: %s != %s"

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_resource.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_resource.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -15,6 +15,7 @@
 ##
 
 from twistedcaldav.resource import CalDAVResource
+from twisted.internet.defer import inlineCallbacks
 
 from twistedcaldav.test.util import InMemoryPropertyStore
 from twistedcaldav.test.util import TestCase
@@ -31,8 +32,9 @@
         self.resource = CalDAVResource()
         self.resource._dead_properties = InMemoryPropertyStore()
 
+    @inlineCallbacks
     def test_writeDeadPropertyWritesProperty(self):
         prop = StubProperty()
-        self.resource.writeDeadProperty(prop)
-        self.assertEquals(self.resource._dead_properties.get("StubQname"),
+        yield self.resource.writeDeadProperty(prop)
+        self.assertEquals((yield self.resource._dead_properties.get("StubQname")),
                           prop)

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_sql.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_sql.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -182,9 +182,11 @@
         fd.close()
         fd = open(os.path.join(colpath, "test"), "w")
         fd.close()
-        children = self.site.resource.listChildren()
-        self.assertTrue("test" in children)
-        self.assertFalse(db_prefix + "sqlite" in children)
+        d = self.site.resource.listChildren()
+        def _gotChildren(children):
+            self.assertTrue("test" in children)
+            self.assertFalse(db_prefix + "sqlite" in children)
+        return d.addCallback(_gotChildren)
 
     def test_duplicate_create(self):
         dbname = self.mktemp()

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_static.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_static.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_static.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -38,8 +38,10 @@
 
 
     def test_childrenHaveCacheNotifier(self):
-        child = self.calendarHome.createSimilarFile('/fake/path')
-        self.assertEquals(child.cacheNotifier, self.calendarHome.cacheNotifier)
+        d = self.calendarHome.createSimilarFile('/fake/path')
+        def _gotResource(child):
+            self.assertEquals(child.cacheNotifier, self.calendarHome.cacheNotifier)
+        return d.addCallback(_gotResource)
 
 
 class CalDAVFileTests(TestCase):

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_upgrade.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/test/test_upgrade.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -14,6 +14,7 @@
 # limitations under the License.
 ##
 
+from twisted.internet.defer import inlineCallbacks, succeed
 
 from twisted.web2.dav import davxml
 
@@ -24,6 +25,7 @@
 from twistedcaldav.mail import MailGatewayTokensDatabase
 from twistedcaldav.upgrade import UpgradeError, upgradeData, updateFreeBusySet
 from twistedcaldav.test.util import TestCase
+
 from calendarserver.tools.util import getDirectory
 
 import hashlib
@@ -35,7 +37,7 @@
 
 
 class ProxyDBUpgradeTests(TestCase):
-    
+
     def setUpXMLDirectory(self):
         xmlFile = os.path.join(os.path.dirname(os.path.dirname(__file__)),
             "directory", "test", "accounts.xml")
@@ -48,12 +50,12 @@
         self.setUpOldDocRoot()
         self.setUpOldDocRootWithoutDB()
         self.setUpNewDocRoot()
-        
+
         self.setUpNewDataRoot()
         self.setUpDataRootWithProxyDB()
 
     def setUpOldDocRoot(self):
-        
+
         # Set up doc root
         self.olddocroot = self.mktemp()
         os.mkdir(self.olddocroot)
@@ -76,7 +78,7 @@
 
 
     def setUpOldDocRootWithoutDB(self):
-        
+
         # Set up doc root
         self.olddocrootnodb = self.mktemp()
         os.mkdir(self.olddocrootnodb)
@@ -92,7 +94,7 @@
         os.mkdir(os.path.join(self.olddocrootnodb, "calendars"))
 
     def setUpNewDocRoot(self):
-        
+
         # Set up doc root
         self.newdocroot = self.mktemp()
         os.mkdir(self.newdocroot)
@@ -100,13 +102,13 @@
         os.mkdir(os.path.join(self.newdocroot, "calendars"))
 
     def setUpNewDataRoot(self):
-        
+
         # Set up data root
         self.newdataroot = self.mktemp()
         os.mkdir(self.newdataroot)
 
     def setUpDataRootWithProxyDB(self):
-        
+
         # Set up data root
         self.existingdataroot = self.mktemp()
         os.mkdir(self.existingdataroot)
@@ -124,7 +126,7 @@
         config.DocumentRoot = self.olddocroot
         config.DataRoot = self.newdataroot
 
-        
+
         # Check pre-conditions
         self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
         self.assertTrue(os.path.isdir(os.path.join(config.DocumentRoot, "principals")))
@@ -132,7 +134,7 @@
         self.assertFalse(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
 
         upgradeData(config)
-        
+
         # Check post-conditions
         self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals",)))
         self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
@@ -147,18 +149,19 @@
 
         config.DocumentRoot = self.newdocroot
         config.DataRoot = self.existingdataroot
-        
+
         # Check pre-conditions
         self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
         self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
 
         upgradeData(config)
-        
+
         # Check post-conditions
         self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals",)))
         self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
 
 
+    @inlineCallbacks
     def test_freeBusyUpgrade(self):
         """
         Test the updating of calendar-free-busy-set xattrs on inboxes
@@ -173,18 +176,18 @@
 
         # Uncompressed XML
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/BB05932F-DCE7-4195-9ED4-0896EAFF3B0B/calendar</href>\r\n</calendar-free-busy-set>\r\n"
-        self.assertEquals(updateFreeBusySet(value, directory), None)
+        self.assertEquals((yield updateFreeBusySet(value, directory)), None)
 
         # Zlib compressed XML
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/BB05932F-DCE7-4195-9ED4-0896EAFF3B0B/calendar</href>\r\n</calendar-free-busy-set>\r\n"
         value = zlib.compress(value)
-        self.assertEquals(updateFreeBusySet(value, directory), None)
+        self.assertEquals((yield updateFreeBusySet(value, directory)), None)
 
         # Pickled XML
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/BB05932F-DCE7-4195-9ED4-0896EAFF3B0B/calendar</href>\r\n</calendar-free-busy-set>\r\n"
         doc = davxml.WebDAVDocument.fromString(value)
         value = cPickle.dumps(doc.root_element)
-        self.assertEquals(updateFreeBusySet(value, directory), None)
+        self.assertEquals((yield updateFreeBusySet(value, directory)), None)
 
 
         #
@@ -195,14 +198,14 @@
 
         # Uncompressed XML
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/wsanchez/calendar</href>\r\n</calendar-free-busy-set>\r\n"
-        newValue = updateFreeBusySet(value, directory)
+        newValue = yield updateFreeBusySet(value, directory)
         newValue = zlib.decompress(newValue)
         self.assertEquals(newValue, expected)
 
         # Zlib compressed XML
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/wsanchez/calendar</href>\r\n</calendar-free-busy-set>\r\n"
         value = zlib.compress(value)
-        newValue = updateFreeBusySet(value, directory)
+        newValue = yield updateFreeBusySet(value, directory)
         newValue = zlib.decompress(newValue)
         self.assertEquals(newValue, expected)
 
@@ -210,7 +213,7 @@
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/wsanchez/calendar</href>\r\n</calendar-free-busy-set>\r\n"
         doc = davxml.WebDAVDocument.fromString(value)
         value = cPickle.dumps(doc.root_element)
-        newValue = updateFreeBusySet(value, directory)
+        newValue = yield updateFreeBusySet(value, directory)
         newValue = zlib.decompress(newValue)
         self.assertEquals(newValue, expected)
 
@@ -221,7 +224,7 @@
 
         expected = "<?xml version='1.0' encoding='UTF-8'?><calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/>"
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/nonexistent/calendar</href>\r\n</calendar-free-busy-set>\r\n"
-        newValue = updateFreeBusySet(value, directory)
+        newValue = yield updateFreeBusySet(value, directory)
         newValue = zlib.decompress(newValue)
         self.assertEquals(newValue, expected)
 
@@ -605,7 +608,7 @@
         self.verifyDirectoryComparison(before, after, reverify=True)
 
 
-    def test_calendarsUpgradeWithUnknownFiles(self):
+    def test_calendarsUpgradeWithDSStore(self):
         """
         Unknown files, including .DS_Store files at any point in the hierarchy,
         as well as non-directory in a user's calendar home, will be ignored and not
@@ -1136,9 +1139,13 @@
 
         config.DocumentRoot = root
         config.DataRoot = root
+        def failTest(result):
+            self.fail("oops")
+        def checkUpgradeError(result):
+            self.assertIsInstance(result.value, UpgradeError)
+            self.assertTrue(self.verifyHierarchy(root, after))
 
-        self.assertRaises(UpgradeError, upgradeData, config)
-        self.assertTrue(self.verifyHierarchy(root, after))
+        return upgradeData(config).addCallbacks(failTest, checkUpgradeError)
 
     def test_migrateResourceInfo(self):
         # Fake getResourceInfo( )
@@ -1154,7 +1161,7 @@
             results = []
             for guid, info in assignments.iteritems():
                 results.append( (guid, info[0], info[1], info[2]) )
-            return results
+            return succeed(results)
 
         self.setUpInitialStates()
         # Override the normal getResourceInfo method with our own:

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/timezoneservice.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/timezoneservice.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/timezoneservice.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -24,6 +24,7 @@
 
 from twext.web2.dav.davxml import ErrorResponse
 
+from twisted.internet.defer import succeed
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.http import HTTPError
@@ -61,28 +62,30 @@
         self.cache = {}
 
     def defaultAccessControlList(self):
-        return davxml.ACL(
-            # DAV:Read for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
+        return succeed(
+            davxml.ACL(
+                # DAV:Read for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                    ),
+                    davxml.Protected(),
                 ),
-                davxml.Protected(),
-            ),
+            )
         )
 
     def resourceType(self):
-        return davxml.ResourceType.timezones
+        return succeed(davxml.ResourceType.timezones)
 
     def isCollection(self):
         return False
 
     def isCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def isPseudoCalendarCollection(self):
-        return False
+        return succeed(False)
 
     def render(self, request):
         output = """<html>

Modified: CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/upgrade.py	2009-12-15 05:36:09 UTC (rev 4860)
+++ CalendarServer/branches/users/glyph/more-deferreds-5/twistedcaldav/upgrade.py	2009-12-15 07:22:08 UTC (rev 4861)
@@ -17,6 +17,7 @@
 
 from __future__ import with_statement
 
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web2.dav.fileop import rmdir
 from twisted.web2.dav import davxml
 from twistedcaldav.directory.directory import DirectoryService
@@ -53,14 +54,12 @@
 
     return uid, gid
 
-#
-# upgrade_to_1
-#
-# Upconverts data from any calendar server version prior to data format 1
-#
 
+ at inlineCallbacks
 def upgrade_to_1(config):
-
+    """
+    Upconverts data from any calendar server version prior to data format 1
+    """
     errorOccurred = False
 
     def fixBadQuotes(data):
@@ -83,30 +82,32 @@
             return data, False
 
 
-
+    @inlineCallbacks
     def normalizeCUAddrs(data, directory):
         cal = Component.fromString(data)
 
+        @inlineCallbacks
         def lookupFunction(cuaddr):
             try:
-                principal = directory.principalForCalendarUserAddress(cuaddr)
+                principal = (yield directory.principalForCalendarUserAddress(cuaddr))
             except Exception, e:
                 log.debug("Lookup of %s failed: %s" % (cuaddr, e))
                 principal = None
 
             if principal is None:
-                return (None, None, None)
+                returnValue((None, None, None))
             else:
-                return (principal.record.fullName.decode("utf-8"),
+                returnValue((principal.record.fullName.decode("utf-8"),
                     principal.record.guid,
-                    principal.record.calendarUserAddresses)
+                    principal.record.calendarUserAddresses))
 
-        cal.normalizeCalendarUserAddresses(lookupFunction)
+        yield cal.normalizeCalendarUserAddresses(lookupFunction)
 
         newData = str(cal)
-        return newData, not newData == data
+        returnValue((newData, not newData == data))
 
 
+    @inlineCallbacks
     def upgradeCalendarCollection(calPath, directory):
 
         errorOccurred = False
@@ -140,7 +141,8 @@
                     continue
 
                 try:
-                    data, fixed = normalizeCUAddrs(data, directory)
+                    # MOR: Defer this:
+                    data, fixed = yield normalizeCUAddrs(data, directory)
                     if fixed:
                         log.debug("Normalized CUAddrs in %s" % (resPath,))
                         needsRewrite = True
@@ -166,9 +168,10 @@
             ctagValue = zlib.compress(ctagValue)
             xattr.setxattr(calPath, "WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag", ctagValue)
 
-        return errorOccurred
+        returnValue(errorOccurred)
 
 
+    @inlineCallbacks
     def upgradeCalendarHome(homePath, directory):
 
         errorOccurred = False
@@ -187,7 +190,7 @@
                     rmdir(calPath)
                     continue
                 log.debug("Upgrading calendar: %s" % (calPath,))
-                if not upgradeCalendarCollection(calPath, directory):
+                if not (yield upgradeCalendarCollection(calPath, directory)):
                     errorOccurred = True
 
                 # Change the calendar-free-busy-set xattrs of the inbox to the
@@ -195,7 +198,7 @@
                 if cal == "inbox":
                     for attr, value in xattr.xattr(calPath).iteritems():
                         if attr == "WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set":
-                            value = updateFreeBusySet(value, directory)
+                            value = yield updateFreeBusySet(value, directory)
                             if value is not None:
                                 # Need to write the xattr back to disk
                                 xattr.setxattr(calPath, attr, value)
@@ -204,7 +207,7 @@
             log.error("Failed to upgrade calendar home %s: %s" % (homePath, e))
             raise
 
-        return errorOccurred
+        returnValue(errorOccurred)
 
 
     def doProxyDatabaseMoveUpgrade(config, uid=-1, gid=-1):
@@ -260,11 +263,12 @@
         os.rename(oldHome, newHome)
 
 
+    @inlineCallbacks
     def migrateResourceInfo(config, directory, uid, gid):
         log.info("Fetching delegate assignments and auto-schedule settings from directory")
         resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
         calendarUserProxyDatabase = CalendarUserProxyDatabase(config.DataRoot)
-        resourceInfo = directory.getResourceInfo()
+        resourceInfo = (yield directory.getResourceInfo())
         for guid, autoSchedule, proxy, readOnlyProxy in resourceInfo:
             resourceInfoDatabase.setAutoScheduleInDatabase(guid, autoSchedule)
             if proxy:
@@ -372,7 +376,7 @@
                 dirPath = os.path.join(calRoot, dirName)
                 if os.path.exists(dirPath):
                     for shortName in os.listdir(dirPath):
-                        record = directory.recordWithShortName(recordType,
+                        record = yield directory.recordWithShortName(recordType,
                             shortName)
                         oldHome = os.path.join(dirPath, shortName)
                         if record is not None:
@@ -429,8 +433,8 @@
                                     homePath = os.path.join(secondPath, home)
 
 
-                                    if not upgradeCalendarHome(homePath,
-                                        directory):
+                                    if not (yield upgradeCalendarHome(
+                                            homePath, directory)):
                                         errorOccurred = True
 
                                     count += 1
@@ -455,6 +459,7 @@
     (1, upgrade_to_1),
 ]
 
+ at inlineCallbacks
 def upgradeData(config):
 
     docRoot = config.DocumentRoot
@@ -478,7 +483,7 @@
     for version, method in upgradeMethods:
         if onDiskVersion < version:
             log.warn("Upgrading to version %d" % (version,))
-            method(config)
+            yield method(config)
             with open(versionFilePath, "w") as verFile:
                 verFile.write(str(version))
             os.chown(versionFilePath, uid, gid)
@@ -494,25 +499,27 @@
 #
 # Utility functions
 #
+ at inlineCallbacks
 def updateFreeBusyHref(href, directory):
     pieces = href.split("/")
     if pieces[2] == "__uids__":
         # Already updated
-        return None
+        returnValue(None)
 
     recordType = pieces[2]
     shortName = pieces[3]
-    record = directory.recordWithShortName(recordType, shortName)
+    record = yield directory.recordWithShortName(recordType, shortName)
     if record is None:
         # We will simply ignore this and not write out an fb-set entry
         log.error("Can't update free-busy href; %s is not in the directory" % shortName)
-        return ""
+        returnValue("")
 
     uid = record.uid
     newHref = "/calendars/__uids__/%s/%s/" % (uid, pieces[4])
-    return newHref
+    returnValue(newHref)
 
 
+ at inlineCallbacks
 def updateFreeBusySet(value, directory):
 
     try:
@@ -530,13 +537,13 @@
         except UnpicklingError:
             log.err("Invalid free/busy property value")
             # MOR: continue on?
-            return None
+            returnValue(None)
 
     fbset = set()
     didUpdate = False
     for href in freeBusySet.children:
         href = str(href)
-        newHref = updateFreeBusyHref(href, directory)
+        newHref = yield updateFreeBusyHref(href, directory)
         if newHref is None:
             fbset.add(href)
         else:
@@ -548,9 +555,9 @@
         property = caldavxml.CalendarFreeBusySet(*[davxml.HRef(href)
             for href in fbset])
         value = compress(property.toxml())
-        return value
+        returnValue(value)
 
-    return None # no update required
+    returnValue(None) # no update required
 
 
 def makeDirsUserGroup(path, uid=-1, gid=-1):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091214/166f5a7d/attachment-0001.html>


More information about the calendarserver-changes mailing list