[CalendarServer-changes] [1138] PyOpenDirectory/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Feb 6 14:10:09 PST 2007


Revision: 1138
          http://trac.macosforge.org/projects/calendarserver/changeset/1138
Author:   cdaboo at apple.com
Date:     2007-02-06 14:10:09 -0800 (Tue, 06 Feb 2007)

Log Message:
-----------
Merge branches/users/cdaboo/od-scheme-1044 to trunk.

Modified Paths:
--------------
    PyOpenDirectory/trunk/pysrc/dsattributes.py
    PyOpenDirectory/trunk/pysrc/opendirectory.py
    PyOpenDirectory/trunk/src/CDirectoryService.cpp
    PyOpenDirectory/trunk/src/CDirectoryService.h
    PyOpenDirectory/trunk/src/CFStringUtil.cpp
    PyOpenDirectory/trunk/src/PythonWrapper.cpp
    PyOpenDirectory/trunk/support/test.cpp
    PyOpenDirectory/trunk/test.py

Modified: PyOpenDirectory/trunk/pysrc/dsattributes.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/dsattributes.py	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/pysrc/dsattributes.py	2007-02-06 22:10:09 UTC (rev 1138)
@@ -21,6 +21,18 @@
 This comes directly (with C->Python conversion) from <DirectoryServices/DirServicesConst.h>
 """
 
+# Specific match types
+
+eDSExact             =    0x2001
+eDSStartsWith        =    0x2002
+eDSEndsWith          =    0x2003
+eDSContains          =    0x2004
+
+eDSLessThan          =    0x2005
+eDSGreaterThan       =    0x2006
+eDSLessEqual         =    0x2007
+eDSGreaterEqual      =    0x2008
+
 # Specific Record Type Constants
 
 """
@@ -1551,6 +1563,12 @@
 kDSNAttrResourceType = "dsAttrTypeStandard:ResourceType"
 
 """
+ kDSNAttrServicesLocator
+ Attribute describing the services hosted for the record.
+"""
+kDSNAttrServicesLocator = "dsAttrTypeStandard:ServicesLocator"
+
+"""
  kDSNAttrState
  The state or province of a country.
 """

Modified: PyOpenDirectory/trunk/pysrc/opendirectory.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/opendirectory.py	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/pysrc/opendirectory.py	2007-02-06 22:10:09 UTC (rev 1138)
@@ -40,6 +40,21 @@
         or C{None} otherwise.
     """
 
+def queryRecordsWithAttributes(obj, query, matchType, casei, allmatch, 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 query: C{dict} containing attribute/value pairs to search.
+    @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 authenticateUserBasic(obj, user, pswd):
     """
     Authenticate a user with a password to Open Directory.

Modified: PyOpenDirectory/trunk/src/CDirectoryService.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.cpp	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/CDirectoryService.cpp	2007-02-06 22:10:09 UTC (rev 1138)
@@ -23,7 +23,7 @@
 
 #include "CFStringUtil.h"
 
-#include <Python.h>
+#include <Python/Python.h>
 
 #include <stdlib.h>
 #include <string.h>
@@ -108,6 +108,39 @@
 	}
 }
 
+// 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 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)
+{
+	try
+	{
+		// Get attribute map
+		return _QueryRecordsWithAttributes(query, matchType, casei, allmatch, 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;
+	}
+}
+
 // AuthenticateUserBasic
 // 
 // Authenticate a user to the directory using plain text credentials.
@@ -169,6 +202,7 @@
 // Get specific attributes for records of a specified type in the directory.
 //
 // @param type: the record type to check.
+// @param names: a list of record names to target if NULL all records are matched.
 // @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,
@@ -375,6 +409,234 @@
 	return result;
 }
 
