[CalendarServer-changes] [1839]

source_changes at macosforge.org source_changes at macosforge.org
Tue Sep 4 09:59:40 PDT 2007


Revision: 1839
          http://trac.macosforge.org/projects/calendarserver/changeset/1839
Author:   cdaboo at apple.com
Date:     2007-09-04 09:59:39 -0700 (Tue, 04 Sep 2007)

Log Message:
-----------
Support for handling REQUEST and CANCEL on recurrence instances with tests. The old code for REQUEST is still present and will
be removed in a future commit once all testing is done. Tests only cover the CANCEL operation right now.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/ical.py
    CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/itip.py

Added Paths:
-----------
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/1.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/10.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/11.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/12.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/13.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/14.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/2.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/3.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/4.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/5.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/6.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/7.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/8.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/9.ics
    CalDAVTester/branches/users/cdaboo/better-itip-1837/scripts/tests/schedulepostautorecur.xml

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/1.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/1.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/1.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,18 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/10.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/10.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/10.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,73 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060209T200000Z
+DTEND:20060209T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060216T200000Z
+DTSTART:20060216T210000Z
+DTEND:20060216T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060223T200000Z
+DTSTART:20060223T210000Z
+DTEND:20060223T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060309T200000Z
+DTSTART:20060309T210000Z
+DTEND:20060309T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060323T200000Z
+DTSTART:20060324T210000Z
+DTEND:20060324T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/11.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/11.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/11.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,20 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060216T210000Z
+DTEND:20060216T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060216T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:1
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/12.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/12.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/12.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,61 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060209T200000Z
+DTEND:20060209T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+EXDATE:20060216T200000Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:1
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060223T200000Z
+DTSTART:20060223T210000Z
+DTEND:20060223T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060309T200000Z
+DTSTART:20060309T210000Z
+DTEND:20060309T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060323T200000Z
+DTSTART:20060324T210000Z
+DTEND:20060324T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/13.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/13.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/13.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,35 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060223T210000Z
+DTEND:20060223T220000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060223T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060309T210000Z
+DTEND:20060309T220000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060309T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/14.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/14.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/14.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,34 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060209T200000Z
+DTEND:20060209T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+EXDATE:20060216T200000Z
+EXDATE:20060223T200000Z,20060309T200000Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+RECURRENCE-ID:20060323T200000Z
+DTSTART:20060324T210000Z
+DTEND:20060324T220000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/2.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/2.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/2.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,17 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/3.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/3.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/3.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,20 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060215T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:1
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/4.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/4.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/4.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,19 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+EXDATE:20060215T200000Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:1
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/5.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/5.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/5.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,50 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060308T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060322T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060405T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/6.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/6.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/6.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,20 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+EXDATE:20060215T200000Z
+EXDATE:20060308T200000Z,20060322T200000Z,20060405T200000Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:2
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/7.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/7.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/7.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,20 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-1
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060419T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:1
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/8.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/8.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/8.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,20 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-2
+DTSTART:20060208T200000Z
+DTEND:20060208T210000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060419T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SEQUENCE:1
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/9.ics
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/9.ics	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/Resource/scheduleautorecur/9.ics	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,74 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//ORACLE//NONSGML Windows Desktop Calendar 10.1.2.0.5.278//EN
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060209T200000Z
+DTEND:20060209T210000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060216T210000Z
+DTEND:20060216T220000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060216T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060223T210000Z
+DTEND:20060223T220000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060223T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060309T210000Z
+DTEND:20060309T220000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060309T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73-3
+DTSTART:20060324T210000Z
+DTEND:20060324T220000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Resource 01:$rcuaddralt1:
+CREATED:20060110T231240Z
+DESCRIPTION:+1 605 990 0100\nAccess: 879307#
+DTSTAMP:20060309T185105Z
+ORGANIZER;CN=Cyrus Daboo:$cuaddralt1:
+RECURRENCE-ID:20060323T200000Z
+RRULE:FREQ=WEEKLY;COUNT=20
+SUMMARY:TC Realtime
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR

