[CalendarServer-changes] [2811] CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/ scheduling

source_changes at macosforge.org source_changes at macosforge.org
Tue Aug 12 14:12:00 PDT 2008


Revision: 2811
          http://trac.macosforge.org/projects/calendarserver/changeset/2811
Author:   cdaboo at apple.com
Date:     2008-08-12 14:12:00 -0700 (Tue, 12 Aug 2008)
Log Message:
-----------
First implementation of automatic scheduling message processing. Currently just handles a full event CANCEL.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/caldav.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/processing.py

Modified: CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/caldav.py	2008-08-12 21:10:58 UTC (rev 2810)
+++ CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/caldav.py	2008-08-12 21:12:00 UTC (rev 2811)
@@ -37,6 +37,8 @@
 from twistedcaldav.scheduling.cuaddress import LocalCalendarUser,\
     RemoteCalendarUser
 from twistedcaldav.scheduling.delivery import DeliveryService
+from twistedcaldav.scheduling.processing import ImplicitProcessor,\
+    ImplicitProcessorException
 
 import md5
 import time
@@ -145,42 +147,62 @@
         childURL = joinURL(recipient.inboxURL, name)
         child = (yield self.scheduler.request.locateResource(childURL))
 
-        # Copy calendar to inbox (doing fan-out)
+        # Do implicit scheduling message processing
         try:
-            from twistedcaldav.method.put_common import StoreCalendarObjectResource
-            yield StoreCalendarObjectResource(
-                         request=self.scheduler.request,
-                         destination = child,
-                         destination_uri = childURL,
-                         destinationparent = recipient.inbox,
-                         destinationcal = True,
-                         calendar = self.scheduler.calendar,
-                         isiTIP = True
-                     ).run()
-        except:
-            # FIXME: Bare except
+            processor = ImplicitProcessor()
+            processed, autoprocessed = (yield processor.doImplicitProcessing(
+                self.scheduler.request,
+                self.scheduler.calendar,
+                self.scheduler.originator,
+                recipient
+            ))
+        except ImplicitProcessorException, e:
             log.err("Could not store data in Inbox : %s" % (recipient.inbox,))
             err = HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "recipient-permissions")))
-            responses.add(recipient.cuaddr, Failure(exc_value=err), reqstatus="3.8;No authority")
+            responses.add(recipient.cuaddr, Failure(exc_value=err), reqstatus=e.msg)
             returnValue(False)
-        else:
+
+        if autoprocessed:
+            # No need to write the inbox item as it has already been auto-processed
             responses.add(recipient.cuaddr, responsecode.OK, reqstatus="2.0;Success")
-
-            # Store CALDAV:originator property
-            child.writeDeadProperty(caldavxml.Originator(davxml.HRef(self.scheduler.originator.cuaddr)))
-        
-            # Store CALDAV:recipient property
-            child.writeDeadProperty(caldavxml.Recipient(davxml.HRef(recipient.cuaddr)))
-        
-            # Store CALDAV:schedule-state property
-            child.writeDeadProperty(caldavxml.ScheduleState(caldavxml.ScheduleUnprocessed()))
-        
-            # Look for auto-schedule option
-            if recipient.principal.autoSchedule():
-                autoresponses.append((recipient.principal, recipient.inbox, child))
-                
             returnValue(True)
+        else:
+            # Copy calendar to inbox 
+            try:
+                from twistedcaldav.method.put_common import StoreCalendarObjectResource
+                yield StoreCalendarObjectResource(
+                             request=self.scheduler.request,
+                             destination = child,
+                             destination_uri = childURL,
+                             destinationparent = recipient.inbox,
+                             destinationcal = True,
+                             calendar = self.scheduler.calendar,
+                             isiTIP = True
+                         ).run()
+            except:
+                # FIXME: Bare except
+                log.err("Could not store data in Inbox : %s" % (recipient.inbox,))
+                err = HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "recipient-permissions")))
+                responses.add(recipient.cuaddr, Failure(exc_value=err), reqstatus="3.8;No authority")
+                returnValue(False)
+            else:
+                responses.add(recipient.cuaddr, responsecode.OK, reqstatus="2.0;Success")
     
+                # Store CALDAV:originator property
+                child.writeDeadProperty(caldavxml.Originator(davxml.HRef(self.scheduler.originator.cuaddr)))
+            
+                # Store CALDAV:recipient property
+                child.writeDeadProperty(caldavxml.Recipient(davxml.HRef(recipient.cuaddr)))
+            
+                # Store CALDAV:schedule-state property
+                child.writeDeadProperty(caldavxml.ScheduleState(caldavxml.ScheduleProcessed() if processed else caldavxml.ScheduleUnprocessed()))
+            
+                # Look for auto-schedule option
+                if not processed and recipient.principal.autoSchedule():
+                    autoresponses.append((recipient.principal, recipient.inbox, child))
+                    
+                returnValue(True)
+    
     @inlineCallbacks
     def generateFreeBusyResponse(self, recipient, responses, organizerProp, uid):
 

