[CalendarServer-changes] [9905] CalendarServer/branches/release/CalendarServer-4.2-dev

source_changes at macosforge.org source_changes at macosforge.org
Mon Oct 8 14:17:08 PDT 2012


Revision: 9905
          http://trac.calendarserver.org//changeset/9905
Author:   cdaboo at apple.com
Date:     2012-10-08 14:17:08 -0700 (Mon, 08 Oct 2012)
Log Message:
-----------
Merge r9904 from trunk.

Revision Links:
--------------
    http://trac.calendarserver.org//changeset/9904

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/ical.py
    CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/scheduling/scheduler.py
    CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/test/test_icalendar.py

Property Changed:
----------------
    CalendarServer/branches/release/CalendarServer-4.2-dev/


Property changes on: CalendarServer/branches/release/CalendarServer-4.2-dev
___________________________________________________________________
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/case-insensitive-uid:8772-8805
/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/ipv6-client:9054-9105
/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/q:9560-9688
/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/sharing-api:9192-9205
/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/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/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:9867,9870,9876,9895,9899,9901
   + /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/case-insensitive-uid:8772-8805
/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/ipv6-client:9054-9105
/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/q:9560-9688
/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/sharing-api:9192-9205
/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/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/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:9867,9870,9876,9895,9899,9901,9904

Modified: CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/ical.py	2012-10-08 18:59:17 UTC (rev 9904)
+++ CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/ical.py	2012-10-08 21:17:08 UTC (rev 9905)
@@ -3149,6 +3149,59 @@
 # Utilities
 ##
 
+def normalizeCUAddress(cuaddr, lookupFunction, principalFunction, toUUID=True):
+    # Check that we can lookup this calendar user address - if not
+    # we cannot do anything with it
+    _ignore_name, guid, cuaddrs = lookupFunction(normalizeCUAddr(cuaddr), principalFunction, config)
+
+    if toUUID:
+        # Always re-write value to urn:uuid
+        if guid:
+            return "urn:uuid:%s" % (guid,)
+
+    # If it is already a non-UUID address leave it be
+    elif cuaddr.startswith("urn:uuid:"):
+
+        # Pick the first mailto,
+        # or failing that the first path one,
+        # or failing that the first http one,
+        # or failing that the first one
+        first_mailto = None
+        first_path = None
+        first_http = None
+        first = None
+        for addr in cuaddrs:
+            if addr.startswith("mailto:"):
+                first_mailto = addr
+                break
+            elif addr.startswith("/"):
+                if not first_path:
+                    first_path = addr
+            elif addr.startswith("http:"):
+                if not first_http:
+                    first_http = addr
+            elif not first:
+                first = addr
+
+        if first_mailto:
+            newaddr = first_mailto
+        elif first_path:
+            newaddr = first_path
+        elif first_http:
+            newaddr = first_http
+        elif first:
+            newaddr = first
+        else:
+            newaddr = None
+
+        # Make the change
+        if newaddr:
+            return newaddr
+
+    return cuaddr
+
+
+
 #
 # This function is from "Python Cookbook, 2d Ed., by Alex Martelli, Anna
 # Martelli Ravenscroft, and David Ascher (O'Reilly Media, 2005) 0-596-00797-3."

Modified: CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/scheduling/scheduler.py	2012-10-08 18:59:17 UTC (rev 9904)
+++ CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/scheduling/scheduler.py	2012-10-08 21:17:08 UTC (rev 9905)
@@ -36,7 +36,7 @@
 from twistedcaldav.customxml import calendarserver_namespace
 from twistedcaldav.accounting import accountingEnabled, emitAccounting
 from twistedcaldav.config import config
-from twistedcaldav.ical import Component
+from twistedcaldav.ical import Component, normalizeCUAddress
 from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
 from twistedcaldav.scheduling import addressmapping
 from twistedcaldav.scheduling.caldav import ScheduleViaCalDAV
@@ -914,6 +914,15 @@
             self.calendar.normalizeCalendarUserAddresses(normalizationLookup,
                 self.resource.principalForCalendarUserAddress)
 
+    def loadRecipientsFromRequestHeaders(self):
+        """
+        Need to normalize the calendar data and recipient values to keep those in sync,
+        as we might later try to match them
+        """
+        super(IScheduleScheduler, self).loadRecipientsFromRequestHeaders()
+        self.recipients = [normalizeCUAddress(recipient, normalizationLookup, self.resource.principalForCalendarUserAddress) for recipient in self.recipients]
+
+
     def checkAuthorization(self):
         # Must have an unauthenticated user
         if self.resource.currentPrincipal(self.request) != davxml.Principal(davxml.Unauthenticated()):

