[CalendarServer-changes] [3059] PyOpenDirectory/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Sep 24 18:26:48 PDT 2008


Revision: 3059
          http://trac.macosforge.org/projects/calendarserver/changeset/3059
Author:   cdaboo at apple.com
Date:     2008-09-24 18:26:48 -0700 (Wed, 24 Sep 2008)
Log Message:
-----------
Merge datatypes branch to trunk.

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/PyOpenDirectory.xcodeproj/project.pbxproj
    PyOpenDirectory/trunk/support/test.cpp
    PyOpenDirectory/trunk/test.py

Added Paths:
-----------
    PyOpenDirectory/trunk/src/base64.cpp
    PyOpenDirectory/trunk/src/base64.h

Property Changed:
----------------
    PyOpenDirectory/trunk/


Property changes on: PyOpenDirectory/trunk
___________________________________________________________________
Added: svn:mergeinfo
   + /PyOpenDirectory/branches/users/cdaboo/datatypes-3001:3002-3058

Modified: PyOpenDirectory/trunk/pysrc/opendirectory.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/opendirectory.py	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/pysrc/opendirectory.py	2008-09-25 01:26:48 UTC (rev 3059)
@@ -32,10 +32,12 @@
 def listAllRecordsWithAttributes(obj, recordType, attributes):
     """
     List records in Open Directory, and return key attributes for each one.
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
     
     @param obj: C{object} the object obtained from an odInit call.
     @param recordType: C{str} containing the OD record type to lookup.
-    @param attributes: C{list} containing the attributes to return for each record.
+    @param attributes: C{list} or C{tuple} 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.
     """
@@ -43,6 +45,8 @@
 def queryRecordsWithAttribute(obj, attr, value, matchType, casei, recordType, attributes):
     """
     List records in Open Directory matching specified attribute/value, and return key attributes for each one.
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
     
     @param obj: C{object} the object obtained from an odInit call.
     @param attr: C{str} containing the attribute to search.
@@ -50,7 +54,7 @@
     @param matchType: C{int} DS match type to use when searching.
     @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.
+    @param attributes: C{list} or C{tuple} 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.
     """
@@ -58,12 +62,14 @@
 def queryRecordsWithAttributes(obj, compound, casei, recordType, attributes):
     """
     List records in Open Directory matching specified criteria, and return key attributes for each one.
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
     
     @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.
+    @param attributes: C{list} or C{tuple} 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.
     """
@@ -71,10 +77,12 @@
 def listAllRecordsWithAttributes_list(obj, recordType, attributes):
     """
     List records in Open Directory, and return key attributes for each one.
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
     
     @param obj: C{object} the object obtained from an odInit call.
     @param recordType: C{str} containing the OD record type to lookup.
-    @param attributes: C{list} containing the attributes to return for each record.
+    @param attributes: C{list} or C{tuple} containing the attributes to return for each record.
     @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes 
         for each record found, or C{None} otherwise.
     """
@@ -82,6 +90,8 @@
 def queryRecordsWithAttribute_list(obj, attr, value, matchType, casei, recordType, attributes):
     """
     List records in Open Directory matching specified attribute/value, and return key attributes for each one.
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
     
     @param obj: C{object} the object obtained from an odInit call.
     @param attr: C{str} containing the attribute to search.
@@ -89,20 +99,22 @@
     @param matchType: C{int} DS match type to use when searching.
     @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.
+    @param attributes: C{list} or C{tuple} containing the attributes to return for each record.
     @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes 
         for each record found, or C{None} otherwise.
     """
 
-def queryRecordsWithAttributes_list(obj, compound, casei, recordType, attributes):
+def queryRecordsWithAttributes_list(obj, compound, casei, recordType, attributes, types=None):
     """
     List records in Open Directory matching specified criteria, and return key attributes for each one.
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
     
     @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.
+    @param attributes: C{list} or C{tuple} containing the attributes to return for each record.
     @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes 
         for each record found, or C{None} otherwise.
     """

Modified: PyOpenDirectory/trunk/setup.py
===================================================================
--- PyOpenDirectory/trunk/setup.py	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/setup.py	2008-09-25 01:26:48 UTC (rev 3059)
@@ -35,6 +35,7 @@
             'src/CDirectoryService.cpp',
             'src/CDirectoryServiceException.cpp',
             'src/CFStringUtil.cpp',
+            'src/base64.cpp',
         ],
     )
     

Modified: PyOpenDirectory/trunk/src/CDirectoryService.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.cpp	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/src/CDirectoryService.cpp	2008-09-25 01:26:48 UTC (rev 3059)
@@ -23,6 +23,7 @@
 
 #include "CDirectoryServiceException.h"
 
+#include "base64.h"
 #include "CFStringUtil.h"
 
 #include <Python.h>
@@ -89,11 +90,11 @@
 //            and value entries for each attribute/value requested in the record indexed by uid,
 //            or NULL if it fails.
 //