Added: CalDAVTester/branches/users/cdaboo/better-itip-1837/scripts/tests/schedulepostautorecur.xml
===================================================================
--- CalDAVTester/branches/users/cdaboo/better-itip-1837/scripts/tests/schedulepostautorecur.xml	                        (rev 0)
+++ CalDAVTester/branches/users/cdaboo/better-itip-1837/scripts/tests/schedulepostautorecur.xml	2007-09-04 16:59:39 UTC (rev 1839)
@@ -0,0 +1,640 @@
+<?xml version="1.0" standalone="no"?>
+
+<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd">
+
+<!--
+ Copyright (c) 2006-2007 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.
+
+ DRI: Cyrus Daboo, cdaboo at apple.com
+ -->
+
+<caldavtest>
+	<description>Test iTIP with recurrence instances</description>
+
+	<start>
+		<request>
+			<method>DELETEALL</method>
+			<ruri>$pathprefix:/$outbox:/</ruri>
+		</request>
+		<request>
+			<method>DELETEALL</method>
+			<ruri>$pathprefix:/$inbox:/</ruri>
+		</request>
+		<request user="$useradmin:" pswd="$pswdadmin:">
+			<method>DELETEALL</method>
+			<ruri>$rpathprefix1:/$outbox:/</ruri>
+		</request>
+		<request user="$useradmin:" pswd="$pswdadmin:">
+			<method>DELETEALL</method>
+			<ruri>$rpathprefix1:/$inbox:/</ruri>
+		</request>
+		<request user="$useradmin:" pswd="$pswdadmin:">
+			<method>DELETEALL</method>
+			<ruri>$rpathprefix1:/calendar/</ruri>
+		</request>
+	</start>
+	
+	<test-suite name='POST recurring' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/1.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>6</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/2.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>1</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST cancel one instance' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/3.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>4</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/4.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>1</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST cancel multiple instances' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/5.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>4</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/6.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>1</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST cancel wrong SEQUENCE' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/7.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>4</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/6.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>1</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST cancel no match UID' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/8.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>4</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>1</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>1</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST recurring with overridden instances' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/9.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>6</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/10.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>2</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST cancel one overridden instance' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/11.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>4</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/12.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>2</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<test-suite name='POST cancel multiple instances' ignore='no'>
+		<test name='1' ignore='no'>
+			<description>Do POST</description>
+			<request>
+				<method>POST</method>
+				<header>
+					<name>Originator</name>
+					<value>$cuaddralt1:</value>
+				</header>
+				<header>
+					<name>Recipient</name>
+					<value>$rcuaddralt1:</value>
+				</header>
+				<ruri>$pathprefix:/$outbox:/</ruri>
+				<data>
+					<content-type>text/calendar; charset=utf-8</content-type>
+					<filepath>Resource/scheduleautorecur/13.ics</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELAY</method>
+				<ruri>4</ruri>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>No items in resource01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$rpathprefix1:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>0</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='3' ignore='no'>
+			<description>One item in resource01 Calendar</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>GETNEW</method>
+				<ruri>$rpathprefix1:/calendar/</ruri>
+				<verify>
+					<callback>dataMatch</callback>
+					<arg>
+						<name>filepath</name>
+						<value>Resource/scheduleautorecur/14.ics</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+		<test name='4' ignore='no'>
+			<description>One item in user01 Inbox</description>
+			<request user="$useradmin:" pswd="$pswdadmin:" print-response="no">
+				<method>PROPFIND</method>
+				<ruri>$pathprefix:/$inbox:/</ruri>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>count</name>
+						<value>2</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
+
+	<end>
+		<request>
+			<method>DELETEALL</method>
+			<ruri>$pathprefix:/$outbox:/</ruri>
+		</request>
+		<request>
+			<method>DELETEALL</method>
+			<ruri>$pathprefix:/$inbox:/</ruri>
+		</request>
+		<request user="$useradmin:" pswd="$pswdadmin:">
+			<method>DELETEALL</method>
+			<ruri>$rpathprefix1:/$outbox:/</ruri>
+		</request>
+		<request user="$useradmin:" pswd="$pswdadmin:">
+			<method>DELETEALL</method>
+			<ruri>$rpathprefix1:/$inbox:/</ruri>
+		</request>
+		<request user="$useradmin:" pswd="$pswdadmin:">
+			<method>DELETEALL</method>
+			<ruri>$rpathprefix1:/calendar/</ruri>
+		</request>
+	</end>
+	
+</caldavtest>

Modified: CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/ical.py	2007-09-01 19:13:55 UTC (rev 1838)
+++ CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/ical.py	2007-09-04 16:59:39 UTC (rev 1839)
@@ -46,7 +46,7 @@
 from twisted.web2.stream import IStream
 from twisted.web2.dav.util import allDataFromStream
 
-from twistedcaldav.dateops import normalizeToUTC, timeRangesOverlap
+from twistedcaldav.dateops import compareDateTime, normalizeToUTC, timeRangesOverlap
 from twistedcaldav.instance import InstanceList
 
 iCalendarProductID = "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"
@@ -309,7 +309,7 @@
         
         return type
     