Modified: CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/test/test_icalendar.py	2012-10-08 18:59:17 UTC (rev 9904)
+++ CalendarServer/branches/release/CalendarServer-4.2-dev/twistedcaldav/test/test_icalendar.py	2012-10-08 21:17:08 UTC (rev 9905)
@@ -21,7 +21,8 @@
 from twisted.trial.unittest import SkipTest
 
 from twistedcaldav.config import config
-from twistedcaldav.ical import Component, Property, InvalidICalendarDataError
+from twistedcaldav.ical import Component, Property, InvalidICalendarDataError, \
+    normalizeCUAddress
 from twistedcaldav.instance import InvalidOverriddenInstanceError
 import twistedcaldav.test.util
 
@@ -42,7 +43,8 @@
         Properties in components
         """
         calendar = Component.fromStream(file(os.path.join(self.data_dir, "Holidays.ics")))
-        if calendar.name() != "VCALENDAR": self.fail("Calendar is not a VCALENDAR")
+        if calendar.name() != "VCALENDAR":
+            self.fail("Calendar is not a VCALENDAR")
 
         for subcomponent in calendar.subcomponents():
             if subcomponent.name() == "VEVENT":
@@ -78,7 +80,7 @@
 #            calendar2 = Component.fromString(data)
 #
 #            self.assertEqual(calendar1, calendar2)
-            
+
         data1 = (
             (
                 "1.1 Switch property order",
@@ -243,7 +245,7 @@
                 True,
             ),
         )
-        
+
         for description, item1, item2, result in data1:
             if "1.3" not in description:
                 continue
@@ -253,6 +255,7 @@
                 calendar1, calendar2, "%s" % (description,)
             )
 
+
     def test_component_validate(self):
         """
         CalDAV resource validation.
@@ -268,7 +271,8 @@
 
         resource_dir = os.path.join(self.data_dir, "Holidays")
         for filename in resource_dir:
-            if os.path.splitext(filename)[1] != ".ics": continue
+            if os.path.splitext(filename)[1] != ".ics":
+                continue
             filename = os.path.join(resource_dir, filename)
 
             calendar = Component.fromStream(file(filename))
@@ -278,6 +282,7 @@
             except ValueError:
                 self.fail("Resource iCalendar %s didn't validate for CalDAV" % (filename,))
 
+
     def test_component_validate_and_fix(self):
         """
         CalDAV resource validation and fixing.
@@ -570,7 +575,8 @@
             end = instance.end
             self.assertEqual(start, PyCalendarDateTime(year, 7, 4))
             self.assertEqual(end  , PyCalendarDateTime(year, 7, 5))
-            if year == 2050: break
+            if year == 2050:
+                break
             year += 1
 
         self.assertEqual(year, 2050)
@@ -596,7 +602,8 @@
             if year in results:
                 self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
                 self.assertEqual(end  , PyCalendarDateTime(year, results[year][0], results[year][2]))
-            if year == 2050: break
+            if year == 2050:
+                break
             year += 1
 
         self.assertEqual(year, 2050)
@@ -622,11 +629,13 @@
             if year in results:
                 self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
                 self.assertEqual(end  , PyCalendarDateTime(year, results[year][0], results[year][2]))
-            if year == 2050: break
+            if year == 2050:
+                break
             year += 1
 
         self.assertEqual(year, 2050)
 
+
     def test_component_timerange(self):
         """
         Component summary time range query.
@@ -640,16 +649,18 @@
             end = instance.end
             self.assertEqual(start, PyCalendarDateTime(2004, 11, 25))
             self.assertEqual(end, PyCalendarDateTime(2004, 11, 27))
-            break;
+            break
 
     #test_component_timerange.todo = "recurrence expansion should give us no end date here"
 
+
     def test_parse_date(self):
         """
         parse_date()
         """
         self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
 
+
     def test_parse_datetime(self):
         """
         parse_datetime()
@@ -661,6 +672,7 @@
         dt = PyCalendarDateTime.parseText("19980119T070000Z")
         self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
 
+
     def test_parse_date_or_datetime(self):
         """
         parse_date_or_datetime()