-CFMutableArrayRef CDirectoryService::ListAllRecordsWithAttributes(const char* recordType, CFArrayRef attributes)
+CFMutableArrayRef CDirectoryService::ListAllRecordsWithAttributes(const char* recordType, CFDictionaryRef attributes, bool using_python)
 {
     try
     {
-        StPythonThreadState threading;
+        StPythonThreadState threading(using_python);
 
         // Get attribute map
         return _ListAllRecordsWithAttributes(recordType, NULL, attributes);
@@ -126,11 +127,11 @@
 //          and value entries for each attribute/value requested in the record indexed by uid,
 //          or NULL if it fails.
 //
-CFMutableArrayRef CDirectoryService::QueryRecordsWithAttribute(const char* attr, const char* value, int matchType, bool casei, const char* recordType, CFArrayRef attributes)
+CFMutableArrayRef CDirectoryService::QueryRecordsWithAttribute(const char* attr, const char* value, int matchType, bool casei, const char* recordType, CFDictionaryRef attributes, bool using_python)
 {
     try
     {
-        StPythonThreadState threading;
+        StPythonThreadState threading(using_python);
 
         // Get attribute map
         return _QueryRecordsWithAttributes(attr, value, matchType, NULL, casei, recordType, attributes);
@@ -161,11 +162,11 @@
 //          and value entries for each attribute/value requested in the record indexed by uid,
 //          or NULL if it fails.
 //
-CFMutableArrayRef CDirectoryService::QueryRecordsWithAttributes(const char* query, bool casei, const char* recordType, CFArrayRef attributes)
+CFMutableArrayRef CDirectoryService::QueryRecordsWithAttributes(const char* query, bool casei, const char* recordType, CFDictionaryRef attributes, bool using_python)
 {
     try
     {
-        StPythonThreadState threading;
+        StPythonThreadState threading(using_python);
 
         // Get attribute map
         return _QueryRecordsWithAttributes(NULL, NULL, 0, query, casei, recordType, attributes);
@@ -192,11 +193,11 @@
 // @param pswd: the plain text password to authenticate with.
 // @return: true if authentication succeeds, false otherwise.
 //
-bool CDirectoryService::AuthenticateUserBasic(const char* nodename, const char* user, const char* pswd, bool& result)
+bool CDirectoryService::AuthenticateUserBasic(const char* nodename, const char* user, const char* pswd, bool& result, bool using_python)
 {
     try
     {
-        StPythonThreadState threading;
+        StPythonThreadState threading(using_python);
 
         result = NativeAuthenticationBasicToNode(nodename, user, pswd);
         return true;
@@ -223,11 +224,11 @@
 // @param response: HTTP response sent by client.
 // @return: true if authentication succeeds, false otherwise.
 //
-bool CDirectoryService::AuthenticateUserDigest(const char* nodename, const char* user, const char* challenge, const char* response, const char* method, bool& result)
+bool CDirectoryService::AuthenticateUserDigest(const char* nodename, const char* user, const char* challenge, const char* response, const char* method, bool& result, bool using_python)
 {
     try
     {
-        StPythonThreadState threading;
+        StPythonThreadState threading(using_python);
 
         result = NativeAuthenticationDigestToNode(nodename, user, challenge, response, method);
         return true;
@@ -253,13 +254,13 @@
 //
 // @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.
+// @param attributes: a list of attributes to return.
 // @return: CFMutableArrayRef composed of CFMutableArrayRef with a CFStringRef/CFMutableDictionaryRef tuple for
 //          each record, where the CFStringRef is the record name and CFMutableDictionaryRef of CFStringRef key
 //          and value entries for each attribute/value requested in the record indexed by uid,
 //          or NULL if it fails.
 //
-CFMutableArrayRef CDirectoryService::_ListAllRecordsWithAttributes(const char* type, CFArrayRef names, CFArrayRef attrs)
+CFMutableArrayRef CDirectoryService::_ListAllRecordsWithAttributes(const char* type, CFArrayRef names, CFDictionaryRef attributes)
 {
     CFMutableArrayRef result = NULL;
     CFMutableArrayRef record_tuple = NULL;
@@ -273,7 +274,7 @@
     tRecordEntry* pRecEntry = NULL;
     
     // Must have attributes
-    if (::CFArrayGetCount(attrs) == 0)
+    if (::CFDictionaryGetCount(attributes) == 0)
         return NULL;
 
     try
@@ -303,7 +304,7 @@
         // Build data list of attributes
         attrTypes = ::dsDataListAllocate(mDir);
         ThrowIfNULL(attrTypes);
-        BuildStringDataList(attrs, attrTypes);
+        BuildStringDataListFromKeys(attributes, attrTypes);
         
         result = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
         
@@ -346,6 +347,12 @@
                         std::auto_ptr<char> attrname(CStringFromBuffer(&attributeInfoPtr->fAttributeSignature));
                         CFStringUtil cfattrname(attrname.get());
                         
+						// Determine whether string/base64 encoding is needed
+						bool base64 = false;
+						CFStringRef encoding = (CFStringRef)::CFDictionaryGetValue(attributes, cfattrname.get());
+						if (encoding && (::CFStringCompare(encoding, CFSTR("base64"), 0) == kCFCompareEqualTo))
+							base64 = true;
+						
                         if (attributeInfoPtr->fAttributeValueCount > 1)
                         {
                             values = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
@@ -355,7 +362,11 @@
                                 // 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));
+								std::auto_ptr<char> data;
+								if (base64)
+									data.reset(CStringBase64FromBuffer(&attributeValue->fAttributeValueData));
+								else
+									data.reset(CStringFromBuffer(&attributeValue->fAttributeValueData));
                                 CFStringUtil strvalue(data.get());
                                 ::CFArrayAppendValue(values, strvalue.get());
                                 ::dsDeallocAttributeValueEntry(mDir, attributeValue);
@@ -370,10 +381,17 @@
                             // 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));
+                            std::auto_ptr<char> data;
+							if (base64)
+								data.reset(CStringBase64FromBuffer(&attributeValue->fAttributeValueData));
+							else
+								data.reset(CStringFromBuffer(&attributeValue->fAttributeValueData));
                             CFStringUtil strvalue(data.get());
-                            ::CFDictionarySetValue(record, cfattrname.get(), strvalue.get());
-                            ::dsDeallocAttributeValueEntry(mDir, attributeValue);
+							if (strvalue.get() != NULL)
+							{
+								::CFDictionarySetValue(record, cfattrname.get(), strvalue.get());
+							}
+							::dsDeallocAttributeValueEntry(mDir, attributeValue);
                             attributeValue = NULL;
                         }
                     }
@@ -484,13 +502,13 @@
 // @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 type: the record type to check.
-// @param attrs: a list of attributes to return.
+// @param attributes: a list of attributes to return.
 // @return: CFMutableArrayRef composed of CFMutableArrayRef with a CFStringRef/CFMutableDictionaryRef tuple for
 //          each record, where the CFStringRef is the record name and CFMutableDictionaryRef of CFStringRef key
 //          and value entries for each attribute/value requested in the record indexed by uid,
 //          or NULL if it fails.
 //
-CFMutableArrayRef CDirectoryService::_QueryRecordsWithAttributes(const char* attr, const char* value, int matchType, const char* compound, bool casei, const char* type, CFArrayRef attrs)
+CFMutableArrayRef CDirectoryService::_QueryRecordsWithAttributes(const char* attr, const char* value, int matchType, const char* compound, bool casei, const char* type, CFDictionaryRef attributes)
 {
     CFMutableArrayRef result = NULL;
     CFMutableArrayRef record_tuple = NULL;
@@ -505,7 +523,7 @@
     tRecordEntry* pRecEntry = NULL;
     
     // Must have attributes
-    if (::CFArrayGetCount(attrs) == 0)
+    if (::CFDictionaryGetCount(attributes) == 0)
         return NULL;
 
     try
@@ -552,7 +570,7 @@
         // Build data list of attributes
         attrTypes = ::dsDataListAllocate(mDir);
         ThrowIfNULL(attrTypes);
-        BuildStringDataList(attrs, attrTypes);
+        BuildStringDataListFromKeys(attributes, attrTypes);
         
         result = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
         
@@ -595,6 +613,12 @@
                         std::auto_ptr<char> attrname(CStringFromBuffer(&attributeInfoPtr->fAttributeSignature));
                         CFStringUtil cfattrname(attrname.get());
                         
+						// Determine whether string/base64 encoding is needed
+						bool base64 = false;
+						CFStringRef encoding = (CFStringRef)::CFDictionaryGetValue(attributes, cfattrname.get());
+						if (encoding && (::CFStringCompare(encoding, CFSTR("base64"), 0) == kCFCompareEqualTo))
+							base64 = true;
+						
                         if (attributeInfoPtr->fAttributeValueCount > 1)
                         {
                             values = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
@@ -604,9 +628,14 @@
                                 // 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));
+								std::auto_ptr<char> data;
+								if (base64)
+									data.reset(CStringBase64FromBuffer(&attributeValue->fAttributeValueData));
+								else
+									data.reset(CStringFromBuffer(&attributeValue->fAttributeValueData));
                                 CFStringUtil strvalue(data.get());
-                                ::CFArrayAppendValue(values, strvalue.get());
+								if (strvalue.get() != NULL)
+									::CFArrayAppendValue(values, strvalue.get());
                                 ::dsDeallocAttributeValueEntry(mDir, attributeValue);
                                 attributeValue = NULL;
                             }
@@ -619,9 +648,14 @@
                             // 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));
+                            std::auto_ptr<char> data;
+							if (base64)
+								data.reset(CStringBase64FromBuffer(&attributeValue->fAttributeValueData));
+							else
+								data.reset(CStringFromBuffer(&attributeValue->fAttributeValueData));
                             CFStringUtil strvalue(data.get());
-                            ::CFDictionarySetValue(record, cfattrname.get(), strvalue.get());
+							if (strvalue.get() != NULL)
+								::CFDictionarySetValue(record, cfattrname.get(), strvalue.get());
                             ::dsDeallocAttributeValueEntry(mDir, attributeValue);
                             attributeValue = NULL;
                         }
@@ -1099,6 +1133,21 @@
     }
 }
 
+void CDirectoryService::BuildStringDataListFromKeys(CFDictionaryRef strs, tDataListPtr data)
+{
+	CFStringRef strings[::CFDictionaryGetCount(strs)];
+	::CFDictionaryGetKeysAndValues(strs, (const void**)&strings, NULL);
+    CFStringUtil add_cfname(strings[0]);
+    std::auto_ptr<char> add_name(add_cfname.c_str());
+    ThrowIfDSErr(::dsBuildListFromStringsAlloc(mDir, data,  add_name.get(), NULL));
+    for(CFIndex i = 1; i < ::CFDictionaryGetCount(strs); i++)
+    {
+        add_cfname.reset((CFStringRef)strings[i]);
+        add_name.reset(add_cfname.c_str());
+        ThrowIfDSErr(::dsAppendStringToListAlloc(mDir, data,  add_name.get()));
+    }
+}
+
 // CStringFromBuffer
 // 
 // Convert data in a buffer into a c-string.
@@ -1113,6 +1162,17 @@
     return result;
 }
 
+// CStringBase64FromBuffer
+// 
+// Convert data in a buffer into a base64 encoded c-string.
+//
+// @return: the converted string.
+//
+char* CDirectoryService::CStringBase64FromBuffer(tDataBufferPtr data)
+{
+	return ::base64_encode((const unsigned char*)data->fBufferData, data->fBufferLength);
+}
+
 // CStringFromData
 // 
 // Convert data to a c-string.
@@ -1126,4 +1186,3 @@
     result[len] = 0;
     return result;
 }
-

Modified: PyOpenDirectory/trunk/src/CDirectoryService.h
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.h	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/src/CDirectoryService.h	2008-09-25 01:26:48 UTC (rev 3059)
@@ -33,26 +33,30 @@
     CDirectoryService(const char* nodename);
     ~CDirectoryService();
     
-    CFMutableArrayRef ListAllRecordsWithAttributes(const char* recordType, CFArrayRef attributes);
-    CFMutableArrayRef QueryRecordsWithAttribute(const char* attr, const char* value, int matchType, bool casei, const char* recordType, CFArrayRef attributes);
-    CFMutableArrayRef QueryRecordsWithAttributes(const char* query, bool casei, const char* recordType, CFArrayRef attributes);
+    CFMutableArrayRef ListAllRecordsWithAttributes(const char* recordType, CFDictionaryRef attributes, bool using_python=true);
+    CFMutableArrayRef QueryRecordsWithAttribute(const char* attr, const char* value, int matchType, bool casei, const char* recordType, CFDictionaryRef attributes, bool using_python=true);
+    CFMutableArrayRef QueryRecordsWithAttributes(const char* query, bool casei, const char* recordType, CFDictionaryRef attributes, bool using_python=true);
 
-    bool AuthenticateUserBasic(const char* nodename, const char* user, const char* pswd, bool& result);
-    bool AuthenticateUserDigest(const char* nodename, const char* user, const char* challenge, const char* response, const char* method, bool& result);
+    bool AuthenticateUserBasic(const char* nodename, const char* user, const char* pswd, bool& result, bool using_python=true);
+    bool AuthenticateUserDigest(const char* nodename, const char* user, const char* challenge, const char* response, const char* method, bool& result, bool using_python=true);
     
 private:
 
     class StPythonThreadState
     {
     public:
-        StPythonThreadState()
+        StPythonThreadState(bool using_python=true)
         {
-            mSavedState = PyEval_SaveThread();
-         }
+			if (using_python)
+				mSavedState = PyEval_SaveThread();
+			else
+				mSavedState = NULL;
+		}
         
         ~StPythonThreadState()
         {
-            PyEval_RestoreThread(mSavedState);
+			if (mSavedState != NULL)
+				PyEval_RestoreThread(mSavedState);
         }
     
     private:
@@ -65,8 +69,8 @@
     tDataBufferPtr        mData;
     UInt32                mDataSize;
     
-    CFMutableArrayRef _ListAllRecordsWithAttributes(const char* type, CFArrayRef names, CFArrayRef attrs);
-    CFMutableArrayRef _QueryRecordsWithAttributes(const char* attr, const char* value, int matchType, const char* compound, bool casei, const char* recordType, CFArrayRef attributes);
+    CFMutableArrayRef _ListAllRecordsWithAttributes(const char* type, CFArrayRef names, CFDictionaryRef attrs);
+    CFMutableArrayRef _QueryRecordsWithAttributes(const char* attr, const char* value, int matchType, const char* compound, bool casei, const char* recordType, CFDictionaryRef attrs);
 
     bool NativeAuthenticationBasicToNode(const char* nodename, const char* user, const char* pswd);
     bool NativeAuthenticationDigestToNode(const char* nodename, const char* user, const char* challenge, const char* response, const char* method);
@@ -83,7 +87,9 @@
     void ReallocBuffer();
 
     void BuildStringDataList(CFArrayRef strs, tDataListPtr data);
+    void BuildStringDataListFromKeys(CFDictionaryRef strs, tDataListPtr data);
 
     char* CStringFromBuffer(tDataBufferPtr data);
+    char* CStringBase64FromBuffer(tDataBufferPtr data);
     char* CStringFromData(const char* data, size_t len);
 };

Modified: PyOpenDirectory/trunk/src/PythonWrapper.cpp
===================================================================
--- PyOpenDirectory/trunk/src/PythonWrapper.cpp	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/src/PythonWrapper.cpp	2008-09-25 01:26:48 UTC (rev 3059)
@@ -24,6 +24,7 @@
 #include "CFStringUtil.h"
 
 #include <memory>
+#include <string>
 
 #ifndef Py_RETURN_TRUE
 #define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
@@ -35,6 +36,72 @@
 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
 #endif
 
+class PyObjectException : public std::exception
+{
+public:
+	PyObjectException(const char* msg)
+	{
+		message = msg;
+	}
+
+	virtual const char* what()
+	{
+		return message;
+	}
+
+private:
+	const char* message;
+};
+
+class PyTupleOrList
+{
+public:
+	PyTupleOrList(PyObject* item)
+	{
+		if (PyTuple_Check(item))
+		{
+			tuple = item;
+			list = NULL;
+		}
+		else if (PyList_Check(item))
+		{
+			list = item;
+			tuple = NULL;
+		}
+		else
+			throw PyObjectException("Expecting a list or tuple in 'PyTupleOrList'.");
+	}
+
+	Py_ssize_t getSize() const
+	{
+		if (tuple)
+			return PyTuple_Size(tuple);
+		else if (list)
+			return PyList_Size(list);
+		else
+			return 0;
+	}
+
+	PyObject* get(Py_ssize_t index) const
+	{
+		if (tuple)
+			return PyTuple_GetItem(tuple, index);
+		else if (list)
+			return PyList_GetItem(list, index);
+		else
+			return NULL;
+	}
+
+	static bool typeOK(PyObject* item)
+	{
+		return PyTuple_Check(item) || PyList_Check(item);
+	}
+
+private:
+	PyObject* tuple;
+	PyObject* list;
+};
+
 // Utility function - not exposed to Python
 static PyObject* CFStringToPyStr(CFStringRef str)
 {
@@ -48,50 +115,97 @@
     CFIndex lsize = (list != NULL) ? CFArrayGetCount(list) : 0;
     if (sorted and (list != NULL))
         CFArraySortValues((CFMutableArrayRef)list, CFRangeMake(0, lsize), (CFComparatorFunction)CFStringCompare, NULL);
-    
+
     PyObject* result = PyList_New(lsize);
     for(CFIndex i = 0; i < lsize; i++)
     {
         CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(list, i);
         PyObject* pystr = CFStringToPyStr(str);
-        
+
         PyList_SetItem(result, i, pystr);
     }
-    
+
     return result;
 }
 
 // Utility function - not exposed to Python
-static CFArrayRef PyListToCFArray(PyObject* list)
+static CFArrayRef PyTupleOrListToCFArray(PyObject* item)
 {
-    CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, PyList_Size(list), &kCFTypeArrayCallBacks);
-    for(int i = 0; i < PyList_Size(list); i++)
+	PyTupleOrList pyitem(item);
+    CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, pyitem.getSize(), &kCFTypeArrayCallBacks);
+    for(int i = 0; i < pyitem.getSize(); i++)
     {
-        PyObject* str = PyList_GetItem(list, i);
+        PyObject* str = pyitem.get(i);
         if ((str == NULL) || !PyString_Check(str))
         {
             CFRelease(result);
-            return NULL;
+            throw PyObjectException("Expecting a str type in 'PyTupleOrListToCFArray'.");
         }
         const char* cstr = PyString_AsString(str);
         if (cstr == NULL)
         {
             CFRelease(result);
-            return NULL;
+            throw PyObjectException("Could not extract string in 'PyTupleOrListToCFArray'.");
         }
         CFStringUtil cfstr(cstr);
         CFArrayAppendValue(result, cfstr.get());
     }
-    
+
     return result;
 }
 
 // Utility function - not exposed to Python
+static CFDictionaryRef AttributesToCFDictionary(PyObject* item)
+{
+	PyTupleOrList pyitem(item);
+    CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, pyitem.getSize(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    for(int i = 0; i < pyitem.getSize(); i++)
+    {
+        PyObject* str = pyitem.get(i);
+        if ((str == NULL) || !PyString_Check(str) && !PyList_Check(str) && !PyTuple_Check(str))
+        {
+            CFRelease(result);
+            throw PyObjectException("Expecting a str, list or tuple type in 'AttributesToCFDictionary'.");
+        }
+		if (PyString_Check(str))
+		{
+			const char* cstr = PyString_AsString(str);
+			if (cstr == NULL)
+			{
+				CFRelease(result);
+				throw PyObjectException("Could not extract string in 'PyTupleOrListToCFArray'.");
+			}
+			CFStringUtil cfstr(cstr);
+			CFDictionarySetValue(result, cfstr.get(), CFSTR("str"));
+		}
+		else if (PyList_Check(str) || PyTuple_Check(str))
+		{
+			CFArrayRef strs = PyTupleOrListToCFArray(str);
+			CFIndex strsize = CFArrayGetCount(strs);
+			if ((strsize != 1) && (strsize != 2))
+			{
+				CFRelease(strs);
+				CFRelease(result);
+				throw PyObjectException("Expecting one or two items in tuple or list in 'PyTupleOrListToCFArray'.");
+			}
+
+			if (strsize == 2)
+				CFDictionarySetValue(result, CFArrayGetValueAtIndex(strs, 0), CFArrayGetValueAtIndex(strs, 1));
+			else
+				CFDictionarySetValue(result, CFArrayGetValueAtIndex(strs, 0), CFSTR("str"));
+			CFRelease(strs);
+		}
+    }
+
+    return result;
+}
+
+// Utility function - not exposed to Python
 static void CFDictionaryIterator(const void* key, const void* value, void* ref)
 {
     CFStringRef strkey = (CFStringRef)key;
     PyObject* dict = (PyObject*)ref;
-    
+
     PyObject* pystrkey = CFStringToPyStr(strkey);
 
     // The dictionary value may be a string or a list
@@ -118,7 +232,7 @@
     PyObject* result = PyDict_New();
     if (dict != NULL)
         CFDictionaryApplyFunction(dict, CFDictionaryIterator, result);
-    
+
     return result;
 }
 
@@ -128,10 +242,10 @@
     CFStringRef strkey = (CFStringRef)key;
     CFDictionaryRef dictvalue = (CFDictionaryRef)value;
     PyObject* dict = (PyObject*)ref;
-    
+
     PyObject* pystrkey = CFStringToPyStr(strkey);
     PyObject* pydictvalue = CFDictionaryToPyDict(dictvalue);
-    
+
     PyDict_SetItem(dict, pystrkey, pydictvalue);
     Py_DECREF(pystrkey);
     Py_DECREF(pydictvalue);
@@ -143,7 +257,7 @@
     PyObject* result = PyDict_New();
     if (dict != NULL)
         CFDictionaryApplyFunction(dict, CFDictionaryDictionaryIterator, result);
-    
+
     return result;
 }
 
@@ -153,17 +267,17 @@
     CFIndex lsize = (list != NULL) ? CFArrayGetCount(list) : 0;
     if (lsize != 2)
         return NULL;
-    
+
     PyObject* result = PyList_New(lsize);
 
     CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(list, 0);
     PyObject* pystr = CFStringToPyStr(str);
     PyList_SetItem(result, 0, pystr);
-    
+
     CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(list, 1);
     PyObject* pydict = CFDictionaryToPyDict(dict);
     PyList_SetItem(result, 1, pydict);
-    
+
     return result;
 }
 
@@ -171,7 +285,7 @@
 static PyObject* CFArrayArrayDictionaryToPyList(CFArrayRef list)
 {
     CFIndex lsize = (list != NULL) ? CFArrayGetCount(list) : 0;
-    
+
     PyObject* result = PyList_New(lsize);
     for(CFIndex i = 0, j = 0; i < lsize; i++)
     {
@@ -182,7 +296,7 @@
             PyList_SetItem(result, j++, pylist);
         }
     }
-    
+
     return result;
 }
 
@@ -192,13 +306,13 @@
     CFIndex lsize = (list != NULL) ? CFArrayGetCount(list) : 0;
     if (lsize != 2)
         return;
-    
+
     CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(list, 0);
     PyObject* pystrkey = CFStringToPyStr(str);
-    
+
     CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(list, 1);
     PyObject* pydictvalue = CFDictionaryToPyDict(dict);
-    
+
     PyDict_SetItem(result, pystrkey, pydictvalue);
     Py_DECREF(pystrkey);
     Py_DECREF(pydictvalue);
@@ -208,14 +322,14 @@
 static PyObject* CFArrayArrayDictionaryToPyDict(CFArrayRef list)
 {
     CFIndex lsize = (list != NULL) ? CFArrayGetCount(list) : 0;
-    
+
     PyObject* result = PyDict_New();
     for(CFIndex i = 0; i < lsize; i++)
     {
         CFArrayRef nested = (CFArrayRef)CFArrayGetValueAtIndex(list, i);
         CFArrayStringDictionaryToPyDict(nested, result);
     }
-    
+
     return result;
 }
 
@@ -235,7 +349,7 @@
  def odInit(nodename):
     """
     Create an Open Directory object to operate on the specified directory service node name.
- 
+
     @param nodename: C{str} containing the node name.
     @return: C{object} an object to be passed to all subsequent functions on success,
         C{None} on failure.
@@ -246,7 +360,7 @@
     const char* nodename;
     if (!PyArg_ParseTuple(args, "s", &nodename))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices odInit: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices odInit: could not parse arguments", 0));
         return NULL;
     }
 
@@ -255,20 +369,22 @@
     {
         return PyCObject_FromVoidPtr(dsmgr, odDestroy);
     }
-    
-    PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices odInit: could not initialize directory service", 0));        
+
+    PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices odInit: could not initialize directory service", 0));
     return NULL;
 }
 
 /*
 def listAllRecordsWithAttributes(obj, recordType, attributes):
     """
-    List records in Open Directory, and return key attributes for each one.
-    
+    List records in Open Directory, and return key attributes for each one. The attributes
+    can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
+
     @param obj: C{object} the object obtained from an odInit call.
     @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,  
+    @param attributes: C{list} or C{tuple} 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 *listAllRecordsWithAttributes(PyObject *self, PyObject *args)
@@ -276,19 +392,25 @@
     PyObject* pyds;
     const char* recordType;
     PyObject* attributes;
-    if (!PyArg_ParseTuple(args, "OsO", &pyds, &recordType, &attributes) || !PyCObject_Check(pyds) || !PyList_Check(attributes))
+    if (!PyArg_ParseTuple(args, "OsO", &pyds, &recordType, &attributes) || !PyCObject_Check(pyds) || !PyTupleOrList::typeOK(attributes))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: could not parse arguments", 0));
         return NULL;
     }