+// _QueryRecordsWithAttributes
+// 
+// Get specific attributes for records of a specified type in the directory.
+//
+// @param query: a dictionary containg attribute/value pairs to match in records.
+// @param matchType: the match type to 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 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 result = NULL;
+	CFMutableDictionaryRef vresult = NULL;
+	CFMutableArrayRef values = NULL;
+	tDataNodePtr queryAttr = NULL;
+	tDataNodePtr queryValue = NULL;
+	tDataListPtr recTypes = NULL;
+	tDataListPtr attrTypes = NULL;
+	tContextData context = NULL;
+	tAttributeListRef attrListRef = 0L;
+	tRecordEntry* pRecEntry = NULL;
+	
+	// Must have attributes
+	if (::CFArrayGetCount(attrs) == 0)
+		return NULL;
+
+	try
+	{
+		// Make sure we have a valid directory service
+		OpenService();
+		
+		// Open the node we want to query
+		OpenNode();
+		
+		// We need a buffer for what comes next
+		CreateBuffer();
+		
+		// Determine attribute to search
+		if (::CFDictionaryGetCount(query) == 1)
+		{
+			BuildSimpleQuery(query, queryAttr, queryValue);
+			if (casei)
+				matchType |= 0x0100;
+			else
+				matchType &= 0xFEFF;
+		}
+		else
+		{
+			queryAttr = ::dsDataNodeAllocateString(mDir, kDS1AttrDistinguishedName);
+			BuildCompoundQuery(query, (tDirPatternMatch)matchType, allmatch, queryValue);
+			matchType = (casei) ? eDSiCompoundExpression : eDSCompoundExpression;
+		}
+
+		// Build data list of types
+		recTypes = ::dsDataListAllocate(mDir);
+		ThrowIfNULL(recTypes);
+		ThrowIfDSErr(::dsBuildListFromStringsAlloc(mDir, recTypes,  type, NULL));
+
+		// Build data list of attributes
+		attrTypes = ::dsDataListAllocate(mDir);
+		ThrowIfNULL(attrTypes);
+		BuildStringDataList(attrs, attrTypes);
+		
+		result = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+		
+		do
+		{
+			// List all the appropriate records
+			unsigned long recCount = 0;
+			tDirStatus err;
+			do
+			{
+				err = ::dsDoAttributeValueSearchWithData(mNode, mData, recTypes, queryAttr, (tDirPatternMatch)matchType, queryValue, attrTypes, false, &recCount, &context);
+				if (err == eDSBufferTooSmall)
+					ReallocBuffer();
+			} while(err == eDSBufferTooSmall);
+			ThrowIfDSErr(err);
+			for(unsigned long i = 1; i <= recCount; i++)
+			{
+				// Get the record entry
+				ThrowIfDSErr(::dsGetRecordEntry(mNode, mData, i, &attrListRef, &pRecEntry));
+				
+				// Get the entry's name
+				char* temp = NULL;
+				ThrowIfDSErr(::dsGetRecordNameFromEntry(pRecEntry, &temp));
+				std::auto_ptr<char> recname(temp);
+				
+				// Create a dictionary for the values
+				vresult = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+				
+				// Look at each requested attribute and get one value
+				for(unsigned long j = 1; j <= pRecEntry->fRecordAttributeCount; j++)
+				{
+					tAttributeValueListRef attributeValueListRef = NULL;
+					tAttributeEntryPtr attributeInfoPtr = NULL;
+					
+					ThrowIfDSErr(::dsGetAttributeEntry(mNode, mData, attrListRef, j, &attributeValueListRef, &attributeInfoPtr));
+					
+					if (attributeInfoPtr->fAttributeValueCount > 0)
+					{
+						// Determine what the attribute is and where in the result list it should be put
+						std::auto_ptr<char> attrname(CStringFromBuffer(&attributeInfoPtr->fAttributeSignature));
+						CFStringUtil cfattrname(attrname.get());
+						
+						if (attributeInfoPtr->fAttributeValueCount > 1)
+						{
+							values = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+							for(unsigned long k = 1; k <= attributeInfoPtr->fAttributeValueCount; k++)
+							{
+								// Get the attribute value and store in results
+								tAttributeValueEntryPtr attributeValue = NULL;
+								ThrowIfDSErr(::dsGetAttributeValue(mNode, mData, k, attributeValueListRef, &attributeValue));
+								std::auto_ptr<char> data(CStringFromBuffer(&attributeValue->fAttributeValueData));
+								CFStringUtil strvalue(data.get());
+								::CFArrayAppendValue(values, strvalue.get());
+								::dsDeallocAttributeValueEntry(mDir, attributeValue);
+								attributeValue = NULL;
+							}
+							::CFDictionarySetValue(vresult, cfattrname.get(), values);
+							::CFRelease(values);
+							values = NULL;
+						}
+						else
+						{
+							// Get the attribute value and store in results
+							tAttributeValueEntryPtr attributeValue = NULL;
+							ThrowIfDSErr(::dsGetAttributeValue(mNode, mData, 1, attributeValueListRef, &attributeValue));
+							std::auto_ptr<char> data(CStringFromBuffer(&attributeValue->fAttributeValueData));
+							CFStringUtil strvalue(data.get());
+							::CFDictionarySetValue(vresult, cfattrname.get(), strvalue.get());
+							::dsDeallocAttributeValueEntry(mDir, attributeValue);
+							attributeValue = NULL;
+						}
+					}
+					
+					::dsCloseAttributeValueList(attributeValueListRef);
+					attributeValueListRef = NULL;
+					::dsDeallocAttributeEntry(mDir, attributeInfoPtr);
+					attributeInfoPtr = NULL;
+				}
+				
+				// Add dictionary of values to result array
+				CFStringUtil str(recname.get());
+				::CFDictionarySetValue(result, str.get(), vresult);
+				::CFRelease(vresult);
+				vresult = NULL;
+				
+				// Clean-up
+				::dsCloseAttributeList(attrListRef);
+				attrListRef = 0L;
+				::dsDeallocRecordEntry(mDir, pRecEntry);
+				pRecEntry = NULL;
+			}
+		} while (context != NULL); // Loop until all data has been obtained.
+		
+		// Cleanup
+		::dsDataListDeallocate(mDir, recTypes);
+		::dsDataListDeallocate(mDir, attrTypes);
+		::dsDataNodeDeAllocate(mDir, queryValue);
+		::dsDataNodeDeAllocate(mDir, queryAttr);
+		free(recTypes);
+		free(attrTypes);
+		RemoveBuffer();
+		CloseNode();
+		CloseService();
+	}
+	catch(long dsStatus)
+	{
+		// Cleanup
+		if (context != NULL)
+			::dsReleaseContinueData(mDir, context);
+		if (attrListRef != 0L)
+			::dsCloseAttributeList(attrListRef);
+		if (pRecEntry != NULL)
+			dsDeallocRecordEntry(mDir, pRecEntry);
+		if (recTypes != NULL)
+		{
+			::dsDataListDeallocate(mDir, recTypes);
+			free(recTypes);
+			recTypes = NULL;
+		}
+		if (attrTypes != NULL)
+		{
+			::dsDataListDeallocate(mDir, attrTypes);
+			free(attrTypes);
+			attrTypes = NULL;
+		}
+		if (queryValue != NULL)
+		{
+			::dsDataNodeDeAllocate(mDir, queryValue);
+			queryValue = NULL;
+		}
+		if (queryAttr != NULL)
+		{
+			::dsDataNodeDeAllocate(mDir, queryAttr);
+			queryAttr = NULL;
+		}
+		RemoveBuffer();
+		CloseNode();
+		CloseService();
+		
+		if (values != NULL)
+		{
+			::CFRelease(values);
+			values = NULL;
+		}
+		if (vresult != NULL)
+		{
+			::CFRelease(vresult);
+			vresult = NULL;
+		}
+		if (result != NULL)
+		{
+			::CFRelease(result);
+			result = NULL;
+		}
+		throw;
+	}
+	
+	return result;
+}
+
 // AuthenticationGetNode
 // 
 // Authenticate a user to the directory.