@@ -674,18 +686,20 @@
         dt = PyCalendarDateTime.parseText("19980119T070000Z")
         self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
 
+
     def test_parse_duration(self):
         """
         parse_duration()
         """
-        self.assertEqual(PyCalendarDuration.parseText( "P15DT5H0M20S"), PyCalendarDuration(days= 15, hours= 5, minutes=0, seconds= 20))
-        self.assertEqual(PyCalendarDuration.parseText("+P15DT5H0M20S"), PyCalendarDuration(days= 15, hours= 5, minutes=0, seconds= 20))
+        self.assertEqual(PyCalendarDuration.parseText("P15DT5H0M20S"), PyCalendarDuration(days=15, hours=5, minutes=0, seconds=20))
+        self.assertEqual(PyCalendarDuration.parseText("+P15DT5H0M20S"), PyCalendarDuration(days=15, hours=5, minutes=0, seconds=20))
         self.assertEqual(PyCalendarDuration.parseText("-P15DT5H0M20S"), PyCalendarDuration(days=-15, hours=-5, minutes=0, seconds=-20))
 
         self.assertEqual(PyCalendarDuration.parseText("P7W"), PyCalendarDuration(weeks=7))
 
+
     def test_correct_attendee_properties(self):
-        
+
         data = """BEGIN:VCALENDAR
 VERSION:2.0
 PRODID:-//Apple Computer\, Inc//iCal 2.0//EN
@@ -700,10 +714,11 @@
 """
 
         component = Component.fromString(data)
-        self.assertEqual([p.value() for p in component.getAttendeeProperties(("mailto:user2 at example.com",))], ["mailto:user2 at example.com",])
+        self.assertEqual([p.value() for p in component.getAttendeeProperties(("mailto:user2 at example.com",))], ["mailto:user2 at example.com", ])
 
+
     def test_empty_attendee_properties(self):
-        
+
         data = """BEGIN:VCALENDAR
 VERSION:2.0
 DTSTART:20071114T000000Z
@@ -720,8 +735,9 @@
         component = Component.fromString(data)
         self.assertEqual(component.getAttendeeProperties(("user3 at example.com",)), [])
 
+
     def test_organizers_by_instance(self):
-        
+
         data = (
             (
                 """BEGIN:VCALENDAR
@@ -847,13 +863,14 @@
                 )
             ),
         )
-        
+
         for caldata, result in data:
             component = Component.fromString(caldata)
             self.assertEqual(component.getOrganizersByInstance(), result)
 
+
     def test_attendees_by_instance(self):
-        
+
         data = (
             (
                 """BEGIN:VCALENDAR
@@ -979,11 +996,12 @@
                 )
             ),
         )
-        
+
         for caldata, checkScheduleAgent, result in data:
             component = Component.fromString(caldata)
             self.assertEqual(component.getAttendeesByInstance(onlyScheduleAgentServer=checkScheduleAgent), result)
 
+
     def test_set_parameter_value(self):
         data = (
             # ATTENDEE - no existing parameter
@@ -1131,8 +1149,9 @@
         for original, result, args in data:
             component = Component.fromString(original)
             component.setParameterToValueForPropertyWithValue(*args)
-            self.assertEqual(result, str(component).replace("\r", ""))        
+            self.assertEqual(result, str(component).replace("\r", ""))
 
+
     def test_add_property(self):
         data = (
             # Simple component
@@ -1203,10 +1222,11 @@
         for original, result in data:
             component = Component.fromString(original)
             component.addPropertyToAllComponents(Property("REQUEST-STATUS", ["2.0", "Success"]))
-            self.assertEqual(result, str(component).replace("\r", ""))        
+            self.assertEqual(result, str(component).replace("\r", ""))
 
+
     def test_attendees_views(self):
-        
+
         data = (
             (
                 "1.1 Simple component, no Attendees - no filtering",
@@ -1386,7 +1406,7 @@
 END:VCALENDAR
 """,
                 ("mailto:user3 at example.com",)
-            ),        
+            ),
 
             (
                 "2.3 Recurring component with one instance, master with one attendee, instance without attendee - filtering match",
@@ -1683,14 +1703,15 @@
             ),
 
         )
-        
+
         for description, original, checkScheduleAgent, filtered, attendees in data:
             component = Component.fromString(original)
             component.attendeesView(attendees, onlyScheduleAgentServer=checkScheduleAgent)
             self.assertEqual(filtered, str(component).replace("\r", ""), "Failed: %s" % (description,))
 
