[CalendarServer-changes] [8916] CalendarServer/branches/users/wsanchez/d2

source_changes at macosforge.org source_changes at macosforge.org
Tue Mar 20 06:47:45 PDT 2012


Revision: 8916
          http://trac.macosforge.org/projects/calendarserver/changeset/8916
Author:   cdaboo at apple.com
Date:     2012-03-20 06:47:45 -0700 (Tue, 20 Mar 2012)
Log Message:
-----------
Pulling up r8607 to d2 branch.

Revision Links:
--------------
    http://trac.macosforge.org/projects/calendarserver/changeset/8607

Modified Paths:
--------------
    CalendarServer/branches/users/wsanchez/d2/twistedcaldav/ical.py
    CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/icaldiff.py
    CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/test/test_icaldiff.py
    CalendarServer/branches/users/wsanchez/d2/twistedcaldav/test/test_icalendar.py
    CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/test_sql.py

Added Paths:
-----------
    CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics

Property Changed:
----------------
    CalendarServer/branches/users/wsanchez/d2/


Property changes on: CalendarServer/branches/users/wsanchez/d2
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:8610,8628,8648,8714,8716,8724-8725,8727,8730-8737,8739,8749,8752,8754-8755,8764,8767,8810,8820-8821,8838
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:8607,8610,8628,8648,8714,8716,8724-8725,8727,8730-8737,8739,8749,8752,8754-8755,8764,8767,8810,8820-8821,8838

Modified: CalendarServer/branches/users/wsanchez/d2/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/wsanchez/d2/twistedcaldav/ical.py	2012-03-20 01:37:26 UTC (rev 8915)
+++ CalendarServer/branches/users/wsanchez/d2/twistedcaldav/ical.py	2012-03-20 13:47:45 UTC (rev 8916)
@@ -1,6 +1,6 @@
 # -*- test-case-name: twistedcaldav.test.test_icalendar -*-
 ##
-# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -1333,15 +1333,19 @@
             if None in all_rids:
                 all_rids.remove(None)
 
-            # Get the set of all valid recurrence IDs
-            valid_rids = self.validInstances(all_rids, ignoreInvalidInstances=True)
+            # If the master has no recurrence properties treat any other components as invalid
+            if master.isRecurring():
+                # Get the set of all valid recurrence IDs
+                valid_rids = self.validInstances(all_rids, ignoreInvalidInstances=True)
+    
+                # Get the set of all RDATEs and add those to the valid set
+                rdates = []
+                for property in master.properties("RDATE"):
+                    rdates.extend([_rdate.getValue() for _rdate in property.value()])
+                valid_rids.update(set(rdates))
+            else:
+                valid_rids = set()
 
-            # Get the set of all RDATEs and add those to the valid set
-            rdates = []
-            for property in master.properties("RDATE"):
-                rdates.extend([_rdate.getValue() for _rdate in property.value()])
-            valid_rids.update(set(rdates))
-
             # Determine the invalid recurrence IDs by set subtraction
             invalid_rids = all_rids - valid_rids
 

Modified: CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/icaldiff.py	2012-03-20 01:37:26 UTC (rev 8915)
+++ CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/icaldiff.py	2012-03-20 13:47:45 UTC (rev 8916)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -254,8 +254,8 @@
             except (ValueError, TypeError), ex:
                 log.err("Cannot truncate calendar resource: %s" % (ex,))
 
-        self.newCalendar = self.oldcalendar.duplicate()
-        self.newMaster = self.newCalendar.masterComponent()
+        returnCalendar = self.oldcalendar.duplicate()
+        returnMaster = returnCalendar.masterComponent()
 
         changeCausesReply = False
         changedRids = []
@@ -330,7 +330,7 @@
                 # Attendee may decline by EXDATE'ing an instance - we need to handle that
                 if exdatesnew is None or rid in exdatesnew:
                     # Mark Attendee as DECLINED in the server instance
