[CalendarServer-changes] [4393] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue Jun 30 08:36:53 PDT 2009


Revision: 4393
          http://trac.macosforge.org/projects/calendarserver/changeset/4393
Author:   cdaboo at apple.com
Date:     2009-06-30 08:36:52 -0700 (Tue, 30 Jun 2009)
Log Message:
-----------
If processing of an attendee's iTIP message fails because of inconsistent data with the organizer, we try
to "self-heal" by replacing the attendee's current data with the full data from the organizer for that
attendee, then we try re-applying the iTIP message changes.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/method/delete_common.py
    CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
    CalendarServer/trunk/twistedcaldav/scheduling/processing.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/scheduling/utils.py

Modified: CalendarServer/trunk/twistedcaldav/method/delete_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/delete_common.py	2009-06-30 01:17:13 UTC (rev 4392)
+++ CalendarServer/trunk/twistedcaldav/method/delete_common.py	2009-06-30 15:36:52 UTC (rev 4393)
@@ -168,7 +168,7 @@
                 yield parent.updateCTag()
     
                 # Do scheduling
-                if scheduler:
+                if scheduler and not self.internal_request:
                     yield scheduler.doImplicitScheduling()
     
         except MemcacheLockTimeoutError:

Modified: CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2009-06-30 01:17:13 UTC (rev 4392)
+++ CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2009-06-30 15:36:52 UTC (rev 4393)
@@ -16,7 +16,7 @@
 
 from twext.web2.dav.davxml import ErrorResponse
 
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.dav.util import joinURL
@@ -35,6 +35,7 @@
 from twistedcaldav.scheduling.icaldiff import iCalDiff
 from twistedcaldav.scheduling.itip import iTipGenerator
 from twistedcaldav.scheduling.scheduler import CalDAVScheduler