Added: CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/processing.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/implicit-2805/twistedcaldav/scheduling/processing.py	2008-08-12 21:12:00 UTC (rev 2811)
@@ -0,0 +1,207 @@
+#
+# Copyright (c) 2005-2008 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, returnValue, succeed
+from twistedcaldav.log import Logger
+from twistedcaldav.method import report_common
+from twisted.web2.dav.fileop import delete
+
+__all__ = [
+    "ImplicitProcessor",
+]
+
+log = Logger()
+
+class ImplicitProcessorException(Exception):
+    
+    def __init__(self, msg):
+        self.msg = msg
+
+class ImplicitProcessor(object):
+    
+    def __init__(self):
+        pass
+
+    @inlineCallbacks
+    def doImplicitProcessing(self, request, message, originator, recipient):
+        """
+        Do implicit processing of a scheduling message, and possibly also auto-process it
+        if the recipient has auto-accept on.
+
+        @param message:
+        @type message:
+        @param originator:
+        @type originator:
+        @param recipient:
+        @type recipient:
+        
+        @return: a C{tuple} of (C{bool}, C{bool}) indicating whether the message was processed, and if it was whether
+            auto-processing has taken place.
+        """
+
+        self.request = request
+        self.message = message
+        self.originator = originator
+        self.recipient = recipient
+        
+        # TODO: for now going to assume that the originator is local - i.e. the scheduling message sent
+        # represents the actual organizer's view.
+        
+        # First see whether this is the organizer or attendee sending the message
+        self.extractCalendarData()
+
+        if self.isOrganizerReceivingMessage():
+            result = (yield self.doImplicitOrganizer())
+        elif self.isAttendeeReceivingMessage():
+            result = (yield self.doImplicitAttendee())
+        else:
+            log.error("METHOD:%s not supported for implicit scheduling." % (self.method,))
+            raise ImplicitProcessorException("3.14;Unsupported capability")
+
+        returnValue(result)
+
+    def extractCalendarData(self):
+        
+        # Some other useful things
+        self.method = self.message.propertyValue("METHOD")
+        self.uid = self.message.resourceUID()
+    
+    def isOrganizerReceivingMessage(self):
+        return self.method in ("REPLY", "REFRESH")
+
+    def isAttendeeReceivingMessage(self):
+        return self.method in ("REQUEST", "ADD", "CANCEL")
+
+    @inlineCallbacks
+    def doImplicitOrganizer(self):
+
+        # Locate the organizer's copy of the event.
+        yield self.getRecipientsCopy()
+        if self.recipient_calendar is None:
+            log.debug("Implicit - originator '%s' to recipient '%s' ignoring UID: '%s' - organizer has no copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+            returnValue((True, True,))
+
+        # Update the organizer's copy with the partstat's of the replying attendee
+        
+        # Send out a request to all attendees to update them
+
+        returnValue((False, False,))
+
+    @inlineCallbacks
+    def getRecipientsCopy(self):
+        """
+        Get the Recipient's copy of the event being processed.
+        """
+        
+        self.recipient_calendar = None
+        self.recipient_calendar_collection = 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())
+    
+            # 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_collection = collection
+                    self.recipient_calendar_name = 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)
+    
+    @inlineCallbacks
+    def doImplicitAttendee(self):
+
+        # Locate the attendee's copy of the event if it exists.
+        yield self.getRecipientsCopy()
+        self.new_resource = self.recipient_calendar is None
+
+        # Handle new items differently than existing ones.
+        if self.new_resource:
+            result = (False, False,)
+        else:
+            result = (yield self.doImplicitAttendeeUpdate())
+        
+        returnValue(result)
+
+    @inlineCallbacks
+    def doImplicitAttendeeUpdate(self):
+        
+        # Different based on method
+        if self.method == "REQUEST":
+            #result = (yield self.doImplicitAttendeRequest())
+            result = (False, False,)
+        elif self.method == "CANCEL":
+            result = (yield self.doImplicitAttendeCancel())
+        elif self.method == "ADD":
+            # TODO: implement ADD
+            result = (False, False,)
+            
+        returnValue(result)
+
+    @inlineCallbacks
+    def doImplicitAttendeCancel(self):
+
+        # If there is no existing copy, then ignore
+        if self.recipient_calendar is None:
+            log.debug("Implicit - originator '%s' to recipient '%s' ignoring METHOD:CANCEL, UID: '%s' - attendee has no copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+            result = (True, True,)
+        else:
+            # Check to see if this is a cancel of the entire event
+            if self.message.masterComponent() is not None:
+                
+                # Delete the attendee's copy of the event
+                log.debug("Implicit - originator '%s' to recipient '%s' processing METHOD:CANCEL, UID: '%s' - deleting entire event" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+                self.deleteCalendarResource(self.recipient_calendar_collection, self.recipient_calendar_name)
+                result = (True, False,)
+     
+            else:
+                # Get each cancelled Recurrence-ID and exclude those from the existing event
+                
+                result = (False, False,)
+
+        returnValue(result)
+
+    @inlineCallbacks
+    def deleteCalendarResource(self, collection, name):
+        """
+        Delete the calendar resource in the specified calendar.
+        
+        @param collection: the L{CalDAVFile} for the calendar collection to store the resource in.
+        @param name: the C{str} for the resource name to write into, or {None} to write a new resource.
+        @return: L{Deferred}
+        """
+        
+        delchild = collection.getChild(name)
+        index = collection.index()
+        index.deleteResource(delchild.fp.basename())
+        
+        yield delete("", delchild.fp, "0")
+        
+        # Change CTag on the parent calendar collection
+        yield collection.updateCTag()
+    
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080812/226f832e/attachment-0001.html 


More information about the calendarserver-changes mailing list