-    def mainComponent(self):
+    def mainComponent(self, allow_multiple=False):
         """
         Return the primary iCal component in this calendar.
         @return: the L{Component} of the primary type.
@@ -321,13 +321,51 @@
         for component in self.subcomponents():
             if component.name() == "VTIMEZONE":
                 continue
-            elif (result is not None):
+            elif not allow_multiple and (result is not None):
                 raise ValueError("Calendar contains more than one primary component: %r" % (self,))
             else:
                 result = component
+                if allow_multiple:
+                    break
         
         return result
     
+    def masterComponent(self):
+        """
+        Return the master iCal component in this calendar.
+        @return: the L{Component} for the master component,
+            or C{None} if there isn't one.
+        """
+        assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
+        
+        for component in self.subcomponents():
+            if component.name() == "VTIMEZONE":
+                continue
+            if not component.hasProperty("RECURRENCE-ID"):
+                return component
+        
+        return None
+    
+    def overriddenComponent(self, recurrence_id):
+        """
+        Return the overridden iCal component in this calendar matching the supplied RECURRENCE-ID property.
+
+        @param recurrence_id: The RECURRENCE-ID property value to match.
+        @type recurrence_id: L{datetime.datetime} or L{datetime.date}
+        @return: the L{Component} for the overridden component,
+            or C{None} if there isn't one.
+        """
+        assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
+        
+        for component in self.subcomponents():
+            if component.name() == "VTIMEZONE":
+                continue
+            rid = component.getRecurrenceIDUTC()
+            if rid and compareDateTime(rid, recurrence_id) == 0:
+                return component
+        
+        return None
+    
     def duplicate(self):
         """
         Duplicate this object and all its contents.
@@ -355,6 +393,13 @@
         self._vobject.add(component._vobject)
         component._parent = self
 
+    def removeComponent(self, component):
+        """
+        Removes a subcomponent from this component.
+        @param component: the L{Component} to remove.
+        """
+        self._vobject.remove(component._vobject)
+
     def hasProperty(self, name):
         """
         @param name: the name of the property whose existence is being tested.
@@ -982,6 +1027,37 @@
 
         return None
 
+    def getAttendeeProperties(self, match):
+        """
+        Get all the attendees matching a value in each component. Works on a VCALENDAR component only.
+        
+        @param match: a C{list} of calendar user address strings to try and match.
+        @return: the string value of the Organizer property, or None
+        """
+        
+        assert self.name() == "VCALENDAR", "Not a calendar: %r" % (self,)
+
+        # FIXME: we should really have a URL class and have it manage comparisons
+        # in a sensible fashion.
+        def _normalizeCUAddress(addr):
+            if addr.startswith("/") or addr.startswith("http:") or addr.startswith("https:"):
+                return addr.rstrip("/")
+            else:
+                return addr
+
+        # Need to normalize http/https cu addresses
+        test = set()
+        for item in match:
+           test.add(_normalizeCUAddress(item))
+        
+        # Extract appropriate sub-component if this is a VCALENDAR
+        results = []
+        for component in self.subcomponents():
+            if component.name() != "VTIMEZONE":
+                results.append(component.getAttendeeProperty(match))
+
+        return results
+
     def getMaskUID(self):
         """
         Get the X-CALENDARSEREVR-MASK-UID value. Works on either a VCALENDAR or on a component.

Modified: CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/itip.py	2007-09-01 19:13:55 UTC (rev 1838)
+++ CalendarServer/branches/users/cdaboo/better-itip-1835/twistedcaldav/itip.py	2007-09-04 16:59:39 UTC (rev 1839)
@@ -75,7 +75,7 @@
     
     method = calendar.propertyValue("METHOD")
     if method == "REQUEST":
-        f = processRequest
+        f = processRequest2
     elif method == "ADD":
         f = processAdd
     elif method == "CANCEL":
@@ -151,22 +151,22 @@
         doreply, replycal, accepted = d.getResult()
         if calmatch:
             # See whether the current component is older than any existing ones and throw it away if so
-            cal = updatecal.iCalendar(calmatch[0])
-            info = getSyncInfo(calmatch[0], cal)
+            cal = updatecal.iCalendar(calmatch)
+            info = getSyncInfo(calmatch, cal)
             if compareSyncInfo(info, newinfo) < 0:
                 # Re-write existing resource with new one, if accepted, otherwise delete existing as the
                 # update to it was not accepted.
                 try:
                     if accepted:
-                        newchild = waitForDeferred(writeResource(request, calURL, updatecal, calmatch[0], calendar))
+                        newchild = waitForDeferred(writeResource(request, calURL, updatecal, calmatch, calendar))
                         yield newchild
                         newchild = newchild.getResult()
-                        logging.info("[ITIP]: replaced calendar component %s with new iTIP message in %s." % (calmatch[0], calURL))
+                        logging.info("[ITIP]: replaced calendar component %s with new iTIP message in %s." % (calmatch, calURL))
                     else:
-                        d = waitForDeferred(deleteResource(updatecal, calmatch[0]))
+                        d = waitForDeferred(deleteResource(updatecal, calmatch))
                         yield d
                         d.getResult()
-                        logging.info("[ITIP]: deleted calendar component %s in %s as update was not accepted." % (calmatch[0], calURL))
+                        logging.info("[ITIP]: deleted calendar component %s in %s as update was not accepted." % (calmatch, calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
                     raise iTipException
@@ -177,7 +177,7 @@
                     d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
                     yield d
                     d.getResult()
-                    logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch[0], calURL))
+                    logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch, calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
                     raise iTipException