+
     def test_all_but_one_attendee(self):
-        
+
         data = (
             # One component, no attendees
             (
@@ -1834,14 +1855,15 @@
             ),
 
         )
-        
+
         for original, result, attendee in data:
             component = Component.fromString(original)
             component.removeAllButOneAttendee(attendee)
             self.assertEqual(result, str(component).replace("\r", ""))
 
+
     def test_filter_properties_keep(self):
-        
+
         data = (
             # One component
             (
@@ -1931,14 +1953,15 @@
             ),
 
         )
-        
+
         for original, result, keep_properties in data:
             component = Component.fromString(original)
             component.filterProperties(keep=keep_properties)
             self.assertEqual(result, str(component).replace("\r", ""))
 
+
     def test_filter_properties_remove(self):
-        
+
         data = (
             # One component
             (
@@ -2029,14 +2052,15 @@
             ),
 
         )
-        
+
         for original, result, remove_properties in data:
             component = Component.fromString(original)
             component.filterProperties(remove=remove_properties)
             self.assertEqual(result, str(component).replace("\r", ""))
 
+
     def test_remove_alarms(self):
-        
+
         data = (
             # One component, no alarms
             (
@@ -2198,14 +2222,15 @@
 """,
             ),
         )
-        
+
         for original, result in data:
             component = Component.fromString(original)
             component.removeAlarms()
             self.assertEqual(result, str(component).replace("\r", ""))
 
+
     def test_expand_instances(self):
-        
+
         data = (
             (
                 "Non recurring",
@@ -2430,7 +2455,7 @@
                 )
             ),
         )
-        
+
         for description, original, ignoreInvalidInstances, results in data:
             component = Component.fromString(original)
             if results is None:
@@ -2443,9 +2468,10 @@
                 for start, end in periods:
                     self.assertEqual(start.isDateOnly(), results[0][0].isDateOnly(), "%s: %s wrong date/time start state" % (description, start,))
                     self.assertEqual(end.isDateOnly(), results[0][1].isDateOnly(), "%s: %s wrong date/time end state" % (description, end,))
-       
+
+
     def test_expand_instances_for_expand(self):
-        
+
         data = (
             (
                 "Non recurring utc",
@@ -2772,7 +2798,7 @@
                 None
             ),
         )
-        
+
         for description, original, ignoreInvalidInstances, results in data:
             component = Component.fromString(original)
             if results is None:
@@ -2785,9 +2811,10 @@
                 for start, end in periods:
                     self.assertEqual(start.isDateOnly(), results[0][0].isDateOnly(), "%s: %s wrong date/time start state" % (description, start,))
                     self.assertEqual(end.isDateOnly(), results[0][1].isDateOnly(), "%s: %s wrong date/time end state" % (description, end,))
-       
+
+
     def test_expand_instances_lowerlimit(self):
-        
+
         data = (
             (
                 "Non recurring - no limit",
@@ -3129,7 +3156,7 @@
                 PyCalendarDateTime(2012, 1, 1),
             ),
         )
-        
+
         for description, original, lowerLimit, results, limited in data:
             component = Component.fromString(original)
             instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), lowerLimit=lowerLimit)
@@ -3140,9 +3167,10 @@
                 self.assertEqual(start.isDateOnly(), results[0][0].isDateOnly(), "%s: %s wrong date/time start state" % (description, start,))
                 self.assertEqual(end.isDateOnly(), results[0][1].isDateOnly(), "%s: %s wrong date/time end state" % (description, end,))
             self.assertEqual(instances.lowerLimit, limited)
-       
+
+
     def test_has_property_in_any_component(self):
-        
+
         data = (
             (
                 "Single component - True",
@@ -3321,13 +3349,14 @@
                 False,
             ),
         )
-        
+
         for description, caldata, propnames, result in data:
             component = Component.fromString(caldata)
             self.assertTrue(component.hasPropertyInAnyComponent(propnames) == result, "Property name match incorrect: %s" % (description,))
-       
+
+
     def test_transfer_properties(self):
-        
+
         data = (
             (
                 "Non recurring - one property",
@@ -3411,7 +3440,7 @@
 END:VEVENT
 END:VCALENDAR
 """,
-            ("X-ITEM2","X-ITEM3",),
+            ("X-ITEM2", "X-ITEM3",),
             ),
             (
                 "Non recurring - two properties - one overlap",
@@ -3454,7 +3483,7 @@
 END:VEVENT
 END:VCALENDAR
 """,
-            ("X-ITEM2","X-ITEM1",),
+            ("X-ITEM2", "X-ITEM1",),
             ),
             (
                 "Non recurring - one property",
@@ -3587,7 +3616,7 @@
             ("X-ITEM2",),
             ),
         )
-        
+
         for description, transfer_to, transfer_from, result, propnames in data:
             component_to = Component.fromString(transfer_to)
             component_from = Component.fromString(transfer_from)
@@ -3595,8 +3624,9 @@
             component_to.transferProperties(component_from, propnames)
             self.assertEqual(str(component_to), str(component_result), "%s: mismatch" % (description,))
 
+
     def test_normalize_all(self):
-        
+
         data = (
             (
                 "1.1",
@@ -3763,7 +3793,7 @@
 """,
             ),
         )
