[CalendarServer-changes] [4877] CalendarServer/branches/users/cdaboo/partition-4464

source_changes at macosforge.org source_changes at macosforge.org
Thu Dec 17 12:29:38 PST 2009


Revision: 4877
          http://trac.macosforge.org/projects/calendarserver/changeset/4877
Author:   cdaboo at apple.com
Date:     2009-12-17 12:29:37 -0800 (Thu, 17 Dec 2009)
Log Message:
-----------
Merging r4858 through r4876.

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

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh
    CalendarServer/branches/users/cdaboo/partition-4464/twext/internet/tcp.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/dateops.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/resource.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/test/test_opendirectory.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/extensions.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/icaldiff.py
    CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/itip.py

Modified: CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh	2009-12-17 20:29:37 UTC (rev 4877)
@@ -504,7 +504,7 @@
     false true true true 212;
 
   # Tool dependencies.  The code itself doesn't depend on these, but you probably want them.
-  svn_get "CalDAVTester" "${top}/CalDAVTester" "${svn_uri_base}/CalDAVTester/trunk" 4853;
+  svn_get "CalDAVTester" "${top}/CalDAVTester" "${svn_uri_base}/CalDAVTester/trunk" 4874;
   svn_get "Pyflakes" "${top}/Pyflakes" http://divmod.org/svn/Divmod/trunk/Pyflakes 17198;
 }
 

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twext/internet/tcp.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twext/internet/tcp.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twext/internet/tcp.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -41,11 +41,11 @@
         self.numberAccepts = min(self.factory.maxRequests - self.factory.outstandingRequests, self.factory.maxAccepts)
         tcp.Port.doRead(self)
 
-class MaxAcceptTCPPort(tcp.Port, MaxAcceptPortMixin):
+class MaxAcceptTCPPort(MaxAcceptPortMixin, tcp.Port):
     """ Use for non-inheriting tcp ports """
     pass
 
