[CalendarServer-changes] [1251] PyOpenDirectory/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Feb 23 13:21:24 PST 2007


Revision: 1251
          http://trac.macosforge.org/projects/calendarserver/changeset/1251
Author:   cdaboo at apple.com
Date:     2007-02-23 13:21:24 -0800 (Fri, 23 Feb 2007)

Log Message:
-----------
Split api for query into "simple" and "compound" versions that take simple string objects as arguments, rather than complex
nested dicts. Now the compound expression has to be genereted in Python and passed in. A new dsquery python module has been
created to aid creation of compound exprssion strings.

Modified Paths:
--------------
    PyOpenDirectory/trunk/pysrc/opendirectory.py
    PyOpenDirectory/trunk/setup.py
    PyOpenDirectory/trunk/src/CDirectoryService.cpp
    PyOpenDirectory/trunk/src/CDirectoryService.h
    PyOpenDirectory/trunk/src/PythonWrapper.cpp
    PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3
    PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser
    PyOpenDirectory/trunk/support/test.cpp
    PyOpenDirectory/trunk/test.py

Added Paths:
-----------
    PyOpenDirectory/trunk/pysrc/dsquery.py

Added: PyOpenDirectory/trunk/pysrc/dsquery.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/dsquery.py	                        (rev 0)
+++ PyOpenDirectory/trunk/pysrc/dsquery.py	2007-02-23 21:21:24 UTC (rev 1251)
@@ -0,0 +1,106 @@
+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# DRI: Cyrus Daboo, cdaboo at apple.com
+##
+
+"""
+Compound query builder. We do this in Python to avoid having to mess
+with pass a complex Python object hierarchy into C. These classes allow us to
+build the query in Python and generate the compound query string that the directory
+service C api requires.
+"""
+
+import dsattributes
+
+class match(object):
+    """
+    Represents and attribute/value match operation.
+    """
+
+    def __init__(self, attribute, value, matchType):
+        self.attribute = attribute
+        self.value = value
+        self.matchType = matchType
+    
+    def generate(self):
+        return {
+            dsattributes.eDSExact :        "(%s=%s)",
+            dsattributes.eDSStartsWith :   "(%s=%s*)",
+            dsattributes.eDSEndsWith :     "(%s=*%s)",
+            dsattributes.eDSContains :     "(%s=*%s*)",
+            dsattributes.eDSLessThan :     "(%s<%s)",
+            dsattributes.eDSGreaterThan :  "(%s>%s)",
+        }.get(self.matchType, "(%s=*%s*)") % (self.attribute, self.value,)
+
+class expression(object):
+    """
+    Represents a query expression that includes a boolean operator, and a list
+    of sub-expressions operated on. The sub-expressions can either be another expression
+    object or a match object.
+    """
+
+    AND = "&"
+    OR  = "|"
+
+    def __init__(self, operator, subexpressions):
+        assert(operator == expression.AND or operator == expression.OR)
+        self.operator = operator
+        self.subexpressions = subexpressions
+    
+    def generate(self):
+        result = ""
+        if len(self.subexpressions) > 1:
+            result += "("
+            result += self.operator
+        for sub in self.subexpressions:
+            result += sub.generate()
+        if len(self.subexpressions) > 1:
+            result += ")"
+        return result
+
+# Do some tests
+if __name__=='__main__':
+    exprs = (
+        (expression(
+            expression.AND, (
+                expression(expression.OR, (match("ResourceType", "xyz", dsattributes.eDSExact), match("ResourceType", "abc", dsattributes.eDSExact))),
+                match("ServicesLocator", "GUID:VGUID:calendar", dsattributes.eDSStartsWith),
+            )
+        ), "(&(|(ResourceType=xyz)(ResourceType=abc))(ServicesLocator=GUID:VGUID:calendar*))"),
+        (expression(
+            expression.AND, (
+                expression(expression.OR, (match("ResourceType", "xyz", dsattributes.eDSStartsWith), match("ResourceType", "abc", dsattributes.eDSEndsWith))),
+                match("ServicesLocator", "GUID:VGUID:calendar", dsattributes.eDSContains),
+            )
+        ), "(&(|(ResourceType=xyz*)(ResourceType=*abc))(ServicesLocator=*GUID:VGUID:calendar*))"),
+        (expression(
+            expression.AND, (
+                expression(expression.AND, (match("ResourceType", "xyz", dsattributes.eDSLessThan), match("ResourceType", "abc", dsattributes.eDSGreaterThan))),
+                match("ServicesLocator", "GUID:VGUID:calendar", 0xBAD),
+            )
+        ), "(&(&(ResourceType<xyz)(ResourceType>abc))(ServicesLocator=*GUID:VGUID:calendar*))"),
+        (expression(
+            expression.AND, (
+                match("ServicesLocator", "GUID:VGUID:calendar", 0xBAD),
+            )
+        ), "(ServicesLocator=*GUID:VGUID:calendar*)"),
+    )
+    
+    for expr, result in exprs:
+        gen = expr.generate()
+        if gen != result:
+            print "Generate expression %s != %s" % (gen, result,)
+    print "Done."

Modified: PyOpenDirectory/trunk/pysrc/opendirectory.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/opendirectory.py	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/pysrc/opendirectory.py	2007-02-23 21:21:24 UTC (rev 1251)
@@ -40,21 +40,34 @@
         or C{None} otherwise.
     """
 
-def queryRecordsWithAttributes(obj, query, matchType, casei, allmatch, recordType, attributes):
+def queryRecordsWithAttribute(obj, attr, value, matchType, casei, recordType, attributes):
     """
-    List records in Open Directory matching specified criteria, and return key attributes for each one.
+    List records in Open Directory matching specified attribute/value, and return key attributes for each one.
     
     @param obj: C{object} the object obtained from an odInit call.
-    @param query: C{dict} containing attribute/value pairs to search.
+    @param attr: C{str} containing the attribute to search.
+    @param value: C{str} containing the value to search for.
     @param matchType: C{int} DS match type to use when searching.
     @param casei: C{True} to do case-insenstive match, C{False} otherwise.
-    @param allmatch: C{True} to do require all attribute/value pairs to match (AND), C{False} otherwise (OR).
     @param recordType: C{str} containing the OD record type to lookup.
     @param attributes: C{list} containing the attributes to return for each record.
     @return: C{dict} containing a C{dict} of attributes for each record found, 
         or C{None} otherwise.
     """
 
+def queryRecordsWithAttributes(obj, compound, casei, recordType, attributes):
+    """
+    List records in Open Directory matching specified criteria, and return key attributes for each one.
+    
+    @param obj: C{object} the object obtained from an odInit call.
+    @param compound: C{str} containing the compound search query to use.
+    @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+    @param recordType: C{str} containing the OD record type to lookup.
+    @param attributes: C{list} containing the attributes to return for each record.
+    @return: C{dict} containing a C{dict} of attributes for each record found, 
+        or C{None} otherwise.
+    """
+
 def authenticateUserBasic(obj, guid, user, pswd):
     """
     Authenticate a user with a password to Open Directory.

Modified: PyOpenDirectory/trunk/setup.py
===================================================================
--- PyOpenDirectory/trunk/setup.py	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/setup.py	2007-02-23 21:21:24 UTC (rev 1251)
@@ -38,7 +38,7 @@
         description = 'This is a high-level interface to Open Directory for operations specific to a CalDAV server.',
         ext_modules = [module1],
         package_dir={'': 'pysrc'},
-        py_modules = ['dsattributes']
+        py_modules = ['dsattributes', 'dsquery',]
     )
 
 else:

Modified: PyOpenDirectory/trunk/src/CDirectoryService.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.cpp	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/src/CDirectoryService.cpp	2007-02-23 21:21:24 UTC (rev 1251)
@@ -108,26 +108,57 @@
 	}
 }
 
+// QueryRecordsWithAttribute
+// 
+// Get specific attributes for one or more user records with matching attribute/value in the directory.
+//
+// @param attr: the attribute to query.
+// @param value: the value to query.
+// @param matchType: the match type to use.
+// @param casei: true if case-insensitive match is to be used, false otherwise.
+// @param recordType: the record type to list.
+// @param attributes: CFArray of CFString listing the attributes to return for each record.
+// @return: CFMutableDictionaryRef composed of CFMutableDictionaryRef of CFStringRef key and value entries
+//			for each attribute/value requested in the record indexed by uid,
+//		    or NULL if it fails.
+//
+CFMutableDictionaryRef CDirectoryService::QueryRecordsWithAttribute(const char* attr, const char* value, int matchType, bool casei, const char* recordType, CFArrayRef attributes)
+{
+	try
+	{
+		// Get attribute map
+		return _QueryRecordsWithAttributes(attr, value, matchType, NULL, casei, recordType, attributes);
+	}
+	catch(long dserror)
+	{
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices Error", dserror));		
+		return NULL;
+	}
+	catch(...)
+	{
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "Unknown Error", -1));		
+		return NULL;
+	}
+}
+
 // QueryRecordsWithAttributes
 // 
 // Get specific attributes for one or more user records with matching attributes in the directory.
 //
-// @param query: CFDictionary of CFString listing the attributes to query.
-// @param matchType: the match type to query.
+// @param query: the compund query string to use.
 // @param casei: true if case-insensitive match is to be used, false otherwise.
-// @param allmatch: true if a match to every attribute/value must occur (AND), false if only one needs to match (OR).
 // @param recordType: the record type to list.
 // @param attributes: CFArray of CFString listing the attributes to return for each record.
 // @return: CFMutableDictionaryRef composed of CFMutableDictionaryRef of CFStringRef key and value entries
 //			for each attribute/value requested in the record indexed by uid,
 //		    or NULL if it fails.
 //
