[CalendarServer-changes] [2462] CalendarServer/branches/unified-cache/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Fri May 23 15:44:40 PDT 2008
Revision: 2462
http://trac.macosforge.org/projects/calendarserver/changeset/2462
Author: dreid at apple.com
Date: 2008-05-23 15:44:39 -0700 (Fri, 23 May 2008)
Log Message:
-----------
Make PROPPATCH, ACL, and DELETE work properly.
Modified Paths:
--------------
CalendarServer/branches/unified-cache/twistedcaldav/cache.py
CalendarServer/branches/unified-cache/twistedcaldav/extensions.py
CalendarServer/branches/unified-cache/twistedcaldav/method/put_common.py
CalendarServer/branches/unified-cache/twistedcaldav/resource.py
CalendarServer/branches/unified-cache/twistedcaldav/test/util.py
Modified: CalendarServer/branches/unified-cache/twistedcaldav/cache.py
===================================================================
--- CalendarServer/branches/unified-cache/twistedcaldav/cache.py 2008-05-23 21:02:17 UTC (rev 2461)
+++ CalendarServer/branches/unified-cache/twistedcaldav/cache.py 2008-05-23 22:44:39 UTC (rev 2462)
@@ -65,7 +65,7 @@
return: A L{Deferred} that fires when the token has been changed.
"""
self.log_debug("Changing Cache Token for %r" % (
- self._propertyStore))
+ self._propertyStore.resource.fp))
property = CacheTokensProperty.fromString(self._newCacheToken())
self._propertyStore.set(property)
return succeed(True)
Modified: CalendarServer/branches/unified-cache/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/unified-cache/twistedcaldav/extensions.py 2008-05-23 21:02:17 UTC (rev 2461)
+++ CalendarServer/branches/unified-cache/twistedcaldav/extensions.py 2008-05-23 22:44:39 UTC (rev 2462)
@@ -34,6 +34,7 @@
import time
from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
+from twisted.internet.defer import maybeDeferred
from twisted.web2 import responsecode
from twisted.web2.http import HTTPError, Response, RedirectResponse
from twisted.web2.http_headers import MimeType
@@ -185,10 +186,42 @@
authorizationPrincipal = deferredGenerator(authorizationPrincipal)
+def updateCacheTokenOnCallback(f):
+ def fun(self, *args, **kwargs):
+ def _updateToken(response):
+ return self.cacheNotifier.changed().addCallback(
+ lambda _: response)
+
+ d = maybeDeferred(f, self, *args, **kwargs)
+
+ if hasattr(self, 'cacheNotifier'):
+ d.addCallback(_updateToken)
+
+ return d
+
+ return fun
+
+
class DAVResource (SudoSACLMixin, SuperDAVResource, LoggingMixIn):
"""
Extended L{twisted.web2.dav.resource.DAVResource} implementation.
"""
+
+ @updateCacheTokenOnCallback
+ def http_PROPPATCH(self, request):
+ return super(DAVResource, self).http_PROPPATCH(request)
+
+
+ @updateCacheTokenOnCallback
+ def http_DELETE(self, request):
+ return super(DAVResource, self).http_DELETE(request)
+
+
+ @updateCacheTokenOnCallback
+ def http_ACL(self, request):
+ return super(DAVResource, self).http_ACL(request)
+
+
def findChildrenFaster(self, depth, request, okcallback, badcallback, names, privileges, inherited_aces):
"""
See L{IDAVResource.findChildren}.
Modified: CalendarServer/branches/unified-cache/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/unified-cache/twistedcaldav/method/put_common.py 2008-05-23 21:02:17 UTC (rev 2461)
+++ CalendarServer/branches/unified-cache/twistedcaldav/method/put_common.py 2008-05-23 22:44:39 UTC (rev 2462)
@@ -22,8 +22,9 @@
from twisted.internet import reactor
from twisted.internet.defer import Deferred
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import deferredGenerator
from twisted.internet.defer import maybeDeferred
+from twisted.internet.defer import waitForDeferred
from twisted.python import failure
from twisted.python.filepath import FilePath
from twisted.web2 import responsecode
@@ -44,7 +45,7 @@
from twistedcaldav.caldavxml import NoUIDConflict
from twistedcaldav.caldavxml import NumberOfRecurrencesWithinLimits
from twistedcaldav.caldavxml import caldav_namespace
-from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.customxml import TwistedCalendarAccessProperty
from twistedcaldav.fileops import copyToWithXAttrs
from twistedcaldav.fileops import putWithXAttrs
@@ -56,9 +57,7 @@
log = Logger()
-import sys
-
- at inlineCallbacks
+ at deferredGenerator
def storeCalendarObjectResource(
request,
sourcecal, destinationcal,
@@ -70,7 +69,7 @@
):
"""
Function that does common PUT/COPY/MOVE behaviour.
-
+
@param request: the L{twisted.web2.server.Request} for the current HTTP request.
@param source: the L{CalDAVFile} for the source resource to copy from, or None if source data
is to be read from the request.
@@ -86,7 +85,7 @@
@param isiTIP: True if relaxed calendar data validation is to be done, False otherwise.
@return: a Deferred with a status response result.
"""
-
+
try:
assert destination is not None and destinationparent is not None and destination_uri is not None
assert (source is None and sourceparent is None) or (source is not None and sourceparent is not None)
@@ -114,7 +113,7 @@
transaction, leaving the server state the same as it was before the request was
processed. The DoRollback method will actually execute the rollback operations.
"""
-
+
def __init__(self):
self.active = True
self.source_copy = None
@@ -123,7 +122,7 @@
self.source_deleted = False
self.source_index_deleted = False
self.destination_index_deleted = False
-
+
def Rollback(self):
"""
Rollback the server state. Do not allow this to raise another exception. If
@@ -183,7 +182,7 @@
self.source_deleted = False
self.source_index_deleted = False
self.destination_index_deleted = False
-
+
rollback = RollbackState()
def validResourceName():
@@ -198,7 +197,7 @@
message = "File name %s not allowed in calendar collection" % (filename,)
return result, message
-
+
def validContentType():
"""
Make sure that the content-type of the source resource is text/calendar.
@@ -212,7 +211,7 @@
message = "MIME type %s not allowed in calendar collection" % (content_type,)
return result, message
-
+
def validCalendarDataCheck():
"""
Check that the calendar data is valid iCalendar.
@@ -230,9 +229,9 @@
except ValueError, e:
result = False
message = "Invalid calendar data: %s" % (e,)
-
+
return result, message
-
+
def validCalDAVDataCheck():
"""
Check that the calendar data is valid as a CalDAV calendar object resource.
@@ -249,9 +248,9 @@
except ValueError, e:
result = False
message = "Calendar data does not conform to CalDAV requirements: %s" % (e,)
-
+
return result, message
-
+
def validSizeCheck():
"""
Make sure that the content-type of the source resource is text/calendar.
@@ -267,21 +266,24 @@
return result, message
- @inlineCallbacks
+ @deferredGenerator
def validAccess():
"""
Make sure that the X-CALENDARSERVER-ACCESS property is properly dealt with.
"""
-
+
if calendar.hasProperty(Component.ACCESS_PROPERTY):
-
+
# Must be a value we know about
access = calendar.accessLevel(default=None)
if access is None:
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (calendarserver_namespace, "valid-access-restriction")))
-
+
# Only DAV:owner is able to set the property to other than PUBLIC
- parent_owner = yield destinationparent.owner(request)
+ d = waitForDeferred(destinationparent.owner(request))
+ yield d
+ parent_owner = d.getResult()
+
authz = destinationparent.currentPrincipal(request)
if davxml.Principal(parent_owner) != authz and access != Component.ACCESS_PUBLIC:
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (calendarserver_namespace, "valid-access-restriction-change")))
@@ -325,7 +327,7 @@
# the other PUT tries to reserve and fails but no index entry exists yet.
if rname is None:
rname = "<<Unknown Resource>>"
-
+
result = False
message = "Calendar resource %s already exists with same UID %s" % (rname, uid)
else:
@@ -336,7 +338,7 @@
rname = destination.fp.basename()
result = False
message = "Cannot overwrite calendar resource %s with different UID %s" % (rname, olduid)
-
+
return result, message, rname
try:
@@ -359,7 +361,7 @@
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
calendar = source.iCalendar()
else:
@@ -368,13 +370,13 @@
except ValueError, e:
log.err(str(e))
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
-
+
# Valid calendar data check
result, message = validCalendarDataCheck()
if not result:
log.err(message)
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data")))
-
+
# Valid calendar data for CalDAV check
result, message = validCalDAVDataCheck()
if not result:
@@ -403,11 +405,13 @@
# Check access
if destinationcal and config.EnablePrivateEvents:
- access, calendardata = yield validAccess()
+ d = waitForDeferred(validAccess())
+ yield d
+ access, calendardata = d.getResult()
# Reserve UID
destination_index = destinationparent.index()
-
+
# Lets use a deferred for this and loop a few times if we cannot reserve so that we give
# time to whoever has the reservation to finish and release it.
failure_count = 0
@@ -419,16 +423,18 @@
except ReservationError:
reserved = False
failure_count += 1
-
+
d = Deferred()
def _timedDeferred():
d.callback(True)
reactor.callLater(0.1, _timedDeferred)
- pause = yield d
-
+ pause = waitForDeferred(d)
+ yield pause
+ pause.getResult()
+
if destination_uri and not reserved:
raise HTTPError(StatusResponse(responsecode.CONFLICT, "Resource: %s currently in use." % (destination_uri,)))
-
+
# 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 isiTIP:
@@ -438,7 +444,7 @@
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN,
NoUIDConflict(davxml.HRef.fromString(joinURL(parentForURL(destination_uri), rname.encode("utf-8"))))
))
-
+
"""
Handle rollback setup here.
"""
@@ -447,18 +453,26 @@
if request is None:
destquota = None
else:
- destquota = yield destination.quota(request)
+ destquota = waitForDeferred(destination.quota(request))
+ yield destquota
+ destquota = destquota.getResult()
if destquota is not None and destination.exists():
- old_dest_size = yield destination.quotaSize(request)
+ old_dest_size = waitForDeferred(destination.quotaSize(request))
+ yield old_dest_size
+ old_dest_size = old_dest_size.getResult()
else:
old_dest_size = 0
-
+
if request is None:
sourcequota = None
elif source is not None:
- sourcequota = yield source.quota(request)
+ sourcequota = waitForDeferred(source.quota(request))
+ yield sourcequota
+ sourcequota = sourcequota.getResult()
if sourcequota is not None and source.exists():
- old_source_size = yield source.quotaSize(request)
+ old_source_size = waitForDeferred(source.quotaSize(request))
+ yield old_source_size
+ old_source_size = old_source_size.getResult()
else:
old_source_size = 0
else:
@@ -482,16 +496,16 @@
rollback.source_copy.path += ".rollback"
copyToWithXAttrs(source.fp, rollback.source_copy)
log.debug("Rollback: backing up source %s to %s" % (source.fp.path, rollback.source_copy.path))
-
+
"""
Handle actual store operations here.
-
+
The order in which this is done is import:
-
+
1. Do store operation for new data
2. Delete source and source index if needed
3. Do new indexing if needed
-
+
Note that we need to remove the source index BEFORE doing the destination index to cover the
case of a resource being 'renamed', i.e. moved within the same collection. Since the index UID
column must be unique in SQL, we cannot add the new index before remove the old one.
@@ -499,13 +513,13 @@
# Do put or copy based on whether source exists
if source is not None:
- response = yield maybeDeferred(copyWithXAttrs,
- source.fp,
- destination.fp,
- destination_uri)
+ response = maybeDeferred(copyWithXAttrs, source.fp, destination.fp, destination_uri)
else:
md5 = MD5StreamWrapper(MemoryStream(calendardata))
- response = yield maybeDeferred(putWithXAttrs, md5, destination.fp)
+ response = maybeDeferred(putWithXAttrs, md5, destination.fp)
+ response = waitForDeferred(response)
+ yield response
+ response = response.getResult()
# Update the MD5 value on the resource
if source is not None:
@@ -522,26 +536,26 @@
# Update calendar-access property value on the resource
if access:
destination.writeDeadProperty(TwistedCalendarAccessProperty(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 destinationcal:
- destination.removeDeadProperty(TwistedCalendarAccessProperty)
+ destination.removeDeadProperty(TwistedCalendarAccessProperty)
response = IResponse(response)
def doDestinationIndex(caltoindex):
"""
Do destination resource indexing, replacing any index previous stored.
-
+
@return: None if successful, ErrorResponse on failure
"""
-
+
# Delete index for original item
if overwrite:
doRemoveDestinationIndex()
-
+
# Add or update the index for this resource.
try:
destination_index.addResource(destination.fp.basename(), caltoindex)
@@ -563,7 +577,7 @@
"""
Remove any existing destination index.
"""
-
+
# Delete index for original item
if destinationcal:
destination_index.deleteResource(destination.fp.basename())
@@ -586,10 +600,10 @@
"""
Do source resource indexing. This only gets called when restoring
the source after its index has been deleted.
-
+
@return: None if successful, ErrorResponse on failure
"""
-
+
# Add or update the index for this resource.
try:
source_index.addResource(source.fp.basename(), calendar)
@@ -607,32 +621,43 @@
# Update quota
if sourcequota is not None:
delete_size = 0 - old_source_size
- _ignore = yield source.quotaSizeAdjust(request, delete_size)
+ d = waitForDeferred(source.quotaSizeAdjust(request, delete_size))
+ yield d
+ d.getResult()
if sourcecal:
# Change CTag on the parent calendar collection
- _ignore = yield sourceparent.updateCTag()
+ d = waitForDeferred(sourceparent.updateCTag())
+ yield d
+ d.getResult()
if destinationcal:
result = doDestinationIndex(calendar)
if result is not None:
rollback.Rollback()
- returnValue(result)
+ yield result
+ return
# Do quota check on destination
if destquota is not None:
# Get size of new/old resources
- new_dest_size = yield destination.quotaSize(request)
+ new_dest_size = waitForDeferred(destination.quotaSize(request))
+ yield new_dest_size
+ new_dest_size = new_dest_size.getResult()
diff_size = new_dest_size - old_dest_size
if diff_size >= destquota[0]:
log.err("Over quota: available %d, need %d" % (destquota[0], diff_size))
raise HTTPError(ErrorResponse(responsecode.INSUFFICIENT_STORAGE_SPACE, (dav_namespace, "quota-not-exceeded")))
- yield destination.quotaSizeAdjust(request, diff_size)
+ d = waitForDeferred(destination.quotaSizeAdjust(request, diff_size))
+ yield d
+ d.getResult()
if destinationcal:
# Change CTag on the parent calendar collection
- yield destinationparent.updateCTag()
+ d = waitForDeferred(destinationparent.updateCTag())
+ yield d
+ d.getResult()
# Can now commit changes and forget the rollback details
rollback.Commit()
@@ -641,7 +666,8 @@
destination_index.unreserveUID(uid)
reserved = False
- returnValue(response)
+ yield response
+ return
except:
if reserved:
@@ -652,4 +678,3 @@
# if the rollback has already ocurred or changes already committed.
rollback.Rollback()
raise
-
Modified: CalendarServer/branches/unified-cache/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/unified-cache/twistedcaldav/resource.py 2008-05-23 21:02:17 UTC (rev 2461)
+++ CalendarServer/branches/unified-cache/twistedcaldav/resource.py 2008-05-23 22:44:39 UTC (rev 2462)
@@ -75,6 +75,22 @@
return tuple(super(CalDAVComplianceMixIn, self).davComplianceClasses()) + extra_compliance
+def updateCacheTokenOnCallback(f):
+ def fun(self, *args, **kwargs):
+ def _updateToken(response):
+ return self.cacheNotifier.changed().addCallback(
+ lambda _: response)
+
+ d = maybeDeferred(f, self, *args, **kwargs)
+
+ if hasattr(self, 'cacheNotifier'):
+ d.addCallback(_updateToken)
+
+ return d
+
+ return fun
+
+
class CalDAVResource (CalDAVComplianceMixIn, DAVResource):
"""
CalDAV resource.
@@ -129,6 +145,19 @@
return response
+ @updateCacheTokenOnCallback
+ def http_PROPPATCH(self, request):
+ return super(CalDAVResource, self).http_PROPPATCH(request)
+
+ @updateCacheTokenOnCallback
+ def http_DELETE(self, request):
+ return super(CalDAVResource, self).http_DELETE(request)
+
+ @updateCacheTokenOnCallback
+ def http_ACL(self, request):
+ return super(CalDAVResource, self).http_ACL(request)
+
+
##
# WebDAV
##
Modified: CalendarServer/branches/unified-cache/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/branches/unified-cache/twistedcaldav/test/util.py 2008-05-23 21:02:17 UTC (rev 2461)
+++ CalendarServer/branches/unified-cache/twistedcaldav/test/util.py 2008-05-23 22:44:39 UTC (rev 2462)
@@ -28,7 +28,11 @@
class InMemoryPropertyStore(object):
def __init__(self):
+ class __FauxResource(object):
+ fp = ':memory:'
+
self._properties = {}
+ self.resource = __FauxResource()
def get(self, qname):
data = self._properties.get(qname)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080523/aba92097/attachment-0001.htm
More information about the calendarserver-changes
mailing list