[CalendarServer-changes] [3560] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Dec 19 12:26:58 PST 2008


Revision: 3560
          http://trac.macosforge.org/projects/calendarserver/changeset/3560
Author:   cdaboo at apple.com
Date:     2008-12-19 12:26:58 -0800 (Fri, 19 Dec 2008)
Log Message:
-----------
Merged normalize-cuaddr branch to trunk.

Modified Paths:
--------------
    CalendarServer/trunk/run
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/directory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py
    CalendarServer/trunk/twistedcaldav/method/put_common.py
    CalendarServer/trunk/twistedcaldav/method/report_common.py
    CalendarServer/trunk/twistedcaldav/resource.py

Modified: CalendarServer/trunk/run
===================================================================
--- CalendarServer/trunk/run	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/run	2008-12-19 20:26:58 UTC (rev 3560)
@@ -696,7 +696,7 @@
 
 caldavtester="${top}/CalDAVTester";
 
-svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 3557;
+svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 3559;
 
 #
 # Calendar Server

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2008-12-19 20:26:58 UTC (rev 3560)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2008 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -274,17 +274,25 @@
 
             # Cache miss; try looking the record up, in case it is new
             # FIXME: This is a blocking call (hopefully it's a fast one)
-            self.reloadCache(recordType, shortName=shortName)
+            self.reloadCache(recordType, lookup=("shortName", shortName,))
             record = self.recordsForType(recordType).get(shortName, None)
             if record is None:
                 # Add to negative cache
                 self._storage(recordType)["disabled names"].add(shortName)
             return record
 
+    def recordWithEmailAddress(self, emailAddress):
+        return self._recordWithAttribute("emails", "disabled emails", "email", emailAddress)
+
     def recordWithGUID(self, guid):
+        return self._recordWithAttribute("guids", "disabled guids", "guid", guid)
+
+    recordWithUID = recordWithGUID
+
+    def _recordWithAttribute(self, cacheKey, disabledKey, lookupKey, value):
         def lookup():
             for recordType in self.recordTypes():
-                record = self._storage(recordType)["guids"].get(guid, None)
+                record = self._storage(recordType)[cacheKey].get(value, None)
                 if record:
                     return record
             else:
@@ -296,26 +304,24 @@
             # Cache miss; try looking the record up, in case it is new
             for recordType in self.recordTypes():
                 # Check negative cache
-                if guid in self._storage(recordType)["disabled guids"]:
+                if value in self._storage(recordType)[disabledKey]:
                     continue
 
-                self.reloadCache(recordType, guid=guid)
+                self.reloadCache(recordType, lookup=(lookupKey, value,))
                 record = lookup()
 
                 if record is None:
-                    self._storage(recordType)["disabled guids"].add(guid)
+                    self._storage(recordType)[disabledKey].add(value)
                 else:
-                    self.log_info("Faulted record with GUID %s into %s record cache"
-                                  % (guid, recordType))
+                    self.log_info("Faulted record with %s %s into %s record cache"
+                                  % (lookupKey, value, recordType))
                     break
             else:
                 # Nothing found; add to negative cache
-                self.log_info("Unable to find any record with GUID %s" % (guid,))
+                self.log_info("Unable to find any record with %s %s" % (lookupKey, value,))
 
         return record
 
-    recordWithUID = recordWithGUID
-
     def groupsForGUID(self, guid):
         
         # Lookup in index
@@ -419,19 +425,15 @@
         return deferred
 
 
-    def reloadCache(self, recordType, shortName=None, guid=None):
-        if shortName is not None:
-            self.log_info("Faulting record with shortName %s into %s record cache" % (shortName, recordType))
-        elif guid is not None:
-            self.log_info("Faulting record with guid %s into %s record cache" % (guid, recordType))
-        elif shortName is None and guid is None:
+    def reloadCache(self, recordType, lookup=None):
+        if lookup is not None:
+            self.log_info("Faulting record with %s %s into %s record cache" % (lookup[0], lookup[1], recordType))
+        else:
             self.log_info("Reloading %s record cache" % (recordType,))
