[CalendarServer-changes] [2954] CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue Sep 9 11:48:26 PDT 2008


Revision: 2954
          http://trac.macosforge.org/projects/calendarserver/changeset/2954
Author:   cdaboo at apple.com
Date:     2008-09-09 11:48:26 -0700 (Tue, 09 Sep 2008)
Log Message:
-----------
Auto-accept now works.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/itip.py
    CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/method/put_common.py
    CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/caldav.py
    CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/implicit.py

Modified: CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/itip.py	2008-09-09 16:53:15 UTC (rev 2953)
+++ CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/itip.py	2008-09-09 18:48:26 UTC (rev 2954)
@@ -45,7 +45,7 @@
 from twistedcaldav import caldavxml
 from twistedcaldav.accounting import accountingEnabled, emitAccounting
 from twistedcaldav.log import Logger
-from twistedcaldav.ical import Property, iCalendarProductID
+from twistedcaldav.ical import Property
 from twistedcaldav.method import report_common
 from twistedcaldav.resource import isCalendarCollectionResource
 
@@ -108,10 +108,8 @@
                   2. See if this updates existing ones in free-busy-set calendars.
                   3. Remove existing ones in those calendars.
                   4. See if this fits into a free slot:
-                      1. If not, send REPLY with failure status
-                      2. If so
-                          1. send REPLY with success
-                          2. add to f-b-s calendar
+                      1. If not, add to f-b-s calendar DECLINED
+                      2. If so, add to f-b-s calendar ACCEPTED
               2. If not,
                   1. remove the one we got - its 'stale'
               3. Delete the request from the Inbox.
@@ -153,30 +151,24 @@
                 
             if check_reply:
                 # Process the reply by determining PARTSTAT and sending the reply and booking the event.
-                doreply, replycal, accepted = yield self.checkForReply()
+                valid, accepted = yield self.checkForReply()
                 
-                try:
-                    if accepted:
+                if valid:
+                    try:
                         if calmatch:
+                            log.info("Replaced calendar component %s with new iTIP message in %s (%s)." % (calmatch, calURL, accepted,))
                             yield self.writeResource(calURL, updatecal, calmatch, self.calendar)
-                            log.info("Replaced calendar component %s with new iTIP message in %s." % (calmatch, calURL))
                         else:
+                            log.info("Added new calendar component in %s (%s)." % (calURL, accepted,))
                             yield self.writeResource(calURL, updatecal, None, self.calendar)
-                            log.info("Added new calendar component in %s." % (calURL,))
-                    else:
-                        if calmatch:
-                            yield self.deleteResource(updatecal, calmatch)
-                            log.info("Deleted calendar component %s in %s as update was not accepted." % (calmatch, calURL))
-                            
-                    # Send a reply if needed. 
-                    if doreply:
-                        log.info("Sending iTIP REPLY %s" % (("declined","accepted")[accepted],))
-                        yield self.writeReply(replycal)
-                    processed = "processed"
-                except:
-                    # FIXME: bare except
-                    log.err("Error while auto-processing iTIP: %s" % (Failure(),))
-                    raise iTipException()
+    
+                        processed = "processed"
+                    except:
+                        # FIXME: bare except
+                        log.err("Error while auto-processing iTIP: %s" % (Failure(),))
+                        raise iTipException()
+                else:
+                    processed = "ignored"
                 
         else:
             # So we have a partial update. That means we have to do partial updates to instances in
@@ -215,29 +207,26 @@
     
             if check_reply:
                 # Process the reply by determining PARTSTAT and sending the reply and booking the event.
-                doreply, replycal, accepted = yield self.checkForReply()
+                valid, accepted = yield self.checkForReply()
                 
-                try:
-                    if calmatch:
-                        # Merge the new instances with the old ones
-                        self.mergeComponents(self.calendar, cal)
-                        yield self.writeResource(calURL, updatecal, calmatch, cal)
-                        log.info("Merged calendar component %s with new iTIP message in %s." % (calmatch, calURL))
-                    else:
-                        if accepted:
+                if valid:
+                    try:
+                        if calmatch:
+                            # Merge the new instances with the old ones
+                            self.mergeComponents(self.calendar, cal)
+                            log.info("Merged calendar component %s with new iTIP message in %s (%s)." % (calmatch, calURL, accepted,))
+                            yield self.writeResource(calURL, updatecal, calmatch, cal)
+                        else:
+                            log.info("Added new calendar component in %s (%s)." % (calURL, accepted,))
                             yield self.writeResource(calURL, updatecal, None, self.calendar)
-                            log.info("Added new calendar component in %s." % (calURL,))
                             
