[CalendarServer-changes] [3348] CalendarServer/branches/users/cdaboo/implicit-if-match-3306
source_changes at macosforge.org
source_changes at macosforge.org
Mon Nov 10 13:36:49 PST 2008
Revision: 3348
http://trac.macosforge.org/projects/calendarserver/changeset/3348
Author: cdaboo at apple.com
Date: 2008-11-10 13:36:48 -0800 (Mon, 10 Nov 2008)
Log Message:
-----------
Provide option to allow compatibility with clients that use If-Match on PUT.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd-test.plist
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd.plist
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/lib-patches/Twisted/twisted.web2.dav.static.patch
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/config.py
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/customxml.py
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/method/put_common.py
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/implicit.py
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/processing.py
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/static.py
CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/test/test_collectioncontents.py
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd-test.plist 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd-test.plist 2008-11-10 21:36:48 UTC (rev 3348)
@@ -484,6 +484,8 @@
</array>
<key>OldDraftCompatibility</key>
<true/>
+ <key>ScheduleTagCompatibility</key>
+ <true/>
<key>DefaultCalendarProvisioned</key>
<true/>
<key>EnablePrivateComments</key>
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd.plist 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/conf/caldavd.plist 2008-11-10 21:36:48 UTC (rev 3348)
@@ -358,6 +358,8 @@
</array>
<key>OldDraftCompatibility</key>
<true/>
+ <key>ScheduleTagCompatibility</key>
+ <true/>
<key>DefaultCalendarProvisioned</key>
<true/>
<key>EnablePrivateComments</key>
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/lib-patches/Twisted/twisted.web2.dav.static.patch 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/lib-patches/Twisted/twisted.web2.dav.static.patch 2008-11-10 21:36:48 UTC (rev 3348)
@@ -60,11 +60,12 @@
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.fp.path)
-@@ -75,6 +81,12 @@
+@@ -75,6 +81,13 @@
# WebDAV
##
+ def etag(self):
++ if not self.fp.exists(): return None
+ if self.hasDeadProperty(TwistedGETContentMD5):
+ return http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5)))
+ else:
@@ -73,7 +74,7 @@
def davComplianceClasses(self):
return ("1", "access-control") # Add "2" when we have locking
-@@ -87,7 +99,6 @@
+@@ -87,7 +100,6 @@
"""
See L{IDAVResource.isCollection}.
"""
@@ -81,7 +82,7 @@
return self.fp.isdir()
##
-@@ -98,6 +109,50 @@
+@@ -98,6 +110,50 @@
return succeed(davPrivilegeSet)
##
@@ -132,7 +133,7 @@
# Workarounds for issues with File
##
-@@ -112,8 +167,12 @@
+@@ -112,8 +168,12 @@
See L{IResource}C{.locateChild}.
"""
# If getChild() finds a child resource, return it
@@ -147,7 +148,7 @@
# If we're not backed by a directory, we have no children.
# But check for existance first; we might be a collection resource
-@@ -132,7 +191,9 @@
+@@ -132,7 +192,9 @@
return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
def createSimilarFile(self, path):
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/config.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/config.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/config.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -204,6 +204,7 @@
"HTTPDomain" : "", # Domain for http calendar user addresses on this server
"AddressPatterns" : [], # Reg-ex patterns to match local calendar user addresses
"OldDraftCompatibility" : True, # Whether to maintain compatibility with non-implicit mode
+ "ScheduleTagCompatibility" : True, # Whether to support older clients that do not use Schedule-Tag feature
"DefaultCalendarProvisioned" : True, # Whether the provisioned default calendar is marked as the scheduling default
"EnablePrivateComments" : True, # Private comments from attendees to organizer
},
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/customxml.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/customxml.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -68,6 +68,16 @@
name = "scheduling-object-resource"
hidden = True
+class TwistedScheduleMatchETags(davxml.WebDAVElement):
+ """
+ List of ETags that can be used for a "weak" If-Match comparison.
+ """
+ namespace = twisted_private_namespace
+ name = "scheduling-match-etags"
+ hidden = True
+
+ allowed_children = { (dav_namespace, "getetag"): (0, None) }
+
class TwistedCalendarHasPrivateCommentsProperty (davxml.WebDAVEmptyElement):
"""
Indicates that a calendar resource has private comments.
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/method/put_common.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/method/put_common.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -49,7 +49,8 @@
from twistedcaldav.caldavxml import NumberOfRecurrencesWithinLimits
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.customxml import calendarserver_namespace ,\
- TwistedCalendarHasPrivateCommentsProperty, TwistedSchedulingObjectResource
+ TwistedCalendarHasPrivateCommentsProperty, TwistedSchedulingObjectResource,\
+ TwistedScheduleMatchETags
from twistedcaldav.customxml import TwistedCalendarAccessProperty
from twistedcaldav.fileops import copyToWithXAttrs, copyXAttrs
from twistedcaldav.fileops import putWithXAttrs
@@ -390,7 +391,19 @@
log.debug("If-Schedule-Tag-Match: header value '%s' does not match resource value '%s'" % (header, scheduletag,))
raise HTTPError(responsecode.PRECONDITION_FAILED)
self.schedule_tag_match = True
+
+ elif config.Scheduling["CalDAV"]["ScheduleTagCompatibility"]:
+ # Compatibility with old clients. Policy:
+ #
+ # 1. If If-Match header is not present, never do smart merge.
+ # 2. If If-Match is present and the specified ETag is considered a "weak" match to the
+ # current Schedule-Tag, then do smart merge, else reject with a 412.
+ #
+ # Actually by the time we get here the pre-condition will already have been tested and found to be OK,
+ # so we can just always do smart merge now if If-Match is present.
+ self.schedule_tag_match = self.request.headers.getHeader("If-Match") is not None
+
def validResourceName(self):
"""
Make sure that the resource name for the new resource is valid.
@@ -641,6 +654,7 @@
self.scheduletag = None
data_changed = False
+ did_implicit_action = False
# Do scheduling
if not self.isiTIP:
@@ -689,10 +703,11 @@
self.calendar = new_calendar
self.calendardata = str(self.calendar)
data_changed = True
+ did_implicit_action = True
else:
is_scheduling_resource = False
- returnValue((is_scheduling_resource, data_changed,))
+ returnValue((is_scheduling_resource, data_changed, did_implicit_action,))
@inlineCallbacks
def doStore(self, implicit):
@@ -870,7 +885,7 @@
new_has_private_comments = self.preservePrivateComments()
# Do scheduling
- is_scheduling_resource, data_changed = (yield self.doImplicitScheduling())
+ is_scheduling_resource, data_changed, did_implicit_action = (yield self.doImplicitScheduling())
# Initialize the rollback system
self.setupRollback()
@@ -892,7 +907,15 @@
# Do the actual put or copy
response = (yield self.doStore(data_changed))
+ # Must not set ETag in response if data changed
+ if did_implicit_action:
+ def _removeEtag(request, response):
+ response.headers.removeHeader('etag')
+ return response
+ _removeEtag.handleErrors = True
+ self.request.addResponseFilter(_removeEtag, atEnd=True)
+
# Check for scheduling object resource and write property
if is_scheduling_resource:
self.destination.writeDeadProperty(TwistedSchedulingObjectResource())
@@ -922,9 +945,26 @@
# Add a response header
response.headers.setHeader("Schedule-Tag", self.scheduletag)
+ # Handle weak etag compatibility
+ if config.Scheduling["CalDAV"]["ScheduleTagCompatibility"]:
+ if change_scheduletag:
+ # Schedule-Tag change => weak ETag behavior must not happen
+ etags = ()
+ 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
+ else:
+ etags = ()
+ etags += (davxml.GETETag.fromString(self.destination.etag().tag),)
+ self.destination.writeDeadProperty(TwistedScheduleMatchETags(*etags))
+ else:
+ self.destination.removeDeadProperty(TwistedScheduleMatchETags)
else:
self.destination.removeDeadProperty(TwistedSchedulingObjectResource)
self.destination.removeDeadProperty(ScheduleTag)
+ self.destination.removeDeadProperty(TwistedScheduleMatchETags)
# Check for existence of private comments and write property
if config.Scheduling["CalDAV"].get("EnablePrivateComments", True):
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/implicit.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/implicit.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -77,7 +77,7 @@
# If action is remove we actually need to get state from the existing scheduling object resource
if self.action == "remove":
- # Also make sure that we return the new calendar being be written rather than the old one
+ # 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()
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/processing.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/scheduling/processing.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -331,6 +331,11 @@
customxml.Update(*update_details),
),
)
+
+ # Refresh from another Attendee should not have Inbox item
+ if hasattr(self.request, "doing_attendee_refresh"):
+ autoprocessed = True
+
result = (True, autoprocessed, changes,)
else:
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/static.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/static.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -40,9 +40,10 @@
import errno
from urlparse import urlsplit
-from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue
+from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue,\
+ maybeDeferred
from twisted.python.failure import Failure
-from twisted.web2 import responsecode
+from twisted.web2 import responsecode, http, http_headers
from twisted.web2.http import HTTPError, StatusResponse
from twisted.web2.dav import davxml
from twisted.web2.dav.fileop import mkcollection, rmdir
@@ -56,7 +57,8 @@
from twistedcaldav import customxml
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
-from twistedcaldav.customxml import TwistedCalendarAccessProperty
+from twistedcaldav.customxml import TwistedCalendarAccessProperty,\
+ TwistedScheduleMatchETags
from twistedcaldav.extensions import DAVFile, DirectoryPrincipalPropertySearchMixIn
from twistedcaldav.extensions import CachingXattrPropertyStore
from twistedcaldav.freebusyurl import FreeBusyURLResource
@@ -92,6 +94,51 @@
else:
return super(CalDAVFile, self).__repr__()
+ def checkPreconditions(self, request):
+ """
+ We override the base class to handle the special implicit scheduling weak ETag behavior
+ for compatibility with old clients using If-Match.
+ """
+
+ if config.Scheduling["CalDAV"]["ScheduleTagCompatibility"]:
+
+ if self.exists() and self.hasDeadProperty(TwistedScheduleMatchETags):
+ etags = self.readDeadProperty(TwistedScheduleMatchETags).children
+ if len(etags) > 1:
+ # This is almost verbatim from twisted.web2.static.checkPreconditions
+ if request.method not in ("GET", "HEAD"):
+
+ # Loop over each tag and succeed if any one matches, else re-raise last exception
+ exists = self.exists()
+ last_modified = self.lastModified()
+ last_exception = None
+ for etag in etags:
+ try:
+ http.checkPreconditions(
+ request,
+ entityExists = exists,
+ etag = http_headers.ETag(etag),
+ lastModified = last_modified,
+ )
+ except HTTPError, e:
+ last_exception = e
+ else:
+ break
+ else:
+ if last_exception:
+ raise last_exception
+
+ # Check per-method preconditions
+ method = getattr(self, "preconditions_" + request.method, None)
+ if method:
+ response = maybeDeferred(method, request)
+ response.addCallback(lambda _: request)
+ return response
+ else:
+ return None
+
+ return super(CalDAVFile, self).checkPreconditions(request)
+
def deadProperties(self):
if not hasattr(self, "_dead_properties"):
self._dead_properties = CachingXattrPropertyStore(self)
Modified: CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/test/test_collectioncontents.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/test/test_collectioncontents.py 2008-11-07 21:50:23 UTC (rev 3347)
+++ CalendarServer/branches/users/cdaboo/implicit-if-match-3306/twistedcaldav/test/test_collectioncontents.py 2008-11-10 21:36:48 UTC (rev 3348)
@@ -50,7 +50,7 @@
# Need to not do implicit behavior during these tests
def _fakeDoImplicitScheduling(self):
- return False, False
+ return False, False, False
StoreCalendarObjectResource.doImplicitScheduling = _fakeDoImplicitScheduling
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20081110/fe3d635f/attachment-0001.html>
More information about the calendarserver-changes
mailing list