-        else:
-            raise AssertionError("%r.reloadCache(%s, %s, %s)" % (self, recordType, shortName, guid))
 
-        results = self._queryDirectory(recordType, shortName=shortName, guid=guid)
+        results = self._queryDirectory(recordType, lookup=lookup)
 
-        if shortName is None and guid is None:
+        if lookup is None:
             records = {}
             guids   = {}
             emails  = {}
@@ -638,7 +640,7 @@
                     else:
                         emails[email] = record
 
-        if shortName is None and guid is None:
+        if lookup is None:
             #
             # Replace the entire cache
             #
@@ -686,7 +688,7 @@
                 % (len(self._records[recordType]["guids"]), enabled_count, recordType, cacheTimeout)
             )
 
-    def _queryDirectory(self, recordType, shortName=None, guid=None):
+    def _queryDirectory(self, recordType, lookup=None):
         attrs = [
             dsattributes.kDS1AttrGeneratedUID,
             dsattributes.kDS1AttrDistinguishedName,
@@ -754,10 +756,14 @@
             self.log_debug("Got %d restricted group members" % (len(self.restrictedGUIDs),))
 
         query = None
-        if shortName is not None:
-            query = dsquery.match(dsattributes.kDSNAttrRecordName, shortName, dsattributes.eDSExact)
-        elif guid is not None:
-            query = dsquery.match(dsattributes.kDS1AttrGeneratedUID, guid, dsattributes.eDSExact)
+        if lookup is not None:
+            queryattr = {
+                "shortName" : dsattributes.kDSNAttrRecordName,
+                "guid"      : dsattributes.kDS1AttrGeneratedUID,
+                "email"     : dsattributes.kDSNAttrEMailAddress,
+            }.get(lookup[0])
+            assert queryattr is not None, "Invalid type for record faulting query"
+            query = dsquery.match(queryattr, lookup[1], dsattributes.eDSExact)
 
         try:
             if query:

Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py	2008-12-19 20:26:58 UTC (rev 3560)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2008 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -138,11 +138,28 @@
 
     def recordWithCalendarUserAddress(self, address):
         address = normalizeCUAddr(address)
+        if address.startswith("urn:uuid:"):
+            guid = address[9:]
+            return self.recordWithGUID(guid)
+        elif address.startswith("mailto:"):
+            email = address[7:]
+            result = self.recordWithEmailAddress(email)
+            if result:
+                return result
+
         for record in self.allRecords():
             if address in record.calendarUserAddresses:
                 return record
+                
         return None
 
+    def recordWithEmailAddress(self, email):
+        for record in self.allRecords():
+            if email in record.emailAddresses:
+                return record
+                
+        return None
+
     def allRecords(self):
         for recordType in self.recordTypes():
             for record in self.listRecords(recordType):

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py	2008-12-19 20:26:58 UTC (rev 3560)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2008 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -27,19 +27,26 @@
     from twistedcaldav.directory.util import uuidFromName
 
     class OpenDirectoryService (RealOpenDirectoryService):
-        def _queryDirectory(self, recordType, shortName=None, guid=None):
-            if shortName is None and guid is None:
+        def _queryDirectory(self, recordType, lookup=None):
+            self._didQuery = True
+            if lookup is None:
                 return self.fakerecords[recordType]
 
-            assert shortName is None or guid is None
-            if guid is not None:
-                guid = guid.lower()
+            if lookup[0] == "guid":
+                lookup = (lookup[0], lookup[1].lower(),)
 
             records = []
 
             for name, record in self.fakerecords[recordType]:
-                if name == shortName or record[dsattributes.kDS1AttrGeneratedUID] == guid:
-                    records.append((name, record))
+                if lookup[0] == "shortName":
+                    if name == lookup[1]:
+                        records.append((name, record))
+                elif lookup[0] == "guid":
+                    if record[dsattributes.kDS1AttrGeneratedUID] == lookup[1]:
+                        records.append((name, record))
+                elif lookup[0] == "email":
+                    if record[dsattributes.kDSNAttrEMailAddress] == lookup[1]:
+                        records.append((name, record))
 
             return tuple(records)
     
@@ -95,6 +102,22 @@
             check("disabled names", expectedNames)
             check("disabled guids", (guid.lower() for guid in expectedGUIDs))
 
+        def verifyQuery(self, f, *args):
+            try:
+                delattr(self.service, "_didQuery")
+            except AttributeError:
+                pass
+            self.assertFalse(f(*args))
+            self.assertTrue(hasattr(self.service, "_didQuery"))
+
+        def verifyNoQuery(self, f, *args):
+            try:
+                delattr(self.service, "_didQuery")
+            except AttributeError:
+                pass
+            self.assertFalse(f(*args))
+            self.assertFalse(hasattr(self.service, "_didQuery"))
+
         def test_restrictionGroupName(self):
             service = OpenDirectoryService(
                 node="/Search",
@@ -226,8 +249,8 @@
                 ],
             }
 