-                    # Do reply if needed. 
-                    if doreply:
-                        log.info("Sending iTIP REPLY %s" % (("declined","accepted")[accepted],))
-                        yield self.writeReply(replycal)
-                        
-                    processed = "processed"
-                except:
-                    # FIXME: bare except
-                    log.err("Error while auto-processing iTIP: %s" % (Failure(),))
-                    raise iTipException()
+                        processed = "processed"
+                    except:
+                        # FIXME: bare except
+                        log.err("Error while auto-processing iTIP: %s" % (Failure(),))
+                        raise iTipException()
+                else:
+                    processed = "ignored"
     
         # Remove the now processed incoming request.
         if self.inbox:
@@ -315,8 +304,8 @@
                 if self.compareSyncInfo(info, newinfo) < 0:
                     # Delete existing resource which has been cancelled
                     try:
-                        yield self.deleteResource(updatecal, calmatch,)
                         log.info("Delete calendar component %s in %s as it was cancelled." % (calmatch, calURL))
+                        yield self.deleteResource(updatecal, calmatch,)
                     except:
                         # FIXME: bare except
                         log.err("Error while auto-processing iTIP: %s" % (Failure(),))
@@ -371,12 +360,12 @@
                     # in which case the calendar object is empty (except for VTIMEZONEs).
                     if existing_calendar.mainType() is None:
                         # Delete the now empty calendar object
-                        yield self.deleteResource(updatecal, calmatch)
                         log.info("Deleted calendar component %s after cancellations from iTIP message in %s." % (calmatch, calURL))
+                        yield self.deleteResource(updatecal, calmatch)
                     else:
                         # Update the existing calendar object
-                        yield self.writeResource(calURL, updatecal, calmatch, existing_calendar)
                         log.info("Updated calendar component %s with cancellations from iTIP message in %s." % (calmatch, calURL))
+                        yield self.writeResource(calURL, updatecal, calmatch, existing_calendar)
                     processed = "processed"
                 else:
                     processed = "older"
@@ -405,7 +394,7 @@
         BTW The incoming iTIP message may contain multiple components so we need to iterate over all those.
         At the moment we will treat a failure on one instance as a DECLINE of the entire set.
 
-        @return: C{True} if a reply is needed, C{False} otherwise.
+        @return: a C{tuple} of C{bool} indicating whether a valid iTIP was received, and C{str} new partstat.
         """
         
         # We need to figure out whether the specified component will clash with any others in the f-b-set calendars
@@ -454,58 +443,21 @@
         cuas = self.principal.calendarUserAddresses()
         attendeeProps = self.calendar.getAttendeeProperties(cuas)
         if not attendeeProps:
-            returnValue((False, None, accepted))
+            returnValue((False, accepted,))
     
-        # Look for specific parameters
-        rsvp = True
-        for attendeeProp in attendeeProps:
-            if "RSVP" in attendeeProp.params():
-                if attendeeProp.params()["RSVP"][0] == "FALSE":
-                    rsvp = False
-        
-                # Now modify the original component
-                del attendeeProp.params()["RSVP"]
-    
         if accepted:
             partstat = "ACCEPTED"
         else:
             partstat = "DECLINED"
+            
+            # Make sure declined events are TRANSPARENT on the calendar
+            self.calendar.replacePropertyInAllComponents(Property("TRANSP", "TRANSPARENT"))
+
         for attendeeProp in attendeeProps:
-            if "PARTSTAT" in attendeeProp.params():
-                attendeeProp.params()["PARTSTAT"][0] = partstat
-            else:
-                attendeeProp.params()["PARTSTAT"] = [partstat]
+            attendeeProp.params()["PARTSTAT"] = [partstat]
         
-        # Now create a new calendar object for the reply
-        
-        # First get useful props from the original
-        replycal = self.calendar.duplicate()
-        
-        # Change METHOD
-        replycal.getProperty("METHOD").setValue("REPLY")
-        
-        # Change PRODID to this server
-        replycal.getProperty("PRODID").setValue(iCalendarProductID)
-        
-        # Add REQUEST-STATUS
-        for component in replycal.subcomponents():
-            if accepted:
-                component.addProperty(Property(name="REQUEST-STATUS", value="2.0; Success."))
-            else:
-                component.addProperty(Property(name="REQUEST-STATUS", value="4.0; Event conflict. Date/time is busy."))
+        returnValue((True, accepted,))
     
-        # Remove all attendees other than ourselves
-        for component in replycal.subcomponents():
-            if component.name() == "VTIMEZONE":
-                continue
-            attendeeProp = component.getAttendeeProperty(cuas)
-            attendees = tuple(component.properties("ATTENDEE"))
-            for attendee in attendees:
-                if attendeeProp is None or (attendee.value() != attendeeProp.value()):
-                    component.removeProperty(attendee)
-    
-        returnValue((rsvp, replycal, accepted))
-    
     @inlineCallbacks
     def writeReply(self, replycal):
         """