@@ -224,6 +224,182 @@
 
 processRequest = deferredGenerator(processRequest)
 
+def processRequest2(request, principal, inbox, calendar, child):
+    """
+    Process a METHOD=REQUEST.
+    This is a deferredGenerator function so use yield whenever we have a deferred.
+
+    Steps:
+    
+      1. See if this updates existing ones in Inbox.
+          1. If so,
+              1. Remove existing ones in Inbox.
+              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
+          2. If not,
+              1. remove the one we got - its 'stale'
+          3. Delete the request from the Inbox.
+    
+    @param request: the L{twisted.web2.server.Request} for the current request.
+    @param principal: the L{CalendarPrincipalFile} principal resource for the principal we are dealing with.
+    @param inbox: the L{ScheduleInboxFile} for the principal's Inbox.
+    @param calendar: the L{Component} for the iTIP message we are processing.
+    @param child: the L{CalDAVFile} for the iTIP message resource already saved to the Inbox.
+    """
+    
+    logging.info("[ITIP]: Auto-processing iTIP REQUEST for: %s" % (str(principal),))
+    processed = "ignored"
+
+    # First determine whether this is a full or partial update. A full update is one containing the master
+    # component in a recurrence set (or non-recurring event). Partial is one where overridden instances only are
+    # being changed.
+    
+    new_master = calendar.masterComponent()
+
+    # Next we want to try and find a match to any components on existing calendars listed as contributing
+    # to free-busy as we will need to update those with the new one.
+    d = waitForDeferred(findCalendarMatch(request, principal, calendar))
+    yield d
+    calmatch, updatecal, calURL = d.getResult()
+    
+    if new_master:
+        # So we have a full update. That means we need to delete any existing events completely and
+        # replace with the ones provided so long as the new one is newer.
+        
+        # If we have a match then we need to check whether we are updating etc
+        check_reply = False
+        if calmatch:
+            # See whether the new component is older than any existing ones and throw it away if so
+            newinfo = (None,) + getComponentSyncInfo(new_master)
+            cal = updatecal.iCalendar(calmatch)
+            info = getSyncInfo(calmatch, cal)
+            if compareSyncInfo(info, newinfo) < 0:
+                # Existing resource is older and will be replaced
+                check_reply = True
+            else:
+                processed = "older"
+        else:
+            # We have a new request which we can reply to
+            check_reply = True
+            
+        if check_reply:
+            # Process the reply by determining PARTSTAT and sending the reply and booking the event.
+            d = waitForDeferred(checkForReply2(request, principal, calendar))
+            yield d
+            doreply, replycal, accepted = d.getResult()
+            
+            try:
+                if accepted:
+                    if calmatch:
+                        newchild = waitForDeferred(writeResource(request, calURL, updatecal, calmatch, calendar))
+                        yield newchild
+                        newchild = newchild.getResult()
+                        logging.info("[ITIP]: replaced calendar component %s with new iTIP message in %s." % (calmatch, calURL))
+                    else:
+                        newchild = waitForDeferred(writeResource(request, calURL, updatecal, None, calendar))
+                        yield newchild
+                        newchild.getResult()
+                        logging.info("[ITIP]: added new calendar component in %s." % (calURL,))
+                else:
+                    if calmatch:
+                        d = waitForDeferred(deleteResource(updatecal, calmatch))
+                        yield d
+                        d.getResult()
+                        logging.info("[ITIP]: deleted calendar component %s in %s as update was not accepted." % (calmatch, calURL))
+                        
+                # If we get here we have a new iTIP message that we want to process. Any previous ones
+                # have been removed (so we won't run in to problems when we check that there is free time
+                # to book the new one). 
+                if doreply:
+                    logging.info("[ITIP]: sending iTIP REPLY %s" % (("declined","accepted")[accepted],))
+                    newchild = waitForDeferred(writeReply(request, principal, replycal, inbox))
+                    yield newchild
+                    newchild = newchild.getResult()
+                    newInboxResource(child, newchild)
+                processed = "processed"
+            except:
+                log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
+                raise iTipException
+            
+    else:
+        # So we have a partial update. That means we have to do partial updates to instances in
+        # the existing calendar component.
+
+        # If we have a match then we need to check whether we are updating etc
+        check_reply = False
+        if calmatch:
+            # See whether the new component is older than any existing ones and throw it away if so
+            newinfo = getSyncInfo(calendar)
+            cal = updatecal.iCalendar(calmatch)
+            info = getSyncInfo(calmatch, cal)
+            if compareSyncInfo(info, newinfo) < 0:
+                # Existing resource is older and will be replaced
+                check_reply = True
+            else:
+                processed = "older"
+        else:
+            # We have a new request which we can reply to
+            check_reply = True
+
+        if check_reply:
+            # Process the reply by determining PARTSTAT and sending the reply and booking the event.
+            d = waitForDeferred(checkForReply2(request, principal, calendar))
+            yield d
+            doreply, replycal, accepted = d.getResult()
+            
+            try:
+                if calmatch:
+                    # Merge the new instances with the old ones
+                    mergeComponents(calendar, cal)
+                    newchild = waitForDeferred(writeResource(request, calURL, updatecal, calmatch, cal))
+                    yield newchild
+                    newchild = newchild.getResult()
+                    logging.info("[ITIP]: merged calendar component %s with new iTIP message in %s." % (calmatch, calURL))
+                else:
+                    if accepted:
+                        newchild = waitForDeferred(writeResource(request, calURL, updatecal, None, calendar))
+                        yield newchild
+                        newchild.getResult()
+                        logging.info("[ITIP]: added new calendar component in %s." % (calURL,))
+                        
+                # If we get here we have a new iTIP message that we want to process. Any previous ones
+                # have been removed (so we won't run in to problems when we check that there is free time
+                # to book the new one). 
+                if doreply:
+                    logging.info("[ITIP]: sending iTIP REPLY %s" % (("declined","accepted")[accepted],))
+                    newchild = waitForDeferred(writeReply(request, principal, replycal, inbox))
+                    yield newchild
+                    newchild = newchild.getResult()
+                    newInboxResource(child, newchild)
+                    
+                processed = "processed"
+            except:
+                log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
+                raise iTipException
+
+    # Remove the now processed incoming request.
+    try:
+        d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
+        yield d
+        d.getResult()
+        logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it has been %s." %
+            (child.fp.basename(),
+             {"processed":"processed",
+              "older":    "ignored: older",
+              "ignored":  "ignored: no match"}[processed],))
+    except:
+        log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
+        raise iTipException
+    yield None
+    return
+
+processRequest2 = deferredGenerator(processRequest2)
+
 def processAdd(request, principal, inbox, calendar, child):
     """
     Process a METHOD=ADD.
@@ -277,6 +453,7 @@
     """
     
     logging.info("[ITIP]: Auto-processing iTIP CANCEL for: %s" % (str(principal),))
