[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