@@ -583,7 +535,8 @@
                      destinationparent = collection,
                      destinationcal = True,
                      calendar = calendar,
-                     isiTIP = itipper
+                     isiTIP = itipper,
+                     internal_request=True,
                  ).run()
         
         returnValue(newchild)

Modified: CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/method/put_common.py	2008-09-09 16:53:15 UTC (rev 2953)
+++ CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/method/put_common.py	2008-09-09 18:48:26 UTC (rev 2954)
@@ -183,6 +183,7 @@
         calendar=None,
         isiTIP=False,
         allowImplicitSchedule=True,
+        internal_request=False,
     ):
         """
         Function that does common PUT/COPY/MOVE behavior.
@@ -201,6 +202,7 @@
         @param deletesource:      True if the source resource is to be deleted on successful completion, False otherwise.
         @param isiTIP:                True if relaxed calendar data validation is to be done, False otherwise.
         @param allowImplicitSchedule: True if implicit scheduling should be attempted, False otherwise.
+        @param internal_request:   True if this request originates internally and needs to bypass scheduling authorization checks.
         """
         
         # Check that all arguments are valid
@@ -239,6 +241,7 @@
         self.deletesource = deletesource
         self.isiTIP = isiTIP
         self.allowImplicitSchedule = allowImplicitSchedule
+        self.internal_request = internal_request
         
         self.rollback = None
         self.access = None
@@ -690,7 +693,7 @@
             # Do scheduling
             if not self.isiTIP and self.allowImplicitSchedule:
                 scheduler = ImplicitScheduler()
-                new_calendar = (yield scheduler.doImplicitScheduling(self.request, self.destination, self.calendar, False))
+                new_calendar = (yield scheduler.doImplicitScheduling(self.request, self.destination, self.calendar, False, internal_request=self.internal_request))
                 if new_calendar:
                     self.calendar = new_calendar
                     self.calendardata = str(self.calendar)

Modified: CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/caldav.py	2008-09-09 16:53:15 UTC (rev 2953)
+++ CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/caldav.py	2008-09-09 18:48:26 UTC (rev 2954)
@@ -135,7 +135,7 @@
         for principal, inbox, child in autoresponses:
             # Add delayed reactor task to handle iTIP responses
             itip = iTipProcessor()
-            reactor.callLater(0.0, itip.handleRequest, *(self.scheduler.request, principal, inbox, self.scheduler.calendar.duplicate(), child))
+            reactor.callLater(2.0, itip.handleRequest, *(self.scheduler.request, principal, inbox, self.scheduler.calendar.duplicate(), child))
 
     @inlineCallbacks
     def generateResponse(self, recipient, responses, autoresponses):
@@ -147,20 +147,24 @@
         childURL = joinURL(recipient.inboxURL, name)
         child = (yield self.scheduler.request.locateResource(childURL))
 
-        # Do implicit scheduling message processing
-        try:
-            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=e.msg)
-            returnValue(False)
+        # Do implicit scheduling message processing - but not for auto-accept principals.
+        # Auto-accepts will be processed internally by the server a little later on.
+        if not recipient.principal.autoSchedule():
+            try:
+                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=e.msg)
+                returnValue(False)
+        else:
+            processed = autoprocessed = False
 
         if autoprocessed:
             # No need to write the inbox item as it has already been auto-processed
@@ -198,7 +202,7 @@
                 child.writeDeadProperty(caldavxml.ScheduleState(caldavxml.ScheduleProcessed() if processed else caldavxml.ScheduleUnprocessed()))
             
                 # Look for auto-schedule option
-                if not processed and recipient.principal.autoSchedule():
+                if recipient.principal.autoSchedule():
                     autoresponses.append((recipient.principal, recipient.inbox, child))
                     
                 returnValue(True)

Modified: CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/implicit.py	2008-09-09 16:53:15 UTC (rev 2953)
+++ CalendarServer/branches/users/cdaboo/implicitauto-2947/twistedcaldav/scheduling/implicit.py	2008-09-09 18:48:26 UTC (rev 2954)
@@ -47,7 +47,7 @@
         pass
 
     @inlineCallbacks
-    def doImplicitScheduling(self, request, resource, calendar, deleting):
+    def doImplicitScheduling(self, request, resource, calendar, deleting, internal_request=False):
         """
         Do implicit scheduling operation based on the calendar data that is being PUT
 
@@ -69,7 +69,7 @@
         self.calendar = calendar
         self.calendar_owner = (yield self.resource.owner(self.request))
         self.deleting = deleting
-        self.internal_request = False
+        self.internal_request = internal_request
         self.except_attendees = ()
 
         # When deleting we MUST have the calendar as the actual resource
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080909/a6cd9e4c/attachment-0001.html 


More information about the calendarserver-changes mailing list