-    
+
     // Convert list to CFArray of CFString
-    CFArrayRef cfattributes = PyListToCFArray(attributes);
-    if (cfattributes == NULL)
-    {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: could not parse attributes list", 0));        
-        return NULL;
-    }
+    CFDictionaryRef cfattributes = NULL;
+	try
+	{
+		cfattributes = AttributesToCFDictionary(attributes);
+	}
+	catch(PyObjectException& ex)
+	{
+		std::string msg("DirectoryServices listAllRecordsWithAttributes: could not parse attributes list: ");
+		msg += ex.what();
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", msg.c_str(), 0));
+		return NULL;
+	}
 
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
@@ -301,13 +423,13 @@
             PyObject* result = CFArrayArrayDictionaryToPyDict(list);
             CFRelease(list);
             CFRelease(cfattributes);
-            
+
             return result;
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: invalid directory service argument", 0));
+
     CFRelease(cfattributes);
     return NULL;
 }
@@ -316,15 +438,17 @@
 def queryRecordsWithAttribute(obj, attr, value, matchType, casei, recordType, attributes):
     """
     List records in Open Directory matching specified attribute and value, and return key attributes for each one.
-    
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
+
     @param obj: C{object} the object obtained from an odInit call.
     @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 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,  
+    @param attributes: C{list} or C{tuple} 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.
     """
  */
