[CalendarServer-changes] [8845] CalendarServer/branches/users/gaya/ldapdirectorybacker
source_changes at macosforge.org
source_changes at macosforge.org
Mon Mar 12 20:59:12 PDT 2012
Revision: 8845
http://trac.macosforge.org/projects/calendarserver/changeset/8845
Author: gaya at apple.com
Date: 2012-03-12 20:59:12 -0700 (Mon, 12 Mar 2012)
Log Message:
-----------
use opendirectorybacker's _getDSFilter in ldapdirectorybacker
Modified Paths:
--------------
CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/carddav-ldaptest.plist
CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py
CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/opendirectorybacker.py
Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/carddav-ldaptest.plist
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/carddav-ldaptest.plist 2012-03-13 00:22:30 UTC (rev 8844)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/carddav-ldaptest.plist 2012-03-13 03:59:12 UTC (rev 8845)
@@ -547,7 +547,7 @@
<!-- Log levels -->
<key>DefaultLogLevel</key>
- <string>info</string> <!-- debug, info, warn, error -->
+ <string>debug</string> <!-- debug, info, warn, error -->
<!-- Log level overrides for specific functionality -->
<key>LogLevels</key>
@@ -1145,7 +1145,7 @@
<key>people</key>
<dict>
<!-- map ldap attributes to ds attribute types. -->
- <key>ldapdsattrmap</key>
+ <key>ldapDSAttrMap</key>
<dict>
<key>givenName</key>
<string>FirstName</string>
@@ -1206,7 +1206,7 @@
<key>filter</key>
<string></string>
<!-- map from ab query to indexed ldap attribute. If unindexed, too slow. -->
- <key>searchmap</key>
+ <key>searchMap</key>
<dict>
<key>FN</key>
<string>cn</string>
@@ -1221,7 +1221,7 @@
<!-- people vCards for another server. Unused -->
<key>people - 2</key>
<dict>
- <key>ldapdsattrmap</key>
+ <key>ldapDSAttrMap</key>
<dict>
<key>givenName</key>
<string>FirstName</string>
@@ -1241,7 +1241,7 @@
<string>ou=People</string>
<key>filter</key>
<string></string>
- <key>searchmap</key>
+ <key>searchMap</key>
<dict>
<key>FN</key>
<string>cn</string>
@@ -1260,7 +1260,7 @@
<string>(objectClass=appleGroup)</string>
<key>getAllAttributes</key>
<false/>
- <key>searchmap</key>
+ <key>searchMap</key>
<dict>
<key>EMAIL</key>
<string>appleGroupEmail</string>
@@ -1269,7 +1269,7 @@
<key>UID</key>
<string>appleDSID</string>
</dict>
- <key>ldapdsattrmap</key>
+ <key>ldapDSAttrMap</key>
<dict>
<key>cn</key>
<string>RecordName</string>
Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py 2012-03-13 00:22:30 UTC (rev 8844)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py 2012-03-13 03:59:12 UTC (rev 8845)
@@ -67,7 +67,7 @@
import ldap
from twistedcaldav.directory.ldapdirectory import LdapDirectoryService
-from twistedcaldav.directory.opendirectorybacker import VCardRecord
+from twistedcaldav.directory.opendirectorybacker import VCardRecord, getDSFilter
class LdapDirectoryBackingService(LdapDirectoryService):
@@ -93,10 +93,10 @@
"queryTypes": ("people",),
"people": {
"rdn":"ou=people",
- "searchmap" : { # maps vCard properties to searchable ldap attributes
+ "searchMap" : { # maps vCard properties to searchable ldap attributes
"FN" : "cn",
},
- "ldapdsattrmap" : { # maps ldap attributes to ds record types
+ "ldapDSAttrMap" : { # maps ldap attributes to ds record types
"cn" : "dsAttrTypeStandard:RealName",
},
},
@@ -136,8 +136,8 @@
# or we could just require dsAttrTypeStandard: prefix in the plist
rdnSchema = params["rdnSchema"];
for queryType in rdnSchema["queryTypes"]:
- ldapdsattrmap = rdnSchema[queryType]["ldapdsattrmap"]
- for ldapAttrName, dsAttrNames in ldapdsattrmap.iteritems():
+ ldapDSAttrMap = rdnSchema[queryType]["ldapDSAttrMap"]
+ for ldapAttrName, dsAttrNames in ldapDSAttrMap.iteritems():
if not isinstance(dsAttrNames, list):
dsAttrNames = [dsAttrNames,]
@@ -150,9 +150,9 @@
# not needed, but tests code paths
if len(normalizedDSAttrNames) > 1:
- ldapdsattrmap[ldapAttrName] = normalizedDSAttrNames
+ ldapDSAttrMap[ldapAttrName] = normalizedDSAttrNames
else:
- ldapdsattrmap[ldapAttrName] = normalizedDSAttrNames[0]
+ ldapDSAttrMap[ldapAttrName] = normalizedDSAttrNames[0]
self.log_debug("_actuallyConfigure after clean: params=%s" % (params,))
@@ -200,323 +200,9 @@
#@inlineCallbacks
def createCache(self):
succeed(None)
-
- #to do: use opendirectorybacker._getDSFilter passing in search map
- def _getLdapFilter(self, addressBookFilter, searchmap):
- """
- Convert the supplied addressbook-query into an expression tree.
-
- @param filter: the L{Filter} for the addressbook-query to convert.
- @return: (needsAllRecords, espressionAttributes, expression) tuple
- """
- def propFilterListQuery(filterAllOf, propFilters):
-
- def propFilterExpression(filterAllOf, propFilter):
- #print("propFilterExpression")
- """
- Create an expression for a single prop-filter element.
-
- @param propFilter: the L{PropertyFilter} element.
- @return: (needsAllRecords, espressionAttributes, expressions) tuple
- """
-
- def definedExpression( defined, allOf, filterName, constant, queryAttributes, allAttrStrings):
- if constant or filterName in ("N" , "FN", "UID", ):
- return (defined, [], []) # all records have this property so no records do not have it
- else:
- matchList = list(set([dsquery.match(attrName, "", dsattributes.eDSStartsWith) for attrName in allAttrStrings]))
- if defined:
- return andOrExpression(allOf, queryAttributes, matchList)
- else:
- if len(matchList) > 1:
- expr = dsquery.expression( dsquery.expression.OR, matchList )
- else:
- expr = matchList
- return (False, queryAttributes, [dsquery.expression( dsquery.expression.NOT, expr),])
- #end isNotDefinedExpression()
-
-
- def andOrExpression(propFilterAllOf, queryAttributes, matchList):
- #print("andOrExpression(propFilterAllOf=%r, queryAttributes%r, matchList%r)" % (propFilterAllOf, queryAttributes, matchList))
- if propFilterAllOf and len(matchList):
- # add OR expression because parent will AND
- return (False, queryAttributes, [dsquery.expression( dsquery.expression.OR, matchList),])
- else:
- return (False, queryAttributes, matchList)
- #end andOrExpression()
-
-
- # short circuit parameter filters
- def supportedParamter( filterName, paramFilters, propFilterAllOf ):
-
- def supported( paramFilterName, paramFilterDefined, params ):
- paramFilterName = paramFilterName.upper()
- if len(params.keys()) and ((paramFilterName in params.keys()) != paramFilterDefined):
- return False
- if len(params[paramFilterName]) and str(paramFilter.qualifier).upper() not in params[paramFilterName]:
- return False
- return True
- #end supported()
-
-
- oneSupported = False
- for paramFilter in paramFilters:
- if filterName == "PHOTO":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "ENCODING": ["B",], "TYPE": ["JPEG",], }):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "ADR":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": ["WORK", "PREF", "POSTAL", "PARCEL",], }):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "LABEL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": ["POSTAL", "PARCEL",]}):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "TEL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": [], }): # has params derived from ds attributes
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "EMAIL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": [], }): # has params derived from ds attributes
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "URL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, {}):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "KEY":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "ENCODING": ["B",], "TYPE": ["PGPPUBILICKEY", "USERCERTIFICATE", "USERPKCS12DATA", "USERSMIMECERTIFICATE",] }):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif not filterName.startswith("X-"): #X- IMHandles X-ABRELATEDNAMES excepted, no other params are used
- if propFilterAllOf == paramFilter.defined:
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
-
- if propFilterAllOf:
- return True
- else:
- return oneSupported
- #end supportedParamter()
-
-
- def textMatchElementExpression( propFilterAllOf, textMatchElement ):
-
- # pre process text match strings for ds query
- def getMatchStrings( propFilter, matchString ):
-
- if propFilter.filter_name in ("REV" , "BDAY", ):
- rawString = matchString
- matchString = ""
- for c in rawString:
- if not c in "TZ-:":
- matchString += c
- elif propFilter.filter_name == "GEO":
- matchString = ",".join(matchString.split(";"))
- if propFilter.filter_name in ("N" , "ADR", "ORG", ):
- # for structured properties, change into multiple strings for ds query
- if propFilter.filter_name == "ADR":
- #split by newline and comma
- rawStrings = ",".join( matchString.split("\n") ).split(",")
- else:
- #split by space
- rawStrings = matchString.split(" ")
-
- # remove empty strings
- matchStrings = []
- for oneString in rawStrings:
- if len(oneString):
- matchStrings += [oneString,]
- return matchStrings
-
- elif len(matchString):
- return [matchString,]
- else:
- return []
- # end getMatchStrings
-
- if constant:
- # do the match right now! Return either all or none.
- return( textMatchElement.test([constant,]), [], [] )
- else:
-
- matchStrings = getMatchStrings(propFilter, textMatchElement.text)
-
- if not len(matchStrings) or binaryAttrStrs:
- # no searching text in binary ds attributes, so change to defined/not defined case
- if textMatchElement.negate:
- return definedExpression(False, propFilterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
- # else fall through to attribute exists case below
- else:
-
- # special case UID's formed from node and record name
- if propFilter.filter_name == "UID":
- matchString = matchStrings[0]
- seperatorIndex = matchString.find(VCardRecord.peopleUIDSeparator)
- if seperatorIndex > 1:
- recordNameStart = seperatorIndex + len(VCardRecord.peopleUIDSeparator)
- else:
- seperatorIndex = matchString.find(VCardRecord.userUIDSeparator)
- if seperatorIndex > 1:
- recordNameStart = seperatorIndex + len(VCardRecord.userUIDSeparator)
- else:
- recordNameStart = sys.maxint
-
- if recordNameStart < len(matchString)-1:
- try:
- recordNameQualifier = matchString[recordNameStart:].decode("base64").decode("utf8")
- except Exception, e:
- self.log_debug("Could not decode UID string %r in %r: %r" % (matchString[recordNameStart:], matchString, e,))
- else:
- if textMatchElement.negate:
- return (False, queryAttributes,
- [dsquery.expression(dsquery.expression.NOT, dsquery.match(dsattributes.kDSNAttrRecordName, recordNameQualifier, dsattributes.eDSExact)),]
- )
- else:
- return (False, queryAttributes,
- [dsquery.match(dsattributes.kDSNAttrRecordName, recordNameQualifier, dsattributes.eDSExact),]
- )
-
- # use match_type where possible depending on property/attribute mapping
- # Note that case sensitive negate will not work
- # Should return all records in that case
- matchType = dsattributes.eDSContains
- if propFilter.filter_name in ("NICKNAME" , "TITLE" , "NOTE" , "UID", "URL", "N", "ADR", "ORG", "REV", "LABEL", ):
- if textMatchElement.match_type == "equals":
- matchType = dsattributes.eDSExact
- elif textMatchElement.match_type == "starts-with":
- matchType = dsattributes.eDSStartsWith
- elif textMatchElement.match_type == "ends-with":
- matchType = dsattributes.eDSEndsWith
-
- matchList = []
- for matchString in matchStrings:
- matchList += [dsquery.match(attrName, matchString, matchType) for attrName in stringAttrStrs]
-
- matchList = list(set(matchList))
-
- if textMatchElement.negate:
- if len(matchList) > 1:
- expr = dsquery.expression( dsquery.expression.OR, matchList )
- else:
- expr = matchList
- return (False, queryAttributes, [dsquery.expression( dsquery.expression.NOT, expr),])
- else:
- return andOrExpression(propFilterAllOf, queryAttributes, matchList)
-
- # attribute exists search
- return definedExpression(True, propFilterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
- #end textMatchElementExpression()
-
-
- # get attribute strings from dsqueryAttributesForProperty list
- #queryAttributes = list(set(VCardRecord.dsqueryAttributesForProperty.get(propFilter.filter_name, [])).intersection(set(self.allowedDSQueryAttributes)))
- #queryAttributes = VCardRecord.dsqueryAttributesForProperty.get(propFilter.filter_name, [])
- queryAttributes = searchmap.get(propFilter.filter_name, [])
- if isinstance(queryAttributes, str):
- queryAttributes = (queryAttributes,)
-
- binaryAttrStrs = []
- stringAttrStrs = []
- for attr in queryAttributes:
- if isinstance(attr, tuple):
- binaryAttrStrs.append(attr[0])
- else:
- stringAttrStrs.append(attr)
- allAttrStrings = stringAttrStrs + binaryAttrStrs
-
- constant = VCardRecord.constantProperties.get(propFilter.filter_name)
- if not constant and not allAttrStrings:
- return (False, [], [])
-
- if propFilter.qualifier and isinstance(propFilter.qualifier, addressbookqueryfilter.IsNotDefined):
- return definedExpression(False, filterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
-
- paramFilterElements = [paramFilterElement for paramFilterElement in propFilter.filters if isinstance(paramFilterElement, addressbookqueryfilter.ParameterFilter)]
- textMatchElements = [textMatchElement for textMatchElement in propFilter.filters if isinstance(textMatchElement, addressbookqueryfilter.TextMatch)]
- propFilterAllOf = propFilter.propfilter_test == "allof"
-
- # handle parameter filter elements
- if len(paramFilterElements) > 0:
- if supportedParamter(propFilter.filter_name, paramFilterElements, propFilterAllOf ):
- if len(textMatchElements) == 0:
- return definedExpression(True, filterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
- else:
- if propFilterAllOf:
- return (False, [], [])
-
- # handle text match elements
- propFilterNeedsAllRecords = propFilterAllOf
- propFilterAttributes = []
- propFilterExpressionList = []
- for textMatchElement in textMatchElements:
-
- textMatchNeedsAllRecords, textMatchExpressionAttributes, textMatchExpression = textMatchElementExpression(propFilterAllOf, textMatchElement)
- if propFilterAllOf:
- propFilterNeedsAllRecords &= textMatchNeedsAllRecords
- else:
- propFilterNeedsAllRecords |= textMatchNeedsAllRecords
- propFilterAttributes += textMatchExpressionAttributes
- propFilterExpressionList += textMatchExpression
-
-
- if (len(propFilterExpressionList) > 1) and (filterAllOf != propFilterAllOf):
- propFilterExpressions = [dsquery.expression(dsquery.expression.AND if propFilterAllOf else dsquery.expression.OR , list(set(propFilterExpressionList)))] # remove duplicates
- else:
- propFilterExpressions = list(set(propFilterExpressionList))
-
- return (propFilterNeedsAllRecords, propFilterAttributes, propFilterExpressions)
- #end propFilterExpression
-
- #print("propFilterListQuery: filterAllOf=%r, propFilters=%r" % (filterAllOf, propFilters,))
- """
- Create an expression for a list of prop-filter elements.
-
- @param filterAllOf: the C{True} if parent filter test is "allof"
- @param propFilters: the C{list} of L{ComponentFilter} elements.
- @return: (needsAllRecords, espressionAttributes, expression) tuple
- """
- needsAllRecords = filterAllOf
- attributes = []
- expressions = []
- for propFilter in propFilters:
-
- propNeedsAllRecords, propExpressionAttributes, propExpression = propFilterExpression(filterAllOf, propFilter)
- if filterAllOf:
- needsAllRecords &= propNeedsAllRecords
- else:
- needsAllRecords |= propNeedsAllRecords
- attributes += propExpressionAttributes
- expressions += propExpression
-
- if len(expressions) > 1:
- expr = dsquery.expression(dsquery.expression.AND if filterAllOf else dsquery.expression.OR , list(set(expressions))) # remove duplicates
- elif len(expressions):
- expr = expressions[0]
- else:
- expr = None
-
- return (needsAllRecords, list(set(attributes)), expr)
-
-
- #print("_getDSFilter")
- # Lets assume we have a valid filter from the outset
-
- # Top-level filter contains zero or more prop-filters
- if addressBookFilter:
- filterAllOf = addressBookFilter.filter_test == "allof"
- if len(addressBookFilter.children) > 0:
- return propFilterListQuery(filterAllOf, addressBookFilter.children)
- else:
- return (filterAllOf, [], [])
- else:
- return (False, [], [])
-
-
#to do: use opendirectorybacker: _attributesForAddressBookQuery
- def _ldapAttributesForAddressBookQuery(self, addressBookQuery, ldapdsattrmap ):
+ def _ldapAttributesForAddressBookQuery(self, addressBookQuery, ldapDSAttrMap ):
propertyNames = []
#print( "addressBookQuery.qname=%r" % addressBookQuery.qname)
@@ -540,14 +226,14 @@
if not len(propertyNames):
#return self.returnedAttributes
#return None
- result = ldapdsattrmap.keys()
+ result = ldapDSAttrMap.keys()
self.log_debug("_ldapAttributesForAddressBookQuery returning all props=%s" % result)
else:
#propertyNames.append("X-INTERNAL-MINIMUM-VCARD-PROPERTIES") # these properties are required to make a vCard
queryAttributes = []
for prop in propertyNames:
- searchAttr = ldapdsattrmap.get()
+ searchAttr = ldapDSAttrMap.get()
if searchAttr:
print("adding attributes %r" % searchAttr)
if not isinstance(searchAttr, tuple):
@@ -577,10 +263,10 @@
for queryType in self.rdnSchema["queryTypes"]:
queryMap = self.rdnSchema[queryType]
- searchmap = queryMap["searchmap"]
- ldapdsattrmap = queryMap["ldapdsattrmap"]
+ searchMap = queryMap["searchMap"]
+ ldapDSAttrMap = queryMap["ldapDSAttrMap"]
- allRecords, filterAttributes, dsFilter = self._getLdapFilter( addressBookFilter, searchmap );
+ allRecords, filterAttributes, dsFilter = getDSFilter( addressBookFilter, searchMap );
#print("allRecords = %s, query = %s" % (allRecords, "None" if dsFilter is None else dsFilter.generate(),))
self.log_debug("vCardRecordsForAddressBookQuery: queryType = \"%s\" LDAP allRecords = %s, filterAttributes = %s, query = %s" % (queryType, allRecords, filterAttributes, "None" if dsFilter is None else dsFilter.generate(),))
@@ -592,7 +278,7 @@
clear = False
if not clear:
- queryAttributes = self._ldapAttributesForAddressBookQuery( addressBookQuery, ldapdsattrmap )
+ queryAttributes = self._ldapAttributesForAddressBookQuery( addressBookQuery, ldapDSAttrMap )
attributes = filterAttributes + queryAttributes if queryAttributes else None
self.log_debug("vCardRecordsForAddressBookQuery: attributes = %s, queryAttributes = %s" % (attributes, queryAttributes,))
@@ -612,12 +298,12 @@
elif dsFilter:
filterstr = dsFilter.generate()
- attrlist = attributes
+ #attrlist = attributes
#filterstr = "(&(!(objectClass=organizationalUnit))(sn=Gaya))"
#attrlist = ("cn", "sn", "objectClass", "mail", "telephoneNumber", "appleDSID")
- self.log_debug("LDAP query with base %s and filter %s and attributes %s sizelimit %s" % (ldap.dn.dn2str(base), filterstr, attrlist, maxRecords))
+ self.log_debug("LDAP query with base %s and filter %s and attributes %s sizelimit %s" % (ldap.dn.dn2str(base), filterstr, attributes, maxRecords))
- ldapSearchResult = (yield self.timedSearch(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=attrlist, sizelimit=maxRecords))
+ ldapSearchResult = (yield self.timedSearch(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=attributes, timeout=maxRecords, sizelimit=maxRecords))
self.log_debug("ldapSearchResult=%s" % (ldapSearchResult,))
@@ -635,7 +321,7 @@
ldapAttributeValues = [attr for attr in ldapAttributeValues if len(attr)]
if len(ldapAttributeValues):
- dsAttributeNames = ldapdsattrmap.get(ldapAttributeName)
+ dsAttributeNames = ldapDSAttrMap.get(ldapAttributeName)
if dsAttributeNames:
if not isinstance(dsAttributeNames, list):
Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/opendirectorybacker.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/opendirectorybacker.py 2012-03-13 00:22:30 UTC (rev 8844)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/opendirectorybacker.py 2012-03-13 03:59:12 UTC (rev 8845)
@@ -404,318 +404,8 @@
self.log_info("Timing: Directory query: %.1f ms (%d records, %.2f records/sec)" % (elaspedTime*1000, len(allResults), len(allResults)/elaspedTime))
return succeed(allResults)
- def _getDSFilter(self, addressBookFilter):
- """
- Convert the supplied addressbook-query into a ds expression tree.
-
- @param filter: the L{Filter} for the addressbook-query to convert.
- @return: (needsAllRecords, espressionAttributes, expression) tuple
- """
- def propFilterListQuery(filterAllOf, propFilters):
-
- def propFilterExpression(filterAllOf, propFilter):
- #print("propFilterExpression")
- """
- Create an expression for a single prop-filter element.
-
- @param propFilter: the L{PropertyFilter} element.
- @return: (needsAllRecords, espressionAttributes, expressions) tuple
- """
-
- def definedExpression( defined, allOf, filterName, constant, queryAttributes, allAttrStrings):
- if constant or filterName in ("N" , "FN", "UID", ):
- return (defined, [], []) # all records have this property so no records do not have it
- else:
- matchList = list(set([dsquery.match(attrName, "", dsattributes.eDSStartsWith) for attrName in allAttrStrings]))
- if defined:
- return andOrExpression(allOf, queryAttributes, matchList)
- else:
- if len(matchList) > 1:
- expr = dsquery.expression( dsquery.expression.OR, matchList )
- else:
- expr = matchList
- return (False, queryAttributes, [dsquery.expression( dsquery.expression.NOT, expr),])
- #end isNotDefinedExpression()
-
-
- def andOrExpression(propFilterAllOf, queryAttributes, matchList):
- #print("andOrExpression(propFilterAllOf=%r, queryAttributes%r, matchList%r)" % (propFilterAllOf, queryAttributes, matchList))
- if propFilterAllOf and len(matchList):
- # add OR expression because parent will AND
- return (False, queryAttributes, [dsquery.expression( dsquery.expression.OR, matchList),])
- else:
- return (False, queryAttributes, matchList)
- #end andOrExpression()
-
-
- # short circuit parameter filters
- def supportedParamter( filterName, paramFilters, propFilterAllOf ):
-
- def supported( paramFilterName, paramFilterDefined, params ):
- paramFilterName = paramFilterName.upper()
- if len(params.keys()) and ((paramFilterName in params.keys()) != paramFilterDefined):
- return False
- if len(params[paramFilterName]) and str(paramFilter.qualifier).upper() not in params[paramFilterName]:
- return False
- return True
- #end supported()
-
-
- oneSupported = False
- for paramFilter in paramFilters:
- if filterName == "PHOTO":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "ENCODING": ["B",], "TYPE": ["JPEG",], }):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "ADR":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": ["WORK", "PREF", "POSTAL", "PARCEL",], }):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "LABEL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": ["POSTAL", "PARCEL",]}):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "TEL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": [], }): # has params derived from ds attributes
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "EMAIL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": [], }): # has params derived from ds attributes
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "URL":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, {}):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif filterName == "KEY":
- if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "ENCODING": ["B",], "TYPE": ["PGPPUBILICKEY", "USERCERTIFICATE", "USERPKCS12DATA", "USERSMIMECERTIFICATE",] }):
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
- elif not filterName.startswith("X-"): #X- IMHandles X-ABRELATEDNAMES excepted, no other params are used
- if propFilterAllOf == paramFilter.defined:
- return not propFilterAllOf
- oneSupported |= propFilterAllOf
-
- if propFilterAllOf:
- return True
- else:
- return oneSupported
- #end supportedParamter()
-
-
- def textMatchElementExpression( propFilterAllOf, textMatchElement ):
-
- # pre process text match strings for ds query
- def getMatchStrings( propFilter, matchString ):
-
- if propFilter.filter_name in ("REV" , "BDAY", ):
- rawString = matchString
- matchString = ""
- for c in rawString:
- if not c in "TZ-:":
- matchString += c
- elif propFilter.filter_name == "GEO":
- matchString = ",".join(matchString.split(";"))
+ def attributesForAddressBookQuery(self, addressBookQuery, fakeETag=True ):
- if propFilter.filter_name in ("N" , "ADR", "ORG", ):
- # for structured properties, change into multiple strings for ds query
- if propFilter.filter_name == "ADR":
- #split by newline and comma
- rawStrings = ",".join( matchString.split("\n") ).split(",")
- else:
- #split by space
- rawStrings = matchString.split(" ")
-
- # remove empty strings
- matchStrings = []
- for oneString in rawStrings:
- if len(oneString):
- matchStrings += [oneString,]
- return matchStrings
-
- elif len(matchString):
- return [matchString,]
- else:
- return []
- # end getMatchStrings
-
- if constant:
- # do the match right now! Return either all or none.
- return( textMatchElement.test([constant,]), [], [] )
- else:
-
- matchStrings = getMatchStrings(propFilter, textMatchElement.text)
-
- if not len(matchStrings) or binaryAttrStrs:
- # no searching text in binary ds attributes, so change to defined/not defined case
- if textMatchElement.negate:
- return definedExpression(False, propFilterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
- # else fall through to attribute exists case below
- else:
-
- # special case UID's formed from node and record name
- if propFilter.filter_name == "UID":
- matchString = matchStrings[0]
- seperatorIndex = matchString.find(VCardRecord.peopleUIDSeparator)
- if seperatorIndex > 1:
- recordNameStart = seperatorIndex + len(VCardRecord.peopleUIDSeparator)
- else:
- seperatorIndex = matchString.find(VCardRecord.userUIDSeparator)
- if seperatorIndex > 1:
- recordNameStart = seperatorIndex + len(VCardRecord.userUIDSeparator)
- else:
- recordNameStart = sys.maxint
-
- if recordNameStart < len(matchString)-1:
- try:
- recordNameQualifier = matchString[recordNameStart:].decode("base64").decode("utf8")
- except Exception, e:
- self.log_debug("Could not decode UID string %r in %r: %r" % (matchString[recordNameStart:], matchString, e,))
- else:
- if textMatchElement.negate:
- return (False, queryAttributes,
- [dsquery.expression(dsquery.expression.NOT, dsquery.match(dsattributes.kDSNAttrRecordName, recordNameQualifier, dsattributes.eDSExact)),]
- )
- else:
- return (False, queryAttributes,
- [dsquery.match(dsattributes.kDSNAttrRecordName, recordNameQualifier, dsattributes.eDSExact),]
- )
-
- # use match_type where possible depending on property/attribute mapping
- # Note that case sensitive negate will not work
- # Should return all records in that case
- matchType = dsattributes.eDSContains
- if propFilter.filter_name in ("NICKNAME" , "TITLE" , "NOTE" , "UID", "URL", "N", "ADR", "ORG", "REV", "LABEL", ):
- if textMatchElement.match_type == "equals":
- matchType = dsattributes.eDSExact
- elif textMatchElement.match_type == "starts-with":
- matchType = dsattributes.eDSStartsWith
- elif textMatchElement.match_type == "ends-with":
- matchType = dsattributes.eDSEndsWith
-
- matchList = []
- for matchString in matchStrings:
- matchList += [dsquery.match(attrName, matchString, matchType) for attrName in stringAttrStrs]
-
- matchList = list(set(matchList))
-
- if textMatchElement.negate:
- if len(matchList) > 1:
- expr = dsquery.expression( dsquery.expression.OR, matchList )
- else:
- expr = matchList
- return (False, queryAttributes, [dsquery.expression( dsquery.expression.NOT, expr),])
- else:
- return andOrExpression(propFilterAllOf, queryAttributes, matchList)
-
- # attribute exists search
- return definedExpression(True, propFilterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
- #end textMatchElementExpression()
-
-
- # get attribute strings from dsqueryAttributesForProperty list
- queryAttributes = list(set(VCardRecord.dsqueryAttributesForProperty.get(propFilter.filter_name, [])).intersection(set(self.allowedDSQueryAttributes)))
-
- binaryAttrStrs = []
- stringAttrStrs = []
- for attr in queryAttributes:
- if isinstance(attr, tuple):
- binaryAttrStrs.append(attr[0])
- else:
- stringAttrStrs.append(attr)
- allAttrStrings = stringAttrStrs + binaryAttrStrs
-
- constant = VCardRecord.constantProperties.get(propFilter.filter_name)
- if not constant and not allAttrStrings:
- return (False, [], [])
-
- if propFilter.qualifier and isinstance(propFilter.qualifier, addressbookqueryfilter.IsNotDefined):
- return definedExpression(False, filterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
-
- paramFilterElements = [paramFilterElement for paramFilterElement in propFilter.filters if isinstance(paramFilterElement, addressbookqueryfilter.ParameterFilter)]
- textMatchElements = [textMatchElement for textMatchElement in propFilter.filters if isinstance(textMatchElement, addressbookqueryfilter.TextMatch)]
- propFilterAllOf = propFilter.propfilter_test == "allof"
-
- # handle parameter filter elements
- if len(paramFilterElements) > 0:
- if supportedParamter(propFilter.filter_name, paramFilterElements, propFilterAllOf ):
- if len(textMatchElements) == 0:
- return definedExpression(True, filterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
- else:
- if propFilterAllOf:
- return (False, [], [])
-
- # handle text match elements
- propFilterNeedsAllRecords = propFilterAllOf
- propFilterAttributes = []
- propFilterExpressionList = []
- for textMatchElement in textMatchElements:
-
- textMatchNeedsAllRecords, textMatchExpressionAttributes, textMatchExpression = textMatchElementExpression(propFilterAllOf, textMatchElement)
- if propFilterAllOf:
- propFilterNeedsAllRecords &= textMatchNeedsAllRecords
- else:
- propFilterNeedsAllRecords |= textMatchNeedsAllRecords
- propFilterAttributes += textMatchExpressionAttributes
- propFilterExpressionList += textMatchExpression
-
-
- if (len(propFilterExpressionList) > 1) and (filterAllOf != propFilterAllOf):
- propFilterExpressions = [dsquery.expression(dsquery.expression.AND if propFilterAllOf else dsquery.expression.OR , list(set(propFilterExpressionList)))] # remove duplicates
- else:
- propFilterExpressions = list(set(propFilterExpressionList))
-
- return (propFilterNeedsAllRecords, propFilterAttributes, propFilterExpressions)
- #end propFilterExpression
-
- #print("propFilterListQuery: filterAllOf=%r, propFilters=%r" % (filterAllOf, propFilters,))
- """
- Create an expression for a list of prop-filter elements.
-
- @param filterAllOf: the C{True} if parent filter test is "allof"
- @param propFilters: the C{list} of L{ComponentFilter} elements.
- @return: (needsAllRecords, espressionAttributes, expression) tuple
- """
- needsAllRecords = filterAllOf
- attributes = []
- expressions = []
- for propFilter in propFilters:
-
- propNeedsAllRecords, propExpressionAttributes, propExpression = propFilterExpression(filterAllOf, propFilter)
- if filterAllOf:
- needsAllRecords &= propNeedsAllRecords
- else:
- needsAllRecords |= propNeedsAllRecords
- attributes += propExpressionAttributes
- expressions += propExpression
-
- if len(expressions) > 1:
- expr = dsquery.expression(dsquery.expression.AND if filterAllOf else dsquery.expression.OR , list(set(expressions))) # remove duplicates
- elif len(expressions):
- expr = expressions[0]
- else:
- expr = None
-
- return (needsAllRecords, attributes, expr)
-
-
- #print("_getDSFilter")
- # Lets assume we have a valid filter from the outset
-
- # Top-level filter contains zero or more prop-filters
- if addressBookFilter:
- filterAllOf = addressBookFilter.filter_test == "allof"
- if len(addressBookFilter.children) > 0:
- return propFilterListQuery(filterAllOf, addressBookFilter.children)
- else:
- return (filterAllOf, [], [])
- else:
- return (False, [], [])
-
-
-
- def _attributesForAddressBookQuery(self, addressBookQuery ):
-
propertyNames = []
#print( "addressBookQuery.qname=%r" % addressBookQuery.qname)
if addressBookQuery.qname() == ("DAV:", "prop"):
@@ -729,7 +419,7 @@
#print("Adding property %r", addressProperty.attributes["name"])
propertyNames.append(addressProperty.attributes["name"])
- elif not self.fakeETag and property.qname() == ("DAV:", "getetag"):
+ elif not fakeETag and property.qname() == ("DAV:", "getetag"):
# for a real etag == md5(vCard), we need all attributes
propertyNames = None
break;
@@ -757,7 +447,7 @@
Get vCards for a given addressBookFilter and addressBookQuery
"""
- allRecords, filterAttributes, dsFilter = self._getDSFilter( addressBookFilter );
+ allRecords, filterAttributes, dsFilter = getDSFilter( addressBookFilter, searchMap=VCardRecord.dsqueryAttributesForProperty, allowedAttributes=self.allowedDSQueryAttributes );
#print("allRecords = %s, query = %s" % (allRecords, "None" if dsFilter is None else dsFilter.generate(),))
# testing:
@@ -771,7 +461,7 @@
limited = False
if not clear:
- queryAttributes = self._attributesForAddressBookQuery( addressBookQuery )
+ queryAttributes = self.attributesForAddressBookQuery( addressBookQuery, fakeETag=self.fakeETag )
attributes = filterAttributes + queryAttributes
#calc maxRecords from passed in maxResults allowing extra for second stage filtering in caller
@@ -796,6 +486,322 @@
returnValue((queryRecords, limited,))
+#utility
+def getDSFilter(addressBookFilter, searchMap, allowedAttributes=None):
+ """
+ Convert the supplied addressbook-query into a ds expression tree.
+
+ @param filter: the L{Filter} for the addressbook-query to convert.
+ @return: (needsAllRecords, espressionAttributes, expression) tuple
+ """
+ def propFilterListQuery(filterAllOf, propFilters):
+
+ def propFilterExpression(filterAllOf, propFilter):
+ #print("propFilterExpression")
+ """
+ Create an expression for a single prop-filter element.
+
+ @param propFilter: the L{PropertyFilter} element.
+ @return: (needsAllRecords, espressionAttributes, expressions) tuple
+ """
+
+ def definedExpression( defined, allOf, filterName, constant, queryAttributes, allAttrStrings):
+ if constant or filterName in ("N" , "FN", "UID", ):
+ return (defined, [], []) # all records have this property so no records do not have it
+ else:
+ matchList = list(set([dsquery.match(attrName, "", dsattributes.eDSStartsWith) for attrName in allAttrStrings]))
+ if defined:
+ return andOrExpression(allOf, queryAttributes, matchList)
+ else:
+ if len(matchList) > 1:
+ expr = dsquery.expression( dsquery.expression.OR, matchList )
+ else:
+ expr = matchList
+ return (False, queryAttributes, [dsquery.expression( dsquery.expression.NOT, expr),])
+ #end isNotDefinedExpression()
+
+
+ def andOrExpression(propFilterAllOf, queryAttributes, matchList):
+ #print("andOrExpression(propFilterAllOf=%r, queryAttributes%r, matchList%r)" % (propFilterAllOf, queryAttributes, matchList))
+ if propFilterAllOf and len(matchList):
+ # add OR expression because parent will AND
+ return (False, queryAttributes, [dsquery.expression( dsquery.expression.OR, matchList),])
+ else:
+ return (False, queryAttributes, matchList)
+ #end andOrExpression()
+
+
+ # short circuit parameter filters
+ def supportedParamter( filterName, paramFilters, propFilterAllOf ):
+
+ def supported( paramFilterName, paramFilterDefined, params ):
+ paramFilterName = paramFilterName.upper()
+ if len(params.keys()) and ((paramFilterName in params.keys()) != paramFilterDefined):
+ return False
+ if len(params[paramFilterName]) and str(paramFilter.qualifier).upper() not in params[paramFilterName]:
+ return False
+ return True
+ #end supported()
+
+
+ oneSupported = False
+ for paramFilter in paramFilters:
+ if filterName == "PHOTO":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "ENCODING": ["B",], "TYPE": ["JPEG",], }):
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif filterName == "ADR":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": ["WORK", "PREF", "POSTAL", "PARCEL",], }):
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif filterName == "LABEL":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": ["POSTAL", "PARCEL",]}):
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif filterName == "TEL":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": [], }): # has params derived from ds attributes
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif filterName == "EMAIL":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "TYPE": [], }): # has params derived from ds attributes
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif filterName == "URL":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, {}):
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif filterName == "KEY":
+ if propFilterAllOf != supported( paramFilter.filter_name, paramFilter.defined, { "ENCODING": ["B",], "TYPE": ["PGPPUBILICKEY", "USERCERTIFICATE", "USERPKCS12DATA", "USERSMIMECERTIFICATE",] }):
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+ elif not filterName.startswith("X-"): #X- IMHandles X-ABRELATEDNAMES excepted, no other params are used
+ if propFilterAllOf == paramFilter.defined:
+ return not propFilterAllOf
+ oneSupported |= propFilterAllOf
+
+ if propFilterAllOf:
+ return True
+ else:
+ return oneSupported
+ #end supportedParamter()
+
+
+ def textMatchElementExpression( propFilterAllOf, textMatchElement ):
+
+ # pre process text match strings for ds query
+ def getMatchStrings( propFilter, matchString ):
+
+ if propFilter.filter_name in ("REV" , "BDAY", ):
+ rawString = matchString
+ matchString = ""
+ for c in rawString:
+ if not c in "TZ-:":
+ matchString += c
+ elif propFilter.filter_name == "GEO":
+ matchString = ",".join(matchString.split(";"))
+
+ if propFilter.filter_name in ("N" , "ADR", "ORG", ):
+ # for structured properties, change into multiple strings for ds query
+ if propFilter.filter_name == "ADR":
+ #split by newline and comma
+ rawStrings = ",".join( matchString.split("\n") ).split(",")
+ else:
+ #split by space
+ rawStrings = matchString.split(" ")
+
+ # remove empty strings
+ matchStrings = []
+ for oneString in rawStrings:
+ if len(oneString):
+ matchStrings += [oneString,]
+ return matchStrings
+
+ elif len(matchString):
+ return [matchString,]
+ else:
+ return []
+ # end getMatchStrings
+
+ if constant:
+ # do the match right now! Return either all or none.
+ return( textMatchElement.test([constant,]), [], [] )
+ else:
+
+ matchStrings = getMatchStrings(propFilter, textMatchElement.text)
+
+ if not len(matchStrings) or binaryAttrStrs:
+ # no searching text in binary ds attributes, so change to defined/not defined case
+ if textMatchElement.negate:
+ return definedExpression(False, propFilterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
+ # else fall through to attribute exists case below
+ else:
+
+ # special case UID's formed from node and record name
+ if propFilter.filter_name == "UID":
+ matchString = matchStrings[0]
+ seperatorIndex = matchString.find(VCardRecord.peopleUIDSeparator)
+ if seperatorIndex > 1:
+ recordNameStart = seperatorIndex + len(VCardRecord.peopleUIDSeparator)
+ else:
+ seperatorIndex = matchString.find(VCardRecord.userUIDSeparator)
+ if seperatorIndex > 1:
+ recordNameStart = seperatorIndex + len(VCardRecord.userUIDSeparator)
+ else:
+ recordNameStart = sys.maxint
+
+ if recordNameStart < len(matchString)-1:
+ try:
+ recordNameQualifier = matchString[recordNameStart:].decode("base64").decode("utf8")
+ except Exception, e:
+ self.log_debug("Could not decode UID string %r in %r: %r" % (matchString[recordNameStart:], matchString, e,))
+ else:
+ if textMatchElement.negate:
+ return (False, queryAttributes,
+ [dsquery.expression(dsquery.expression.NOT, dsquery.match(dsattributes.kDSNAttrRecordName, recordNameQualifier, dsattributes.eDSExact)),]
+ )
+ else:
+ return (False, queryAttributes,
+ [dsquery.match(dsattributes.kDSNAttrRecordName, recordNameQualifier, dsattributes.eDSExact),]
+ )
+
+ # use match_type where possible depending on property/attribute mapping
+ # Note that case sensitive negate will not work
+ # Should return all records in that case
+ matchType = dsattributes.eDSContains
+ if propFilter.filter_name in ("NICKNAME" , "TITLE" , "NOTE" , "UID", "URL", "N", "ADR", "ORG", "REV", "LABEL", ):
+ if textMatchElement.match_type == "equals":
+ matchType = dsattributes.eDSExact
+ elif textMatchElement.match_type == "starts-with":
+ matchType = dsattributes.eDSStartsWith
+ elif textMatchElement.match_type == "ends-with":
+ matchType = dsattributes.eDSEndsWith
+
+ matchList = []
+ for matchString in matchStrings:
+ matchList += [dsquery.match(attrName, matchString, matchType) for attrName in stringAttrStrs]
+
+ matchList = list(set(matchList))
+
+ if textMatchElement.negate:
+ if len(matchList) > 1:
+ expr = dsquery.expression( dsquery.expression.OR, matchList )
+ else:
+ expr = matchList
+ return (False, queryAttributes, [dsquery.expression( dsquery.expression.NOT, expr),])
+ else:
+ return andOrExpression(propFilterAllOf, queryAttributes, matchList)
+
+ # attribute exists search
+ return definedExpression(True, propFilterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
+ #end textMatchElementExpression()
+
+
+ # get attribute strings from dsqueryAttributesForProperty list
+ #queryAttributes = list(set(VCardRecord.dsqueryAttributesForProperty.get(propFilter.filter_name, [])).intersection(set(self.allowedDSQueryAttributes)))
+ queryAttributes = searchMap.get(propFilter.filter_name, [])
+ if isinstance(queryAttributes, str):
+ queryAttributes = [queryAttributes,]
+ if allowedAttributes:
+ queryAttributes = list(set(queryAttributes).intersection(set(allowedAttributes)))
+
+ binaryAttrStrs = []
+ stringAttrStrs = []
+ for attr in queryAttributes:
+ if isinstance(attr, tuple):
+ binaryAttrStrs.append(attr[0])
+ else:
+ stringAttrStrs.append(attr)
+ allAttrStrings = stringAttrStrs + binaryAttrStrs
+
+ constant = VCardRecord.constantProperties.get(propFilter.filter_name)
+ if not constant and not allAttrStrings:
+ return (False, [], [])
+
+ if propFilter.qualifier and isinstance(propFilter.qualifier, addressbookqueryfilter.IsNotDefined):
+ return definedExpression(False, filterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
+
+ paramFilterElements = [paramFilterElement for paramFilterElement in propFilter.filters if isinstance(paramFilterElement, addressbookqueryfilter.ParameterFilter)]
+ textMatchElements = [textMatchElement for textMatchElement in propFilter.filters if isinstance(textMatchElement, addressbookqueryfilter.TextMatch)]
+ propFilterAllOf = propFilter.propfilter_test == "allof"
+
+ # handle parameter filter elements
+ if len(paramFilterElements) > 0:
+ if supportedParamter(propFilter.filter_name, paramFilterElements, propFilterAllOf ):
+ if len(textMatchElements) == 0:
+ return definedExpression(True, filterAllOf, propFilter.filter_name, constant, queryAttributes, allAttrStrings)
+ else:
+ if propFilterAllOf:
+ return (False, [], [])
+
+ # handle text match elements
+ propFilterNeedsAllRecords = propFilterAllOf
+ propFilterAttributes = []
+ propFilterExpressionList = []
+ for textMatchElement in textMatchElements:
+
+ textMatchNeedsAllRecords, textMatchExpressionAttributes, textMatchExpression = textMatchElementExpression(propFilterAllOf, textMatchElement)
+ if propFilterAllOf:
+ propFilterNeedsAllRecords &= textMatchNeedsAllRecords
+ else:
+ propFilterNeedsAllRecords |= textMatchNeedsAllRecords
+ propFilterAttributes += textMatchExpressionAttributes
+ propFilterExpressionList += textMatchExpression
+
+
+ if (len(propFilterExpressionList) > 1) and (filterAllOf != propFilterAllOf):
+ propFilterExpressions = [dsquery.expression(dsquery.expression.AND if propFilterAllOf else dsquery.expression.OR , list(set(propFilterExpressionList)))] # remove duplicates
+ else:
+ propFilterExpressions = list(set(propFilterExpressionList))
+
+ return (propFilterNeedsAllRecords, propFilterAttributes, propFilterExpressions)
+ #end propFilterExpression
+
+ #print("propFilterListQuery: filterAllOf=%r, propFilters=%r" % (filterAllOf, propFilters,))
+ """
+ Create an expression for a list of prop-filter elements.
+
+ @param filterAllOf: the C{True} if parent filter test is "allof"
+ @param propFilters: the C{list} of L{ComponentFilter} elements.
+ @return: (needsAllRecords, espressionAttributes, expression) tuple
+ """
+ needsAllRecords = filterAllOf
+ attributes = []
+ expressions = []
+ for propFilter in propFilters:
+
+ propNeedsAllRecords, propExpressionAttributes, propExpression = propFilterExpression(filterAllOf, propFilter)
+ if filterAllOf:
+ needsAllRecords &= propNeedsAllRecords
+ else:
+ needsAllRecords |= propNeedsAllRecords
+ attributes += propExpressionAttributes
+ expressions += propExpression
+
+ if len(expressions) > 1:
+ expr = dsquery.expression(dsquery.expression.AND if filterAllOf else dsquery.expression.OR , list(set(expressions))) # remove duplicates
+ elif len(expressions):
+ expr = expressions[0]
+ else:
+ expr = None
+
+ return (needsAllRecords, attributes, expr)
+
+
+ #print("_getDSFilter")
+ # Lets assume we have a valid filter from the outset
+
+ # Top-level filter contains zero or more prop-filters
+ if addressBookFilter:
+ filterAllOf = addressBookFilter.filter_test == "allof"
+ if len(addressBookFilter.children) > 0:
+ return propFilterListQuery(filterAllOf, addressBookFilter.children)
+ else:
+ return (filterAllOf, [], [])
+ else:
+ return (False, [], [])
+
+
+
class VCardRecord(DirectoryRecord, DAVPropertyMixIn):
"""
Open Directory implementation of L{IDirectoryRecord}.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120312/8a467fc3/attachment-0001.html>
More information about the calendarserver-changes
mailing list