@@ -846,6 +1108,106 @@
 	}
 }
 
+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-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/CDirectoryService.h	2007-02-06 22:10:09 UTC (rev 1138)
@@ -33,6 +33,7 @@
 	~CDirectoryService();
 	
 	CFMutableDictionaryRef ListAllRecordsWithAttributes(const char* recordType, CFArrayRef attributes);
+	CFMutableDictionaryRef QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes);
 
 	bool AuthenticateUserBasic(const char* user, const char* pswd, bool& result);
 	bool AuthenticateUserDigest(const char* user, const char* challenge, const char* response, const char* method, bool& result);
@@ -45,6 +46,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);
 
 	CFStringRef CDirectoryService::AuthenticationGetNode(const char* user);
 	bool NativeAuthenticationBasic(const char* user, const char* pswd);
@@ -64,6 +66,9 @@
 	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/CFStringUtil.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CFStringUtil.cpp	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/CFStringUtil.cpp	2007-02-06 22:10:09 UTC (rev 1138)
@@ -77,13 +77,39 @@
 	
 	if (bytes == NULL)
 	{
-		char localBuffer[256];
-		localBuffer[0] = 0;
-		Boolean success = ::CFStringGetCString(mRef, localBuffer, 256, kCFStringEncodingUTF8);
-		if (!success)
-			localBuffer[0] = 0;
+		// Need to convert the CFString to UTF-8. Since we don't know the exact length of the UTF-8 data
+		// we have to iterate over the conversion process until we succeed or the length we are allocating
+		// is greater than the value it could maximally be. We start with half the UTF-16 encoded value,
+		// which will give an accurate count for an ascii only string (plus add one for \0).
+		CFIndex len = ::CFStringGetLength(mRef)/2 + 1;
+		CFIndex maxSize = ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(mRef), kCFStringEncodingUTF8) + 1;
+		char* buffer = NULL;
+		while(true)
+		{
+			buffer = (char*)::malloc(len);
+			if (buffer == NULL)
+				break;
+			buffer[0] = 0;
+			Boolean success = ::CFStringGetCString(mRef, buffer, len, kCFStringEncodingUTF8);
+			if (!success)
+			{
+				::free(buffer);
+				buffer = NULL;
+				if (len == maxSize)
+				{
+					buffer = (char*)::malloc(1);
+					buffer[0] = 0;
+					break;
+				}
+				len *= 2;
+				if (len > maxSize)
+					len = maxSize;
+			}
+			else
+				break;
+		}
 		