-        
+
         for title, original, result in data:
             ical1 = Component.fromString(original)
             ical1.normalizeAll()
@@ -3773,8 +3803,9 @@
             diff = "\n".join(unified_diff(ical1.split("\n"), ical2.split("\n")))
             self.assertEqual(str(ical1), str(ical2), "Failed comparison: %s\n%s" % (title, diff,))
 
+
     def test_normalize_attachments(self):
-        
+
         data = (
             (
                 "1.1 - no attach",
@@ -3878,7 +3909,7 @@
 """,
             ),
         )
-        
+
         for title, original, result in data:
             ical1 = Component.fromString(original)
             ical1.normalizeAttachments()
@@ -3888,8 +3919,9 @@
             diff = "\n".join(unified_diff(ical1.split("\n"), ical2.split("\n")))
             self.assertEqual(str(ical1), str(ical2), "Failed comparison: %s\n%s" % (title, diff,))
 
+
     def test_recurring_unbounded(self):
-        
+
         data = (
             (
                 "1.1 - non-recurring",
@@ -3955,14 +3987,15 @@
                 True
             ),
         )
-        
+
         for title, calendar, expected in data:
             ical = Component.fromString(calendar)
             result = ical.isRecurringUnbounded()
             self.assertEqual(result, expected, "Failed recurring unbounded test: %s" % (title,))
 
+
     def test_derive_instance(self):
-        
+
         data = (
             (
                 "1.1 - simple",
@@ -4223,15 +4256,16 @@
                 None,
             ),
         )
-        
+
         for title, calendar, rid, result in data:
             ical = Component.fromString(calendar)
             derived = ical.deriveInstance(rid)
             derived = str(derived).replace("\r", "") if derived else None
             self.assertEqual(derived, result, "Failed derive instance test: %s" % (title,))
 
+
     def test_derive_instance_multiple(self):
-        
+
         data = (
             (
                 "1.1 - simple",
@@ -4441,7 +4475,7 @@
                 ),
             ),
         )
-        
+
         for title, calendar, rids, results in data:
             ical = Component.fromString(calendar)
             for rid, result in itertools.izip(rids, results):
@@ -4449,11 +4483,12 @@
                 derived = str(derived).replace("\r", "") if derived else None
                 self.assertEqual(derived, result, "Failed derive instance test: %s" % (title,))
 
+
     def test_derive_instance_with_cancel(self):
         """
         Test that derivation of cancelled instances works and only results in one STATUS property present.
         """
-        
+
         data = (
             (
                 "1.1 - simple no existing STATUS",
@@ -4509,19 +4544,20 @@
 """,
             ),
         )
-        
+
         for title, calendar, rid, result in data:
             ical = Component.fromString(calendar)
             derived = ical.deriveInstance(rid, allowCancelled=True)
             derived = str(derived).replace("\r", "") if derived else None
             self.assertEqual(derived, result, "Failed derive instance test: %s" % (title,))
 
+
     def test_derive_instance_cache(self):
         """
         Test that derivation of instances only triggers an instance cache re-expansion when it
         goes past the end of the last cache.
         """