-class MaxAcceptSSLPort(ssl.Port, MaxAcceptPortMixin):
+class MaxAcceptSSLPort(MaxAcceptPortMixin, ssl.Port):
     """ Use for non-inheriting SSL ports """
     pass
 

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/dateops.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/dateops.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/dateops.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -52,8 +52,9 @@
     DTSTART and DTEND.
     """
     
-    assert dtend is None or duration is None, "Cannot specify both dtend and duration" 
-    dtstart = normalizeToUTC(dtstart)
+    assert dtend is None or duration is None, "Cannot specify both dtend and duration"
+    if dtstart is not None:
+        dtstart = normalizeToUTC(dtstart)
     if dtend is not None:
         dtend = normalizeToUTC(dtend)
     elif duration:

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/appleopendirectory.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/appleopendirectory.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -322,22 +322,84 @@
 
     _fromODRecordTypes = dict([(b, a) for a, b in _toODRecordTypes.iteritems()])
 
+    def _uniqueTupleFromAttribute(self, attribute):
+        if attribute:
+            if isinstance(attribute, str):
+                return (attribute,)
+            else:
+                s = set()
+                return tuple([(s.add(x), x)[1] for x in attribute if x not in s])
+        else:
+            return ()
 
-    def recordsMatchingFields(self, fields, operand="or", recordType=None):
+    def _setFromAttribute(self, attribute, lower=False):
+        if attribute:
+            if isinstance(attribute, str):
+                return set((attribute.lower() if lower else attribute,))
+            else:
+                return set([item.lower() if lower else item for item in attribute])
+        else:
+            return ()
 
+    def recordsMatchingFields(self, fields, operand="or", recordType=None,
+        lookupMethod=opendirectory.queryRecordsWithAttributes_list):
+
         # Note that OD applies case-sensitivity globally across the entire
         # query, not per expression, so the current code uses whatever is
         # specified in the last field in the fields list
 
         def collectResults(results):
             self.log_info("Got back %d records from OD" % (len(results),))
-            for key, val in results:
-                self.log_debug("OD result: %s %s" % (key, val))
+            for key, value in results:
+                self.log_debug("OD result: %s %s" % (key, value))
                 try:
-                    guid = val[dsattributes.kDS1AttrGeneratedUID]
-                    record = self.recordWithGUID(guid)
-                    if record:
-                        yield record
+                    recordNodeName = value.get(
+                        dsattributes.kDSNAttrMetaNodeLocation)
+                    recordShortNames = self._uniqueTupleFromAttribute(
+                        value.get(dsattributes.kDSNAttrRecordName))
+                    if (recordNodeName == "/Local/Default" and not
+                        (config.Scheduling.iMIP.Username in recordShortNames)):
+                        continue
+
+                    recordGUID = value.get(dsattributes.kDS1AttrGeneratedUID)
+                    recordType = value.get(dsattributes.kDSNAttrRecordType)
+                    if isinstance(recordType, list):
+                        recordType = recordType[0]
+                    if not recordType:
+                        continue
+                    recordType = self._fromODRecordTypes[recordType]
+
+                    recordAuthIDs = self._setFromAttribute(
+                        value.get(dsattributes.kDSNAttrAltSecurityIdentities))
+                    recordFullName = value.get(
+                        dsattributes.kDS1AttrDistinguishedName)
+                    recordFirstName = value.get(dsattributes.kDS1AttrFirstName)
+                    recordLastName = value.get(dsattributes.kDS1AttrLastName)
+                    recordEmailAddresses = self._setFromAttribute(
+                        value.get(dsattributes.kDSNAttrEMailAddress),
+                        lower=True)
+
+                    # Create records but don't store them in our index or
+                    # send them to memcached, because these are transient,
+                    # existing only so we can create principal resource
+                    # objects that are used to generate the REPORT result.
+
+                    record = OpenDirectoryRecord(
+                        service               = self,
+                        recordType            = recordType,
+                        guid                  = recordGUID,
+                        nodeName              = recordNodeName,
+                        shortNames            = recordShortNames,
+                        authIDs               = recordAuthIDs,
+                        fullName              = recordFullName,
+                        firstName             = recordFirstName,
+                        lastName              = recordLastName,
+                        emailAddresses        = recordEmailAddresses,
+                        enabledForCalendaring = True,
+                        memberGUIDs           = (),
+                    )
+                    yield record
+
                 except KeyError:
                     pass
 
@@ -364,7 +426,7 @@
                     (recordTypes, operand, caseless, complexExpression))
 
                 results.extend(
-                    opendirectory.queryRecordsWithAttributes_list(
+                    lookupMethod(
                         directory,
                         complexExpression,
                         caseless,
@@ -392,7 +454,17 @@
             multiQuery,
             self.directory,
             queries,
-            [ dsattributes.kDS1AttrGeneratedUID ],
+            [
+                dsattributes.kDS1AttrGeneratedUID,
+                dsattributes.kDSNAttrRecordName,
+                dsattributes.kDSNAttrAltSecurityIdentities,
+                dsattributes.kDSNAttrRecordType,
+                dsattributes.kDS1AttrDistinguishedName,
+                dsattributes.kDS1AttrFirstName,
+                dsattributes.kDS1AttrLastName,
+                dsattributes.kDSNAttrEMailAddress,
+                dsattributes.kDSNAttrMetaNodeLocation,
+            ],
             operand
         )
         deferred.addCallback(collectResults)
@@ -491,25 +563,7 @@
                 self.log_error("OpenDirectory (node=%s) error: %s" % (self.realmName, str(ex)))
                 raise
 
-        def _uniqueTupleFromAttribute(attribute):
-            if attribute:
-                if isinstance(attribute, str):
-                    return (attribute,)
-                else:
-                    s = set()
-                    return tuple([(s.add(x), x)[1] for x in attribute if x not in s])
-            else:
-                return ()
 
-        def _setFromAttribute(attribute, lower=False):
-            if attribute:
-                if isinstance(attribute, str):
-                    return set((attribute.lower() if lower else attribute,))
-                else:
-                    return set([item.lower() if lower else item for item in attribute])
-            else:
-                return ()
-
         enabledRecords = []
         disabledRecords = []
 
@@ -517,15 +571,15 @@
 
             # Now get useful record info.
             recordGUID           = value.get(dsattributes.kDS1AttrGeneratedUID)
-            recordShortNames     = _uniqueTupleFromAttribute(value.get(dsattributes.kDSNAttrRecordName))
+            recordShortNames     = self._uniqueTupleFromAttribute(value.get(dsattributes.kDSNAttrRecordName))
             recordType           = value.get(dsattributes.kDSNAttrRecordType)
             if isinstance(recordType, list):
                 recordType = recordType[0]
-            recordAuthIDs        = _setFromAttribute(value.get(dsattributes.kDSNAttrAltSecurityIdentities))
+            recordAuthIDs        = self._setFromAttribute(value.get(dsattributes.kDSNAttrAltSecurityIdentities))
             recordFullName       = value.get(dsattributes.kDS1AttrDistinguishedName)
             recordFirstName      = value.get(dsattributes.kDS1AttrFirstName)
             recordLastName       = value.get(dsattributes.kDS1AttrLastName)
-            recordEmailAddresses = _setFromAttribute(value.get(dsattributes.kDSNAttrEMailAddress), lower=True)
+            recordEmailAddresses = self._setFromAttribute(value.get(dsattributes.kDSNAttrEMailAddress), lower=True)
             recordNodeName       = value.get(dsattributes.kDSNAttrMetaNodeLocation)
 
             if recordNodeName == "/Local/Default" and not (config.Scheduling.iMIP.Username in recordShortNames):

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/principal.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/principal.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -183,14 +183,14 @@
     def principalForUID(self, uid):
         raise NotImplementedError("Subclass must implement principalForUID()")
 
+    def principalForCalendarUserAddress(self, address):
+        raise NotImplementedError("Subclass must implement principalForCalendarUserAddress()")
+
     def principalForRecord(self, record):
         if record is None or not record.enabled:
             return None
         return self.principalForUID(record.uid)
 
-    def principalForCalendarUserAddress(self, address):
-        raise NotImplementedError("Subclass must implement principalForCalendarUserAddress()")
-
     ##
     # DAV-property-to-record-field mapping
     ##
@@ -332,6 +332,8 @@
         log.debug("No principal for calendar user address: %r" % (address,))
         return None
 
+    def principalForRecord(self, record):
+        return self.getChild(uidsResourceName).principalForRecord(record)
 
     ##
     # Static
@@ -383,6 +385,9 @@
     def principalForCalendarUserAddress(self, address):
         return self.parent.principalForCalendarUserAddress(address)
 
+    def principalForRecord(self, record):
+        return self.parent.principalForRecord(record)
+
     ##
     # Static
     ##
@@ -443,6 +448,16 @@
     def principalForCalendarUserAddress(self, address):
         return self.parent.principalForCalendarUserAddress(address)
 
+    def principalForRecord(self, record):
+        if record is None:
+            return None
+
+        if record.enabledForCalendaring:
+            principal = DirectoryCalendarPrincipalResource(self, record)
+        else:
+            principal = DirectoryPrincipalResource(self, record)
+        return principal
+
     ##
     # Static
     ##
@@ -464,15 +479,11 @@
 
         record = self.directory.recordWithUID(primaryUID)
 
-        if record is None or not record.enabled:
+        primaryPrincipal = self.principalForRecord(record) if record.enabled else None
+        if primaryPrincipal is None:
             log.err("No principal found for UID: %s" % (name,))
             return None
 
-        if record.enabledForCalendaring:
-            primaryPrincipal = DirectoryCalendarPrincipalResource(self, record)
-        else:
-            primaryPrincipal = DirectoryPrincipalResource(self, record)
-
         if subType is None:
             return primaryPrincipal
         else:

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/resource.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/resource.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -60,7 +60,10 @@
 
         name = segments[0]
         if name != "":
-            child = self.provisionChild(name)
+            # If getChild() finds a child resource, return it
+            child = self.getChild(name)
+            if child is None:
+                child = self.provisionChild(name)
             if child:
                 returnValue((child, segments[1:],))
         

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/test/test_opendirectory.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/directory/test/test_opendirectory.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -22,6 +22,7 @@
     import twisted.web2.auth.digest
     import twistedcaldav.directory.test.util
     from twistedcaldav.directory import augment
+    from twisted.internet.defer import inlineCallbacks
     from twistedcaldav.directory.directory import DirectoryService
     from twistedcaldav.directory.appleopendirectory import OpenDirectoryRecord
     import dsattributes
@@ -239,3 +240,24 @@
 
             recordTypes = [DirectoryService.recordType_users, DirectoryService.recordType_groups, DirectoryService.recordType_locations, DirectoryService.recordType_resources]
             self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_CUA, "mailto:user1 at example.com", lookupMethod=lookupMethod)
+
+
+        @inlineCallbacks
+        def test_recordsMatchingFields(self):
+
+            def lookupMethod(obj, compound, casei, recordType, attributes, count=0):
+                if dsattributes.kDSStdRecordTypeUsers in recordType:
+                    return [
+                        ('morgen', {'dsAttrTypeStandard:RecordType': 'dsRecTypeStandard:Users', 'dsAttrTypeStandard:AppleMetaNodeLocation': '/LDAPv3/127.0.0.1', 'dsAttrTypeStandard:RecordName': ['morgen', 'Morgen Sagen'], 'dsAttrTypeStandard:FirstName': 'Morgen', 'dsAttrTypeStandard:GeneratedUID': '83479230-821E-11DE-B6B0-DBB02C6D659D', 'dsAttrTypeStandard:LastName': 'Sagen', 'dsAttrTypeStandard:EMailAddress': 'morgen at example.com', 'dsAttrTypeStandard:RealName': 'Morgen Sagen'}),
+                        ('morehouse', {'dsAttrTypeStandard:RecordType': 'dsRecTypeStandard:Users', 'dsAttrTypeStandard:AppleMetaNodeLocation': '/LDAPv3/127.0.0.1', 'dsAttrTypeStandard:RecordName': ['morehouse', 'Joe Morehouse'], 'dsAttrTypeStandard:FirstName': 'Joe', 'dsAttrTypeStandard:GeneratedUID': '98342930-90DC-11DE-A842-A29601FB13E8', 'dsAttrTypeStandard:LastName': 'Morehouse', 'dsAttrTypeStandard:EMailAddress': 'morehouse at example.com', 'dsAttrTypeStandard:RealName': 'Joe Morehouse'}),
+                    ]
+                else:
+                    return []
+
+            fields = [('fullName', 'mor', True, u'starts-with'), ('emailAddresses', 'mor', True, u'starts-with'), ('firstName', 'mor', True, u'starts-with'), ('lastName', 'mor', True, u'starts-with')]
+
+            results = (yield self.service().recordsMatchingFields(fields, lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 2)
+            for record in results:
+                self.assertTrue(isinstance(record, OpenDirectoryRecord))

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/extensions.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/extensions.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -36,7 +36,7 @@
 
 from twisted.internet.defer import succeed, DeferredList, inlineCallbacks, returnValue
 from twisted.internet.defer import maybeDeferred
-from twisted.cred.error import UnauthorizedLogin
+from twisted.cred.error import LoginFailed, UnauthorizedLogin
 from twisted.web2 import responsecode
 from twisted.web2.auth.wrapper import UnauthorizedResponse
 from twisted.web2.http import HTTPError, Response, RedirectResponse
@@ -142,7 +142,11 @@
             else:
                 factory = request.credentialFactories[authHeader[0]]
 
-                creds = (yield factory.decode(authHeader[1], request))
+                try:
+                    creds = (yield factory.decode(authHeader[1], request))
+                except (UnauthorizedLogin, LoginFailed,):
+                    raise HTTPError((yield UnauthorizedResponse.makeResponse(
+                                request.credentialFactories, request.remoteAddr)))
 
                 # Try to match principals in each principal collection on the resource
                 authnPrincipal, authzPrincipal = (yield self.principalsForAuthID(request, creds))

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/icaldiff.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/icaldiff.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -530,7 +530,7 @@
         # Need to special case EXDATEs as an Attendee can effectively DECLINE by adding an EXDATE
         if serverProps[:-1] != clientProps[:-1]:
             invalidChanges = []
-            propNames = ("DTSTART", "DTEND", "RRULE", "RDATE", "EXDATE")
+            propNames = ("DTSTART", "DTEND", "DUE", "RRULE", "RDATE", "EXDATE")
             invalidChanges = [propName for ctr, propName in enumerate(propNames) if serverProps[ctr] != clientProps[ctr]]
             log.debug("Critical properties do not match: %s" % (", ".join(invalidChanges),))
             return False
@@ -548,16 +548,35 @@
     def _getNormalizedDateTimeProperties(self, component):
         
         # Basic time properties
-        dtstart = component.getProperty("DTSTART")
-        dtend = component.getProperty("DTEND")
-        duration = component.getProperty("DURATION")
-        
-        newdtstart, newdtend = normalizeStartEndDuration(
-            dtstart.value(),
-            dtend.value() if dtend is not None else None,
-            duration.value() if duration is not None else None,
-        )
-        
+        if component.name() in ("VEVENT", "VJOURNAL",):
+            dtstart = component.getProperty("DTSTART")
+            dtend = component.getProperty("DTEND")
+            duration = component.getProperty("DURATION")
+            
+            newdtstart, newdtend = normalizeStartEndDuration(
+                dtstart.value() if dtstart is not None else None,
+                dtend.value() if dtend is not None else None,
+                duration.value() if duration is not None else None,
+            )
+            newdue = None
+            
+        elif component.name() == "VTODO":
+            dtstart = component.getProperty("DTSTART")
+            duration = component.getProperty("DURATION")
+            
+            if dtstart or duration:
+                newdtstart, newdtend = normalizeStartEndDuration(
+                    dtstart.value() if dtstart is not None else None,
+                    None,
+                    duration.value() if duration is not None else None,
+                )
+            else:
+                newdtstart = newdtend = None
+
+            newdue = component.getProperty("DUE")
+            if newdue is not None:
+                newdue = normalizeToUTC(newdue.value())
+            
         # Recurrence rules - we need to normalize the order of the value parts
         newrrules = set()
         rrules = component.properties("RRULE")
@@ -579,7 +598,7 @@
         for exdate in exdates:
             newexdates.update([normalizeToUTC(value) for value in exdate.value()])
 
-        return newdtstart, newdtend, newrrules, newrdates, newexdates
+        return newdtstart, newdtend, newdue, newrrules, newrdates, newexdates
 
     def _transferProperty(self, propName, serverComponent, clientComponent):
 

Modified: CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/itip.py	2009-12-17 19:46:55 UTC (rev 4876)
+++ CalendarServer/branches/users/cdaboo/partition-4464/twistedcaldav/scheduling/itip.py	2009-12-17 20:29:37 UTC (rev 4877)
@@ -491,7 +491,7 @@
     def fixForiCal3(components, recipient, compatibilityMode):
         # For each component where the ATTENDEE property of the recipient has PARTSTAT
         # NEEDS-ACTION we need to add X-APPLE-NEEDS-REPLY:TRUE
-        # We also add TRANSP:TRANSPARENT
+        # We also add TRANSP:TRANSPARENT for VEVENTs
         for component in components:
             if component.name() == "VTIMEZONE":
                 continue
@@ -501,7 +501,8 @@
                 if partstat == "NEEDS-ACTION":
                     if compatibilityMode:
                         component.addProperty(Property("X-APPLE-NEEDS-REPLY", "TRUE"))
-                    component.replaceProperty(Property("TRANSP", "TRANSPARENT"))
+                    if component.name() == "VEVENT":
+                        component.replaceProperty(Property("TRANSP", "TRANSPARENT"))
 
 class iTipGenerator(object):
     
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091217/5cda9c2a/attachment-0001.html>


More information about the calendarserver-changes mailing list