+    processed = "ignored"
 
     # Get all component info for this iTIP message
     newinfo = getSyncInfo(child.fp.basename(), calendar)
@@ -311,43 +488,99 @@
         # If we have a match then we need to check whether we are updating etc
         if calmatch:
             # See whether the current component is older than any existing ones and throw it away if so
-            cal = updatecal.iCalendar(calmatch[0])
-            info = getSyncInfo(calmatch[0], cal)
+            cal = updatecal.iCalendar(calmatch)
+            info = getSyncInfo(calmatch, cal)
             if compareSyncInfo(info, newinfo) < 0:
                 # Delete existing resource which has been cancelled
                 try:
-                    d = waitForDeferred(deleteResource(updatecal, calmatch[0],))
+                    d = waitForDeferred(deleteResource(updatecal, calmatch,))
                     yield d
                     d.getResult()
-                    logging.info("[ITIP]: delete calendar component %s in %s as it was cancelled." % (calmatch[0], calURL))
+                    logging.info("[ITIP]: delete calendar component %s in %s as it was cancelled." % (calmatch, calURL))
                 except:
                     log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
                     raise iTipException
+                processed = "processed"
             else:
-                # Delete new one in Inbox as it is old
-                try:
-                    d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
-                    yield d
-                    d.getResult()
-                    logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it was older than %s in %s." % (child.fp.basename(), calmatch[0], calURL))
-                except:
-                    log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
-                    raise iTipException
-                yield None
-                return
+                processed = "older"
         else:
-            # Nothing to do
-            pass
+            # Nothing to do except delete the inbox item as we have nothing to cancel.
+            processed = "ignored"
     else:
-        raise NotImplementedError
+        # Try and find a match to any components on existing calendars listed as contributing
+        # to free-busy as we will need to update those with the new one.
+        d = waitForDeferred(findCalendarMatch(request, principal, calendar))
+        yield d
+        calmatch, updatecal, calURL = d.getResult()
+        
+        # If we have a match then we need to check whether we are updating etc
+        if calmatch:
+            # iTIP CANCEL can contain multiple components being cancelled in the RECURRENCE-ID case.
+            # So we need to iterate over each iTIP component.
 