-CFMutableDictionaryRef CDirectoryService::QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes)
+CFMutableDictionaryRef CDirectoryService::QueryRecordsWithAttributes(const char* query, bool casei, const char* recordType, CFArrayRef attributes)
 {
 	try
 	{
 		// Get attribute map
-		return _QueryRecordsWithAttributes(query, matchType, casei, allmatch, recordType, attributes);
+		return _QueryRecordsWithAttributes(NULL, NULL, 0, query, casei, recordType, attributes);
 	}
 	catch(long dserror)
 	{
@@ -415,17 +446,18 @@
 // 
 // Get specific attributes for records of a specified type in the directory.
 //
-// @param query: a dictionary containing attribute/value pairs to match in records.
-// @param matchType: the match type to to use.
+// @param attr: the attribute to query (NULL if compound is being used).
+// @param value: the value to query (NULL if compound is being used).
+// @param matchType: the match type to use (0 if compound is being used).
+// @param attr: the compound query to use rather than single attribute/value (NULL if compound is not being used).
 // @param casei: true if case-insensitive match is to be used, false otherwise.
-// @param allmatch: true if a match to every attribute/value must occur (AND), false if only one needs to match (OR).
 // @param type: the record type to check.
 // @param attrs: a list of attributes to return.
 // @return: CFMutableDictionaryRef composed of CFMutableDictionaryRef of CFStringRef key and value entries
 //			for each attribute/value requested in the record indexed by uid,
 //		    or NULL if it fails.
 //
-CFMutableDictionaryRef CDirectoryService::_QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* type, CFArrayRef attrs)
+CFMutableDictionaryRef CDirectoryService::_QueryRecordsWithAttributes(const char* attr, const char* value, int matchType, const char* compound, bool casei, const char* type, CFArrayRef attrs)
 {
 	CFMutableDictionaryRef result = NULL;
 	CFMutableDictionaryRef vresult = NULL;
@@ -453,10 +485,15 @@
 		// We need a buffer for what comes next
 		CreateBuffer();
 		
-		// Determine attribute to search
-		if (::CFDictionaryGetCount(query) == 1)
+		if (compound == NULL)
 		{
-			BuildSimpleQuery(query, queryAttr, queryValue);
+			// Determine attribute to search
+			queryAttr = ::dsDataNodeAllocateString(mDir, attr);
+			ThrowIfNULL(queryAttr);
+
+			queryValue = ::dsDataNodeAllocateString(mDir, value);
+			ThrowIfNULL(queryValue);
+
 			if (casei)
 				matchType |= 0x0100;
 			else
@@ -465,10 +502,14 @@
 		else
 		{
 			queryAttr = ::dsDataNodeAllocateString(mDir, kDS1AttrDistinguishedName);
-			BuildCompoundQuery(query, (tDirPatternMatch)matchType, allmatch, queryValue);
+			ThrowIfNULL(queryAttr);
+
+			queryValue = ::dsDataNodeAllocateString(mDir, compound);
+			ThrowIfNULL(queryValue);
+
 			matchType = (casei) ? eDSiCompoundExpression : eDSCompoundExpression;
 		}
-
+	
 		// Build data list of types
 		recTypes = ::dsDataListAllocate(mDir);
 		ThrowIfNULL(recTypes);
@@ -651,19 +692,13 @@
 	// We need to find the 'native' node for the specified record guid as the current node
 	// may not support this user's authentication directly.
 	CFStringRef result = NULL;
-
-	CFStringRef keys[1] = {CFSTR(kDS1AttrGeneratedUID)};
-	CFStringUtil cfguid(guid);
-	CFStringRef values[1] = {cfguid.get()};
-	CFDictionaryRef query = ::CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void**)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 	
 	CFMutableArrayRef attrs = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
 	CFStringUtil cfattr(kDSNAttrMetaNodeLocation);
 	::CFArrayAppendValue(attrs, cfattr.get());
 	
 	// First list the record for the current GUID and get its node.
-	CFMutableDictionaryRef found = _QueryRecordsWithAttributes(query, eDSExact, false, false, kDSStdRecordTypeUsers, attrs);
-	::CFRelease(query);
+	CFMutableDictionaryRef found = _QueryRecordsWithAttributes(kDS1AttrGeneratedUID, guid, eDSExact, NULL, false, kDSStdRecordTypeUsers, attrs);
 	::CFRelease(attrs);
 	if (found == NULL)
 		return result;
@@ -1122,106 +1157,6 @@
 	}
 }
 
-struct SBuildSimpleQueryIterator
-{
-	tDirReference		mDir;
-	tDataNodePtr&		mAttr;
-	tDataNodePtr&		mValue;
-	
-	SBuildSimpleQueryIterator(tDirReference dir, tDataNodePtr& attr, tDataNodePtr& value) :
-		mDir(dir), mAttr(attr), mValue(value) {}
-};
-
-// CFDictionary iterator callback that adds either the key or value to a DS list.
-static void BuildSimpleQueryIterator(const void* key, const void* value, void* ref)
-{
-	SBuildSimpleQueryIterator* info = (SBuildSimpleQueryIterator*)ref;
-
-	CFStringRef strkey = (CFStringRef)key;
-	CFStringUtil cstrkey(strkey);
-	info->mAttr = ::dsDataNodeAllocateString(info->mDir, cstrkey.temp_str());
-	ThrowIfNULL(info->mAttr);
-
-	CFStringRef strvalue = (CFStringRef)value;
-	CFStringUtil cstrvalue(strvalue);
-	info->mValue = ::dsDataNodeAllocateString(info->mDir, cstrvalue.temp_str());
-	ThrowIfNULL(info->mValue);
-}
-
-void CDirectoryService::BuildSimpleQuery(CFDictionaryRef dict, tDataNodePtr& attr, tDataNodePtr& value)
-{
-	SBuildSimpleQueryIterator info(mDir, attr, value);
-
-	::CFDictionaryApplyFunction(dict, BuildSimpleQueryIterator, &info);
-}
-
-struct SBuildCompoundQueryIterator
-{
-	CFStringRef			mFormat;
-	CFMutableStringRef	mCompound;
-	
-	SBuildCompoundQueryIterator(CFStringRef format, CFMutableStringRef compound) :
-		mFormat(format), mCompound(compound) {}
-};
-
-// CFDictionary iterator callback that adds either the key or value to a DS list.
-static void BuildCompoundQueryIterator(const void* key, const void* value, void* ref)
-{
-	SBuildCompoundQueryIterator* info = (SBuildCompoundQueryIterator*)ref;
-
-	CFStringRef strkey = (CFStringRef)key;
-	CFStringRef strvalue = (CFStringRef)value;
-	::CFStringAppendFormat(info->mCompound, NULL, info->mFormat, strkey, strvalue);
-}
-
-void CDirectoryService::BuildCompoundQuery(CFDictionaryRef dict, tDirPatternMatch matchType, bool allmatch, tDataNodePtr& value)
-{	
-	CFStringRef format = NULL;
-	switch(matchType)
-	{
-		case eDSExact:
-		case eDSiExact:
-			format = CFSTR("(%@=%@)");
-			break;
-		case eDSStartsWith:
-		case eDSiStartsWith:
-			format = CFSTR("(%@=%@*)");
-			break;
-		case eDSEndsWith:
-		case eDSiEndsWith:
-			format = CFSTR("(%@=*%@)");
-			break;
-		case eDSContains:
-		case eDSiContains:
-			format = CFSTR("(%@=*%@*)");
-			break;
-		case eDSLessThan:
-		case eDSiLessThan:
-			format = CFSTR("(%@<%@)");
-			break;
-		case eDSGreaterThan:
-		case eDSiGreaterThan:
-			format = CFSTR("(%@>%@)");
-			break;
-		default:
-			format = CFSTR("(%@=*%@*)");
-			break;
-	}
-
-	// Now build the compound expression using the match type format we just setup
-	CFMutableStringRef compound = CFStringCreateMutable(kCFAllocatorDefault, 0);
-	::CFStringAppend(compound, allmatch ? CFSTR("(&") : CFSTR("(|"));
-	
-	SBuildCompoundQueryIterator info(format, compound);
-	::CFDictionaryApplyFunction(dict, BuildCompoundQueryIterator, &info);
-
-	::CFStringAppend(compound, CFSTR(")"));
-	CFStringUtil cstrvalue(compound);
-	value = ::dsDataNodeAllocateString(mDir, cstrvalue.temp_str());
-	::CFRelease(compound);
-	ThrowIfNULL(value);
-}
-
 // CStringFromBuffer
 // 
 // Convert data in a buffer into a c-string.

Modified: PyOpenDirectory/trunk/src/CDirectoryService.h
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.h	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/src/CDirectoryService.h	2007-02-23 21:21:24 UTC (rev 1251)
@@ -33,7 +33,8 @@
 	~CDirectoryService();
 	
 	CFMutableDictionaryRef ListAllRecordsWithAttributes(const char* recordType, CFArrayRef attributes);
-	CFMutableDictionaryRef QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes);
+	CFMutableDictionaryRef QueryRecordsWithAttribute(const char* attr, const char* value, int matchType, bool casei, const char* recordType, CFArrayRef attributes);
+	CFMutableDictionaryRef QueryRecordsWithAttributes(const char* query, bool casei, const char* recordType, CFArrayRef attributes);
 
 	bool AuthenticateUserBasic(const char* guid, const char* user, const char* pswd, bool& result);
 	bool AuthenticateUserDigest(const char* guid, const char* user, const char* challenge, const char* response, const char* method, bool& result);
@@ -46,7 +47,7 @@
 	UInt32				mDataSize;
 	
 	CFMutableDictionaryRef _ListAllRecordsWithAttributes(const char* type, CFArrayRef names, CFArrayRef attrs);
-	CFMutableDictionaryRef _QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes);
+	CFMutableDictionaryRef _QueryRecordsWithAttributes(const char* attr, const char* value, int matchType, const char* compound, bool casei, const char* recordType, CFArrayRef attributes);
 
 	CFStringRef CDirectoryService::AuthenticationGetNode(const char* guid);
 	bool NativeAuthenticationBasic(const char* guid, const char* user, const char* pswd);
@@ -66,9 +67,6 @@
 	void ReallocBuffer();
 
 	void BuildStringDataList(CFArrayRef strs, tDataListPtr data);
-	
-	void BuildSimpleQuery(CFDictionaryRef dict, tDataNodePtr& attr, tDataNodePtr& value);
-	void BuildCompoundQuery(CFDictionaryRef dict, tDirPatternMatch matchType, bool allmatch, tDataNodePtr& value);
 
 	char* CStringFromBuffer(tDataBufferPtr data);
 	char* CStringFromData(const char* data, size_t len);

Modified: PyOpenDirectory/trunk/src/PythonWrapper.cpp
===================================================================
--- PyOpenDirectory/trunk/src/PythonWrapper.cpp	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/src/PythonWrapper.cpp	2007-02-23 21:21:24 UTC (rev 1251)
@@ -330,69 +330,115 @@
 }
 
 /*
-def queryRecordsWithAttributes(obj, query, matchType, casei, allmatch, recordType, attributes):
+def queryRecordsWithAttribute(obj, attr, value, matchType, casei, recordType, attributes):
     """
-    List records in Open Directory matching specified criteria, and return key attributes for each one.
+    List records in Open Directory matching specified attribute and value, and return key attributes for each one.
     
     @param obj: C{object} the object obtained from an odInit call.
-    @param query: C{dict} containing attribute/value pairs to search.
+    @param attr: C{str} for the attribute to query.
+    @param value: C{str} for the attribute value to query.
     @param matchType: C{int} DS match type to use when searching.
     @param casei: C{True} to do case-insenstive match, C{False} otherwise.
-    @param allmatch: C{True} to do require all attribute/value pairs to match (AND), C{False} otherwise (OR).
     @param recordType: C{str} containing the OD record type to lookup.
     @param attributes: C{list} containing the attributes to return for each record.
     @return: C{dict} containing a C{dict} of attributes for each record found, 
         or C{None} otherwise.
     """
  */