@@ -339,21 +463,27 @@
     const char* recordType;
     PyObject* attributes;
     if (!PyArg_ParseTuple(args, "OssiOsO", &pyds, &attr, &value, &matchType, &caseio, &recordType, &attributes) ||
-        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyList_Check(attributes))
+        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyTupleOrList::typeOK(attributes))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttribute: 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;
-    }
+    CFDictionaryRef cfattributes = NULL;
+	try
+	{
+		cfattributes = AttributesToCFDictionary(attributes);
+	}
+	catch(PyObjectException& ex)
+	{
+		std::string msg("DirectoryServices queryRecordsWithAttribute: could not parse attributes list: ");
+		msg += ex.what();
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", msg.c_str(), 0));
+		return NULL;
+	}
 
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
@@ -366,13 +496,13 @@
             PyObject* result = CFArrayArrayDictionaryToPyDict(list);
             CFRelease(list);
             CFRelease(cfattributes);
-            
+
             return result;
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttribute: invalid directory service argument", 0));
+
     CFRelease(cfattributes);
     return NULL;
 }
@@ -381,13 +511,15 @@
 def queryRecordsWithAttributes(obj, query, casei, recordType, attributes):
     """
     List records in Open Directory matching specified compound query, and return key attributes for each one.
-    
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
+
     @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,  
+    @param attributes: C{list} or C{tuple} 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.
     """
  */
@@ -400,21 +532,27 @@
     const char* recordType;
     PyObject* attributes;
     if (!PyArg_ParseTuple(args, "OsOsO", &pyds, &query, &caseio, &recordType, &attributes) ||
-        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyList_Check(attributes))
+        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyTupleOrList::typeOK(attributes))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));        
+        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;
-    }
+    CFDictionaryRef cfattributes = NULL;
+	try
+	{
+		cfattributes = AttributesToCFDictionary(attributes);
+	}
+	catch(PyObjectException& ex)
+	{
+		std::string msg("DirectoryServices queryRecordsWithAttributes: could not parse attributes list: ");
+		msg += ex.what();
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", msg.c_str(), 0));
+		return NULL;
+	}
 
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
@@ -427,13 +565,13 @@
             PyObject* result = CFArrayArrayDictionaryToPyDict(list);
             CFRelease(list);
             CFRelease(cfattributes);
-            
+
             return result;
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));
+
     CFRelease(cfattributes);
     return NULL;
 }
@@ -442,11 +580,13 @@
 def listAllRecordsWithAttributes_list(obj, recordType, attributes):
     """
     List records in Open Directory, and return key attributes for each one.
-    
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
+
     @param obj: C{object} the object obtained from an odInit call.
     @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{list} containing a C{list} of C{str} (record name) and C{dict} attributes 
+    @param attributes: C{list} or C{tuple} containing the attributes to return for each record.
+    @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes
          for each record found, or C{None} otherwise.
     """
  */
@@ -455,19 +595,25 @@
     PyObject* pyds;
     const char* recordType;
     PyObject* attributes;
-    if (!PyArg_ParseTuple(args, "OsO", &pyds, &recordType, &attributes) || !PyCObject_Check(pyds) || !PyList_Check(attributes))
+    if (!PyArg_ParseTuple(args, "OsO", &pyds, &recordType, &attributes) || !PyCObject_Check(pyds) || !PyTupleOrList::typeOK(attributes))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes_list: could not parse arguments", 0));
         return NULL;
     }
-    
+
     // Convert list to CFArray of CFString
-    CFArrayRef cfattributes = PyListToCFArray(attributes);
-    if (cfattributes == NULL)
-    {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: could not parse attributes list", 0));        
-        return NULL;
-    }
+    CFDictionaryRef cfattributes = NULL;
+	try
+	{
+		cfattributes = AttributesToCFDictionary(attributes);
+	}
+	catch(PyObjectException& ex)
+	{
+		std::string msg("DirectoryServices listAllRecordsWithAttributes_list: could not parse attributes list: ");
+		msg += ex.what();
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", msg.c_str(), 0));
+		return NULL;
+	}
 
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
@@ -480,13 +626,13 @@
             PyObject* result = CFArrayArrayDictionaryToPyList(list);
             CFRelease(list);
             CFRelease(cfattributes);
-            
+
             return result;
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices listAllRecordsWithAttributes_list: invalid directory service argument", 0));
+
     CFRelease(cfattributes);
     return NULL;
 }
@@ -495,15 +641,17 @@
 def queryRecordsWithAttribute_list(obj, attr, value, matchType, casei, recordType, attributes):
     """
     List records in Open Directory matching specified attribute and value, and return key attributes for each one.
-    
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
+
     @param obj: C{object} the object obtained from an odInit call.
     @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 recordType: C{str} containing the OD record type to lookup.
-    @param attributes: C{list} containing the attributes to return for each record.
-    @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes 
+    @param attributes: C{list} or C{tuple} containing the attributes to return for each record.
+    @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes
          for each record found, or C{None} otherwise.
     """
  */
@@ -518,21 +666,27 @@
     const char* recordType;
     PyObject* attributes;
     if (!PyArg_ParseTuple(args, "OssiOsO", &pyds, &attr, &value, &matchType, &caseio, &recordType, &attributes) ||
-        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyList_Check(attributes))
+        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyTupleOrList::typeOK(attributes))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttribute_list: 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;
-    }
+    CFDictionaryRef cfattributes = NULL;
+	try
+	{
+		cfattributes = AttributesToCFDictionary(attributes);
+	}
+	catch(PyObjectException& ex)
+	{
+		std::string msg("DirectoryServices queryRecordsWithAttribute_list: could not parse attributes list: ");
+		msg += ex.what();
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", msg.c_str(), 0));
+		return NULL;
+	}
 
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
@@ -545,13 +699,13 @@
             PyObject* result = CFArrayArrayDictionaryToPyList(list);
             CFRelease(list);
             CFRelease(cfattributes);