-        
+            # Get the existing calendar object
+            existing_calendar = updatecal.iCalendar(calmatch)
+            existing_master = existing_calendar.masterComponent()
+            exdates = []
+            max_sequence = existing_master.propertyValue("SEQUENCE")
+            if max_sequence is None:
+                max_sequence = 0
+
+            for component in calendar.subcomponents():
+                if component.name() == "VTIMEZONE":
+                    continue
+            
+                # Find matching component in existing calendar
+                old_component = findMatchingComponent(component, existing_calendar)
+                
+                if old_component:
+                    # We are cancelling an overridden component, so we need to check the
+                    # SEQUENCE/DTSAMP with the master.
+                    if compareComponents(old_component, component) < 0:
+                        # Exclude the cancelled instance
+                        exdates.append(component.getRecurrenceIDUTC())
+                        max_sequence = max(max_sequence, component.propertyValue("SEQUENCE"))
+                        
+                        # Remove the existing component.
+                        existing_calendar.removeComponent(old_component)
+                else:
+                    # We are trying to CANCEL a non-overridden instance, so we need to
+                    # check SEQUENCE/DTSTAMP with the master.
+                    if compareComponents(existing_master, component) < 0:
+                        # Exclude the cancelled instance
+                        exdates.append(component.getRecurrenceIDUTC())
+                        max_sequence = max(max_sequence, component.propertyValue("SEQUENCE"))
+
+            # If we have any EXDATEs lets add them to the existing calendar object and write
+            # it back.
+            if exdates:
+                existing_master.addProperty(Property("EXDATE", exdates))
+                seq = existing_master.getProperty("SEQUENCE")
+                if seq:
+                    seq.setValue(max_sequence)
+                else:
+                    existing_master.addProperty(Property("SEQUENCE", max_sequence))
+                newchild = waitForDeferred(writeResource(request, calURL, updatecal, calmatch, existing_calendar))
+                yield newchild
+                newchild = newchild.getResult()
+                logging.info("[ITIP]: updated calendar component %s with cancellations from iTIP message in %s." % (calmatch, calURL))
+                processed = "processed"
+            else:
+                processed = "older"
+        else:
+            # Nothing to do except delete the inbox item as we have nothing to cancel.
+            processed = "ignored"
+
     # Remove the now processed incoming request.
     try:
         d = waitForDeferred(deleteResource(inbox, child.fp.basename()))
         yield d
         d.getResult()
-        logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it has been processed." % (child.fp.basename(),))
+        logging.info("[ITIP]: deleted new iTIP message %s in Inbox because it has been %s." %
+            (child.fp.basename(),
+             {"processed":"processed",
+              "older":    "ignored: older",
+              "ignored":  "ignored: no match"}[processed],))
     except:
         log.err("Error while auto-processing iTIP: %s" % (failure.Failure(),))
         raise iTipException
@@ -469,6 +702,129 @@
 
 checkForReply = deferredGenerator(checkForReply)
 