-extern "C" PyObject *queryRecordsWithAttributes(PyObject *self, PyObject *args)
+extern "C" PyObject *queryRecordsWithAttribute(PyObject *self, PyObject *args)
 {
 	PyObject* pyds;
-	PyObject* query;
+	const char* attr;
+	const char* value;
 	int matchType;
 	PyObject* caseio;
 	bool casei;
-	PyObject* allmatcho;
-	bool allmatch;
 	const char* recordType;
 	PyObject* attributes;
-    if (!PyArg_ParseTuple(args, "OOiOOsO", &pyds, &query, &matchType, &caseio, &allmatcho, &recordType, &attributes) ||
-    	!PyCObject_Check(pyds) || !PyDict_Check(query) || !PyBool_Check(caseio) || !PyBool_Check(allmatcho) || !PyList_Check(attributes))
+    if (!PyArg_ParseTuple(args, "OssiOsO", &pyds, &attr, &value, &matchType, &caseio, &recordType, &attributes) ||
+    	!PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyList_Check(attributes))
     {
 		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));		
         return NULL;
     }
+
+	casei = (caseio == Py_True);
+
+	// Convert list to CFArray of CFString
+	CFArrayRef cfattributes = PyListToCFArray(attributes);
+	if (cfattributes == NULL)
+    {
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse attributes list", 0));		
+        return NULL;
+    }
+
+	CDirectoryService* ds = static_cast<CDirectoryService*>(PyCObject_AsVoidPtr(pyds));
+	if (ds != NULL)
+	{
+		CFMutableDictionaryRef dict = ds->QueryRecordsWithAttribute(attr, value, matchType, casei, recordType, cfattributes);
+		if (dict != NULL)
+		{
+			PyObject* result = CFDictionaryDictionaryToPyDict(dict);
+			CFRelease(dict);
+			CFRelease(cfattributes);
+			
+			return result;
+		}
+	}
+	else
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));		
 	
-	// Convert dict to CFDictionary of CFString
-	CFDictionaryRef cfquery = PyDictToCFDictionary(query);
-	if (cfquery == NULL)
+	CFRelease(cfattributes);
+	return NULL;
+}
+
+/*
+def queryRecordsWithAttributes(obj, query, casei, recordType, attributes):
+    """
+    List records in Open Directory matching specified compound query, and return key attributes for each one.
+    
+    @param obj: C{object} the object obtained from an odInit call.
+    @param query: C{str} the compound query string.
+    @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+    @param recordType: C{str} containing the OD record type to lookup.
+    @param attributes: C{list} containing the attributes to return for each record.
+    @return: C{dict} containing a C{dict} of attributes for each record found, 
+        or C{None} otherwise.
+    """
+ */
+extern "C" PyObject *queryRecordsWithAttributes(PyObject *self, PyObject *args)
+{
+	PyObject* pyds;
+	const char* query;
+	PyObject* caseio;
+	bool casei;
+	const char* recordType;
+	PyObject* attributes;
+    if (!PyArg_ParseTuple(args, "OsOsO", &pyds, &query, &caseio, &recordType, &attributes) ||
+    	!PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyList_Check(attributes))
     {
-		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse query dict", 0));		
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));		
         return NULL;
     }
 
 	casei = (caseio == Py_True);
 
-	allmatch = (allmatcho == Py_True);
-
 	// Convert list to CFArray of CFString
 	CFArrayRef cfattributes = PyListToCFArray(attributes);
 	if (cfattributes == NULL)
     {
 		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse attributes list", 0));		
-		CFRelease(cfquery);
         return NULL;
     }
 
 	CDirectoryService* ds = static_cast<CDirectoryService*>(PyCObject_AsVoidPtr(pyds));
 	if (ds != NULL)
 	{
-		CFMutableDictionaryRef dict = ds->QueryRecordsWithAttributes(cfquery, matchType, casei, allmatch, recordType, cfattributes);
+		CFMutableDictionaryRef dict = ds->QueryRecordsWithAttributes(query, casei, recordType, cfattributes);
 		if (dict != NULL)
 		{
 			PyObject* result = CFDictionaryDictionaryToPyDict(dict);
 			CFRelease(dict);
-			CFRelease(cfquery);
 			CFRelease(cfattributes);
 			
 			return result;
@@ -401,7 +447,6 @@
 	else
 		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));		
 	
-	CFRelease(cfquery);
 	CFRelease(cfattributes);
 	return NULL;
 }
@@ -499,6 +544,8 @@
 		"Initialize the Open Directory system."},
     {"listAllRecordsWithAttributes",  listAllRecordsWithAttributes, METH_VARARGS,
 		"List all records of the specified type in Open Directory, returning requested attributes."},
+    {"queryRecordsWithAttribute",  queryRecordsWithAttribute, METH_VARARGS,
+		"List records in Open Directory matching specified attribute/value, and return key attributes for each one."},
     {"queryRecordsWithAttributes",  queryRecordsWithAttributes, METH_VARARGS,
 		"List records in Open Directory matching specified criteria, and return key attributes for each one."},
     {"authenticateUserBasic",  authenticateUserBasic, METH_VARARGS,

Modified: PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3
===================================================================
--- PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3	2007-02-23 21:21:24 UTC (rev 1251)
@@ -195,48 +195,7 @@
 	<key>Notifications</key>
 	<array/>
 	<key>OpenEditors</key>
-	<array>
-		<dict>
-			<key>Content</key>
-			<dict>
-				<key>PBXProjectModuleGUID</key>
-				<string>AF0015D10B8A2F490045DAEE</string>
-				<key>PBXProjectModuleLabel</key>
-				<string>test.cpp</string>
-				<key>PBXSplitModuleInNavigatorKey</key>
-				<dict>
-					<key>Split0</key>
-					<dict>
-						<key>PBXProjectModuleGUID</key>
-						<string>AF0015D20B8A2F490045DAEE</string>
-						<key>PBXProjectModuleLabel</key>
-						<string>test.cpp</string>
-						<key>_historyCapacity</key>
-						<integer>0</integer>
-						<key>bookmark</key>
-						<string>AF0015DC0B8A2F9B0045DAEE</string>
-						<key>history</key>
-						<array>
-							<string>AF0015CF0B8A2ED80045DAEE</string>
-						</array>
-					</dict>
-					<key>SplitCount</key>
-					<string>1</string>
-				</dict>
-				<key>StatusBarVisibility</key>
-				<true/>
-			</dict>
-			<key>Geometry</key>
-			<dict>
-				<key>Frame</key>
-				<string>{{0, 20}, {811, 731}}</string>
-				<key>PBXModuleWindowStatusBarHidden2</key>
-				<false/>
-				<key>RubberWindowFrame</key>
-				<string>436 60 811 772 0 0 1920 1178 </string>
-			</dict>
-		</dict>
-	</array>
+	<array/>
 	<key>PerspectiveWidths</key>
 	<array>
 		<integer>-1</integer>
@@ -270,8 +229,6 @@
 			<key>Layout</key>
 			<array>
 				<dict>
-					<key>BecomeActive</key>
-					<true/>
 					<key>ContentConfiguration</key>
 					<dict>
 						<key>PBXBottomSmartGroupGIDs</key>
@@ -296,7 +253,7 @@
 						<dict>
 							<key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
 							<array>
-								<real>186</real>
+								<real>316</real>
 							</array>
 							<key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
 							<array>
@@ -318,7 +275,7 @@
 								</array>
 							</array>
 							<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
-							<string>{{0, 0}, {186, 741}}</string>
+							<string>{{0, 0}, {316, 1060}}</string>
 						</dict>
 						<key>PBXTopSmartGroupGIDs</key>
 						<array/>
@@ -330,19 +287,19 @@
 					<key>GeometryConfiguration</key>
 					<dict>
 						<key>Frame</key>
-						<string>{{0, 0}, {203, 759}}</string>
+						<string>{{0, 0}, {333, 1078}}</string>
 						<key>GroupTreeTableConfiguration</key>
 						<array>
 							<string>MainColumn</string>
-							<real>186</real>
+							<real>316</real>
 						</array>
 						<key>RubberWindowFrame</key>
-						<string>13 374 1064 800 0 0 1920 1178 </string>
+						<string>0 59 1920 1119 0 0 1920 1178 </string>
 					</dict>
 					<key>Module</key>
 					<string>PBXSmartGroupTreeModule</string>
 					<key>Proportion</key>
-					<string>203pt</string>
+					<string>333pt</string>
 				</dict>
 				<dict>
 					<key>Dock</key>
@@ -365,29 +322,17 @@
 										<key>_historyCapacity</key>
 										<integer>0</integer>
 										<key>bookmark</key>
-										<string>AF0015DB0B8A2F9B0045DAEE</string>
+										<string>AF63114C0B8F89FB000D1C1A</string>
 										<key>history</key>
 										<array>
-											<string>AF00156C0B8A25E20045DAEE</string>
-											<string>AF0015810B8A27C70045DAEE</string>
-											<string>AF00159A0B8A290F0045DAEE</string>
-											<string>AF0015BE0B8A2C7F0045DAEE</string>
-											<string>AF0015D70B8A2F9B0045DAEE</string>
-											<string>AF0015D80B8A2F9B0045DAEE</string>
+											<string>AF6311050B8F74FC000D1C1A</string>
+											<string>AF63112B0B8F85FF000D1C1A</string>
+											<string>AF63112C0B8F85FF000D1C1A</string>
 										</array>
 										<key>prevStack</key>
 										<array>
-											<string>AF0015700B8A25E20045DAEE</string>
-											<string>AF0015710B8A25E20045DAEE</string>
-											<string>AF0015720B8A25E20045DAEE</string>
-											<string>AF0015730B8A25E20045DAEE</string>
-											<string>AF0015820B8A27C70045DAEE</string>
-											<string>AF00159C0B8A290F0045DAEE</string>
-											<string>AF00159D0B8A290F0045DAEE</string>
-											<string>AF0015C00B8A2C7F0045DAEE</string>
-											<string>AF0015C10B8A2C7F0045DAEE</string>
-											<string>AF0015D90B8A2F9B0045DAEE</string>
-											<string>AF0015DA0B8A2F9B0045DAEE</string>
+											<string>AF6311070B8F74FC000D1C1A</string>
+											<string>AF63112D0B8F85FF000D1C1A</string>
 										</array>
 									</dict>
 									<key>SplitCount</key>
@@ -399,16 +344,18 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {856, 383}}</string>
+								<string>{{0, 0}, {1582, 702}}</string>
 								<key>RubberWindowFrame</key>