-            self.service.reloadCache(DirectoryService.recordType_users, shortName="user02")
-            self.service.reloadCache(DirectoryService.recordType_users, guid="D10F3EE0-5014-41D3-8488-3819D3EF3B2A")
+            self.service.reloadCache(DirectoryService.recordType_users, lookup=("shortName", "user02",))
+            self.service.reloadCache(DirectoryService.recordType_users, lookup=("guid", "D10F3EE0-5014-41D3-8488-3819D3EF3B2A",))
 
             self.verifyRecords(DirectoryService.recordType_users, ("user01", "user02", "user03"))
             self.verifyDisabledRecords(DirectoryService.recordType_users, (), ())
@@ -353,8 +376,8 @@
                 ],
             }
 
-            self.service.reloadCache(DirectoryService.recordType_users, shortName="user04")
-            self.service.reloadCache(DirectoryService.recordType_users, guid="62368DDF-0C62-4C97-9A58-DE9FD46131A0")
+            self.service.reloadCache(DirectoryService.recordType_users, lookup=("shortName", "user04",))
+            self.service.reloadCache(DirectoryService.recordType_users, lookup=("guid", "62368DDF-0C62-4C97-9A58-DE9FD46131A0",))
 
             self.verifyRecords(DirectoryService.recordType_users, ("user01",))
             self.verifyDisabledRecords(
@@ -434,7 +457,7 @@
                     guidForShortName("user02"),
                 ]),
             ]
-            self.service.reloadCache(DirectoryService.recordType_groups, guid=guidForShortName("group03"))
+            self.service.reloadCache(DirectoryService.recordType_groups, lookup=("guid", guidForShortName("group03"),))
 
             group1 = self.service.recordWithShortName(DirectoryService.recordType_groups, "group01")
             self.assertTrue(group1 is not None)
@@ -453,6 +476,138 @@
             self.assertTrue(user2 is not None)
             self.assertEqual(set((group2, group3)), user2.groups()) 
 