-            
+
             return result;
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttribute_list: invalid directory service argument", 0));
+
     CFRelease(cfattributes);
     return NULL;
 }
@@ -560,13 +714,15 @@
 def queryRecordsWithAttributes_list(obj, query, casei, recordType, attributes):
     """
     List records in Open Directory matching specified compound query, and return key attributes for each one.
-    
+    The attributes can be a C{str} for the attribute name, or a C{tuple} or C{list} where the first C{str}
+    is the attribute name, and the second C{str} is an encoding type, either "str" or "base64".
+
     @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{list} containing a C{list} of C{str} (record name) and C{dict} attributes 
+    @param attributes: C{list} or C{tuple} containing the attributes to return for each record.
+    @return: C{list} containing a C{list} of C{str} (record name) and C{dict} attributes
          for each record found, or C{None} otherwise.
     """
  */
@@ -579,21 +735,27 @@
     const char* recordType;
     PyObject* attributes;
     if (!PyArg_ParseTuple(args, "OsOsO", &pyds, &query, &caseio, &recordType, &attributes) ||
-        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyList_Check(attributes))
+        !PyCObject_Check(pyds) || !PyBool_Check(caseio) || !PyTupleOrList::typeOK(attributes))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes_list: 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;
-    }
+    CFDictionaryRef cfattributes = NULL;
+	try
+	{
+		cfattributes = AttributesToCFDictionary(attributes);
+	}
+	catch(PyObjectException& ex)
+	{
+		std::string msg("DirectoryServices queryRecordsWithAttributes_list: could not parse attributes list: ");
+		msg += ex.what();
+		PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", msg.c_str(), 0));
+		return NULL;
+	}
 
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
@@ -606,13 +768,13 @@
             PyObject* result = CFArrayArrayDictionaryToPyList(list);
             CFRelease(list);
             CFRelease(cfattributes);
-            
+
             return result;
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes_list: invalid directory service argument", 0));
+
     CFRelease(cfattributes);
     return NULL;
 }
@@ -621,7 +783,7 @@
 def authenticateUserBasic(obj, nodename, user, pswd):
     """
     Authenticate a user with a password to Open Directory.
-    
+
     @param obj: C{object} the object obtained from an odInit call.
     @param nodename: C{str} the directory nodename for the record to check.
     @param user: C{str} the user identifier/directory record name to check.
@@ -637,10 +799,10 @@
     const char* pswd;
     if (!PyArg_ParseTuple(args, "Osss", &pyds, &nodename, &user, &pswd) || !PyCObject_Check(pyds))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserBasic: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserBasic: could not parse arguments", 0));
         return NULL;
     }
-    
+
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
     {
@@ -657,8 +819,8 @@
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserBasic: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserBasic: invalid directory service argument", 0));
+
     return NULL;
 }
 
@@ -666,7 +828,7 @@
 def authenticateUserDigest(obj, guid, user, challenge, response, method):
     """
     Authenticate using HTTP Digest credentials to Open Directory.
-    
+
     @param obj: C{object} the object obtained from an odInit call.
     @param nodename: C{str} the directory nodename for the record to check.
     @param user: C{str} the user identifier/directory record name to check.
@@ -686,10 +848,10 @@
     const char* method;
     if (!PyArg_ParseTuple(args, "Osssss", &pyds, &nodename, &user, &challenge, &response, &method) || !PyCObject_Check(pyds))
     {
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigest: could not parse arguments", 0));        
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigest: could not parse arguments", 0));
         return NULL;
     }
-    
+
     CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
     if (dsmgr != NULL)
     {
@@ -706,8 +868,8 @@
         }
     }
     else
-        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigest: invalid directory service argument", 0));        
-    
+        PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigest: invalid directory service argument", 0));
+
     return NULL;
 }
 

Copied: PyOpenDirectory/trunk/src/base64.cpp (from rev 3058, PyOpenDirectory/branches/users/cdaboo/datatypes-3001/src/base64.cpp)
===================================================================
--- PyOpenDirectory/trunk/src/base64.cpp	                        (rev 0)
+++ PyOpenDirectory/trunk/src/base64.cpp	2008-09-25 01:26:48 UTC (rev 3059)
@@ -0,0 +1,122 @@
+/**
+ * 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
+ **/
+
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// base64 tables
+static char basis_64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char index_64[128] =
+{
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+// base64_encode    :    base64 encode
+//
+// value            :    data to encode
+// vlen             :    length of data
+// (result)         :    new char[] - c-str of result
+char *base64_encode(const unsigned char *value, int vlen)
+{
+    char *result = (char *)malloc((vlen * 4) / 3 + 5);
+    char *out = result;
+    while (vlen >= 3)
+    {
+        *out++ = basis_64[value[0] >> 2];
+        *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
+        *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
+        *out++ = basis_64[value[2] & 0x3F];
+        value += 3;
+        vlen -= 3;
+    }
+    if (vlen > 0)
+    {
+        *out++ = basis_64[value[0] >> 2];
+        unsigned char oval = (value[0] << 4) & 0x30;
+        if (vlen > 1) oval |= value[1] >> 4;
+        *out++ = basis_64[oval];
+        *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
+        *out++ = '=';
+    }
+    *out = '\0';
+    
+    return result;
+}
+
+// base64_decode    :    base64 decode
+//
+// value            :    c-str to decode
+// rlen             :    length of decoded result
+// (result)         :    new unsigned char[] - decoded result
+unsigned char *base64_decode(const char *value, int *rlen)
+{
+    *rlen = 0;
+    int c1, c2, c3, c4;
+    
+    int vlen = strlen(value);
+    unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+    unsigned char *out = result;
+    
+    while (1)
+    {
+        if (value[0]==0)
+            return result;
+        c1 = value[0];
+        if (CHAR64(c1) == -1)
+            goto base64_decode_error;;
+        c2 = value[1];
+        if (CHAR64(c2) == -1)
+            goto base64_decode_error;;
+        c3 = value[2];
+        if ((c3 != '=') && (CHAR64(c3) == -1))
+            goto base64_decode_error;;
+        c4 = value[3];
+        if ((c4 != '=') && (CHAR64(c4) == -1))
+            goto base64_decode_error;;
+                        
+        value += 4;
+        *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+        *rlen += 1;
+        if (c3 != '=')
+        {
+            *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+            *rlen += 1;
+            if (c4 != '=')
+            {
+                *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+                *rlen += 1;
+            }
+        }
+    }
+    
+base64_decode_error:
+    *result = 0;
+    *rlen = 0;
+    return result;
+}

Copied: PyOpenDirectory/trunk/src/base64.h (from rev 3058, PyOpenDirectory/branches/users/cdaboo/datatypes-3001/src/base64.h)
===================================================================
--- PyOpenDirectory/trunk/src/base64.h	                        (rev 0)
+++ PyOpenDirectory/trunk/src/base64.h	2008-09-25 01:26:48 UTC (rev 3059)
@@ -0,0 +1,20 @@
+/**
+ * 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
+ **/
+
+char *base64_encode(const unsigned char *value, int vlen);
+unsigned char *base64_decode(const char *value, int *rlen);

Modified: PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3
===================================================================
--- PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.mode1v3	2008-09-25 01:26:48 UTC (rev 3059)
@@ -268,17 +268,20 @@
 							<array>
 								<string>08FB7794FE84155DC02AAC07</string>
 								<string>08FB7795FE84155DC02AAC07</string>
+								<string>AF155A3D0A501FA0007E1E6E</string>
+								<string>AF155A350A501F9D007E1E6E</string>
 								<string>1C37FABC05509CD000000102</string>
 							</array>
 							<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
 							<array>
 								<array>
+									<integer>7</integer>
 									<integer>1</integer>
 									<integer>0</integer>
 								</array>
 							</array>
 							<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
-							<string>{{0, 0}, {244, 682}}</string>
+							<string>{{0, 0}, {244, 760}}</string>
 						</dict>
 						<key>PBXTopSmartGroupGIDs</key>
 						<array/>
@@ -290,14 +293,14 @@
 					<key>GeometryConfiguration</key>
 					<dict>
 						<key>Frame</key>
-						<string>{{0, 0}, {261, 700}}</string>
+						<string>{{0, 0}, {261, 778}}</string>
 						<key>GroupTreeTableConfiguration</key>
 						<array>
 							<string>MainColumn</string>
 							<real>244</real>
 						</array>
 						<key>RubberWindowFrame</key>
-						<string>0 437 1259 741 0 0 1920 1178 </string>
+						<string>0 59 1440 819 0 0 1440 878 </string>
 					</dict>
 					<key>Module</key>
 					<string>PBXSmartGroupTreeModule</string>
@@ -308,12 +311,14 @@
 					<key>Dock</key>
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
 								<string>1CE0B20306471E060097A5F4</string>
 								<key>PBXProjectModuleLabel</key>
-								<string>MyNewFile14.java</string>
+								<string>CDirectoryService.h</string>
 								<key>PBXSplitModuleInNavigatorKey</key>
 								<dict>
 									<key>Split0</key>
@@ -321,7 +326,15 @@
 										<key>PBXProjectModuleGUID</key>
 										<string>1CE0B20406471E060097A5F4</string>
 										<key>PBXProjectModuleLabel</key>
-										<string>MyNewFile14.java</string>
+										<string>CDirectoryService.h</string>
+										<key>_historyCapacity</key>
+										<integer>0</integer>
+										<key>bookmark</key>
+										<string>AF42FB9D0E8B1F1B008B0A12</string>
+										<key>history</key>
+										<array>
+											<string>AF42FB690E8B1C2F008B0A12</string>
+										</array>
 									</dict>
 									<key>SplitCount</key>
 									<string>1</string>
@@ -332,18 +345,16 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {993, 0}}</string>
+								<string>{{0, 0}, {1174, 719}}</string>
 								<key>RubberWindowFrame</key>