-        
+
         event = """BEGIN:VCALENDAR
 VERSION:2.0
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -4533,23 +4569,23 @@
 RRULE:FREQ=DAILY
 END:VEVENT
 END:VCALENDAR
-"""        
+"""
 
         ical = Component.fromString(event)
         self.assertFalse(hasattr(ical, "cachedInstances"))
-        
+
         # Derive one day apart - no re-cache
         ical.deriveInstance(PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
         self.assertTrue(hasattr(ical, "cachedInstances"))
         oldLimit = ical.cachedInstances.limit
         ical.deriveInstance(PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
         self.assertEqual(ical.cachedInstances.limit, oldLimit)
-        
+
         # Derive several years ahead - re-cached
         ical.deriveInstance(PyCalendarDateTime(2011, 1, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
         self.assertNotEqual(ical.cachedInstances.limit, oldLimit)
         oldLimit = ical.cachedInstances.limit
-        
+
         # Check one day ahead again - no re-cache
         ical.deriveInstance(PyCalendarDateTime(2011, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
         self.assertEqual(ical.cachedInstances.limit, oldLimit)
@@ -4560,7 +4596,7 @@
         Test that derivation of instances only triggers an instance cache re-expansion when it
         goes past the end of the last cache.
         """
-        
+
         event = """BEGIN:VCALENDAR
 VERSION:2.0
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -4594,7 +4630,7 @@
 
         ical = Component.fromString(event)
         masterDerived = ical.masterDerived()
-        
+
         # Derive one day apart - no re-cache
         result = ical.deriveInstance(PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
         self.assertEqual(str(result), derived1)
@@ -4609,7 +4645,7 @@
 
 
     def test_truncate_recurrence(self):
-        
+
         data = (
             (
                 "1.1 - no recurrence",
@@ -4746,7 +4782,7 @@
 """,
             ),
         )
-        
+
         for title, original, result in data:
             ical1 = Component.fromString(original)
             changed = ical1.truncateRecurrence(400)
@@ -4758,14 +4794,15 @@
                 else:
                     ical2 = Component.fromString(result)
                     ical2 = str(ical2)
-    
+
                     diff = "\n".join(unified_diff(ical1.split("\n"), ical2.split("\n")))
                     self.assertEqual(str(ical1), str(ical2), "Failed comparison: %s\n%s" % (title, diff,))
             elif changed:
                 self.fail("Truncation happened when not expected: %s" % (title,))
 
+
     def test_valid_recurrence(self):
-        
+
         data = (
             (
                 "1.1 - no recurrence",
@@ -5022,23 +5059,24 @@
                 )
             ),
         )
-        
+
         for clear_cache in (True, False):
             for title, calendar, tests in data:
                 ical = Component.fromString(calendar)
                 for ctr, item in enumerate(tests):
                     rid, result = item
-                    self.assertEqual(ical.validInstance(rid, clear_cache=clear_cache), result, "Failed comparison: %s #%d" % (title, ctr+1,))
+                    self.assertEqual(ical.validInstance(rid, clear_cache=clear_cache), result, "Failed comparison: %s #%d" % (title, ctr + 1,))
 
         for title, calendar, tests in data:
             ical = Component.fromString(calendar)
             rids = set([rid for rid, result in tests])
-            expected_results = set([rid for rid, result in tests if result==True])
+            expected_results = set([rid for rid, result in tests if result == True])
             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",
@@ -5241,7 +5279,7 @@
             1, 0,
             ),
         )
-        
+
         for title, calendar, result_calendar, result_fixed, result_unfixed in data:
             ical = Component.fromString(calendar)
             fixed, unfixed = ical.validRecurrenceIDs(doFix=True)
@@ -5249,6 +5287,7 @@
             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