-                    if self._attendeeDecline(self.newCalendar.overriddenComponent(rid)):
+                    if self._attendeeDecline(returnCalendar.overriddenComponent(rid)):
                         changeCausesReply = True
                         changedRids.append(rid.getText() if rid else "")
                 else:
@@ -344,12 +344,12 @@
                     self._logDiffError("attendeeMerge: Missing EXDATE for cancelled components from first calendar: %s" % (key,))
                 else:
                     # Remove the CANCELLED component from the new calendar and add an EXDATE
-                    overridden = self.newCalendar.overriddenComponent(rid)
-                    self.newCalendar.removeComponent(overridden)
-                    if self.newMaster:
+                    overridden = returnCalendar.overriddenComponent(rid)
+                    returnCalendar.removeComponent(overridden)
+                    if returnMaster:
                         # Use the original R-ID value so we preserve the timezone
                         original_rid = component.propertyValue("RECURRENCE-ID")
-                        self.newMaster.addProperty(Property("EXDATE", [original_rid,]))
+                        returnMaster.addProperty(Property("EXDATE", [original_rid,]))
         
         # Derive a new component in the new calendar for each new one in setnew
         for key in setnew - setold:
@@ -366,40 +366,40 @@
                     setnew.remove(key)
                 else:
                     # Derive new component with STATUS:CANCELLED and remove EXDATE
-                    newOverride = self.newCalendar.deriveInstance(rid, allowCancelled=True)
+                    newOverride = returnCalendar.deriveInstance(rid, allowCancelled=True)
                     if newOverride is None:
                         # We used to generate a 403 here - but instead we now ignore this error and let the server data
                         # override the client
                         self._logDiffError("attendeeMerge: Could not derive instance for cancelled component: %s" % (key,))
                         setnew.remove(key)
                     else:
-                        self.newCalendar.addComponent(newOverride)
+                        returnCalendar.addComponent(newOverride)
             else:
                 # Derive new component
-                newOverride = self.newCalendar.deriveInstance(rid)
+                newOverride = returnCalendar.deriveInstance(rid)
                 if newOverride is None:
                     # We used to generate a 403 here - but instead we now ignore this error and let the server data
                     # override the client
                     self._logDiffError("attendeeMerge: Could not derive instance for uncancelled component: %s" % (key,))
                     setnew.remove(key)
                 else:
-                    self.newCalendar.addComponent(newOverride)
+                    returnCalendar.addComponent(newOverride)
 
-        # So now newCalendar has all the same components as set2. Check changes and do transfers.
+        # So now returnCalendar has all the same components as set2. Check changes and do transfers.
         
         # Make sure the same VCALENDAR properties match
-        if not self._checkVCALENDARProperties(self.newCalendar, self.newcalendar):
+        if not self._checkVCALENDARProperties(returnCalendar, self.newcalendar):
             # We used to generate a 403 here - but instead we now ignore this error and let the server data
             # override the client
             self._logDiffError("attendeeMerge: VCALENDAR properties do not match")
 
         # Now we transfer per-Attendee
-        # data from newcalendar into newCalendar to sync up changes, whilst verifying that other
+        # data from newcalendar into returnCalendar to sync up changes, whilst verifying that other
         # key properties are unchanged
         declines = []
         for key in setnew:
             _ignore_name, _ignore_uid, rid = key
-            serverData = self.newCalendar.overriddenComponent(rid)
+            serverData = returnCalendar.overriddenComponent(rid)
             clientData = mapnew[key]
             
             allowed, reply = self._transferAttendeeData(serverData, clientData, declines)
@@ -414,11 +414,11 @@
 
         # We need to derive instances for any declined using an EXDATE
         for decline in sorted(declines):
-            overridden = self.newCalendar.overriddenComponent(decline)
+            overridden = returnCalendar.overriddenComponent(decline)
             if not overridden:
-                overridden = self.newCalendar.deriveInstance(decline)
+                overridden = returnCalendar.deriveInstance(decline)
                 if overridden:
-                    self.newCalendar.addComponent(overridden)
+                    returnCalendar.addComponent(overridden)
                     if self._attendeeDecline(overridden):
                         changeCausesReply = True
                         changedRids.append(decline.getText() if decline else "")
@@ -426,7 +426,7 @@
                     self._logDiffError("attendeeMerge: Unable to override an instance to mark as DECLINED: %s" % (decline,))
                     return False, False, (), None
 
-        return True, changeCausesReply, changedRids, self.newCalendar
+        return True, changeCausesReply, changedRids, returnCalendar
 
     def _checkVCALENDARProperties(self, serverData, clientData):
 
@@ -459,6 +459,12 @@
         # ATTENDEE/PARTSTAT/RSVP
         serverAttendee = serverComponent.getAttendeeProperty((self.attendee,))
         clientAttendee = clientComponent.getAttendeeProperty((self.attendee,))
+        
+        # Possible case where one ATTENDEE prop is missing - this happens with a "fake" master sometimes
+        if serverAttendee is None or clientAttendee is None:
+            log.err("ATTENDEE for user making an attendee change is missing: %s" % (self.attendee,))
+            return False, False
+    
         if serverAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION"):
             serverAttendee.setParameter("PARTSTAT", clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION"))
             replyNeeded = True
@@ -632,6 +638,12 @@
         @return: C{bool} indicating whether the PARTSTAT value was in fact changed
         """
         attendee = component.getAttendeeProperty((self.attendee,))
+
+        # Possible case where ATTENDEE prop is missing - this happens with a "fake" master sometimes
+        if attendee is None:
+            log.err("ATTENDEE for user making an attendee change is missing: %s" % (self.attendee,))
+            return False
+    
         partstatChanged = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != "DECLINED"
         attendee.setParameter("PARTSTAT", "DECLINED")
         prop = component.getProperty("X-APPLE-NEEDS-REPLY")

Modified: CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/test/test_icaldiff.py	2012-03-20 01:37:26 UTC (rev 8915)
+++ CalendarServer/branches/users/wsanchez/d2/twistedcaldav/scheduling/test/test_icaldiff.py	2012-03-20 13:47:45 UTC (rev 8916)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -3000,6 +3000,210 @@
                 "mailto:user2 at example.com",
                 (False, False, (), None,)
             ),
+            (
+                "#3.1 Single overridden component, fake master, missing attendee",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+RECURRENCE-ID:20080601T120000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+                "mailto:user2 at example.com",
+                (True, True, ('20080601T120000Z',), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
+            ),
+            (
+                "#3.2 Single overridden component, fake master, missing attendee in old",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+RECURRENCE-ID:20080601T120000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+                "mailto:user2 at example.com",
+                (True, True, ('20080601T120000Z',), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
+            ),
+            (
+                "#3.3 Single overridden component, fake master, missing attendee in new",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+RECURRENCE-ID:20080601T120000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+                "mailto:user2 at example.com",
+                (True, True, ('20080601T120000Z',), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
+            ),
         )
 
         for description, calendar1, calendar2, attendee, result in data:

Modified: CalendarServer/branches/users/wsanchez/d2/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/branches/users/wsanchez/d2/twistedcaldav/test/test_icalendar.py	2012-03-20 01:37:26 UTC (rev 8915)
+++ CalendarServer/branches/users/wsanchez/d2/twistedcaldav/test/test_icalendar.py	2012-03-20 13:47:45 UTC (rev 8916)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -4062,6 +4062,28 @@
                     (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
                 )
             ),
+            (
+                "1.12 - fake master OK",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071114T000000Z
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+                (
+                    (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+                )
+            ),
         )
         
         for clear_cache in (True, False):
@@ -4078,6 +4100,55 @@
             actual_results = ical.validInstances(rids)
             self.assertEqual(actual_results, expected_results, "Failed comparison: %s %s" % (title, actual_results,))
 
+    def test_valid_recurrence_ids(self):
+        
+        data = (
+            (
+                "1.1 - fake master",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071114T000000Z
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+RDATE:20071114T000000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071114T000000Z
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+            1, 0,
+            ),
+        )
+        
+        for title, calendar, result_calendar, result_fixed, result_unfixed in data:
+            ical = Component.fromString(calendar)
+            fixed, unfixed = ical.validRecurrenceIDs(doFix=True)
+            self.assertEqual(str(ical), result_calendar.replace("\n", "\r\n"), "Failed comparison: %s %s" % (title, str(ical),))
+            self.assertEqual(len(fixed), result_fixed, "Failed fixed comparison: %s %s" % (title, fixed,))
+            self.assertEqual(len(unfixed), result_unfixed, "Failed unfixed: %s %s" % (title, unfixed,))
+
     def test_mismatched_until(self):
         invalid = (
             """BEGIN:VCALENDAR

Copied: CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics (from rev 8607, CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics)
===================================================================
--- CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics	                        (rev 0)
+++ CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics	2012-03-20 13:47:45 UTC (rev 8916)
@@ -0,0 +1,42 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US/Eastern
+LAST-MODIFIED:20040110T032845Z
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:uid4
+DTSTART;TZID=US/Eastern:20060104T160000
+DURATION:PT1H
+CREATED:20060102T190000Z
+DESCRIPTION:Some notes
+DTSTAMP:20051222T210507Z
+SUMMARY:event 6-%ctr changed again
+END:VEVENT
+BEGIN:VEVENT
+UID:uid4
+RECURRENCE-ID;TZID=US/Eastern:20060104T160000
+DTSTART;TZID=US/Eastern:20060104T160000
+DURATION:PT1H
+CREATED:20060102T190000Z
+DESCRIPTION:Some notes
+DTSTAMP:20051222T210507Z
+SUMMARY:event 6-%ctr changed again
+END:VEVENT
+END:VCALENDAR

Modified: CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/test_sql.py	2012-03-20 01:37:26 UTC (rev 8915)
+++ CalendarServer/branches/users/wsanchez/d2/txdav/caldav/datastore/test/test_sql.py	2012-03-20 13:47:45 UTC (rev 8916)
@@ -189,7 +189,7 @@
         toCalendar = yield toHome.calendarWithName("calendar")
         ok, bad = (yield _migrateCalendar(fromCalendar, toCalendar,
                                lambda x: x.component()))
-        self.assertEqual(ok, 2)
+        self.assertEqual(ok, 3)
         self.assertEqual(bad, 0)
 
         self.transactionUnderTest().commit()
@@ -302,6 +302,53 @@
 END:VCALENDAR
 """.replace("\n", "\r\n"))
         
+        toResource = yield toCalendar.calendarObjectWithName("3.ics")
+        caldata = yield toResource.component()
+        self.assertEqual(str(caldata), """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US/Eastern
+LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:uid4
+DTSTART;TZID=US/Eastern:20060104T160000
+DURATION:PT1H
+CREATED:20060102T190000Z
+DESCRIPTION:Some notes
+DTSTAMP:20051222T210507Z
+RDATE;TZID=US/Eastern:20060104T160000
+SUMMARY:event 6-%ctr changed again
+END:VEVENT
+BEGIN:VEVENT
+UID:uid4
+RECURRENCE-ID;TZID=US/Eastern:20060104T160000
+DTSTART;TZID=US/Eastern:20060104T160000
+DURATION:PT1H
+CREATED:20060102T190000Z
+DESCRIPTION:Some notes
+DTSTAMP:20051222T210507Z
+SUMMARY:event 6-%ctr changed again
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"))
+        
     @inlineCallbacks
     def test_migrateDuplicateAttachmentsCalendarFromFile(self):
         """
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120320/2bdcd9cc/attachment-0001.html>


More information about the calendarserver-changes mailing list