-								<string>0 437 1259 741 0 0 1920 1178 </string>
+								<string>0 59 1440 819 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXNavigatorGroup</string>
 							<key>Proportion</key>
-							<string>0pt</string>
+							<string>719pt</string>
 						</dict>
 						<dict>
-							<key>BecomeActive</key>
-							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -354,18 +365,18 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 5}, {993, 695}}</string>
+								<string>{{0, 724}, {1174, 54}}</string>
 								<key>RubberWindowFrame</key>
-								<string>0 437 1259 741 0 0 1920 1178 </string>
+								<string>0 59 1440 819 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>XCDetailModule</string>
 							<key>Proportion</key>
-							<string>695pt</string>
+							<string>54pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>993pt</string>
+					<string>1174pt</string>
 				</dict>
 			</array>
 			<key>Name</key>
@@ -380,9 +391,9 @@
 			</array>
 			<key>TableOfContents</key>
 			<array>
-				<string>AF02ACB10CBE789100F478B8</string>
+				<string>AF42FB9E0E8B1F1B008B0A12</string>
 				<string>1CE0B1FE06471DED0097A5F4</string>
-				<string>AF02ACB20CBE789100F478B8</string>
+				<string>AF42FB9F0E8B1F1B008B0A12</string>
 				<string>1CE0B20306471E060097A5F4</string>
 				<string>1CE0B20506471E060097A5F4</string>
 			</array>
@@ -520,7 +531,7 @@
 		<string>/Volumes/Data/Users/cyrusdaboo/Documents/Development/Apple/eclipse/PyOpenDirectory/support/PyOpenDirectory.xcodeproj</string>
 	</array>
 	<key>WindowString</key>
-	<string>0 437 1259 741 0 0 1920 1178 </string>
+	<string>0 59 1440 819 0 0 1440 878 </string>
 	<key>WindowToolsV3</key>
 	<array>
 		<dict>
@@ -536,12 +547,14 @@
 					<key>Dock</key>
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
 								<string>1CD0528F0623707200166675</string>
 								<key>PBXProjectModuleLabel</key>
-								<string></string>
+								<string>CDirectoryService.h</string>
 								<key>StatusBarVisibility</key>
 								<true/>
 							</dict>
@@ -550,7 +563,7 @@
 								<key>Frame</key>
 								<string>{{0, 0}, {1224, 453}}</string>
 								<key>RubberWindowFrame</key>
-								<string>25 406 1224 772 0 0 1920 1178 </string>
+								<string>8 106 1224 772 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXNavigatorGroup</string>
@@ -558,10 +571,10 @@
 							<string>453pt</string>
 						</dict>
 						<dict>
-							<key>BecomeActive</key>
-							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
+								<key>PBXBuildLogShowsTranscriptDefaultKey</key>
+								<string>{{0, 232}, {1224, 218}}</string>
 								<key>PBXProjectModuleGUID</key>
 								<string>XCMainBuildResultsModuleGUID</string>
 								<key>PBXProjectModuleLabel</key>
@@ -576,7 +589,7 @@
 								<key>Frame</key>
 								<string>{{0, 458}, {1224, 450}}</string>
 								<key>RubberWindowFrame</key>
-								<string>25 406 1224 772 0 0 1920 1178 </string>
+								<string>8 106 1224 772 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXBuildResultsModule</string>
@@ -599,22 +612,26 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>AF275A0D0CAC0FEA005A6274</string>
-				<string>AF02AC5C0CBE6A8000F478B8</string>
+				<string>AF42FB6B0E8B1C2F008B0A12</string>
 				<string>1CD0528F0623707200166675</string>
 				<string>XCMainBuildResultsModuleGUID</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.buildV3</string>
 			<key>WindowString</key>
-			<string>25 406 1224 772 0 0 1920 1178 </string>
+			<string>8 106 1224 772 0 0 1440 878 </string>
 			<key>WindowToolGUID</key>
 			<string>AF275A0D0CAC0FEA005A6274</string>
 			<key>WindowToolIsVisible</key>
 			<false/>
 		</dict>
 		<dict>
+			<key>FirstTimeWindowDisplayed</key>
+			<false/>
 			<key>Identifier</key>
 			<string>windowTool.debugger</string>
+			<key>IsVertical</key>
+			<true/>
 			<key>Layout</key>
 			<array>
 				<dict>
@@ -637,8 +654,8 @@
 										<string>yes</string>
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {317, 164}}</string>
-											<string>{{317, 0}, {377, 164}}</string>
+											<string>{{0, 0}, {657, 404}}</string>
+											<string>{{657, 0}, {783, 404}}</string>
 										</array>
 									</dict>
 									<key>VerticalSplitView</key>
@@ -653,8 +670,8 @@
 										<string>yes</string>
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {694, 164}}</string>
-											<string>{{0, 164}, {694, 216}}</string>
+											<string>{{0, 0}, {1440, 404}}</string>
+											<string>{{0, 404}, {1440, 373}}</string>
 										</array>
 									</dict>
 								</dict>
@@ -667,8 +684,6 @@
 							</dict>
 							<key>GeometryConfiguration</key>
 							<dict>
-								<key>DebugConsoleDrawerSize</key>
-								<string>{100, 120}</string>
 								<key>DebugConsoleVisible</key>
 								<string>None</string>
 								<key>DebugConsoleWindowFrame</key>
@@ -676,18 +691,34 @@
 								<key>DebugSTDIOWindowFrame</key>
 								<string>{{200, 200}, {500, 300}}</string>
 								<key>Frame</key>
-								<string>{{0, 0}, {694, 380}}</string>
+								<string>{{0, 0}, {1440, 777}}</string>
+								<key>PBXDebugSessionStackFrameViewKey</key>
+								<dict>
+									<key>DebugVariablesTableConfiguration</key>
+									<array>
+										<string>Name</string>
+										<real>265</real>
+										<string>Value</string>
+										<real>85</real>
+										<string>Summary</string>
+										<real>408</real>
+									</array>
+									<key>Frame</key>
+									<string>{{657, 0}, {783, 404}}</string>
+									<key>RubberWindowFrame</key>
+									<string>480 360 1440 818 0 0 1920 1178 </string>
+								</dict>
 								<key>RubberWindowFrame</key>
-								<string>321 238 694 422 0 0 1440 878 </string>
+								<string>480 360 1440 818 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXDebugSessionModule</string>
 							<key>Proportion</key>
-							<string>100%</string>
+							<string>777pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>100%</string>
+					<string>777pt</string>
 				</dict>
 			</array>
 			<key>Name</key>
@@ -697,22 +728,26 @@
 				<string>PBXDebugSessionModule</string>
 			</array>
 			<key>StatusbarIsVisible</key>
-			<integer>1</integer>
+			<true/>
 			<key>TableOfContents</key>
 			<array>
 				<string>1CD10A99069EF8BA00B06720</string>
-				<string>1C0AD2AB069F1E9B00FABCE6</string>
+				<string>AF2FBA020E814EE100C9D8E5</string>
 				<string>1C162984064C10D400B95A72</string>
-				<string>1C0AD2AC069F1E9B00FABCE6</string>
+				<string>AF2FBA030E814EE100C9D8E5</string>
+				<string>AF2FBA040E814EE100C9D8E5</string>
+				<string>AF2FBA050E814EE100C9D8E5</string>
+				<string>AF2FBA060E814EE100C9D8E5</string>
+				<string>AF2FBA070E814EE100C9D8E5</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.debugV3</string>
 			<key>WindowString</key>
-			<string>321 238 694 422 0 0 1440 878 </string>
+			<string>480 360 1440 818 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>1CD10A99069EF8BA00B06720</string>
 			<key>WindowToolIsVisible</key>
-			<integer>0</integer>
+			<false/>
 		</dict>
 		<dict>
 			<key>Identifier</key>
@@ -818,8 +853,12 @@
 			<string>MENUSEPARATOR</string>
 		</dict>
 		<dict>
+			<key>FirstTimeWindowDisplayed</key>
+			<false/>
 			<key>Identifier</key>
 			<string>windowTool.debuggerConsole</string>
+			<key>IsVertical</key>
+			<true/>
 			<key>Layout</key>
 			<array>
 				<dict>
@@ -827,7 +866,7 @@
 					<array>
 						<dict>
 							<key>BecomeActive</key>
-							<integer>1</integer>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -838,18 +877,18 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {650, 250}}</string>
+								<string>{{0, 0}, {1085, 479}}</string>
 								<key>RubberWindowFrame</key>
-								<string>516 632 650 250 0 0 1680 1027 </string>
+								<string>365 528 1085 520 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXDebugCLIModule</string>
 							<key>Proportion</key>
-							<string>209pt</string>
+							<string>479pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>209pt</string>
+					<string>479pt</string>
 				</dict>
 			</array>
 			<key>Name</key>