@@ -5335,7 +5374,6 @@
 """,
         )
 
-
         for text in invalid:
             calendar = Component.fromString(text)
             self.assertRaises(InvalidICalendarDataError, calendar.validCalendarData, doFix=False)
@@ -5347,6 +5385,7 @@
             except:
                 self.fail("Valid calendar should validate")
 
+
     def test_allperuseruids(self):
         data = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -5388,6 +5427,7 @@
             "user02",
         )))
 
+
     def test_perUserTransparency(self):
         data = (
                     (
@@ -5876,6 +5916,7 @@
             for rid, result in results:
                 self.assertEqual(calendar.perUserTransparency(rid), result, "Failed comparison: %s %s" % (title, rid,))
 
+
     def test_needsiTIPSequenceChange(self):
 
         data = (
@@ -6153,6 +6194,7 @@
             ical_new = Component.fromString(new_txt)
             self.assertEqual(ical_new.needsiTIPSequenceChange(ical_old), result, "Failed: %s" % (title,))
 
+
     def test_bumpiTIPInfo(self):
 
         data = (
@@ -6592,16 +6634,16 @@
                 True,
             ),
         )
-        
+
         for title, old_txt, ical_txt, result_txt, doSequence in data:
             old = Component.fromString(old_txt) if old_txt else None
             ical = Component.fromString(ical_txt)
             result = Component.fromString(result_txt)
             ical.bumpiTIPInfo(oldcalendar=old, doSequence=doSequence)
-            
+
             ical1 = str(ical).split("\n")
             ical2 = str(result).split("\n")
-            
+
             # Check without DTSTAMPs which we expect to be different
             ical1_withoutDTSTAMP = [item for item in ical1 if not item.startswith("DTSTAMP:")]
             ical2_withoutDTSTAMP = [item for item in ical2 if not item.startswith("DTSTAMP:")]
@@ -6609,10 +6651,10 @@
             diff = "\n".join(unified_diff(ical1_withoutDTSTAMP, ical2_withoutDTSTAMP))
             self.assertEqual("\n".join(ical1_withoutDTSTAMP), "\n".join(ical2_withoutDTSTAMP), "Failed comparison: %s\n%s" % (title, diff,))
 
-            # Check that all DTSTAMPs changed    
+            # Check that all DTSTAMPs changed
             dtstamps1 = set([item for item in ical1 if item.startswith("DTSTAMP:")])
             dtstamps2 = set([item for item in ical2 if item.startswith("DTSTAMP:")])
-            
+
             diff = "\n".join(unified_diff(ical1, ical2))
             self.assertEqual(len(dtstamps1 & dtstamps2), 0, "Failed comparison: %s\n%s" % (title, diff,))
 
@@ -6860,16 +6902,16 @@
 """,
             ),
         )
-        
+
         for title, old_txt, ical_txt, result_txt in data:
             old = Component.fromString(old_txt)
             ical = Component.fromString(ical_txt)
             result = Component.fromString(result_txt)
             ical.sequenceInSync(old)
-            
+
             ical1 = str(ical).split("\n")
             ical2 = str(result).split("\n")
-            
+
             diff = "\n".join(unified_diff(ical1, ical2))
             self.assertEqual("\n".join(ical1), "\n".join(ical2), "Failed comparison: %s\n%s" % (title, diff,))
 
@@ -7271,6 +7313,7 @@
 
         component = Component.fromString(data)
 
+
         def lookupFunction(cuaddr, ignored1, ignored2):
             return {
                 "urn:uuid:foo" : (
@@ -7328,17 +7371,18 @@
 
         component = Component.fromString(data)
 
+
         def lookupFunction(cuaddr, ignored1, ignored2):
             return {
                 "/principals/users/foo" : (
                     "Foo",
                     "foo",
-                    ("urn:uuid:foo", )
+                    ("urn:uuid:foo",)
                 ),
                 "http://example.com/principals/users/buz" : (
                     "Buz",
                     "buz",
-                    ("urn:uuid:buz", )
+                    ("urn:uuid:buz",)
                 ),
             }[cuaddr]
 
@@ -7442,6 +7486,7 @@
         self.assertEquals(subComponent._parent, None)
         self.assertEquals(component._cachedCopy, None) # cache is invalidated
 
+
     def test_hasDuplicateAlarms(self):
         """
         Test that L{Component.hasDuplicateAlarms} correctly detects, but does not fix, duplicate alarms.
@@ -7465,7 +7510,7 @@
 """,
                 False,
             ),
-            
+
             # One alarm
             (
                 """BEGIN:VCALENDAR
@@ -7488,7 +7533,7 @@
 """,
                 False,
             ),
-            
+
             # Two different alarms
             (
                 """BEGIN:VCALENDAR
@@ -7516,7 +7561,7 @@
 """,
                 False,
             ),
-            
+
             # Two duplicates, one different
             (
                 """BEGIN:VCALENDAR
@@ -7562,7 +7607,7 @@
 """,
                 True,
             ),
-            
+
             # Two duplicates in one component, three different in another
             (
                 """BEGIN:VCALENDAR
@@ -7618,7 +7663,7 @@
 """,
                 True,
             ),
-            
+
             # Four duplicates in X-CALENDARSERVER-PERINSTANCE
             (
                 """BEGIN:VCALENDAR
@@ -7664,11 +7709,12 @@
             ),
         )
 
-        for txt, result in data: 
+        for txt, result in data:
             component = Component.fromString(txt)
             self.assertEqual(component.hasDuplicateAlarms(doFix=False), result)
             self.assertEqual(str(component), txt.replace("\n", "\r\n"))
 
+
     def test_hasDuplicateAlarms_withFix(self):
         """
         Test that L{Component.hasDuplicateAlarms} correctly removes duplicate alarms.
@@ -7705,7 +7751,7 @@
 """,
                 False,
             ),
-            
+
             # One alarm
             (
                 """BEGIN:VCALENDAR
@@ -7746,7 +7792,7 @@
 """,
                 False,
             ),
-            
+
             # Two different alarms
             (
                 """BEGIN:VCALENDAR
@@ -7797,7 +7843,7 @@
 """,
                 False,
             ),
-            
+
             # Two duplicates, one different
             (
                 """BEGIN:VCALENDAR
@@ -7879,7 +7925,7 @@
 """,
                 True,
             ),
-            
+
             # Two duplicates in one component, three different in another
             (
                 """BEGIN:VCALENDAR
@@ -7976,7 +8022,7 @@
 """,
                 True,
             ),
-            
+
             # Four duplicates in X-CALENDARSERVER-PERINSTANCE
             (
                 """BEGIN:VCALENDAR
@@ -8046,8 +8092,81 @@
             ),
         )
 