-		return ::strdup(localBuffer);
+		return buffer;
 	}
 	else
 	{

Modified: PyOpenDirectory/trunk/src/PythonWrapper.cpp
===================================================================
--- PyOpenDirectory/trunk/src/PythonWrapper.cpp	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/PythonWrapper.cpp	2007-02-06 22:10:09 UTC (rev 1138)
@@ -35,23 +35,8 @@
 // Utility function - not exposed to Python
 static PyObject* CFStringToPyStr(CFStringRef str)
 {
-	PyObject* pystr = NULL;
-	const char* bytes = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
-	
-	if (bytes == NULL)
-	{
-		char localBuffer[256];
-		Boolean success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
-		if (!success)
-			localBuffer[0] = 0;
-		pystr = PyString_FromString(localBuffer);
-	}
-	else
-	{
-		pystr = PyString_FromString(bytes);
-	}
-	
-	return pystr;
+	CFStringUtil s(str);
+	return PyString_FromString(s.temp_str());
 }
 
 // Utility function - not exposed to Python
@@ -345,6 +330,83 @@
 }
 
 /*
+def queryRecordsWithAttributes(obj, query, matchType, casei, allmatch, 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 query: C{dict} containing attribute/value pairs to search.
+    @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)
+{
+	PyObject* pyds;
+	PyObject* query;
+	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))
+    {
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));		
+        return NULL;
+    }
+	
+	// Convert dict to CFDictionary of CFString
+	CFDictionaryRef cfquery = PyDictToCFDictionary(query);
+	if (cfquery == NULL)
+    {
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse query dict", 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);
+		if (dict != NULL)
+		{
+			PyObject* result = CFDictionaryDictionaryToPyDict(dict);
+			CFRelease(dict);
+			CFRelease(cfquery);
+			CFRelease(cfattributes);
+			
+			return result;
+		}
+	}
+	else
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));		
+	
+	CFRelease(cfquery);
+	CFRelease(cfattributes);
+	return NULL;
+}
+
+/*
 def authenticateUserBasic(obj, user, pswd):
 	"""
 	Authenticate a user with a password to Open Directory.
@@ -433,6 +495,8 @@
 		"Initialize the Open Directory system."},
     {"listAllRecordsWithAttributes",  listAllRecordsWithAttributes, METH_VARARGS,
 		"List all records of the specified type in Open Directory, returning requested attributes."},
+    {"queryRecordsWithAttributes",  queryRecordsWithAttributes, METH_VARARGS,
+		"List records in Open Directory matching specified criteria, and return key attributes for each one."},
     {"authenticateUserBasic",  authenticateUserBasic, METH_VARARGS,
 		"Authenticate a user with a password to Open Directory using plain text authentication."},
     {"authenticateUserDigest",  authenticateUserDigest, METH_VARARGS,

Modified: PyOpenDirectory/trunk/support/test.cpp
===================================================================
--- PyOpenDirectory/trunk/support/test.cpp	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/support/test.cpp	2007-02-06 22:10:09 UTC (rev 1138)
@@ -34,11 +34,13 @@
 void PrintArrayArray(CFMutableArrayRef list);
 void PrintArray(CFArrayRef list);
 void AuthenticateUser(CDirectoryService* dir, const char* user, const char* pswd);
+void AuthenticateUserDigest(CDirectoryService* dir, const char* user, const char* challenge, const char* response, const char* method);
 
 int main (int argc, const char * argv[]) {
     
 	CDirectoryService* dir = new CDirectoryService("/Search");
 
+#if 0
 	CFStringRef strings[2];
 	strings[0] = CFSTR(kDS1AttrDistinguishedName);
 	strings[1] = CFSTR(kDS1AttrGeneratedUID);
@@ -74,20 +76,89 @@
 	}
 	CFRelease(array);
 
-	AuthenticateUser(dir, "cdaboo", "appledav1234");
-	AuthenticateUser(dir, "cdaboo", "appledav6585");
+	AuthenticateUser(dir, "test", "test-no");
+	AuthenticateUser(dir, "test", "test-yes");
+#elif 0
+	CFStringRef keys[2];
+	keys[0] = CFSTR(kDS1AttrFirstName);
+	keys[1] = CFSTR(kDS1AttrLastName);
+	CFStringRef values[2];
+	values[0] = CFSTR("cyrus");
+	values[1] = CFSTR("daboo");
+	CFDictionaryRef kvdict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void**)values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                        
+	CFStringRef strings[2];
+	strings[0] = CFSTR(kDS1AttrDistinguishedName);
+	strings[1] = CFSTR(kDS1AttrGeneratedUID);
+	CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
+                        
+	CFMutableDictionaryRef dict = dir->QueryRecordsWithAttributes(kvdict, eDSContains, false, false, kDSStdRecordTypeUsers, array);
+	if (dict != NULL)
+	{
+		printf("\n*** Users: %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);
+                        
+	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);
+	if (dict != NULL)
+	{
+		printf("\n*** Computers: %d ***\n", CFDictionaryGetCount(dict));
+		CFDictionaryApplyFunction(dict, PrintDictionaryDictionary, NULL);
+		CFRelease(dict);
+	}
+	else
+	{
+		printf("\nNo Users returned\n");
+	}
+	CFRelease(array);
+	
+#else
+	const char* u = "test";
+	//const char* c = "nonce=\"1\", qop=\"auth\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\"";
+	//const char* r = "username=\"test\", nonce=\"1\", cnonce=\"1\", nc=\"1\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\", qop=\"auth\", uri=\"/\", response=\"4241f31ffe6f9c99b891f88e9c41caa9\"";
+	const char* c = "WWW-Authenticate: digest nonce=\"1621696297327727918745238639\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", realm=\"Test Realm\", algorithm=\"md5\", qop=\"auth\"";
+	const char* r = "Authorization: Digest username=\"test\", realm=\"Test Realm\", nonce=\"1621696297327727918745238639\", uri=\"/principals/users/test/\", response=\"e260f13cffcc15572ddeec9c31de437b\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", algorithm=\"md5\", cnonce=\"70cbd8f04227d8d46c0193b290beaf0d\", nc=00000001, qop=\"auth\"";
+	AuthenticateUserDigest(dir, u, c, r, "GET");
+#endif
 	return 0;
 }
 
 void AuthenticateUser(CDirectoryService* dir, const char* user, const char* pswd)
 {
-	if (dir->AuthenticateUserBasic(user, pswd))
+	bool result = false;
+	if (dir->AuthenticateUserBasic(user, pswd, result))
 		printf("Authenticated user: %s\n", user);
 	else
 		printf("Not Authenticated user: %s\n", user);
 }
 
+void AuthenticateUserDigest(CDirectoryService* dir, const char* user, const char* challenge, const char* response, const char* method)
+{
+	bool result = false;
+	if (dir->AuthenticateUserDigest(user, challenge, response, method, result))
+		printf("Authenticated user: %s\n", user);
+	else
+		printf("Not Authenticated user: %s\n", user);
+}
+
 void CFDictionaryIterator(const void* key, const void* value, void* ref)
 {
 	CFStringRef strkey = (CFStringRef)key;

Modified: PyOpenDirectory/trunk/test.py
===================================================================
--- PyOpenDirectory/trunk/test.py	2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/test.py	2007-02-06 22:10:09 UTC (rev 1138)
@@ -26,34 +26,125 @@
 		print "Failed odInit"
 	else:
 		print "OK odInit"
+
+	def listUsers():
+		d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeUsers,
+													   [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,])
+		if d is None:
+			print "Failed to list users"
+		else:
+			names = [v for v in d.iterkeys()]
+			names.sort()
+			print "\nlistUsers number of results = %d" % (len(names),)
+			for n in names:
+				print "Name: %s" % n
+				print "dict: %s" % str(d[n])
 	
-	d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeUsers,
-												   [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,])
-	if d is None:
-		print "Failed to list users"
-	else:
-		names = [v for v in d.iterkeys()]
-		names.sort()
-		for n in names:
-			print "Name: %s" % n
-			print "dict: %s" % str(d[n])
+	def listGroups():
+		d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeGroups,
+													   [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDSNAttrGroupMembers,])
+		if d is None:
+			print "Failed to list groups"
+		else:
+			names = [v for v in d.iterkeys()]
+			names.sort()
+			print "\nlistGroups number of results = %d" % (len(names),)
+			for n in names:
+				print "Name: %s" % n
+				print "dict: %s" % str(d[n])
 	
-	d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeGroups,
-												   [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDSNAttrGroupMembers,])
-	if d is None:
-		print "Failed to list groups"
-	else:
-		names = [v for v in d.iterkeys()]
-		names.sort()
-		for n in names:
-			print "Name: %s" % n
-			print "dict: %s" % str(d[n])
+	def listComputers():
+		d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeComputers,
+													   [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrXMLPlist,])
+		if d is None:
+			print "Failed to list computers"
+		else:
+			names = [v for v in d.iterkeys()]
+			names.sort()
+			print "\nlistComputers number of results = %d" % (len(names),)
+			for n in names:
+				print "Name: %s" % n
+				print "dict: %s" % str(d[n])
 	
-	if opendirectory.authenticateUserBasic(ref, "test", "test"):
-		print "Authenticated user"
-	else:
-		print "Failed to authenticate user"
+	def query(title, dict, matchType, casei, allmatch, recordType, attrs):
+		d = opendirectory.queryRecordsWithAttributes(
+		    ref,
+		    dict,
+		    matchType,
+		    casei,
+		    allmatch,
+			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(
+			"queryUsers",
+		    {dsattributes.kDS1AttrFirstName: "cyrus",},
+		    dsattributes.eDSExact,
+		    True,
+		    True,
+			dsattributes.kDSStdRecordTypeUsers,
+			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+		)
+		
+	def queryUsersCompoundOr():
+		query(
+			"queryUsersCompoundOr",
+		    {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
+		    dsattributes.eDSContains,
+		    True,
+		    False,
+			dsattributes.kDSStdRecordTypeUsers,
+			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+		)
+		
+	def queryUsersCompoundOrExact():
+		query(
+			"queryUsersCompoundOrExact",
+		    {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
+		    dsattributes.eDSExact,
+		    True,
+		    False,
+			dsattributes.kDSStdRecordTypeUsers,
+			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+		)
+		
+	def queryUsersCompoundAnd():
+		query(
+			"queryUsersCompoundAnd",
+		    {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
+		    dsattributes.eDSContains,
+		    True,
+		    True,
+			dsattributes.kDSStdRecordTypeUsers,
+			[dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+		)
+		
+	def authentciateBasic():
+		if opendirectory.authenticateUserBasic(ref, "test", "test"):
+			print "Authenticated user"
+		else:
+			print "Failed to authenticate user"
 	
+	#listUsers()
+	#listGroups()
+	listComputers()
+	#queryUsers()
+	#queryUsersCompoundOr()
+	#queryUsersCompoundOrExact()
+	#queryUsersCompoundAnd()
+	#authentciateBasic()
+
 	ref = None
 except opendirectory.ODError, ex:
 	print ex

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070206/8fb7953b/attachment.html


More information about the calendarserver-changes mailing list