@@ -859,21 +898,21 @@
 				<string>PBXDebugCLIModule</string>
 			</array>
 			<key>StatusbarIsVisible</key>
-			<integer>1</integer>
+			<true/>
 			<key>TableOfContents</key>
 			<array>
 				<string>1C78EAAD065D492600B07095</string>
-				<string>1C78EAAE065D492600B07095</string>
+				<string>AF2FBA080E814EE100C9D8E5</string>
 				<string>1C78EAAC065D492600B07095</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.consoleV3</string>
 			<key>WindowString</key>
-			<string>650 41 650 250 0 0 1280 1002 </string>
+			<string>365 528 1085 520 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>1C78EAAD065D492600B07095</string>
 			<key>WindowToolIsVisible</key>
-			<integer>0</integer>
+			<false/>
 		</dict>
 		<dict>
 			<key>Identifier</key>

Modified: PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser
===================================================================
--- PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/cyrusdaboo.pbxuser	2008-09-25 01:26:48 UTC (rev 3059)
@@ -9,8 +9,7 @@
 			8DD76F620486A84900D96B5E /* PyOpenDirectory */,
 		);
 		breakpoints = (
-			AF0015620B8A24240045DAEE /* test.cpp:84 */,
-			AF0015650B8A245A0045DAEE /* test.cpp:167 */,
+			AF0015650B8A245A0045DAEE /* test.cpp:209 */,
 		);
 		codeSenseManager = AF155A2E0A501F7B007E1E6E /* Code sense */;
 		executables = (
@@ -46,7 +45,7 @@
 				PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
 				PBXFileTableDataSourceColumnWidthsKey = (
 					20,
-					754,
+					935,
 					20,
 					48,
 					43,
@@ -63,20 +62,24 @@
 					PBXFileDataSource_Target_ColumnID,
 				);
 			};
-			PBXPerProjectTemplateStateSaveDate = 213805048;
-			PBXWorkspaceStateSaveDate = 213805048;
+			PBXPerProjectTemplateStateSaveDate = 243997692;
+			PBXWorkspaceStateSaveDate = 243997692;
 		};
+		perUserProjectItems = {
+			AF42FB690E8B1C2F008B0A12 /* PBXTextBookmark */ = AF42FB690E8B1C2F008B0A12 /* PBXTextBookmark */;
+			AF42FB9D0E8B1F1B008B0A12 /* PBXTextBookmark */ = AF42FB9D0E8B1F1B008B0A12 /* PBXTextBookmark */;
+		};
 		sourceControlManager = AF155A2D0A501F7B007E1E6E /* Source Control */;
 		userBuildSettings = {
 		};
 	};
 	08FB7796FE84155DC02AAC07 /* test.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {926, 4270}}";
-			sepNavSelRange = "{1948, 96}";
-			sepNavVisRange = "{1144, 1674}";
+			sepNavIntBoundsRect = "{{0, 0}, {1118, 4760}}";
+			sepNavSelRange = "{1614, 30}";
+			sepNavVisRange = "{905, 2059}";
 			sepNavVisRect = "{{0, 1574}, {1521, 647}}";
-			sepNavWindowFrame = "{{436, 4}, {811, 828}}";
+			sepNavWindowFrame = "{{344, 35}, {1220, 1036}}";
 		};
 	};
 	8DD76F620486A84900D96B5E /* PyOpenDirectory */ = {
@@ -85,7 +88,7 @@
 			AF155A290A501F5C007E1E6E /* PyOpenDirectory */,
 		);
 	};
-	AF0015620B8A24240045DAEE /* test.cpp:84 */ = {
+	AF0015650B8A245A0045DAEE /* test.cpp:209 */ = {
 		isa = PBXFileBreakpoint;
 		actions = (
 		);
@@ -94,43 +97,27 @@
 		countType = 0;
 		delayBeforeContinue = 0;
 		fileReference = 08FB7796FE84155DC02AAC07 /* test.cpp */;
-		functionName = "main (int argc, const char * argv[])";
-		hitCount = 0;
-		ignoreCount = 0;
-		lineNumber = 84;
-		modificationTime = 196354514.472522;
-		state = 1;
-	};
-	AF0015650B8A245A0045DAEE /* test.cpp:167 */ = {
-		isa = PBXFileBreakpoint;
-		actions = (
-		);
-		breakpointStyle = 0;
-		continueAfterActions = 0;
-		countType = 0;
-		delayBeforeContinue = 0;
-		fileReference = 08FB7796FE84155DC02AAC07 /* test.cpp */;
 		functionName = "AuthenticateUser(CDirectoryService* dir, const char* user, const char* pswd)";
 		hitCount = 0;
 		ignoreCount = 0;
-		lineNumber = 167;
+		lineNumber = 209;
 		location = test.ob;
-		modificationTime = 196354382.775839;
+		modificationTime = 243311414.909002;
 		state = 2;
 	};
 	AF02AC560CBE690500F478B8 /* CDirectoryServiceException.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1086, 831}}";
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 868}}";
 			sepNavSelRange = "{1235, 12}";
-			sepNavVisRange = "{0, 1607}";
+			sepNavVisRange = "{0, 1431}";
 			sepNavWindowFrame = "{{684, 53}, {1145, 959}}";
 		};
 	};
 	AF02AC570CBE690500F478B8 /* CDirectoryServiceException.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1086, 831}}";
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 687}}";
 			sepNavSelRange = "{1019, 0}";
-			sepNavVisRange = "{0, 1300}";
+			sepNavVisRange = "{0, 1370}";
 			sepNavWindowFrame = "{{73, 219}, {1145, 959}}";
 		};
 	};
@@ -141,7 +128,7 @@
 		argumentStrings = (
 		);
 		autoAttachOnCrash = 1;
-		breakpointsEnabled = 1;
+		breakpointsEnabled = 0;
 		configStateDict = {
 		};
 		customDataFormattersEnabled = 1;
@@ -191,45 +178,45 @@
 	};
 	AF155A2F0A501F84007E1E6E /* CDirectoryService.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1221, 16198}}";
-			sepNavSelRange = "{1433, 0}";
-			sepNavVisRange = "{1187, 1367}";
+			sepNavIntBoundsRect = "{{0, 0}, {1190, 16786}}";
+			sepNavSelRange = "{2772, 19}";
+			sepNavVisRange = "{1888, 1680}";
 			sepNavVisRect = "{{0, 0}, {1309, 1017}}";
-			sepNavWindowFrame = "{{85, 73}, {1280, 828}}";
+			sepNavWindowFrame = "{{85, 4}, {1280, 874}}";
 		};
 	};
 	AF155A300A501F84007E1E6E /* CDirectoryService.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1106, 1008}}";
-			sepNavSelRange = "{1618, 0}";
-			sepNavVisRange = "{714, 1839}";
+			sepNavIntBoundsRect = "{{0, 0}, {1136, 1372}}";
+			sepNavSelRange = "{775, 28}";
+			sepNavVisRange = "{0, 1927}";
 			sepNavVisRect = "{{0, 123}, {752, 676}}";
-			sepNavWindowFrame = "{{308, 256}, {811, 828}}";
+			sepNavWindowFrame = "{{50, 9}, {1390, 869}}";
 		};
 	};
 	AF155A310A501F84007E1E6E /* PythonWrapper.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {992, 10668}}";
-			sepNavSelRange = "{23897, 0}";
-			sepNavVisRange = "{22724, 1929}";
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 13370}}";
+			sepNavSelRange = "{661, 0}";
+			sepNavVisRange = "{0, 1304}";
 			sepNavVisRect = "{{0, 0}, {1521, 647}}";
 			sepNavWindowFrame = "{{89, 4}, {1043, 828}}";
 		};
 	};
 	AF155AFB0A502C09007E1E6E /* CFStringUtil.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {752, 700}}";
-			sepNavSelRange = "{0, 0}";
-			sepNavVisRange = "{0, 1120}";
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 687}}";
+			sepNavSelRange = "{762, 19}";
+			sepNavVisRange = "{0, 1165}";
 			sepNavVisRect = "{{0, 0}, {766, 699}}";
 			sepNavWindowFrame = "{{746, 4}, {811, 828}}";
 		};
 	};
 	AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {752, 2114}}";
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 2128}}";
 			sepNavSelRange = "{0, 0}";
-			sepNavVisRange = "{0, 1510}";
+			sepNavVisRange = "{0, 1337}";
 			sepNavVisRect = "{{0, 1572}, {1342, 371}}";
 			sepNavWindowFrame = "{{36, 4}, {811, 1024}}";
 		};
@@ -244,10 +231,43 @@
 	};
 	AF41D9AC0CBDBAE200AB863D /* CDirectoryServiceManager.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {1221, 700}}";
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 687}}";
 			sepNavSelRange = "{775, 0}";
-			sepNavVisRange = "{0, 1033}";
+			sepNavVisRange = "{0, 1060}";
 			sepNavWindowFrame = "{{677, 13}, {1280, 828}}";
 		};
 	};
+	AF42FB690E8B1C2F008B0A12 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		comments = "error: CoreFoundation.h: No such file or directory";
+		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
+		rLen = 1;
+		rLoc = 23;
+		rType = 1;
+	};
+	AF42FB9D0E8B1F1B008B0A12 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = AF155A300A501F84007E1E6E /* CDirectoryService.h */;
+		name = "CDirectoryService.h: 24";
+		rLen = 28;
+		rLoc = 775;
+		rType = 0;
+		vrLen = 1927;
+		vrLoc = 0;
+	};
+	AFC1CA770E809C5200FAB3DB /* base64.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {1113, 687}}";
+			sepNavSelRange = "{674, 13}";
+			sepNavVisRange = "{0, 787}";
+			sepNavWindowFrame = "{{15, -1}, {1067, 874}}";
+		};
+	};
+	AFC1CA780E809C5200FAB3DB /* base64.cpp */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {1379, 1596}}";
+			sepNavSelRange = "{1633, 0}";
+			sepNavVisRange = "{1217, 820}";
+		};
+	};
 }

