[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