+        def test_negativeCacheShortname(self):
+            self.loadRecords({
+                DirectoryService.recordType_users: [
+                    fakeODRecord("User 01"),
+                    fakeODRecord("User 02"),
+                    fakeODRecord("User 03"),
+                    fakeODRecord("User 04"),
+                ],
+                DirectoryService.recordType_groups: [
+                    fakeODRecord("Group 01"),
+                    fakeODRecord("Group 02"),
+                    fakeODRecord("Group 03"),
+                    fakeODRecord("Group 04"),
+                ],
+                DirectoryService.recordType_resources: [
+                    fakeODRecord("Resource 01"),
+                    fakeODRecord("Resource 02"),
+                    fakeODRecord("Resource 03"),
+                    fakeODRecord("Resource 04"),
+                ],
+                DirectoryService.recordType_locations: [
+                    fakeODRecord("Location 01"),
+                    fakeODRecord("Location 02"),
+                    fakeODRecord("Location 03"),
+                    fakeODRecord("Location 04"),
+                ],
+            })
+
+            self.assertTrue(self.service.recordWithShortName(DirectoryService.recordType_users, "user01"))
+            self.verifyQuery(self.service.recordWithShortName, DirectoryService.recordType_users, "user05")
+            self.verifyNoQuery(self.service.recordWithShortName, DirectoryService.recordType_users, "user05")
+
+            self.assertTrue(self.service.recordWithShortName(DirectoryService.recordType_groups, "group01"))
+            self.verifyQuery(self.service.recordWithShortName, DirectoryService.recordType_groups, "group05")
+            self.verifyNoQuery(self.service.recordWithShortName, DirectoryService.recordType_groups, "group05")
+
+            self.assertTrue(self.service.recordWithShortName(DirectoryService.recordType_resources, "resource01"))
+            self.verifyQuery(self.service.recordWithShortName, DirectoryService.recordType_resources, "resource05")
+            self.verifyNoQuery(self.service.recordWithShortName, DirectoryService.recordType_resources, "resource05")
+
+            self.assertTrue(self.service.recordWithShortName(DirectoryService.recordType_locations, "location01"))
+            self.verifyQuery(self.service.recordWithShortName, DirectoryService.recordType_locations, "location05")
+            self.verifyNoQuery(self.service.recordWithShortName, DirectoryService.recordType_locations, "location05")
+
+        def test_negativeCacheGUID(self):
+            self.loadRecords({
+                DirectoryService.recordType_users: [
+                    fakeODRecord("User 01"),
+                    fakeODRecord("User 02"),
+                    fakeODRecord("User 03"),
+                    fakeODRecord("User 04"),
+                ],
+                DirectoryService.recordType_groups: [
+                    fakeODRecord("Group 01"),
+                    fakeODRecord("Group 02"),
+                    fakeODRecord("Group 03"),
+                    fakeODRecord("Group 04"),
+                ],
+                DirectoryService.recordType_resources: [
+                    fakeODRecord("Resource 01"),
+                    fakeODRecord("Resource 02"),
+                    fakeODRecord("Resource 03"),
+                    fakeODRecord("Resource 04"),
+                ],
+                DirectoryService.recordType_locations: [
+                    fakeODRecord("Location 01"),
+                    fakeODRecord("Location 02"),
+                    fakeODRecord("Location 03"),
+                    fakeODRecord("Location 04"),
+                ],
+            })
+
+            self.assertTrue(self.service.recordWithGUID(guidForShortName("user01")))
+            self.verifyQuery(self.service.recordWithGUID, guidForShortName("user05"))
+            self.verifyNoQuery(self.service.recordWithGUID, guidForShortName("user05"))
+
+            self.assertTrue(self.service.recordWithGUID(guidForShortName("group01")))
+            self.verifyQuery(self.service.recordWithGUID, guidForShortName("group05"))
+            self.verifyNoQuery(self.service.recordWithGUID, guidForShortName("group05"))
+
+            self.assertTrue(self.service.recordWithGUID(guidForShortName("resource01")))
+            self.verifyQuery(self.service.recordWithGUID, guidForShortName("resource05"))
+            self.verifyNoQuery(self.service.recordWithGUID, guidForShortName("resource05"))
+
+            self.assertTrue(self.service.recordWithGUID(guidForShortName("location01")))
+            self.verifyQuery(self.service.recordWithGUID, guidForShortName("location05"))
+            self.verifyNoQuery(self.service.recordWithGUID, guidForShortName("location05"))
+
+        def test_negativeCacheEmailAddress(self):
+            self.loadRecords({
+                DirectoryService.recordType_users: [
+                    fakeODRecord("User 01"),
+                    fakeODRecord("User 02"),
+                    fakeODRecord("User 03"),
+                    fakeODRecord("User 04"),
+                ],
+                DirectoryService.recordType_groups: [
+                    fakeODRecord("Group 01"),
+                    fakeODRecord("Group 02"),
+                    fakeODRecord("Group 03"),
+                    fakeODRecord("Group 04"),
+                ],
+                DirectoryService.recordType_resources: [
+                    fakeODRecord("Resource 01"),
+                    fakeODRecord("Resource 02"),
+                    fakeODRecord("Resource 03"),
+                    fakeODRecord("Resource 04"),
+                ],
+                DirectoryService.recordType_locations: [
+                    fakeODRecord("Location 01"),
+                    fakeODRecord("Location 02"),
+                    fakeODRecord("Location 03"),
+                    fakeODRecord("Location 04"),
+                ],
+            })
+
+            self.assertTrue(self.service.recordWithEmailAddress("user01 at example.com"))
+            self.verifyQuery(self.service.recordWithEmailAddress, "user05 at example.com")
+            self.verifyNoQuery(self.service.recordWithEmailAddress, "user05 at example.com")
+
+            self.assertTrue(self.service.recordWithEmailAddress("group01 at example.com"))
+            self.verifyQuery(self.service.recordWithEmailAddress, "group05 at example.com")
+            self.verifyNoQuery(self.service.recordWithEmailAddress, "group05 at example.com")
+
+            self.assertTrue(self.service.recordWithEmailAddress("resource01 at example.com"))
+            self.verifyQuery(self.service.recordWithEmailAddress, "resource05 at example.com")
+            self.verifyNoQuery(self.service.recordWithEmailAddress, "resource05 at example.com")
+
+            self.assertTrue(self.service.recordWithEmailAddress("location01 at example.com"))
+            self.verifyQuery(self.service.recordWithEmailAddress, "location05 at example.com")
+            self.verifyNoQuery(self.service.recordWithEmailAddress, "location05 at example.com")
+
 def fakeODRecord(fullName, shortName=None, guid=None, email=None, members=None):
     if shortName is None:
         shortName = shortNameForFullName(fullName)

Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py	2008-12-19 20:26:58 UTC (rev 3560)
@@ -332,6 +332,10 @@
                     log.err(message)
                     raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-object-resource")))
 