Modified: PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj
===================================================================
--- PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj	2008-09-25 01:26:48 UTC (rev 3059)
@@ -18,6 +18,7 @@
 		AF155AFD0A502C09007E1E6E /* CFStringUtil.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = AF155AFB0A502C09007E1E6E /* CFStringUtil.h */; };
 		AF155AFE0A502C09007E1E6E /* CFStringUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */; };
 		AF41D9AD0CBDBAE200AB863D /* CDirectoryServiceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF41D9AB0CBDBAE200AB863D /* CDirectoryServiceManager.cpp */; };
+		AFC1CA790E809C5200FAB3DB /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFC1CA780E809C5200FAB3DB /* base64.cpp */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -49,6 +50,8 @@
 		AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFStringUtil.cpp; path = ../src/CFStringUtil.cpp; sourceTree = SOURCE_ROOT; };
 		AF41D9AB0CBDBAE200AB863D /* CDirectoryServiceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CDirectoryServiceManager.cpp; path = ../src/CDirectoryServiceManager.cpp; sourceTree = SOURCE_ROOT; };
 		AF41D9AC0CBDBAE200AB863D /* CDirectoryServiceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDirectoryServiceManager.h; path = ../src/CDirectoryServiceManager.h; sourceTree = SOURCE_ROOT; };
+		AFC1CA770E809C5200FAB3DB /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base64.h; path = ../src/base64.h; sourceTree = SOURCE_ROOT; };
+		AFC1CA780E809C5200FAB3DB /* base64.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.c; fileEncoding = 4; name = base64.cpp; path = ../src/base64.cpp; sourceTree = SOURCE_ROOT; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -86,8 +89,10 @@
 				AF155A300A501F84007E1E6E /* CDirectoryService.h */,
 				AF155A310A501F84007E1E6E /* PythonWrapper.cpp */,
 				08FB7796FE84155DC02AAC07 /* test.cpp */,
+				AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */,
 				AF155AFB0A502C09007E1E6E /* CFStringUtil.h */,
-				AF155AFC0A502C09007E1E6E /* CFStringUtil.cpp */,
+				AFC1CA780E809C5200FAB3DB /* base64.cpp */,
+				AFC1CA770E809C5200FAB3DB /* base64.h */,
 			);
 			name = Source;
 			sourceTree = "<group>";
@@ -159,6 +164,7 @@
 				AF00155E0B8A21FD0045DAEE /* PythonWrapper.cpp in Sources */,
 				AF41D9AD0CBDBAE200AB863D /* CDirectoryServiceManager.cpp in Sources */,
 				AF02AC580CBE690500F478B8 /* CDirectoryServiceException.cpp in Sources */,
+				AFC1CA790E809C5200FAB3DB /* base64.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -196,11 +202,12 @@
 		1DEB923608733DC60010E9CD /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_INPUT_FILETYPE = sourcecode.cpp.cpp;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				HEADER_SEARCH_PATHS = /System/Library/Frameworks/Python.framework/Headers;
 				PREBINDING = NO;
-				SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+				SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
 				USER_HEADER_SEARCH_PATHS = ../src;
 			};
 			name = Debug;
@@ -208,11 +215,12 @@
 		1DEB923708733DC60010E9CD /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_INPUT_FILETYPE = sourcecode.cpp.cpp;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				HEADER_SEARCH_PATHS = "";
 				PREBINDING = NO;
-				SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+				SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
 				USER_HEADER_SEARCH_PATHS = ../src;
 			};
 			name = Release;

Modified: PyOpenDirectory/trunk/support/test.cpp
===================================================================
--- PyOpenDirectory/trunk/support/test.cpp	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/support/test.cpp	2008-09-25 01:26:48 UTC (rev 3059)
@@ -38,41 +38,82 @@
 
 #define		kDSStdRecordTypeResources					"dsRecTypeStandard:Resources"
 #define		kDSNAttrServicesLocator						"dsAttrTypeStandard:ServicesLocator"
+#define		kDSNAttrJPEGPhoto						    "dsAttrTypeStandard:JPEGPhoto"
 
 int main (int argc, const char * argv[]) {
     
 	CDirectoryService* dir = new CDirectoryService("/Search");
 
-#if 0
-#if 0
-	CFStringRef strings[2];
-	strings[0] = CFSTR(kDS1AttrDistinguishedName);
-	strings[1] = CFSTR(kDS1AttrGeneratedUID);
-	CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
+#if 1
+#if 1
+	CFStringRef attrs[3];
+	attrs[0] = CFSTR(kDS1AttrDistinguishedName);
+	attrs[1] = CFSTR(kDS1AttrGeneratedUID);
+	attrs[2] = CFSTR(kDSNAttrJPEGPhoto);
                         
-	CFMutableDictionaryRef dict = dir->ListAllRecordsWithAttributes(kDSStdRecordTypeUsers, array);
-	if (dict != NULL)
+	CFStringRef types[3];
+	types[0] = CFSTR("str");
+	types[1] = CFSTR("str");
+	types[2] = CFSTR("base64");
+	CFDictionaryRef attrsdict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)attrs, (const void **)types, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                        
+	CFMutableArrayRef data = dir->ListAllRecordsWithAttributes(kDSStdRecordTypeUsers, attrsdict, false);
+	if (data != NULL)
 	{
-		printf("\n*** Users: %d ***\n", CFDictionaryGetCount(dict));
-		CFDictionaryApplyFunction(dict, PrintDictionaryDictionary, NULL);
-		CFRelease(dict);
+		printf("\n*** Users: %d ***\n", CFArrayGetCount(data));
+		for(CFIndex i = 0; i < CFArrayGetCount(data); i++)
+		{
+			CFArrayRef tuple = (CFArrayRef)CFArrayGetValueAtIndex(data, i);
+			CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(tuple, 0);
+			const char* bytes = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
+			
+			if (bytes == NULL)
+			{
+				char localBuffer[256];
+				Boolean success;
+				success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
+				printf("%d: %s\n", i, localBuffer);
+			}
+			else
+			{
+				printf("%d: %s\n", i, (const char*)bytes);
+			}
+		}
+		CFRelease(data);
 	}
 	else
 	{
 		printf("\nNo Users returned\n");
 	}
-	CFRelease(array);
-
+	CFRelease(attrsdict);
+#if 0
 	strings[0] = CFSTR(kDSNAttrGroupMembers);
 	strings[1] = CFSTR(kDS1AttrGeneratedUID);
 	array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
                         
-	dict = dir->ListAllRecordsWithAttributes(kDSStdRecordTypeGroups, array);
-	if (dict != NULL)
+	data = NULL;//dir->ListAllRecordsWithAttributes(kDSStdRecordTypeGroups, array);
+	if (data != NULL)
 	{
-		printf("\n*** Groups: %d ***\n", CFDictionaryGetCount(dict));
-		CFDictionaryApplyFunction(dict, PrintDictionaryDictionary, NULL);
-		CFRelease(dict);
+		printf("\n*** Groups: %d ***\n", CFArrayGetCount(data));
+		for(CFIndex i = 0; i < CFArrayGetCount(data); i++)
+		{
+			CFArrayRef tuple = (CFArrayRef)CFArrayGetValueAtIndex(data, i);
+			CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(tuple, 0);
+			const char* bytes = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
+			
+			if (bytes == NULL)
+			{
+				char localBuffer[256];
+				Boolean success;
+				success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
+				printf("%d: %s\n", i, localBuffer);
+			}
+			else
+			{
+				printf("%d: %s\n", i, (const char*)bytes);
+			}
+		}
+		CFRelease(data);
 	}
 	else
 	{
@@ -80,9 +121,10 @@
 	}
 	CFRelease(array);
 #endif
+#endif
 
-	AuthenticateUser(dir, "gooeyed", "test", "test-no");
-	AuthenticateUser(dir, "gooeyed", "test", "test-yes");
+	//AuthenticateUser(dir, "gooeyed", "test", "test-no");
+	//AuthenticateUser(dir, "gooeyed", "test", "test-yes");
 #elif 0
 	CFStringRef keys[2];
 	keys[0] = CFSTR(kDS1AttrFirstName);

Modified: PyOpenDirectory/trunk/test.py
===================================================================
--- PyOpenDirectory/trunk/test.py	2008-09-25 01:22:13 UTC (rev 3058)
+++ PyOpenDirectory/trunk/test.py	2008-09-25 01:26:48 UTC (rev 3059)
@@ -18,7 +18,6 @@
 
 import opendirectory
 import dsattributes
-import time
 from dsquery import expression, match
 
 try:
@@ -30,7 +29,11 @@
 
 	def listUsers():
 		d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeUsers,
-													   [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,])
+													   (
+													   	dsattributes.kDS1AttrGeneratedUID,
+													    dsattributes.kDS1AttrDistinguishedName,
+													    ("dsAttrTypeStandard:JPEGPhoto", "base64"),
+													   ))
 		if d is None:
 			print "Failed to list users"
 		else:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080924/9c505600/attachment-0001.html 


More information about the calendarserver-changes mailing list