+def checkForReply2(request, principal, calendar):
+    """
+    Check whether a reply to the given iTIP message is needed. A reply will be needed if the
+    RSVP=TRUE. A reply will either be positive (accepted
+    invitation) or negative (denied invitation). In addition we will modify calendar to reflect
+    any new state (e.g. remove RSVP, set PARTSTAT to ACCEPTED or DECLINED).
+    
+    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 isntances as a DECLINE of the entire set.
+
+    @param request: the L{twisted.web2.server.Request} for the current request.
+    @param principal: the L{CalendarPrincipalFile} principal resource for the principal we are dealing with.
+    @param calendar: the L{Component} for the iTIP message we are processing.
+    @return: C{True} if a reply is needed, C{False} otherwise.
+    """
+    
+    # We need to fugure out whether the specified component will clash with any others in the f-b-set calendars
+    accepted = True
+        
+    # First expand current one to get instances (only go 1 year into the future)
+    default_future_expansion_duration = datetime.timedelta(days=356*1)
+    expand_max = datetime.date.today() + default_future_expansion_duration
+    instances = calendar.expandTimeRanges(expand_max)
+    
+    # Extract UID from primary component as we want to ignore this one if we match it
+    # in any calendars.
+    comp = calendar.mainComponent(allow_multiple=True)
+    uid = comp.propertyValue("UID")
+
+    # Now compare each instance time-range with the index and see if there is an overlap
+    fbset = waitForDeferred(principal.calendarFreeBusyURIs(request))
+    yield fbset
+    fbset = fbset.getResult()
+
+    for calURL in fbset:
+        testcal = waitForDeferred(request.locateResource(calURL))
+        yield testcal
+        testcal = testcal.getResult()
+        
+        # First list is BUSY, second BUSY-TENTATIVE, third BUSY-UNAVAILABLE
+        fbinfo = ([], [], [])
+        
+        # Now do search for overlapping time-range
+        for instance in instances.instances.itervalues():
+            try:
+                tr = caldavxml.TimeRange(start="20000101", end="20000101")
+                tr.start = instance.start
+                tr.end = instance.end
+                d = waitForDeferred(report_common.generateFreeBusyInfo(request, testcal, fbinfo, tr, 0, uid))
+                yield d
+                d.getResult()
+                
+                # If any fbinfo entries exist we have an overlap
+                if len(fbinfo[0]) or len(fbinfo[1]) or len(fbinfo[2]):
+                    accepted = False
+                    break
+            except NumberOfMatchesWithinLimits:
+                accepted = False
+                logging.info("[ITIP]: exceeded number of matches whilst trying to find free-time.")
+                break
+            
+        if not accepted:
+            break
+     
+    # Extract the ATTENDEE property matching current recipient from the calendar data
+    cuas = principal.calendarUserAddresses()
+    attendeeProps = calendar.getAttendeeProperties(cuas)
+    if not attendeeProps:
+        yield False, None, accepted
+        return
+
+    # Look for specific parameters
+    rsvp = False
+    for attendeeProp in attendeeProps:
+        if "RSVP" in attendeeProp.params():
+            if attendeeProp.params()["RSVP"][0] == "TRUE":
+                rsvp = True
+    
+            # Now modify the original component
+            del attendeeProp.params()["RSVP"]
+
+    if accepted:
+        partstat = "ACCEPTED"
+    else:
+        partstat = "DECLINED"
+    for attendeeProp in attendeeProps:
+        if "PARTSTAT" in attendeeProp.params():
+            attendeeProp.params()["PARTSTAT"][0] = partstat
+        else:
+            attendeeProp.params()["PARTSTAT"] = [partstat]
+    
+    # Now create a new calendar object for the reply
+    
+    # First get useful props from the original
+    replycal = 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."))
+
+    # 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()):
+                replycal.mainComponent().removeProperty(attendee)
+
+    yield rsvp, replycal, accepted
+
+checkForReply2 = deferredGenerator(checkForReply2)
+
 def writeReply(request, principal, replycal, ainbox):
     """
     Write an iTIP message reply into the specified Inbox.
@@ -535,7 +891,8 @@
     itipper = True
     if collection.isCalendarCollection():
         method = calendar.getProperty("METHOD")
-        calendar.removeProperty(method)
+        if method:
+            calendar.removeProperty(method)
         itipper = False
     
     # Now write it to the resource
@@ -679,47 +1036,91 @@
             # We will ignore missing calendars. If the recipient has failed to
             # properly manage the free busy set that should not prevent us from working.
             continue
-        calmatch = matchComponentInCalendar(updatecal, calendar, None)
+        calmatch = matchComponentInCalendar(updatecal, calendar)
         if calmatch:
-            logging.info("[ITIP]: found calendar component %s matching new iTIP message in %s." % (calmatch[0], calURL))
+            logging.info("[ITIP]: found calendar component %s matching new iTIP message in %s." % (calmatch, calURL))
             break
     
-    if not calmatch and len(fbset):
+    if calmatch is None and len(fbset):
         calURL = fbset[0]
+        updatecal = waitForDeferred(request.locateResource(calURL))
+        yield updatecal
+        updatecal = updatecal.getResult()
 
     yield calmatch, updatecal, calURL
 
 findCalendarMatch = deferredGenerator(findCalendarMatch)    
 
-def matchComponentInCalendar(collection, calendar, ignore):
+def matchComponentInCalendar(collection, calendar):
     """
     See if the component in the provided iTIP calendar object matches any in the specified calendar
-    collectrion, excluding the resource provided.
+    collection.
     
     @param collection: L{CalDAVFile} for the calendar collection to examine.
     @param calendar: L{Component} for calendar to examine.
-    @param ignore: L{CalDAVFile} to ignore if found, or C{None} if none to ignore.
     @return: C{list} of resource names found.
     """
 
-    result = []
     try:
-        # Extract UID from primary component
-        comp = calendar.mainComponent()
+        # Extract UID from primary component (note we allow multiple components to be present
+        # because CANCEL requests can have multiple components).
+        comp = calendar.mainComponent(allow_multiple=True)
         uid = comp.propertyValue("UID")
         
         # Now use calendar collection index to find all other resources with the same UID
         index = collection.index()
         result = index.resourceNamesForUID(uid)
         
-        # Remove the one we want to ignore
-        if ignore is not None:
-            result = [name for name in result if name != ignore.fp.basename()]
+        # There can be only one
+        if len(result) > 0: 
+            return result[0]
+        else:
+            return None
     except ValueError:
-        return []
+        return None
+
+def findMatchingComponent(component, calendar):
+    """
+    See if any overridden component in the provided iTIP calendar object matches the specified component.
     
-    return result
+    @param component: the component to try and match.
+    @type component: L{Component}
+    @param calendar: the calendar to find a match in.
+    @type calendar: L{Component}
+    @return: L{Component} for matching component,
+        or C{None} if not found.
+    """
 
+    # Extract RECURRENCE-ID value from component
+    rid = component.getRecurrenceIDUTC()
+    
+    # Return the one that matches in the calendar
+    return calendar.overriddenComponent(rid)
+
+def mergeComponents(newcal, oldcal):
+    """
+    Merge the overridden instance components in newcal into old cal replacing any
+    matching components there.
+
+    @param newcal: the new overridden instances to use.
+    @type newcal: L{Component}
+    @param oldcal: the component to merge into.
+    @type oldcal: L{Component}
+    """
+    
+    # FIXME: going to ignore VTIMEZONE - i.e. will assume that the component being added
+    # use a TZID that is already specified in the old component set.
+    
+    for component in newcal.subcomponents():
+        if component.name() == "VTIMEZONE":
+            continue
+        
+        rid = component.getRecurrenceIDUTC()
+        old_component = oldcal.overriddenComponent(rid)
+        if old_component:
+            oldcal.removeComponent(old_component)
+        oldcal.addComponent(component)
+
 def getAllInfo(collection, calendar, ignore):
     """
     Find each component in the calendar collection that has a matching UID with