+                # Normalize the calendar user addresses once we know we have valid
+                # calendar data
+                self.destination.iCalendarAddressDoNormalization(self.calendar)
+
                 # Must have a valid UID at this point
                 self.uid = self.calendar.resourceUID()
             else:

Modified: CalendarServer/trunk/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_common.py	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/twistedcaldav/method/report_common.py	2008-12-19 20:26:58 UTC (rev 3560)
@@ -138,7 +138,7 @@
     Return all (non-hidden) properties for the specified resource.
     @param request: the L{IRequest} for the current request.
     @param prop: the L{PropertyContainer} element for the properties of interest.
-    @param resource: the L{CalDAVFile} for the targetted resource.
+    @param resource: the L{CalDAVFile} for the targeted resource.
     @param calendar: the L{Component} for the calendar for the resource. This may be None
         if the calendar has not already been read in, in which case the resource
         will be used to get the calendar if needed.
@@ -160,7 +160,7 @@
     Return property names for all properties on the specified resource.
     @param request: the L{IRequest} for the current request.
     @param prop: the L{PropertyContainer} element for the properties of interest.
-    @param resource: the L{CalDAVFile} for the targetted resource.
+    @param resource: the L{CalDAVFile} for the targeted resource.
     @param calendar: the L{Component} for the calendar for the resource. This may be None
         if the calendar has not already been read in, in which case the resource
         will be used to get the calendar if needed.
@@ -185,7 +185,7 @@
     Return the specified properties on the specified resource.
     @param request: the L{IRequest} for the current request.
     @param prop: the L{PropertyContainer} element for the properties of interest.
-    @param resource: the L{CalDAVFile} for the targetted resource.
+    @param resource: the L{CalDAVFile} for the targeted resource.
     @param calendar: the L{Component} for the calendar for the resource. This may be None
         if the calendar has not already been read in, in which case the resource
         will be used to get the calendar if needed.