-								<string>13 374 1064 800 0 0 1920 1178 </string>
+								<string>0 59 1920 1119 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXNavigatorGroup</string>
 							<key>Proportion</key>
-							<string>383pt</string>
+							<string>702pt</string>
 						</dict>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -419,9 +366,9 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 388}, {856, 371}}</string>
+								<string>{{0, 707}, {1582, 371}}</string>
 								<key>RubberWindowFrame</key>
-								<string>13 374 1064 800 0 0 1920 1178 </string>
+								<string>0 59 1920 1119 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>XCDetailModule</string>
@@ -430,7 +377,7 @@
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>856pt</string>
+					<string>1582pt</string>
 				</dict>
 			</array>
 			<key>Name</key>
@@ -445,9 +392,9 @@
 			</array>
 			<key>TableOfContents</key>
 			<array>
-				<string>AF0015430B8A1E530045DAEE</string>
+				<string>AF63105B0B8F5542000D1C1A</string>
 				<string>1CE0B1FE06471DED0097A5F4</string>
-				<string>AF0015440B8A1E530045DAEE</string>
+				<string>AF63105C0B8F5542000D1C1A</string>
 				<string>1CE0B20306471E060097A5F4</string>
 				<string>1CE0B20506471E060097A5F4</string>
 			</array>
@@ -581,29 +528,46 @@
 	<integer>5</integer>
 	<key>WindowOrderList</key>
 	<array>
-		<string>AF0015DD0B8A2F9B0045DAEE</string>
-		<string>AF0015C40B8A2C7F0045DAEE</string>
-		<string>AF0015870B8A27C70045DAEE</string>
-		<string>AF00158A0B8A27C70045DAEE</string>
-		<string>AF0015840B8A27C70045DAEE</string>
-		<string>AF00158E0B8A27C70045DAEE</string>
-		<string>AF0015780B8A25E20045DAEE</string>
-		<string>AF0015790B8A25E20045DAEE</string>
-		<string>AF0015750B8A25E20045DAEE</string>
-		<string>AF00157A0B8A25E20045DAEE</string>
-		<string>AF00157B0B8A25E20045DAEE</string>
-		<string>AF00157C0B8A25E20045DAEE</string>
-		<string>AF0015540B8A1E9D0045DAEE</string>
-		<string>AF0015550B8A1E9D0045DAEE</string>
-		<string>AF0015560B8A1E9D0045DAEE</string>
+		<string>AF6311480B8F8910000D1C1A</string>
+		<string>AF63112F0B8F85FF000D1C1A</string>
+		<string>AF6311320B8F85FF000D1C1A</string>
+		<string>AF6311330B8F85FF000D1C1A</string>
+		<string>AF6311170B8F7A2C000D1C1A</string>
+		<string>AF63111B0B8F7A2C000D1C1A</string>
+		<string>AF63110F0B8F7686000D1C1A</string>
+		<string>AF6311120B8F7686000D1C1A</string>
+		<string>AF6311090B8F74FC000D1C1A</string>
+		<string>AF63110C0B8F74FC000D1C1A</string>
+		<string>AF6310FC0B8F7441000D1C1A</string>
+		<string>AF6311020B8F7441000D1C1A</string>
+		<string>AF6310FF0B8F7441000D1C1A</string>
+		<string>AF6310DA0B8F6DD6000D1C1A</string>
+		<string>AF6310D70B8F6DD6000D1C1A</string>
+		<string>AF6310BF0B8F6BA0000D1C1A</string>
+		<string>AF6310C80B8F6BA0000D1C1A</string>
+		<string>AF6310C90B8F6BA0000D1C1A</string>
+		<string>AF6310CA0B8F6BA0000D1C1A</string>
+		<string>AF6310CB0B8F6BA0000D1C1A</string>
+		<string>AF6310CC0B8F6BA0000D1C1A</string>
+		<string>AF63108D0B8F664E000D1C1A</string>
+		<string>1C78EAAD065D492600B07095</string>
 		<string>1CD10A99069EF8BA00B06720</string>
+		<string>AF6310910B8F664E000D1C1A</string>
+		<string>AF6310920B8F664E000D1C1A</string>
+		<string>AF6310930B8F664E000D1C1A</string>
 		<string>AF0015490B8A1E6C0045DAEE</string>
-		<string>AF0015D10B8A2F490045DAEE</string>
+		<string>AF6310730B8F5E00000D1C1A</string>
+		<string>AF63106D0B8F585C000D1C1A</string>
+		<string>AF63106E0B8F585C000D1C1A</string>
+		<string>AF63106A0B8F585C000D1C1A</string>
+		<string>AF63106F0B8F585C000D1C1A</string>
+		<string>AF6310700B8F585C000D1C1A</string>
+		<string>AF63105D0B8F5542000D1C1A</string>
+		<string>AF6310630B8F5542000D1C1A</string>
 		<string>/Users/cyrusdaboo/Documents/Development/Apple/eclipse/PyOpenDirectory/support/PyOpenDirectory.xcodeproj</string>
-		<string>1C78EAAD065D492600B07095</string>
 	</array>
 	<key>WindowString</key>
-	<string>13 374 1064 800 0 0 1920 1178 </string>
+	<string>0 59 1920 1119 0 0 1920 1178 </string>
 	<key>WindowToolsV3</key>
 	<array>
 		<dict>
@@ -619,30 +583,30 @@
 					<key>Dock</key>
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
 								<string>1CD0528F0623707200166675</string>
 								<key>PBXProjectModuleLabel</key>
-								<string>test.cpp</string>
+								<string>PythonWrapper.cpp</string>
 								<key>StatusBarVisibility</key>
 								<true/>
 							</dict>
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {500, 218}}</string>
+								<string>{{0, 0}, {1102, 398}}</string>
 								<key>RubberWindowFrame</key>
-								<string>1095 569 500 500 0 0 1920 1178 </string>
+								<string>674 408 1102 680 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXNavigatorGroup</string>
 							<key>Proportion</key>
-							<string>218pt</string>
+							<string>398pt</string>
 						</dict>
 						<dict>
-							<key>BecomeActive</key>
-							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -657,9 +621,9 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 223}, {500, 236}}</string>
+								<string>{{0, 403}, {1102, 236}}</string>
 								<key>RubberWindowFrame</key>
-								<string>1095 569 500 500 0 0 1920 1178 </string>
+								<string>674 408 1102 680 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXBuildResultsModule</string>
@@ -668,7 +632,7 @@
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>459pt</string>
+					<string>639pt</string>
 				</dict>
 			</array>
 			<key>Name</key>
@@ -682,14 +646,14 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>AF0015490B8A1E6C0045DAEE</string>
-				<string>AF00154A0B8A1E6C0045DAEE</string>
+				<string>AF63107C0B8F62C5000D1C1A</string>
 				<string>1CD0528F0623707200166675</string>
 				<string>XCMainBuildResultsModuleGUID</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.buildV3</string>
 			<key>WindowString</key>
-			<string>1095 569 500 500 0 0 1920 1178 </string>
+			<string>674 408 1102 680 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>AF0015490B8A1E6C0045DAEE</string>
 			<key>WindowToolIsVisible</key>
@@ -724,8 +688,8 @@
 										<string>yes</string>
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {639, 431}}</string>
-											<string>{{639, 0}, {764, 431}}</string>
+											<string>{{0, 0}, {640, 452}}</string>
+											<string>{{640, 0}, {763, 452}}</string>
 										</array>
 									</dict>
 									<key>VerticalSplitView</key>
@@ -740,8 +704,8 @@
 										<string>yes</string>
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {1403, 431}}</string>
-											<string>{{0, 431}, {1403, 456}}</string>
+											<string>{{0, 0}, {1403, 452}}</string>
+											<string>{{0, 452}, {1403, 435}}</string>
 										</array>
 									</dict>
 								</dict>
@@ -771,10 +735,10 @@
 										<string>Value</string>
 										<real>127</real>
 										<string>Summary</string>
-										<real>492</real>
+										<real>491</real>
 									</array>
 									<key>Frame</key>
-									<string>{{639, 0}, {764, 431}}</string>
+									<string>{{640, 0}, {763, 452}}</string>
 									<key>RubberWindowFrame</key>
 									<string>34 224 1403 928 0 0 1920 1178 </string>
 								</dict>
@@ -802,13 +766,13 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>1CD10A99069EF8BA00B06720</string>
-				<string>AF00154B0B8A1E6C0045DAEE</string>
+				<string>AF6310860B8F664E000D1C1A</string>
 				<string>1C162984064C10D400B95A72</string>
-				<string>AF00154C0B8A1E6C0045DAEE</string>
-				<string>AF00154D0B8A1E6C0045DAEE</string>
-				<string>AF00154E0B8A1E6C0045DAEE</string>
-				<string>AF00154F0B8A1E6C0045DAEE</string>
-				<string>AF0015500B8A1E6C0045DAEE</string>
+				<string>AF6310870B8F664E000D1C1A</string>
+				<string>AF6310880B8F664E000D1C1A</string>
+				<string>AF6310890B8F664E000D1C1A</string>
+				<string>AF63108A0B8F664E000D1C1A</string>
+				<string>AF63108B0B8F664E000D1C1A</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.debugV3</string>
@@ -972,7 +936,7 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>1C78EAAD065D492600B07095</string>
-				<string>AF0015510B8A1E6C0045DAEE</string>
+				<string>AF63108C0B8F664E000D1C1A</string>
 				<string>1C78EAAC065D492600B07095</string>
 			</array>
 			<key>ToolbarConfiguration</key>
@@ -982,7 +946,7 @@
 			<key>WindowToolGUID</key>
 			<string>1C78EAAD065D492600B07095</string>
 			<key>WindowToolIsVisible</key>
-			<true/>
+			<false/>
 		</dict>
 		<dict>
 			<key>Identifier</key>