-        for txt, result, result_changed in data: 
+        for txt, result, result_changed in data:
             component = Component.fromString(txt)
             changed = component.hasDuplicateAlarms(doFix=True)
             self.assertEqual(str(component), result.replace("\n", "\r\n"))
             self.assertEqual(changed, result_changed)
+
+
+    def test_normalizeCUAddressFromUUID(self):
+        """
+        Ensure mailto is preferred, followed by path form, then http form.
+        If CALENDARSERVER-OLD-CUA parameter is present, restore that value.
+        """
+
+        data = (
+            ("urn:uuid:foo", "/foo"),
+            ("urn:uuid:bar", "mailto:bar at example.com",),
+            ("urn:uuid:baz", "http://example.com/baz",),
+            ("urn:uuid:buz", "urn:uuid:buz",),
+        )
+
+        def lookupFunction(cuaddr, ignored1, ignored2):
+            return {
+                "urn:uuid:foo" : (
+                    "Foo",
+                    "foo",
+                    ("urn:uuid:foo", "http://example.com/foo", "/foo")
+                ),
+                "urn:uuid:bar" : (
+                    "Bar",
+                    "bar",
+                    ("urn:uuid:bar", "mailto:bar at example.com", "http://example.com/bar", "/bar")
+                ),
+                "urn:uuid:baz" : (
+                    "Baz",
+                    "baz",
+                    ("urn:uuid:baz", "http://example.com/baz")
+                ),
+                "urn:uuid:buz" : (
+                    "Buz",
+                    "buz",
+                    ("urn:uuid:buz",)
+                ),
+            }[cuaddr]
+
+        for cuaddr, result in data:
+            new_cuaddr = normalizeCUAddress(cuaddr, lookupFunction, None, toUUID=False)
+            self.assertEquals(new_cuaddr, result)
+
+
+    def test_normalizeCUAddressToUUID(self):
+        """
+        Ensure http(s) and /path CUA values are tucked away into the property
+        using CALENDARSERVER-OLD-CUA parameter.
+        """
+
+        data = (
+            ("/principals/users/foo", "urn:uuid:foo",),
+            ("http://example.com/principals/users/buz", "urn:uuid:buz",),
+        )
+
+
+        def lookupFunction(cuaddr, ignored1, ignored2):
+            return {
+                "/principals/users/foo" : (
+                    "Foo",
+                    "foo",
+                    ("urn:uuid:foo",)
+                ),
+                "http://example.com/principals/users/buz" : (
+                    "Buz",
+                    "buz",
+                    ("urn:uuid:buz",)
+                ),
+            }[cuaddr]
+
+        for cuaddr, result in data:
+            new_cuaddr = normalizeCUAddress(cuaddr, lookupFunction, None, toUUID=True)
+            self.assertEquals(new_cuaddr, result)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121008/0ad7e522/attachment-0001.html>


More information about the calendarserver-changes mailing list