@@ -226,7 +226,7 @@
     Return the specified properties on the specified resource.
     @param request: the L{IRequest} for the current request.
     @param props: a list of property elements or qname tuples for the properties of interest.
-    @param resource: the L{CalDAVFile} for the targetted resource.
+    @param resource: the L{CalDAVFile} for the targeted resource.
     @param calendar: the L{Component} for the calendar for the resource. This may be None
         if the calendar has not already been read in, in which case the resource
         will be used to get the calendar if needed.
@@ -298,9 +298,9 @@
     @param matchtotal:  the running total for the number of matches.
     @param excludeuid:  a C{str} containing a UID value to exclude any components with that
         UID from contributing to free-busy.
-    @param organizer:   a C{str} containing the value of the ORGANIZER proeprty in the VFREEBUSY request.
+    @param organizer:   a C{str} containing the value of the ORGANIZER property in the VFREEBUSY request.
         This is used in conjunction with the UID value to process exclusions.
-    @param same_calendar_user:   a C{bool} indicating whether the calendar user requesting tyhe free-busy information
+    @param same_calendar_user:   a C{bool} indicating whether the calendar user requesting the free-busy information
         is the same as the calendar user being targeted.
     @param servertoserver:       a C{bool} indicating whether we are doing a local or remote lookup request.
     """
@@ -313,6 +313,10 @@
         except AccessDeniedError:
             returnValue(matchtotal)
 
+    # May need organizer principal
+    organizer_principal = calresource.principalForCalendarUserAddress(organizer) if organizer else None
+    organizer_uid = organizer_principal.principalUID() if organizer_principal else ""
+
     #
     # What we do is a fake calendar-query for VEVENT/VFREEBUSYs in the specified time-range.
     # We then take those results and merge them into one VFREEBUSY component
@@ -339,7 +343,7 @@
         tz = None
     tzinfo = filter.settimezone(tz)
 
-    # Do some optimisation of access control calculation by determining any inherited ACLs outside of
+    # Do some optimization of access control calculation by determining any inherited ACLs outside of
     # the child resource loop and supply those to the checkPrivileges on each child.
     filteredaces = (yield calresource.inheritedACEsforChildren(request))
 
@@ -363,8 +367,8 @@
         calendar = calresource.iCalendar(name)
         
         # The calendar may come back as None if the resource is being changed, or was deleted
-        # between our initial index query and getting here. For now we will ignore this errror, but in
-        # the longer term we need to simplement some form of locking, perhaps.
+        # between our initial index query and getting here. For now we will ignore this error, but in
+        # the longer term we need to implement some form of locking, perhaps.
         if calendar is None:
             log.err("Calendar %s is missing from calendar collection %r" % (name, calresource))
             continue
@@ -373,18 +377,22 @@
         if excludeuid:
             # See if we have a UID match
             if (excludeuid == uid):
+                test_organizer = calendar.getOrganizer()
+                test_principal = calresource.principalForCalendarUserAddress(test_organizer) if test_organizer else None
+                test_uid = test_principal.principalUID() if test_principal else ""
+
                 # Check that ORGANIZER's match (security requirement)
-                if (organizer is None) or (organizer == calendar.getOrganizer()):
+                if (organizer is None) or (organizer_uid == test_uid):
                     continue
                 # Check for no ORGANIZER and check by same calendar user
-                elif (calendar.getOrganizer() is None) and same_calendar_user:
+                elif (test_organizer is None) and same_calendar_user:
                     continue
 
         if filter.match(calendar, None):
             # Check size of results is within limit
             matchtotal += 1
             if matchtotal > max_number_of_matches:
-                raise NumberOfMatchesWithinLimits(max_number_of_results)
+                raise NumberOfMatchesWithinLimits(max_number_of_matches)
 
             if calendar.mainType() == "VEVENT":
                 processEventFreeBusy(calendar, fbinfo, timerange, tzinfo)
@@ -486,7 +494,7 @@
             if fbtype == "FREE":
                 continue
             
-            # Look at each period in the propert
+            # Look at each period in the property
             assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
             for period in fb.value():
                 # Clip period for this instance
@@ -555,7 +563,7 @@
             uid = component.propertyValue("UID")
             uidmap.setdefault(uid, []).append(component)
             
-    # Then we expand each uid set seperately
+    # Then we expand each uid set separately
     for componentSet in uidmap.itervalues():
         instances = InstanceList()
         instances.expandTimeRanges(componentSet, timerange.end)

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2008-12-19 20:23:53 UTC (rev 3559)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2008-12-19 20:26:58 UTC (rev 3560)
@@ -59,8 +59,10 @@
 from twistedcaldav.ical import allowedComponents
 from twistedcaldav.ical import Component as iComponent
 from twistedcaldav.log import LoggingMixIn
+from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
 
 from urlparse import urlsplit
+import itertools
 
 if twistedcaldav.__version__:
     serverVersion = twisted.web2.server.VERSION + " TwistedCalDAV/" + twistedcaldav.__version__
@@ -627,6 +629,73 @@
         """
         return caldavxml.CalendarData.fromCalendar(self.iCalendar(name))
 