@@ -734,8 +1135,9 @@
     """
     names = []
     try:
-        # Extract UID from primary component
-        comp = calendar.mainComponent()
+        # Extract UID from primary component (note we allow multiple components to be present
+        # because CANCEL requests can have multiple components).
+        comp = calendar.mainComponent(allow_multiple=True)
         uid = comp.propertyValue("UID")
         
         # Now use calendar collection index to find all other resources with the same UID
@@ -764,18 +1166,50 @@
     @return: C{tuple} of (uid, seq, dtstamp, r-id) some of which may be C{None} if property does not exist
     """
     try:
-        # Extract items from primary component
-        comp = calendar.mainComponent()
-        uid = comp.propertyValue("UID")
-        seq = comp.propertyValue("SEQUENCE")
-        dtstamp = comp.propertyValue("DTSTAMP")
-        rid = comp.propertyValue("RECURRENCE-ID")
+        # Extract components from primary component (note we allow multiple components to be present
+        # because CANCEL requests can have multiple components).
+        comp = calendar.mainComponent(allow_multiple=True)
+        uid, seq, dtstamp, rid = getComponentSyncInfo(comp)
         
     except ValueError:
         return (name, None, None, None, None)
     
     return (name, uid, seq, dtstamp, rid)
 
+def getComponentSyncInfo(component):
+    """
+    Get property value details needed to synchronize iTIP components.
+    
+    @param component: L{Component} to check.
+    @return: C{tuple} of (uid, seq, dtstamp, r-id) some of which may be C{None} if property does not exist
+    """
+    try:
+        # Extract items from component
+        uid = component.propertyValue("UID")
+        seq = component.propertyValue("SEQUENCE")
+        dtstamp = component.propertyValue("DTSTAMP")
+        rid = component.propertyValue("RECURRENCE-ID")
+        
+    except ValueError:
+        return (None, None, None, None)
+    
+    return (uid, seq, dtstamp, rid)
+
+def compareComponents(component1, component2):
+    """
+    Compare synchronization information for two components to see if they match according to iTIP.
+
+    @param component1: first component to check.
+    @type component1: L{Component}
+    @param component2: second component to check.
+    @type component2: L{Component}
+    
+    @return: 0, 1, -1 as per compareSyncInfo.
+    """
+    info1 = (None,) + getComponentSyncInfo(component1)
+    info2 = (None,) + getComponentSyncInfo(component2)
+    return compareSyncInfo(info1, info2)
+
 def compareSyncInfo(info1, info2):
     """
     Compare two synchronization information records.
@@ -810,50 +1244,3 @@
         return -1
 
     return 0
-
-def updating(collection, names, calendar):
-    """
-    Check whether the specified calendar object is an iTIP message that is "newer" than the
-    others listed, or does not match the component type listed.
-    
-    @param collection: L{CalDAVFile} for the calendar collection to examine.
-    @param names: C{list} of C{str} for names of resources in the collection to check against.
-    @param calendar: L{Component} for calendar to check.
-    @return: C{True} if new component is an update and valid, C{False} otherwise.
-    """
-    
-    # First get useful sync-related info from existing component
-    uid, seq, dtstamp, rid = getSyncInfo(calendar)
-    
-    # Now get info from each named component and compare
-    for name in names:
-        cal = collection.iCalendar(name)
-        cuid, cseq, cdtstamp, crid = getSyncInfo(cal)
-        
-        # UIDs MUST match
-        assert uid == cuid
-        
-        # Look for sequence
-        if (cseq is not None) and (seq is not None):
-            if cseq > seq:
-                return False
-            if cseq < seq:
-                continue
-        elif (cseq is not None) and (seq is None):
-            return False
-        elif (cseq is None) and (seq is not None):
-            continue
-
-        # Look for DTSTAMP
-        if (cdtstamp is not None) and (dtstamp is not None):
-            if cdtstamp > dtstamp:
-                return False
-            if cdtstamp < dtstamp:
-                continue
-        elif (cdtstamp is not None) and (dtstamp is None):
-            return False
-        elif (cdtstamp is None) and (dtstamp is not None):
-            continue
-        
-    return True
-            

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070904/0c0b0346/attachment.html


More information about the calendarserver-changes mailing list