Modified: PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser
===================================================================
--- PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser	2007-02-23 21:21:24 UTC (rev 1251)
@@ -9,8 +9,8 @@
 			8DD76F620486A84900D96B5E /* PyOpenDirectory */,
 		);
 		breakpoints = (
-			AF0015620B8A24240045DAEE /* test.cpp:81 */,
-			AF0015650B8A245A0045DAEE /* test.cpp:149 */,
+			AF0015620B8A24240045DAEE /* test.cpp:84 */,
+			AF0015650B8A245A0045DAEE /* test.cpp:167 */,
 		);
 		codeSenseManager = AF155A2E0A501F7B007E1E6E /* Code sense */;
 		executables = (
@@ -46,7 +46,7 @@
 				PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
 				PBXFileTableDataSourceColumnWidthsKey = (
 					20,
-					617,
+					1343,
 					20,
 					48,
 					43,
@@ -63,85 +63,16 @@
 					PBXFileDataSource_Target_ColumnID,
 				);
 			};
-			PBXPerProjectTemplateStateSaveDate = 193601096;
-			PBXWorkspaceStateSaveDate = 193601096;
+			PBXPerProjectTemplateStateSaveDate = 193942189;
+			PBXWorkspaceStateSaveDate = 193942189;
 		};
 		perUserProjectItems = {
-			AF00156B0B8A25510045DAEE /* PBXBookmark */ = AF00156B0B8A25510045DAEE /* PBXBookmark */;
-			AF00156C0B8A25E20045DAEE /* PBXTextBookmark */ = AF00156C0B8A25E20045DAEE /* PBXTextBookmark */;
-			AF00156D0B8A25E20045DAEE /* PBXTextBookmark */ = AF00156D0B8A25E20045DAEE /* PBXTextBookmark */;
-			AF00156E0B8A25E20045DAEE /* PBXTextBookmark */ = AF00156E0B8A25E20045DAEE /* PBXTextBookmark */;
-			AF00156F0B8A25E20045DAEE /* PBXBookmark */ = AF00156F0B8A25E20045DAEE /* PBXBookmark */;
-			AF0015700B8A25E20045DAEE /* PBXTextBookmark */ = AF0015700B8A25E20045DAEE /* PBXTextBookmark */;
-			AF0015710B8A25E20045DAEE /* PBXTextBookmark */ = AF0015710B8A25E20045DAEE /* PBXTextBookmark */;
-			AF0015720B8A25E20045DAEE /* PBXTextBookmark */ = AF0015720B8A25E20045DAEE /* PBXTextBookmark */;
-			AF0015730B8A25E20045DAEE /* PBXTextBookmark */ = AF0015730B8A25E20045DAEE /* PBXTextBookmark */;
-			AF0015740B8A25E20045DAEE /* PBXTextBookmark */ = AF0015740B8A25E20045DAEE /* PBXTextBookmark */;
-			AF0015770B8A25E20045DAEE /* PBXTextBookmark */ = AF0015770B8A25E20045DAEE /* PBXTextBookmark */;
-			AF00157F0B8A268C0045DAEE /* PBXBookmark */ = AF00157F0B8A268C0045DAEE /* PBXBookmark */;
-			AF0015800B8A27320045DAEE /* PBXBookmark */ = AF0015800B8A27320045DAEE /* PBXBookmark */;
-			AF0015810B8A27C70045DAEE /* PBXTextBookmark */ = AF0015810B8A27C70045DAEE /* PBXTextBookmark */;
-			AF0015820B8A27C70045DAEE /* PBXTextBookmark */ = AF0015820B8A27C70045DAEE /* PBXTextBookmark */;
-			AF0015830B8A27C70045DAEE /* PBXTextBookmark */ = AF0015830B8A27C70045DAEE /* PBXTextBookmark */;
-			AF0015860B8A27C70045DAEE /* PBXTextBookmark */ = AF0015860B8A27C70045DAEE /* PBXTextBookmark */;
-			AF0015890B8A27C70045DAEE /* PBXTextBookmark */ = AF0015890B8A27C70045DAEE /* PBXTextBookmark */;
-			AF00158C0B8A27C70045DAEE /* PBXTextBookmark */ = AF00158C0B8A27C70045DAEE /* PBXTextBookmark */;
-			AF00158D0B8A27C70045DAEE /* PBXTextBookmark */ = AF00158D0B8A27C70045DAEE /* PBXTextBookmark */;
-			AF0015960B8A28F20045DAEE /* PBXTextBookmark */ = AF0015960B8A28F20045DAEE /* PBXTextBookmark */;
-			AF0015970B8A28F20045DAEE /* PBXTextBookmark */ = AF0015970B8A28F20045DAEE /* PBXTextBookmark */;
-			AF0015990B8A290F0045DAEE /* PBXTextBookmark */ = AF0015990B8A290F0045DAEE /* PBXTextBookmark */;
-			AF00159A0B8A290F0045DAEE /* PBXTextBookmark */ = AF00159A0B8A290F0045DAEE /* PBXTextBookmark */;
-			AF00159C0B8A290F0045DAEE /* PBXTextBookmark */ = AF00159C0B8A290F0045DAEE /* PBXTextBookmark */;
-			AF00159D0B8A290F0045DAEE /* PBXTextBookmark */ = AF00159D0B8A290F0045DAEE /* PBXTextBookmark */;
-			AF00159F0B8A290F0045DAEE /* PBXTextBookmark */ = AF00159F0B8A290F0045DAEE /* PBXTextBookmark */;
-			AF0015A00B8A290F0045DAEE /* PBXTextBookmark */ = AF0015A00B8A290F0045DAEE /* PBXTextBookmark */;
-			AF0015A10B8A290F0045DAEE /* PBXTextBookmark */ = AF0015A10B8A290F0045DAEE /* PBXTextBookmark */;
-			AF0015A20B8A290F0045DAEE /* PBXTextBookmark */ = AF0015A20B8A290F0045DAEE /* PBXTextBookmark */;
-			AF0015A30B8A292E0045DAEE /* PBXTextBookmark */ = AF0015A30B8A292E0045DAEE /* PBXTextBookmark */;
-			AF0015A40B8A292E0045DAEE /* PBXTextBookmark */ = AF0015A40B8A292E0045DAEE /* PBXTextBookmark */;
-			AF0015A70B8A2AF00045DAEE /* PBXTextBookmark */ = AF0015A70B8A2AF00045DAEE /* PBXTextBookmark */;
-			AF0015AD0B8A2B890045DAEE /* PBXTextBookmark */ = AF0015AD0B8A2B890045DAEE /* PBXTextBookmark */;
-			AF0015AE0B8A2B890045DAEE /* PBXTextBookmark */ = AF0015AE0B8A2B890045DAEE /* PBXTextBookmark */;
-			AF0015AF0B8A2B890045DAEE /* PBXTextBookmark */ = AF0015AF0B8A2B890045DAEE /* PBXTextBookmark */;
-			AF0015B00B8A2B890045DAEE /* PBXTextBookmark */ = AF0015B00B8A2B890045DAEE /* PBXTextBookmark */;
-			AF0015B10B8A2B890045DAEE /* PBXTextBookmark */ = AF0015B10B8A2B890045DAEE /* PBXTextBookmark */;
-			AF0015B20B8A2C350045DAEE /* PBXTextBookmark */ = AF0015B20B8A2C350045DAEE /* PBXTextBookmark */;
-			AF0015B30B8A2C350045DAEE /* PBXTextBookmark */ = AF0015B30B8A2C350045DAEE /* PBXTextBookmark */;
-			AF0015B60B8A2C6F0045DAEE /* PBXTextBookmark */ = AF0015B60B8A2C6F0045DAEE /* PBXTextBookmark */;
-			AF0015B70B8A2C6F0045DAEE /* PBXTextBookmark */ = AF0015B70B8A2C6F0045DAEE /* PBXTextBookmark */;
-			AF0015B80B8A2C6F0045DAEE /* PBXTextBookmark */ = AF0015B80B8A2C6F0045DAEE /* PBXTextBookmark */;
-			AF0015B90B8A2C6F0045DAEE /* PBXTextBookmark */ = AF0015B90B8A2C6F0045DAEE /* PBXTextBookmark */;
-			AF0015BB0B8A2C790045DAEE /* PBXBookmark */ = AF0015BB0B8A2C790045DAEE /* PBXBookmark */;
-			AF0015BC0B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015BC0B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015BD0B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015BD0B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015BE0B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015BE0B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015C00B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015C00B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015C10B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015C10B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015C20B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015C20B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015C60B8A2C7F0045DAEE /* PBXTextBookmark */ = AF0015C60B8A2C7F0045DAEE /* PBXTextBookmark */;
-			AF0015CD0B8A2D650045DAEE /* PBXTextBookmark */ = AF0015CD0B8A2D650045DAEE /* PBXTextBookmark */;
-			AF0015CE0B8A2D680045DAEE /* PBXTextBookmark */ = AF0015CE0B8A2D680045DAEE /* PBXTextBookmark */;
-			AF0015CF0B8A2ED80045DAEE /* PBXBookmark */ = AF0015CF0B8A2ED80045DAEE /* PBXBookmark */;
-			AF0015D00B8A2F490045DAEE /* PBXTextBookmark */ = AF0015D00B8A2F490045DAEE /* PBXTextBookmark */;
-			AF0015D30B8A2F490045DAEE /* PBXTextBookmark */ = AF0015D30B8A2F490045DAEE /* PBXTextBookmark */;
-			AF0015D70B8A2F9B0045DAEE /* PBXTextBookmark */ = AF0015D70B8A2F9B0045DAEE /* PBXTextBookmark */;
-			AF0015D80B8A2F9B0045DAEE /* PBXTextBookmark */ = AF0015D80B8A2F9B0045DAEE /* PBXTextBookmark */;
-			AF0015D90B8A2F9B0045DAEE /* PBXTextBookmark */ = AF0015D90B8A2F9B0045DAEE /* PBXTextBookmark */;
-			AF0015DA0B8A2F9B0045DAEE /* PBXTextBookmark */ = AF0015DA0B8A2F9B0045DAEE /* PBXTextBookmark */;
-			AF0015DB0B8A2F9B0045DAEE /* PBXTextBookmark */ = AF0015DB0B8A2F9B0045DAEE /* PBXTextBookmark */;
-			AF0015DC0B8A2F9B0045DAEE /* PBXTextBookmark */ = AF0015DC0B8A2F9B0045DAEE /* PBXTextBookmark */;
-			AF8341B60B8A1C5B0055995E = AF8341B60B8A1C5B0055995E /* PBXTextBookmark */;
-			AF8341B80B8A1C5B0055995E = AF8341B80B8A1C5B0055995E /* PBXTextBookmark */;
-			AF8342200B8A1D340055995E = AF8342200B8A1D340055995E /* PBXTextBookmark */;
-			AF8342220B8A1D340055995E = AF8342220B8A1D340055995E /* PBXTextBookmark */;
-			AF8342270B8A1D8F0055995E = AF8342270B8A1D8F0055995E /* PBXTextBookmark */;
-			AF8342280B8A1D8F0055995E = AF8342280B8A1D8F0055995E /* PBXTextBookmark */;
-			AF8342300B8A1E170055995E = AF8342300B8A1E170055995E /* PBXTextBookmark */;
-			AF8974940B4AFA6600965268 = AF8974940B4AFA6600965268 /* PBXTextBookmark */;
-			AFAC4E700B4C18D400D59661 = AFAC4E700B4C18D400D59661 /* PBXTextBookmark */;
-			AFAC4E790B4C23EC00D59661 = AFAC4E790B4C23EC00D59661 /* PBXTextBookmark */;
-			AFAC4E900B4C267800D59661 = AFAC4E900B4C267800D59661 /* PBXTextBookmark */;
-			AFAC4E920B4C267800D59661 = AFAC4E920B4C267800D59661 /* PBXTextBookmark */;
+			AF6311050B8F74FC000D1C1A /* PBXTextBookmark */ = AF6311050B8F74FC000D1C1A /* PBXTextBookmark */;
+			AF6311070B8F74FC000D1C1A /* PBXTextBookmark */ = AF6311070B8F74FC000D1C1A /* PBXTextBookmark */;
+			AF63112B0B8F85FF000D1C1A /* PBXTextBookmark */ = AF63112B0B8F85FF000D1C1A /* PBXTextBookmark */;
+			AF63112C0B8F85FF000D1C1A /* PBXTextBookmark */ = AF63112C0B8F85FF000D1C1A /* PBXTextBookmark */;
+			AF63112D0B8F85FF000D1C1A /* PBXTextBookmark */ = AF63112D0B8F85FF000D1C1A /* PBXTextBookmark */;
+			AF63114C0B8F89FB000D1C1A /* PBXTextBookmark */ = AF63114C0B8F89FB000D1C1A /* PBXTextBookmark */;
 		};
 		sourceControlManager = AF155A2D0A501F7B007E1E6E /* Source Control */;
 		userBuildSettings = {
@@ -149,9 +80,9 @@
 	};
 	08FB7796FE84155DC02AAC07 /* test.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {2462, 3836}}";
-			sepNavSelRange = "{6607, 0}";
-			sepNavVisRect = "{{0, 1751}, {752, 699}}";
+			sepNavIntBoundsRect = "{{0, 0}, {2462, 4060}}";
+			sepNavSelRange = "{5173, 0}";
+			sepNavVisRect = "{{0, 1450}, {1521, 670}}";
 			sepNavWindowFrame = "{{436, 4}, {811, 828}}";
 		};
 	};
@@ -161,7 +92,7 @@
 			AF155A290A501F5C007E1E6E /* PyOpenDirectory */,
 		);
 	};