+    def iCalendarAddressDoNormalization(self, ical):
+        """
+        Normalize calendar user addresses in the supplied iCalendar object into their
+        urn:uuid form where possible. Also reset CN= property and add X-CALENDARSERVER-EMAIL property.
+
+        @param ical: calendar object to normalize.
+        @type ical: L{Component}
+        """
+        
+        def normalizeCalendarUserAddress(prop):
+            """
+            Do the ORGANIZER/ATTENDEE property normalization.
+
+            @param prop: organizer/attendee property
+            @type prop: L{Property}
+            """
+            
+            # Check that we have a principal for this calendar user address - if not we
+            # cannot do anything with it
+            cuaddr = normalizeCUAddr(prop.value())
+            principal = self.principalForCalendarUserAddress(cuaddr)
+            if principal is None:
+                return
+
+            # Always re-write value to urn:uuid
+            prop.setValue("urn:uuid:%s" % (principal.record.guid,))
+
+            # Always re-write the CN parameter
+            if principal.record.fullName:
+                prop.params()["CN"] = [principal.record.fullName,]
+            else:
+                try:
+                    del prop.params()["CN"]
+                except KeyError:
+                    pass
+
+            # Re-write the X-CALENDARSERVER-EMAIL if its value no longer matches
+            oldemail = prop.params().get("X-CALENDARSERVER-EMAIL", (None,))[0]
+            if oldemail:
+                oldemail = "mailto:%s" % (oldemail,)
+            if oldemail is None or oldemail not in principal.record.calendarUserAddresses:
+                if cuaddr.startswith("mailto:") and cuaddr in principal.record.calendarUserAddresses:
+                    email = cuaddr[7:]
+                else:
+                    for addr in principal.record.calendarUserAddresses:
+                        if addr.startswith("mailto:"):
+                            email = addr[7:]
+                            break
+                    else:
+                        email = None
+                        
+                if email:
+                    prop.params()["X-CALENDARSERVER-EMAIL"] = [email,]
+                else:
+                    try:
+                        del prop.params()["X-CALENDARSERVER-EMAIL"]
+                    except KeyError:
+                        pass
+
+        for component in ical.subcomponents():
+            if component.name() != "VTIMEZONE":
+                for prop in itertools.chain(
+                    component.properties("ORGANIZER"),
+                    component.properties("ATTENDEE")
+                ):
+                    normalizeCalendarUserAddress(prop)
+
     def principalForCalendarUserAddress(self, address):
         for principalCollection in self.principalCollections():
             principal = principalCollection.principalForCalendarUserAddress(address)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20081219/1506d5be/attachment-0001.html>


More information about the calendarserver-changes mailing list