+from twistedcaldav.scheduling.utils import getCalendarObjectForPrincipals
 
 __all__ = [
     "ImplicitScheduler",
@@ -88,7 +89,7 @@
             yield self.checkImplicitState()
         
         # Attendees are not allowed to overwrite one type with another
-        if self.state == "attendee" and (existing_type != new_type) and existing_resource:
+        if not self.internal_request and self.state == "attendee" and (existing_type != new_type) and existing_resource:
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-attendee-change")))
 
         returnValue((self.action != "none", new_type == "schedule",))
@@ -817,28 +818,10 @@
         """
         
         self.organizer_calendar = None
-        if self.organizerPrincipal:
-            # Get Organizer's calendar-home
-            calendar_home = self.organizerPrincipal.calendarHome()
-            
-            # FIXME: because of the URL->resource request mapping thing, we have to force the request
-            # to recognize this resource
-            self.request._rememberResource(calendar_home, calendar_home.url())
-    
-            # Run a UID query against the UID
-
-            def queryCalendarCollection(collection, uri):
-                rname = collection.index().resourceNameForUID(self.uid)
-                if rname:
-                    self.organizer_calendar = collection.iCalendar(rname)
-                    return succeed(False)
-                else:
-                    return succeed(True)
-            
-            # 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, self.request, calendar_home.url(), "infinity", queryCalendarCollection, 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()
+        
     def isAttendeeChangeInsignificant(self):
         """
         Check whether the change is significant (PARTSTAT) or allowed

Modified: CalendarServer/trunk/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/processing.py	2009-06-30 01:17:13 UTC (rev 4392)
+++ CalendarServer/trunk/twistedcaldav/scheduling/processing.py	2009-06-30 15:36:52 UTC (rev 4393)
@@ -16,16 +16,19 @@
 
 from hashlib import md5
 from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web2.dav.method.report import NumberOfMatchesWithinLimits
 from twisted.web2.dav.util import joinURL
+from twisted.web2.http import HTTPError
 from twistedcaldav import customxml, caldavxml
 from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.ical import Property
+from twistedcaldav.instance import InvalidOverriddenInstanceError
 from twistedcaldav.log import Logger
 from twistedcaldav.method import report_common
 from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
 from twistedcaldav.scheduling.itip import iTipProcessing, iTIPRequestStatus
+from twistedcaldav.scheduling.utils import getCalendarObjectForPrincipals
 from vobject.icalendar import utc
 import datetime
 import time
@@ -78,7 +81,27 @@
         if self.isOrganizerReceivingMessage():
             result = (yield self.doImplicitOrganizer())
         elif self.isAttendeeReceivingMessage():
-            result = (yield self.doImplicitAttendee())
+            try:
+                result = (yield self.doImplicitAttendee())
+            except ImplicitProcessorException:
+                # These we always pass up
+                raise
+            except Exception, e:
+                # We attempt to recover from this. That involves trying to re-write the attendee data
+                # to match that of the organizer assuming we have the organizer's full data available, then
+                # we try the processing operation again.
+                log.error("ImplicitProcessing - originator '%s' to recipient '%s' with UID: '%s' - exception raised will try to fix: %s" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid, e))
+                result = (yield self.doImplicitAttendeeEventFix(e))
+                if result:
+                    log.error("ImplicitProcessing - originator '%s' to recipient '%s' with UID: '%s' - restored organizer's copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+                    try:
+                        result = (yield self.doImplicitAttendee())
+                    except Exception, e:
+                        log.error("ImplicitProcessing - originator '%s' to recipient '%s' with UID: '%s' - exception raised after fix: %s" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid, e))
+                        raise ImplicitProcessorException("5.1;Service unavailable")
+                else:
+                    log.error("ImplicitProcessing - originator '%s' to recipient '%s' with UID: '%s' - could not fix" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+                    raise ImplicitProcessorException("5.1;Service unavailable")
         else:
             log.error("METHOD:%s not supported for implicit scheduling." % (self.method,))
             raise ImplicitProcessorException("3.14;Unsupported capability")
@@ -105,32 +128,15 @@
         
         self.recipient_calendar = None
         self.recipient_calendar_collection = None
+        self.recipient_calendar_collection_uri = None
         self.recipient_calendar_name = None
-        if self.recipient.principal:
-            # Get Recipient's calendar-home
-            calendar_home = self.recipient.principal.calendarHome()
-            
-            # FIXME: because of the URL->resource request mapping thing, we have to force the request
-            # to recognize this resource
-            self.request._rememberResource(calendar_home, calendar_home.url())
+        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_collection = calendar_collection
+            self.recipient_calendar_collection_uri = calendar_collection_uri
+            self.recipient_calendar_name = resource_name
     
-            # Run a UID query against the UID
-
-            def queryCalendarCollection(collection, uri):
-                rname = collection.index().resourceNameForUID(self.uid)
-                if rname:
-                    self.recipient_calendar = collection.iCalendar(rname)
-                    self.recipient_calendar_name = rname
-                    self.recipient_calendar_collection = collection
-                    self.recipient_calendar_collection_uri = uri
-                    return succeed(False)
-                else:
-                    return succeed(True)
-            
-            # 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, self.request, calendar_home.url(), "infinity", queryCalendarCollection, None)
-    
     @inlineCallbacks
     def doImplicitOrganizer(self):
 
@@ -655,3 +661,37 @@
                 madeChanges = True
         
         return madeChanges
+
+    @inlineCallbacks
+    def doImplicitAttendeeEventFix(self, ex):
+
+        # Only certain types of exception should be handled - ones related to calendar data errors.
+        # All others should result in the scheduling response coming back as a 5.x code
+        
+        if type(ex) not in (InvalidOverriddenInstanceError, HTTPError):
+            raise ImplicitProcessorException("5.1;Service unavailable")
+
+        # Check to see whether the originator is hosted on this server
+        if not self.originator.principal:
+            raise ImplicitProcessorException("5.1;Service unavailable")
+
+        # Locate the originator's copy of the event
+        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()
+
+        # Get attendee's view of that
+        originator_calendar.attendeesView((self.recipient.cuaddr,))
+
+        # Locate the attendee's copy of the event if it exists.
+        recipient_resource, recipient_resource_name, recipient_collection, recipient_collection_uri = (yield getCalendarObjectForPrincipals(self.request, self.recipient.principal, self.uid))
+        
+        # We only need to fix data that already exists
+        if recipient_resource:
+            if originator_calendar.mainType() != None:
+                yield self.writeCalendarResource(recipient_collection_uri, recipient_collection, recipient_resource_name, originator_calendar)
+            else:
+                yield self.deleteCalendarResource(recipient_collection_uri, recipient_collection, recipient_resource_name)
+        
+        returnValue(True)

Added: CalendarServer/trunk/twistedcaldav/scheduling/utils.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/utils.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/scheduling/utils.py	2009-06-30 15:36:52 UTC (rev 4393)
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2005-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.
+##
+
+from twisted.internet.defer import inlineCallbacks, succeed, returnValue
+from twistedcaldav.method import report_common
+
+ at inlineCallbacks
+def getCalendarObjectForPrincipals(request, principal, uid):
+    """
+    Get a copy of the event for a principal.
+    """
+    
+    result = {}
+    result["resource"] = None
+    result["resource_name"] = None
+    result["calendar_collection"] = None
+    result["calendar_collection_uri"] = None
+    if principal:
+        # Get principal's calendar-home
+        calendar_home = 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)
+            if rname:
+                result["resource"] = collection.getChild(rname)
+                result["resource_name"] = rname
+                result["calendar_collection"] = collection
+                result["calendar_collection_uri"] = uri
+                return succeed(False)
+            else:
+                return succeed(True)
+        
+        # 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)
+
+    returnValue((result["resource"], result["resource_name"], result["calendar_collection"], result["calendar_collection_uri"],))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090630/6468d839/attachment-0001.html>


More information about the calendarserver-changes mailing list