-	AF0015620B8A24240045DAEE /* test.cpp:81 */ = {
+	AF0015620B8A24240045DAEE /* test.cpp:84 */ = {
 		isa = PBXFileBreakpoint;
 		actions = (
 		);
@@ -173,12 +104,12 @@
 		functionName = "main (int argc, const char * argv[])";
 		hitCount = 1;
 		ignoreCount = 0;
-		lineNumber = 81;
+		lineNumber = 84;
 		location = test.ob;
-		modificationTime = 193605512.453852;
+		modificationTime = 193955662.576472;
 		state = 1;
 	};
-	AF0015650B8A245A0045DAEE /* test.cpp:149 */ = {
+	AF0015650B8A245A0045DAEE /* test.cpp:167 */ = {
 		isa = PBXFileBreakpoint;
 		actions = (
 		);
@@ -190,607 +121,11 @@
 		functionName = "AuthenticateUser(CDirectoryService* dir, const char* user, const char* pswd)";
 		hitCount = 0;
 		ignoreCount = 0;
-		lineNumber = 149;
+		lineNumber = 167;
 		location = test.ob;
-		modificationTime = 193605510.70575;
+		modificationTime = 193955659.877094;
 		state = 2;
 	};
-	AF00156B0B8A25510045DAEE /* PBXBookmark */ = {
-		isa = PBXBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-	};
-	AF00156C0B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A310A501F84007E1E6E /* PythonWrapper.cpp */;
-		name = "PythonWrapper.cpp: 454";
-		rLen = 0;
-		rLoc = 14224;
-		rType = 0;
-		vrLen = 848;
-		vrLoc = 8760;
-	};
-	AF00156D0B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 82";
-		rLen = 0;
-		rLoc = 2828;
-		rType = 0;
-		vrLen = 1618;
-		vrLoc = 4731;
-	};
-	AF00156E0B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 183";
-		rLen = 0;
-		rLoc = 5658;
-		rType = 0;
-		vrLen = 993;
-		vrLoc = 5756;
-	};
-	AF00156F0B8A25E20045DAEE /* PBXBookmark */ = {
-		isa = PBXBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-	};
-	AF0015700B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 81";
-		rLen = 0;
-		rLoc = 2673;
-		rType = 0;
-		vrLen = 1509;
-		vrLoc = 4633;
-	};
-	AF0015710B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A310A501F84007E1E6E /* PythonWrapper.cpp */;
-		name = "PythonWrapper.cpp: 454";
-		rLen = 0;
-		rLoc = 14224;
-		rType = 0;
-		vrLen = 848;
-		vrLoc = 8760;
-	};
-	AF0015720B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 82";
-		rLen = 0;
-		rLoc = 2828;
-		rType = 0;
-		vrLen = 1618;
-		vrLoc = 4731;
-	};
-	AF0015730B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 183";
-		rLen = 0;
-		rLoc = 5658;
-		rType = 0;
-		vrLen = 993;
-		vrLoc = 5756;
-	};
-	AF0015740B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		name = "CDirectoryService.h: 54";
-		rLen = 0;
-		rLoc = 2212;
-		rType = 0;
-		vrLen = 1523;
-		vrLoc = 963;
-	};
-	AF0015770B8A25E20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 642";
-		rLen = 0;
-		rLoc = 19902;
-		rType = 0;
-		vrLen = 1509;
-		vrLoc = 19664;
-	};
-	AF00157F0B8A268C0045DAEE /* PBXBookmark */ = {
-		isa = PBXBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-	};
-	AF0015800B8A27320045DAEE /* PBXBookmark */ = {
-		isa = PBXBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-	};
-	AF0015810B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		name = "CDirectoryService.h: 54";
-		rLen = 0;
-		rLoc = 2212;
-		rType = 0;
-		vrLen = 1523;
-		vrLoc = 963;
-	};
-	AF0015820B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		name = "CDirectoryService.h: 54";
-		rLen = 0;
-		rLoc = 2212;
-		rType = 0;
-		vrLen = 1523;
-		vrLoc = 963;
-	};
-	AF0015830B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 82";
-		rLen = 0;
-		rLoc = 2828;
-		rType = 0;
-		vrLen = 1618;
-		vrLoc = 4731;
-	};
-	AF0015860B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 654";
-		rLen = 20;
-		rLoc = 20273;
-		rType = 0;
-		vrLen = 1836;
-		vrLoc = 19772;
-	};
-	AF0015890B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 114";
-		rLen = 0;
-		rLoc = 3990;
-		rType = 0;
-		vrLen = 1960;
-		vrLoc = 2740;
-	};
-	AF00158C0B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		rLen = 0;
-		rLoc = 2147483647;
-		rType = 0;
-	};
-	AF00158D0B8A27C70045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		name = "CDirectoryService.h: 73";
-		rLen = 1;
-		rLoc = 2962;
-		rType = 0;
-		vrLen = 2155;
-		vrLoc = 865;
-	};
-	AF0015960B8A28F20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		comments = "error: 'users' was not declared in this scope";
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		rLen = 1;
-		rLoc = 665;
-		rType = 1;
-	};
-	AF0015970B8A28F20045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 665";
-		rLen = 21;
-		rLoc = 20928;
-		rType = 0;
-		vrLen = 766;
-		vrLoc = 20342;
-	};
-	AF0015990B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 82";
-		rLen = 0;
-		rLoc = 2828;
-		rType = 0;
-		vrLen = 1618;
-		vrLoc = 4731;
-	};
-	AF00159A0B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF00159B0B8A290F0045DAEE /* DirServicesConst.h */;
-		name = "DirServicesConst.h: 1";
-		rLen = 0;
-		rLoc = 0;
-		rType = 0;
-		vrLen = 740;
-		vrLoc = 4875;
-	};
-	AF00159B0B8A290F0045DAEE /* DirServicesConst.h */ = {
-		isa = PBXFileReference;
-		name = DirServicesConst.h;
-		path = /System/Library/Frameworks/DirectoryService.framework/Headers/DirServicesConst.h;
-		sourceTree = "<absolute>";
-	};
-	AF00159C0B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 82";
-		rLen = 0;
-		rLoc = 2828;
-		rType = 0;
-		vrLen = 1618;
-		vrLoc = 4731;
-	};
-	AF00159D0B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF00159E0B8A290F0045DAEE /* DirServicesConst.h */;
-		name = "DirServicesConst.h: 1";
-		rLen = 0;
-		rLoc = 0;
-		rType = 0;
-		vrLen = 740;
-		vrLoc = 4875;
-	};
-	AF00159E0B8A290F0045DAEE /* DirServicesConst.h */ = {
-		isa = PBXFileReference;
-		name = DirServicesConst.h;
-		path = /System/Library/Frameworks/DirectoryService.framework/Headers/DirServicesConst.h;
-		sourceTree = "<absolute>";
-	};
-	AF00159F0B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 664";
-		rLen = 0;
-		rLoc = 20946;
-		rType = 0;
-		vrLen = 1156;
-		vrLoc = 19800;
-	};
-	AF0015A00B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 664";
-		rLen = 0;
-		rLoc = 20946;
-		rType = 0;
-		vrLen = 1900;
-		vrLoc = 19772;
-	};
-	AF0015A10B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 114";
-		rLen = 0;
-		rLoc = 3990;
-		rType = 0;
-		vrLen = 1960;
-		vrLoc = 2740;
-	};
-	AF0015A20B8A290F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		name = "CDirectoryService.h: 73";
-		rLen = 1;
-		rLoc = 2962;
-		rType = 0;
-		vrLen = 2155;
-		vrLoc = 865;
-	};
-	AF0015A30B8A292E0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		comments = "error: 'cfuser' was not declared in this scope";
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		rLen = 1;
-		rLoc = 681;
-		rType = 1;
-	};
-	AF0015A40B8A292E0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 670";
-		rLen = 0;
-		rLoc = 21318;
-		rType = 0;
-		vrLen = 657;
-		vrLoc = 20514;
-	};
-	AF0015A70B8A2AF00045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		comments = "error:   initializing argument 3 of 'void CFDictionaryGetKeysAndValues(const __CFDictionary*, const void**, const void**)'";
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		rLen = 0;
-		rLoc = 679;
-		rType = 1;
-	};
-	AF0015AD0B8A2B890045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		comments = "error: invalid conversion from 'const __CFDictionary**' to 'const void**'";
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		rLen = 0;
-		rLoc = 679;
-		rType = 1;
-	};
-	AF0015AE0B8A2B890045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21241;
-		rType = 0;
-		vrLen = 808;
-		vrLoc = 20661;
-	};
-	AF0015AF0B8A2B890045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21254;
-		rType = 0;
-		vrLen = 1660;
-		vrLoc = 20263;
-	};
-	AF0015B00B8A2B890045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 100";
-		rLen = 26;
-		rLoc = 3670;
-		rType = 0;
-		vrLen = 1960;
-		vrLoc = 2740;
-	};
-	AF0015B10B8A2B890045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
-		name = "CDirectoryService.h: 73";
-		rLen = 1;
-		rLoc = 2962;
-		rType = 0;
-		vrLen = 2155;
-		vrLoc = 865;
-	};
-	AF0015B20B8A2C350045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		comments = "error: cannot convert 'const void* (*)[1]' to 'const void**' for argument '3' to 'void CFDictionaryGetKeysAndValues(const __CFDictionary*, const void**, const void**)'";
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		rLen = 1;
-		rLoc = 679;
-		rType = 1;
-	};
-	AF0015B30B8A2C350045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21254;
-		rType = 0;
-		vrLen = 426;
-		vrLoc = 21043;
-	};
-	AF0015B60B8A2C6F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21254;
-		rType = 0;
-		vrLen = 426;
-		vrLoc = 21043;
-	};
-	AF0015B70B8A2C6F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		comments = "error: no matching function for call to 'CDirectoryService::AuthenticateUserBasic(const char*&, const char*&, bool&)'";
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		rLen = 1;
-		rLoc = 148;
-		rType = 1;
-	};
-	AF0015B80B8A2C6F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21254;
-		rType = 0;
-		vrLen = 426;
-		vrLoc = 21043;
-	};
-	AF0015B90B8A2C6F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 149";
-		rLen = 0;
-		rLoc = 6003;
-		rType = 0;
-		vrLen = 305;
-		vrLoc = 5732;
-	};
-	AF0015BB0B8A2C790045DAEE /* PBXBookmark */ = {
-		isa = PBXBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-	};
-	AF0015BC0B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21241;
-		rType = 0;
-		vrLen = 808;
-		vrLoc = 20661;
-	};
-	AF0015BD0B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 158";
-		rLen = 0;
-		rLoc = 6414;
-		rType = 0;
-		vrLen = 789;
-		vrLoc = 2405;
-	};
-	AF0015BE0B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF0015BF0B8A2C7F0045DAEE /* asm __kill  0x90059554 */;
-		rLen = 0;
-		rLoc = 1;
-		rType = 1;
-	};
-	AF0015BF0B8A2C7F0045DAEE /* asm __kill  0x90059554 */ = {
-		isa = PBXFileReference;
-		lastKnownFileType = file;
-		path = "asm __kill  0x90059554";
-		sourceTree = "<group>";
-	};
-	AF0015C00B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 678";
-		rLen = 0;
-		rLoc = 21241;
-		rType = 0;
-		vrLen = 808;
-		vrLoc = 20661;
-	};
-	AF0015C10B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 158";
-		rLen = 0;
-		rLoc = 6414;
-		rType = 0;
-		vrLen = 789;
-		vrLoc = 2405;
-	};
-	AF0015C20B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF0015C30B8A2C7F0045DAEE /* asm __kill  0x90059554 */;
-		name = "(null): 2";
-		rLen = 0;
-		rLoc = 51;
-		rType = 0;
-		vrLen = 333;
-		vrLoc = 0;
-	};
-	AF0015C30B8A2C7F0045DAEE /* asm __kill  0x90059554 */ = {
-		isa = PBXFileReference;
-		path = "asm __kill  0x90059554";
-		sourceTree = "<group>";
-	};
-	AF0015C60B8A2C7F0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 149";
-		rLen = 0;
-		rLoc = 6003;
-		rType = 0;
-		vrLen = 1939;
-		vrLoc = 2740;
-	};
-	AF0015CD0B8A2D650045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 149";
-		rLen = 0;
-		rLoc = 6003;
-		rType = 0;
-		vrLen = 1206;
-		vrLoc = 4944;
-	};
-	AF0015CE0B8A2D680045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 149";
-		rLen = 0;
-		rLoc = 6003;
-		rType = 0;
-		vrLen = 1206;
-		vrLoc = 4944;
-	};
-	AF0015CF0B8A2ED80045DAEE /* PBXBookmark */ = {
-		isa = PBXBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-	};
-	AF0015D00B8A2F490045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 163";
-		rLen = 0;
-		rLoc = 6414;
-		rType = 0;
-		vrLen = 848;
-		vrLoc = 2348;
-	};
-	AF0015D30B8A2F490045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 157";
-		rLen = 0;
-		rLoc = 6164;
-		rType = 0;
-		vrLen = 1939;
-		vrLoc = 5076;
-	};
-	AF0015D70B8A2F9B0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 680";
-		rLen = 0;
-		rLoc = 21241;
-		rType = 0;
-		vrLen = 783;
-		vrLoc = 20647;
-	};
-	AF0015D80B8A2F9B0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		rLen = 0;
-		rLoc = 80;
-		rType = 1;
-	};
-	AF0015D90B8A2F9B0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 163";
-		rLen = 0;
-		rLoc = 6414;
-		rType = 0;
-		vrLen = 848;
-		vrLoc = 2348;
-	};
-	AF0015DA0B8A2F9B0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 680";
-		rLen = 0;
-		rLoc = 21241;
-		rType = 0;
-		vrLen = 783;
-		vrLoc = 20647;
-	};
-	AF0015DB0B8A2F9B0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 81";
-		rLen = 0;
-		rLoc = 2673;
-		rType = 0;
-		vrLen = 846;
-		vrLoc = 2441;
-	};
-	AF0015DC0B8A2F9B0045DAEE /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 172";
-		rLen = 0;
-		rLoc = 6607;
-		rType = 0;
-		vrLen = 1881;
-		vrLoc = 5076;
-	};
 	AF155A290A501F5C007E1E6E /* PyOpenDirectory */ = {
 		isa = PBXExecutable;
 		activeArgIndex = 2147483647;
@@ -849,25 +184,25 @@
 	};
 	AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1100, 10094}}";
-			sepNavSelRange = "{21241, 0}";
-			sepNavVisRect = "{{0, 5361}, {795, 328}}";
-			sepNavWindowFrame = "{{144, 200}, {1046, 828}}";
+			sepNavIntBoundsRect = "{{0, 0}, {1076, 10794}}";
+			sepNavSelRange = "{21927, 0}";
+			sepNavVisRect = "{{0, 7571}, {1041, 366}}";
+			sepNavWindowFrame = "{{117, 32}, {1368, 1146}}";
 		};
 	};
 	AF155A300A501F84007E1E6E /* CDirectoryService.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1010, 1064}}";
-			sepNavSelRange = "{2962, 1}";
-			sepNavVisRect = "{{0, 354}, {752, 699}}";
+			sepNavIntBoundsRect = "{{0, 0}, {1521, 1036}}";
+			sepNavSelRange = "{2851, 0}";
+			sepNavVisRect = "{{0, 366}, {1521, 670}}";
 			sepNavWindowFrame = "{{15, 4}, {811, 828}}";
 		};
 	};
 	AF155A310A501F84007E1E6E /* PythonWrapper.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {992, 5390}}";
-			sepNavSelRange = "{14121, 0}";
-			sepNavVisRect = "{{0, 2496}, {795, 351}}";
+			sepNavIntBoundsRect = "{{0, 0}, {992, 6790}}";
+			sepNavSelRange = "{17612, 0}";
+			sepNavVisRect = "{{0, 6063}, {752, 699}}";
 			sepNavWindowFrame = "{{113, 4}, {811, 828}}";
 		};
 	};
@@ -887,124 +222,62 @@
 			sepNavWindowFrame = "{{36, 4}, {811, 1024}}";
 		};
 	};
-	AF8341B60B8A1C5B0055995E /* PBXTextBookmark */ = {
+	AF6311050B8F74FC000D1C1A /* PBXTextBookmark */ = {
 		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 297";
-		rLen = 26;
-		rLoc = 9395;
-		rType = 0;
-		vrLen = 1547;
-		vrLoc = 8151;
-	};
-	AF8341B80B8A1C5B0055995E /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 297";
-		rLen = 26;
-		rLoc = 9395;
-		rType = 0;
-		vrLen = 1547;
-		vrLoc = 8151;
-	};
-	AF8342200B8A1D340055995E /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 41";
-		rLen = 0;
-		rLoc = 1482;
-		rType = 0;
-		vrLen = 1288;
-		vrLoc = 1178;
-	};
-	AF8342220B8A1D340055995E /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 41";
-		rLen = 0;
-		rLoc = 1482;
-		rType = 0;
-		vrLen = 1288;
-		vrLoc = 1178;
-	};
-	AF8342270B8A1D8F0055995E /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
 		fRef = AF155A310A501F84007E1E6E /* PythonWrapper.cpp */;
-		name = "PythonWrapper.cpp: 454";
+		name = "PythonWrapper.cpp: 355";
 		rLen = 0;
-		rLoc = 14224;
+		rLoc = 10892;
 		rType = 0;
-		vrLen = 1043;
-		vrLoc = 3687;
+		vrLen = 1931;
+		vrLoc = 8603;
 	};
-	AF8342280B8A1D8F0055995E /* PBXTextBookmark */ = {
+	AF6311070B8F74FC000D1C1A /* PBXTextBookmark */ = {
 		isa = PBXTextBookmark;
 		fRef = AF155A310A501F84007E1E6E /* PythonWrapper.cpp */;
-		name = "PythonWrapper.cpp: 454";
+		name = "PythonWrapper.cpp: 355";
 		rLen = 0;
-		rLoc = 14224;
+		rLoc = 10892;
 		rType = 0;
-		vrLen = 1043;
-		vrLoc = 3687;
+		vrLen = 1931;
+		vrLoc = 8603;
 	};
-	AF8342300B8A1E170055995E /* PBXTextBookmark */ = {
+	AF63112B0B8F85FF000D1C1A /* PBXTextBookmark */ = {
 		isa = PBXTextBookmark;
-		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 41";
+		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
+		name = "CDirectoryService.h: 70";
 		rLen = 0;
-		rLoc = 1482;
+		rLoc = 2863;
 		rType = 0;
-		vrLen = 1144;
-		vrLoc = 0;
+		vrLen = 2090;
+		vrLoc = 866;
 	};
-	AF8974940B4AFA6600965268 /* PBXTextBookmark */ = {
+	AF63112C0B8F85FF000D1C1A /* PBXTextBookmark */ = {
 		isa = PBXTextBookmark;
-		fRef = AF155A310A501F84007E1E6E /* PythonWrapper.cpp */;
-		name = Python/Python.h;
-		rLen = 15;
-		rLoc = 726;
-		rType = 0;
-		vrLen = 1115;
-		vrLoc = 0;
-	};
-	AFAC4E700B4C18D400D59661 /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
+		comments = "error: no matching function for call to 'CDirectoryService::QueryRecordsWithAttributes(const __CFDictionary*&, const __CFDictionary*&, tDirPatternMatch, bool, bool, const char [28], const __CFArray*&)'";
 		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		name = "test.cpp: 177";
-		rLen = 0;
-		rLoc = 5271;
-		rType = 0;
-		vrLen = 1062;
-		vrLoc = 1408;
+		rLen = 1;
+		rLoc = 139;
+		rType = 1;
 	};
-	AFAC4E790B4C23EC00D59661 /* PBXTextBookmark */ = {
+	AF63112D0B8F85FF000D1C1A /* PBXTextBookmark */ = {
 		isa = PBXTextBookmark;
-		fRef = AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */;
-		name = "CDirectoryService.cpp: 1069";
+		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
+		name = "CDirectoryService.h: 70";
 		rLen = 0;
-		rLoc = 30871;
+		rLoc = 2863;
 		rType = 0;
-		vrLen = 1047;
-		vrLoc = 11877;
+		vrLen = 2090;
+		vrLoc = 866;
 	};
-	AFAC4E900B4C267800D59661 /* PBXTextBookmark */ = {
+	AF63114C0B8F89FB000D1C1A /* PBXTextBookmark */ = {
 		isa = PBXTextBookmark;
-		fRef = AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */;
-		name = "CFStringUtil.cpp: 124";
+		fRef = 08FB7796FE84155DC02AAC07 /* test.cpp */;
+		name = "test.cpp: 140";
 		rLen = 0;
-		rLoc = 2726;
+		rLoc = 5173;
 		rType = 0;
-		vrLen = 599;
-		vrLoc = 842;
+		vrLen = 2505;
+		vrLoc = 4188;
 	};
-	AFAC4E920B4C267800D59661 /* PBXTextBookmark */ = {
-		isa = PBXTextBookmark;
-		fRef = AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */;
-		name = "CFStringUtil.cpp: 124";
-		rLen = 0;
-		rLoc = 2726;
-		rType = 0;
-		vrLen = 599;
-		vrLoc = 842;
-	};
 }

Modified: PyOpenDirectory/trunk/support/test.cpp
===================================================================
--- PyOpenDirectory/trunk/support/test.cpp	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/support/test.cpp	2007-02-23 21:21:24 UTC (rev 1251)
@@ -36,12 +36,15 @@
 void AuthenticateUser(CDirectoryService* dir, const char* guid, const char* user, const char* pswd);
 void AuthenticateUserDigest(CDirectoryService* dir, const char* guid, const char* user, const char* challenge, const char* response, const char* method);
 
+#define		kDSStdRecordTypeResources					"dsRecTypeStandard:Resources"
+#define		kDSNAttrServicesLocator						"dsAttrTypeStandard:ServicesLocator"
+
 int main (int argc, const char * argv[]) {
     
 	CDirectoryService* dir = new CDirectoryService("/Search");
 
-#if 1
 #if 0
+#if 0
 	CFStringRef strings[2];
 	strings[0] = CFSTR(kDS1AttrDistinguishedName);
 	strings[1] = CFSTR(kDS1AttrGeneratedUID);
@@ -107,19 +110,34 @@
 	}
 	CFRelease(array);
 	
+#elif 0
+	CFStringRef strings[2];
+	strings[0] = CFSTR(kDS1AttrDistinguishedName);
+	strings[1] = CFSTR(kDS1AttrXMLPlist);
+	CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
+                        
+	CFMutableDictionaryRef dict = dir->QueryRecordsWithAttribute(kDSNAttrServicesLocator, "D9A8E41B", eDSStartsWith, false, kDSStdRecordTypeResources, array);
+	if (dict != NULL)
+	{
+		printf("\n*** Computers: %d ***\n", CFDictionaryGetCount(dict));
+		CFDictionaryApplyFunction(dict, PrintDictionaryDictionary, NULL);
+		CFRelease(dict);
+	}
+	else
+	{
+		printf("\nNo Users returned\n");
+	}
+	CFRelease(array);
+	
 #elif 1
-	CFStringRef keys[2];
-	keys[0] = CFSTR(kDS1AttrENetAddress);
-	CFStringRef values[2];
-	values[0] = CFSTR("00:17:f2:02:35:e4");
-	CFDictionaryRef kvdict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void**)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+	const char* compoundtest = "(&(|(dsAttrTypeStandard:RealName=U2*)(dsAttrTypeStandard:RealName=X S*))(dsAttrTypeStandard:ServicesLocator=D9A8E41B-C591-4D6B-A1CA-B57FFB8EF2F5:F967C034-54B8-4E65-9B38-7A6CD2600268:calendar))";
                         
 	CFStringRef strings[2];
 	strings[0] = CFSTR(kDS1AttrDistinguishedName);
 	strings[1] = CFSTR(kDS1AttrXMLPlist);
 	CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
                         
-	CFMutableDictionaryRef dict = dir->QueryRecordsWithAttributes(kvdict, eDSExact, false, false, kDSStdRecordTypeComputers, array);
+	CFMutableDictionaryRef dict = dir->QueryRecordsWithAttributes(compoundtest, true, kDSStdRecordTypeResources, array);
 	if (dict != NULL)
 	{
 		printf("\n*** Computers: %d ***\n", CFDictionaryGetCount(dict));

Modified: PyOpenDirectory/trunk/test.py
===================================================================
--- PyOpenDirectory/trunk/test.py	2007-02-23 19:00:48 UTC (rev 1250)
+++ PyOpenDirectory/trunk/test.py	2007-02-23 21:21:24 UTC (rev 1251)
@@ -19,6 +19,7 @@
 import opendirectory
 import dsattributes
 import time
+from dsquery import expression, match
 
 try:
 	ref = opendirectory.odInit("/Search")
@@ -66,13 +67,13 @@
 				print "Name: %s" % n
 				print "dict: %s" % str(d[n])
 	
-	def query(title, dict, matchType, casei, allmatch, recordType, attrs):
-		d = opendirectory.queryRecordsWithAttributes(
+	def querySimple(title, attr, value, matchType, casei, recordType, attrs):
+		d = opendirectory.queryRecordsWithAttribute(
 		    ref,
-		    dict,
+		    attr,
+		    value,
 		    matchType,
 		    casei,
-		    allmatch,
 			recordType,
 			attrs
 		)
@@ -86,46 +87,64 @@
 				print "Name: %s" % n
 				print "dict: %s" % str(d[n])
 		
+	def queryCompound(title, compound, casei, recordType, attrs):
+		d = opendirectory.queryRecordsWithAttributes(
+		    ref,
+		    compound,
+		    casei,
+			recordType,
+			attrs
+		)
+		if d is None:
+			print "Failed to query users"
+		else:
+			names = [v for v in d.iterkeys()]
+			names.sort()
+			print "\n%s number of results = %d" % (title, len(names),)
+			for n in names:
+				print "Name: %s" % n
+				print "dict: %s" % str(d[n])
+		
 	def queryUsers():
-		query(
+		querySimple(
 			"queryUsers",
-		    {dsattributes.kDS1AttrFirstName: "cyrus",},
+		    dsattributes.kDS1AttrFirstName,
+		    "cyrus",
 		    dsattributes.eDSExact,
 		    True,
-		    True,
 			dsattributes.kDSStdRecordTypeUsers,
 			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
 		)
 		
 	def queryUsersCompoundOr():
-		query(
+		queryCompound(
 			"queryUsersCompoundOr",
-		    {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
-		    dsattributes.eDSContains,
-		    True,
+		    expression(expression.OR,
+					   (match(dsattributes.kDS1AttrFirstName, "chris", dsattributes.eDSContains),
+					    match(dsattributes.kDS1AttrLastName, "roy", dsattributes.eDSContains))).generate(),
 		    False,
 			dsattributes.kDSStdRecordTypeUsers,
 			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
 		)
 		
 	def queryUsersCompoundOrExact():
-		query(
+		queryCompound(
 			"queryUsersCompoundOrExact",
-		    {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
-		    dsattributes.eDSExact,
-		    True,
+		    expression(expression.OR,
+					   (match(dsattributes.kDS1AttrFirstName, "chris", dsattributes.eDSExact),
+					    match(dsattributes.kDS1AttrLastName, "roy", dsattributes.eDSExact))).generate(),
 		    False,
 			dsattributes.kDSStdRecordTypeUsers,
 			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
 		)
 		
 	def queryUsersCompoundAnd():
-		query(
+		queryCompound(
 			"queryUsersCompoundAnd",
-		    {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
-		    dsattributes.eDSContains,
+		    expression(expression.AND,
+					   (match(dsattributes.kDS1AttrFirstName, "chris", dsattributes.eDSContains),
+					    match(dsattributes.kDS1AttrLastName, "roy", dsattributes.eDSContains))).generate(),
 		    True,
-		    True,
 			dsattributes.kDSStdRecordTypeUsers,
 			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
 		)
@@ -138,12 +157,12 @@
 	
 	#listUsers()
 	#listGroups()
-	listComputers()
-	#queryUsers()
-	#queryUsersCompoundOr()
-	#queryUsersCompoundOrExact()
-	#queryUsersCompoundAnd()
-	authentciateBasic()
+	#listComputers()
+	queryUsers()
+	queryUsersCompoundOr()
+	queryUsersCompoundOrExact()
+	queryUsersCompoundAnd()
+	#authentciateBasic()
 
 	ref = None
 except opendirectory.ODError, ex:

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070223/51dcab6f/attachment.html


More information about the calendarserver-changes mailing list