<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[14899] twext/branches/users/cdaboo/cfod</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/14899">14899</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2015-06-18 07:58:26 -0700 (Thu, 18 Jun 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Checkpoint of cffi/OD work.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#twextbranchesuserscdaboocfodproject">twext/branches/users/cdaboo/cfod/.project</a></li>
<li><a href="#twextbranchesuserscdaboocfodsetuppy">twext/branches/users/cdaboo/cfod/setup.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextwhoopendirectory_servicepy">twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_service.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextwhoopendirectorytesttest_servicepy">twext/branches/users/cdaboo/cfod/twext/who/opendirectory/test/test_service.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>twext/branches/users/cdaboo/cfod/twext/platform/</li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatform__init__py">twext/branches/users/cdaboo/cfod/twext/platform/__init__.py</a></li>
<li>twext/branches/users/cdaboo/cfod/twext/platform/osx/</li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosx__init__py">twext/branches/users/cdaboo/cfod/twext/platform/osx/__init__.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosx_corefoundation_cffi_buildpy">twext/branches/users/cdaboo/cfod/twext/platform/osx/_corefoundation_cffi_build.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxcorefoundationpy">twext/branches/users/cdaboo/cfod/twext/platform/osx/corefoundation.py</a></li>
<li>twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/</li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxframeworks__init__py">twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/__init__.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxframeworks_corefoundation_cffipy">twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_corefoundation_cffi.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxframeworks_opendirectory_cffipy">twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_opendirectory_cffi.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxframeworks_utils_cffipy">twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_utils_cffi.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxopendirectorypy">twext/branches/users/cdaboo/cfod/twext/platform/osx/opendirectory.py</a></li>
<li>twext/branches/users/cdaboo/cfod/twext/platform/osx/test/</li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtest__init__py">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/__init__.py</a></li>
<li>twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/</li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtestdatabinary_fmtplist">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/binary_fmt.plist</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtestdataxml_fmtplist">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/xml_fmt.plist</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfarraypy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfarray.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfdatapy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdata.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfdictionarypy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdictionary.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cflocalepy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cflocale.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfpropertylistpy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfpropertylist.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfstringpy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfstring.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_cftimezonepy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cftimezone.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxtesttest_opendirectorypy">twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_opendirectory.py</a></li>
<li><a href="#twextbranchesuserscdaboocfodtwextplatformosxutilspy">twext/branches/users/cdaboo/cfod/twext/platform/osx/utils.py</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#twextbranchesuserscdaboocfodtwextwhoopendirectory_odframeworkpy">twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_odframework.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="twextbranchesuserscdaboocfodproject"></a>
<div class="modfile"><h4>Modified: twext/branches/users/cdaboo/cfod/.project (14898 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/.project        2015-06-17 16:25:23 UTC (rev 14898)
+++ twext/branches/users/cdaboo/cfod/.project        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -3,7 +3,6 @@
</span><span class="cx">         &lt;name&gt;twextpy&lt;/name&gt;
</span><span class="cx">         &lt;comment&gt;&lt;/comment&gt;
</span><span class="cx">         &lt;projects&gt;
</span><del>-                &lt;project&gt;cffi&lt;/project&gt;
</del><span class="cx">                 &lt;project&gt;python-ldap&lt;/project&gt;
</span><span class="cx">                 &lt;project&gt;sqlparse&lt;/project&gt;
</span><span class="cx">                 &lt;project&gt;Twisted&lt;/project&gt;
</span></span></pre></div>
<a id="twextbranchesuserscdaboocfodsetuppy"></a>
<div class="modfile"><h4>Modified: twext/branches/users/cdaboo/cfod/setup.py (14898 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/setup.py        2015-06-17 16:25:23 UTC (rev 14898)
+++ twext/branches/users/cdaboo/cfod/setup.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx">     return modules
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def svn_info(wc_path):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Look up info on a Subversion working copy.
</span><span class="lines">@@ -87,6 +88,7 @@
</span><span class="cx">     )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def svn_status(wc_path):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Look up status on a Subversion working copy.
</span><span class="lines">@@ -127,6 +129,7 @@
</span><span class="cx">         yield dict(path=path)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def version():
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Compute the version number.
</span><span class="lines">@@ -235,8 +238,12 @@
</span><span class="cx"> 
</span><span class="cx"> setup_requirements = []
</span><span class="cx"> 
</span><ins>+cffi_requirements = [
+    &quot;twext/platform/osx/_corefoundation_cffi_build.py:ffi&quot;,
+]
+
</ins><span class="cx"> install_requirements = [
</span><del>-    &quot;cffi&gt;=0.6&quot;,
</del><ins>+    &quot;cffi&gt;=1.1.0&quot;,
</ins><span class="cx">     &quot;twisted&gt;=15.2.0&quot;,
</span><span class="cx"> ]
</span><span class="cx"> 
</span><span class="lines">@@ -248,7 +255,7 @@
</span><span class="cx">     &quot;LDAP&quot;: [&quot;python-ldap&quot;],
</span><span class="cx"> 
</span><span class="cx">     # OpenDirectory
</span><del>-    &quot;OpenDirectory&quot;: [&quot;pyobjc-framework-OpenDirectory&quot;],
</del><ins>+    &quot;OpenDirectory&quot;: [&quot;cffi&gt;=1.1.0&quot;],
</ins><span class="cx"> 
</span><span class="cx">     # Oracle
</span><span class="cx">     &quot;Oracle&quot;: [&quot;cx_Oracle&quot;],
</span><span class="lines">@@ -275,6 +282,7 @@
</span><span class="cx">         pass
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # Run setup
</span><span class="cx"> #
</span><span class="lines">@@ -313,6 +321,7 @@
</span><span class="cx">         ext_modules=extensions,
</span><span class="cx">         py_modules=[],
</span><span class="cx">         setup_requires=setup_requirements,
</span><ins>+        cffi_modules=cffi_requirements,
</ins><span class="cx">         install_requires=install_requirements,
</span><span class="cx">         extras_require=extras_requirements,
</span><span class="cx">     )
</span></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatform__init__py"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/__init__.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/__init__.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/__init__.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosx__init__py"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/__init__.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/__init__.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/__init__.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosx_corefoundation_cffi_buildpy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/_corefoundation_cffi_build.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/_corefoundation_cffi_build.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/_corefoundation_cffi_build.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,47 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+
+from cffi import FFI
+from importlib import import_module
+
+# Load cffi declaration modules
+modules = [import_module(&quot;.frameworks.{}&quot;.format(module), &quot;twext.platform.osx&quot;) for module in (
+    &quot;_corefoundation_cffi&quot;,
+    &quot;_opendirectory_cffi&quot;,
+    &quot;_utils_cffi&quot;,
+)]
+
+# Build list of includes and extra link arguments from each module
+includes = &quot;&quot;.join([m.INCLUDES for m in modules])
+extra_links = []
+for m in modules:
+    extra_links.extend(m.EXTRA_LINKS)
+
+# Build cffi - all modules go into one python extension module
+ffi = FFI()
+ffi.set_source(
+    &quot;_corefoundation&quot;,
+    includes,
+    extra_link_args=extra_links,
+)
+
+# Add each module's details
+for m in modules:
+    ffi.cdef(m.TYPES + m.CONSTANTS + m.FUNCTIONS)
+
+if __name__ == &quot;__main__&quot;:
+    ffi.compile()
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxcorefoundationpy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/corefoundation.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/corefoundation.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/corefoundation.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,390 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+&quot;&quot;&quot;
+A set of Python classes that wrap common CoreFoundation classes.
+&quot;&quot;&quot;
+
+from ._corefoundation import ffi, lib as cf
+
+
+class CFError(Exception):
+    &quot;&quot;&quot;
+    Generic CoreFoundation error occurred
+    &quot;&quot;&quot;
+
+
+
+class CFObjectRef(object):
+    &quot;&quot;&quot;
+    This is a wrapper for any CF objects returned from CFFI calls. For CF
+    objects whose ownership is implicitly retained (CoreFoundation Create Rule),
+    this class by default will CFRelease them. For an unowned object
+    (CoreFoundation Get Rule), the L{owned} parameter must be set to False,
+    which will cause the CF object to be explicitly CFRetain'd (and then
+    automatically released).
+    &quot;&quot;&quot;
+
+    def __init__(self, cfobj, owned=True):
+        &quot;&quot;&quot;
+        Create a wrapper around the supplied cffi object that mirrors a CoreFoundation object.
+
+        NB we have to cache the L{lib} and L{ffi} objects here to prevent them from being
+        garbage collected before this object's __del__ is called.
+
+        @param cfobj: the cffi object to wrap
+        @type cfobj: L{ffi.CData}
+        @param owned: whether the object is already retained
+        @type owned: L{bool}
+        &quot;&quot;&quot;
+        self.cf = cf
+        self.ffi = ffi
+        self.cfobj = cfobj if cfobj is not None else ffi.NULL
+        if not owned and cfobj is not None:
+            cf.CFRetain(self.cfobj)
+
+
+    def __del__(self):
+        &quot;&quot;&quot;
+        When this object is garbage collected, also release the CoreFoundation object, which we
+        know is retained by us.
+        &quot;&quot;&quot;
+        if self.cfobj != self.ffi.NULL:
+            self.cf.CFRelease(self.cfobj)
+        self.cfobj = None
+        self.cf = None
+
+
+    def ref(self):
+        return self.cfobj
+
+
+    def retainCount(self):
+        return cf.CFGetRetainCount(self.ref())
+
+
+    def instanceTypeId(self):
+        return cf.CFGetTypeID(self.ref())
+
+
+    def description(self):
+        description = CFStringRef(cf.CFCopyDescription(self.ref()))
+        return description.toString()
+
+
+    def native(self):
+        &quot;&quot;&quot;
+        Convert this object, if possible, into a native Python type. If it cannot be
+        converted, just return it as is.
+        &quot;&quot;&quot;
+        if self.instanceTypeId() == CFStringRef.typeId():
+            item = CFStringRef(self.ref(), owned=False)
+            result = item.native()
+        elif self.instanceTypeId() == CFArrayRef.typeId():
+            item = CFArrayRef(self.ref(), owned=False)
+            result = item.native()
+        elif self.instanceTypeId() == CFDictionaryRef.typeId():
+            item = CFDictionaryRef(self.ref(), owned=False)
+            result = item.native()
+        else:
+            result = self
+
+        return result
+
+
+
+class CFErrorRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFErrorRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFErrorGetTypeID()
+
+
+    def error(self):
+        &quot;&quot;&quot;
+        Return the error description.
+
+        @return: error description
+        @rtype: L{str}
+        &quot;&quot;&quot;
+        desc = CFStringRef(cf)
+        return desc.toString()
+
+
+
+class CFStringRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFStringRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFStringGetTypeID()
+
+
+    @classmethod
+    def fromString(cls, text):
+        &quot;&quot;&quot;
+        Create a cffi/CFStringRef from a Python L{str} - assume UTF-8 encoding.
+
+        @param text: utf-8 encoded string
+        @type text: L{str}
+
+        @return: the cffi data
+        @rtype: L{ffi.CData}
+        &quot;&quot;&quot;
+        if isinstance(text, unicode):
+            text = text.encode(&quot;utf-8&quot;)
+        cfstringref = cf.CFStringCreateWithCString(ffi.NULL, text, cf.kCFStringEncodingUTF8)
+        if cfstringref == ffi.NULL:
+            raise CFError(&quot;Unable to create a CFStringRef&quot;)
+        return CFStringRef(cfstringref)
+
+
+    def toString(self):
+        return self.native()
+
+
+    def __str__(self):
+        return self.native()
+
+
+    def native(self):
+        &quot;&quot;&quot;
+        Convert the cffi/CFStringRef value to a utf-8 encoded Python string.
+
+        @return: the string value
+        @rtype: L{str}
+        &quot;&quot;&quot;
+        # Try quick conversion first, then try longer one
+        str = cf.CFStringGetCStringPtr(self.ref(), cf.kCFStringEncodingUTF8)
+        if str == ffi.NULL:
+            range = cf.CFStringGetLength(self.ref())
+            actualSize = ffi.new(&quot;CFIndex *&quot;)
+            cf.CFStringGetBytes(self.ref(), [0, range], cf.kCFStringEncodingUTF8, ord(&quot;?&quot;), False, ffi.NULL, 0, actualSize)
+            actualSize = actualSize[0]
+            str = ffi.new(&quot;char []&quot;, actualSize + 1)
+            cf.CFStringGetCString(self.ref(), str, actualSize + 1, cf.kCFStringEncodingUTF8)
+        return ffi.string(str) if str else &quot;&quot;
+
+
+    def copy(self):
+        return CFStringRef(cf.CFStringCreateCopy(ffi.NULL, self.ref()))
+
+
+
+class CFDataRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFDataRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFDataGetTypeID()
+
+
+    @classmethod
+    def fromString(cls, text):
+        &quot;&quot;&quot;
+        Convert a L{str} to a cffi/CFDataRef.
+
+        @param text: string to convert
+        @type text: L{str}
+
+        @return: the  value
+        @rtype: L{CFDataRef}
+        &quot;&quot;&quot;
+        cfdataref = cf.CFDataCreate(ffi.NULL, text, len(text))
+        if cfdataref == ffi.NULL:
+            raise CFError(&quot;Unable to create a CFDataRef&quot;)
+        return CFDataRef(cfdataref)
+
+
+    def toString(self):
+        return self.native()
+
+
+    def count(self):
+        &quot;&quot;&quot;
+        The number of items in the wrapped CFArrayRef.
+
+        @return: the count
+        @rtype: L{int}
+        &quot;&quot;&quot;
+        return cf.CFDataGetLength(self.ref())
+
+
+    def native(self):
+        &quot;&quot;&quot;
+        Convert the cffi/CFDataRef value to a Python string.
+
+        @return: the string value
+        @rtype: L{str}
+        &quot;&quot;&quot;
+        count = self.count()
+        value = ffi.new(&quot;UInt8 []&quot;, count)
+        cf.CFDataGetBytes(self.ref(), [0, count], value)
+        return bytes(ffi.buffer(value, count))
+
+
+
+class CFArrayRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFArrayRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFArrayGetTypeID()
+
+
+    @classmethod
+    def fromList(cls, l):
+        &quot;&quot;&quot;
+        Create a cffi/CFArrayRef from a Python L{list} of L{CFObjectRef}.
+
+        @param l: list to convert
+        @type l: L{list}
+
+        @return: the cffi data
+        @rtype: L{CFArrayRef}
+        &quot;&quot;&quot;
+        lr = [item.ref() for item in l]
+        cfarrayref = cf.CFArrayCreate(ffi.NULL, lr, len(l), ffi.addressof(cf.kCFTypeArrayCallBacks))
+        if cfarrayref == ffi.NULL:
+            raise CFError(&quot;Unable to create a CFArrayRef&quot;)
+        return CFArrayRef(cfarrayref)
+
+
+    def toList(self):
+        return self.native()
+
+
+    def count(self):
+        &quot;&quot;&quot;
+        The number of items in the wrapped CFArrayRef.
+
+        @return: the count
+        @rtype: L{int}
+        &quot;&quot;&quot;
+        return cf.CFArrayGetCount(self.ref())
+
+
+    def valueAtIndex(self, index):
+        &quot;&quot;&quot;
+        Return an item from the CFArrayRef, converted to native type.
+
+        @param index: index of item to return
+        @type index: L{int}
+
+        @return: the cffi data
+        @rtype: L{CFObjectRef}, L{str}, L{list} or L{dict}
+        &quot;&quot;&quot;
+        result = cf.CFArrayGetValueAtIndex(self.ref(), index)
+        if result != ffi.NULL:
+            result = CFObjectRef(result, owned=False)
+            return result.native()
+        else:
+            return None
+
+
+    def native(self):
+        &quot;&quot;&quot;
+        Convert a CFArrayRef into an L{list} of native items.
+
+        @return: list of native items
+        @rtype: L{list}
+        &quot;&quot;&quot;
+        count = self.count()
+        values = ffi.new(&quot;const void * []&quot;, count)
+        cf.CFArrayGetValues(self.ref(), [0, count], values)
+        items = [CFObjectRef(item, owned=False) for item in values]
+        return [item.native() for item in items]
+
+
+
+class CFDictionaryRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFDictionaryRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFDictionaryGetTypeID()
+
+
+    @classmethod
+    def fromDict(cls, d):
+        &quot;&quot;&quot;
+        Create a cffi/CFDictionaryRef from a Python L{dict} of L{CFObjectRef}.
+
+        @param d: dict to convert
+        @type d: L{dict}
+
+        @return: the cffi data
+        @rtype: L{CFDictionaryRef}
+        &quot;&quot;&quot;
+        keys = []
+        values = []
+        for k, v in d.items():
+            keys.append(k.ref())
+            values.append(v.ref())
+        cfdictref = cf.CFDictionaryCreate(
+            ffi.NULL, keys, values, len(keys),
+            ffi.addressof(cf.kCFTypeDictionaryKeyCallBacks),
+            ffi.addressof(cf.kCFTypeDictionaryValueCallBacks),
+        )
+        if cfdictref == ffi.NULL:
+            raise CFError(&quot;Unable to create a CFDictionaryRef&quot;)
+        return CFDictionaryRef(cfdictref)
+
+
+    def toDict(self):
+        return self.native()
+
+
+    def count(self):
+        &quot;&quot;&quot;
+        The number of items in the wrapped CFDictionaryRef.
+
+        @return: the count
+        @rtype: L{int}
+        &quot;&quot;&quot;
+        return cf.CFDictionaryGetCount(self.ref())
+
+
+    def native(self):
+        &quot;&quot;&quot;
+        Convert the cffi/CFDictionaryRef value to a Python L{dict}. The keys and values
+        are all converted to their native values.
+
+        @return: the result
+        @rtype: L{dict}
+        &quot;&quot;&quot;
+        count = self.count()
+        keys = ffi.new(&quot;const void * []&quot;, count)
+        values = ffi.new(&quot;const void * []&quot;, count)
+        cf.CFDictionaryGetKeysAndValues(self.ref(), keys, values)
+
+        result = {}
+        for key, value in zip(keys, values):
+            k = CFStringRef(key, owned=False)
+            v = CFObjectRef(value, owned=False)
+            result[k.native()] = v.native()
+        return result
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxframeworks__init__py"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/__init__.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/__init__.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/__init__.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+all = [
+    &quot;_corefoundation_cffi&quot;,
+    &quot;_opendirectory_cffi&quot;,
+    &quot;_utils_cffi&quot;,
+]
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxframeworks_corefoundation_cffipy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_corefoundation_cffi.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_corefoundation_cffi.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_corefoundation_cffi.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+
+INCLUDES = &quot;&quot;&quot;
+#include &lt;CoreFoundation/CoreFoundation.h&gt;
+&quot;&quot;&quot;
+
+EXTRA_LINKS = []
+
+TYPES = &quot;&quot;&quot;
+typedef bool Boolean;
+typedef signed long OSStatus;
+typedef unsigned char UInt8;
+typedef uint32_t UInt32;
+
+typedef signed long CFIndex;
+typedef UInt32 CFOptionFlags;
+struct CFRange { CFIndex location; CFIndex length; };
+typedef struct CFRange CFRange;
+typedef UInt32 CFStringEncoding;
+typedef enum {
+    kCFStringEncodingUTF8 = 0x08000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF8Format */
+};
+
+typedef unsigned long CFTypeID;
+typedef const void * CFTypeRef;
+typedef const struct __CFAllocator *CFAllocatorRef;
+typedef const struct __CFString *CFStringRef;
+typedef const struct __CFData *CFDataRef;
+typedef const struct __CFArray *CFArrayRef;
+typedef const struct __CFDictionary *CFDictionaryRef;
+typedef struct __CFError * CFErrorRef;
+
+typedef struct {
+    ...;
+} CFArrayCallBacks;
+
+typedef struct {
+    ...;
+} CFDictionaryKeyCallBacks;
+typedef struct {
+    ...;
+} CFDictionaryValueCallBacks;
+&quot;&quot;&quot;
+
+CONSTANTS = &quot;&quot;&quot;
+const CFArrayCallBacks kCFTypeArrayCallBacks;
+
+const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks;
+const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
+&quot;&quot;&quot;
+
+FUNCTIONS = &quot;&quot;&quot;
+CFTypeRef CFRetain ( CFTypeRef cf );
+void CFRelease ( CFTypeRef cf );
+CFIndex CFGetRetainCount ( CFTypeRef cf );
+CFTypeID CFGetTypeID ( CFTypeRef cf );
+
+CFStringRef CFCopyDescription ( CFTypeRef cf );
+
+CFTypeID CFErrorGetTypeID ( void );
+
+CFTypeID CFStringGetTypeID ( void );
+CFStringRef CFStringCreateCopy ( CFAllocatorRef alloc, CFStringRef theString );
+CFStringRef CFStringCreateWithCString ( CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding );
+CFIndex CFStringGetBytes ( CFStringRef theString, CFRange range, CFStringEncoding encoding, UInt8 lossByte, Boolean isExternalRepresentation, UInt8 *buffer, CFIndex maxBufLen, CFIndex *usedBufLen );
+Boolean CFStringGetCString ( CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding );
+const char * CFStringGetCStringPtr ( CFStringRef theString, CFStringEncoding encoding );
+CFIndex CFStringGetLength ( CFStringRef theString );
+
+CFTypeID CFDataGetTypeID ( void );
+CFDataRef CFDataCreate ( CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length );
+void CFDataGetBytes ( CFDataRef theData, CFRange range, UInt8 *buffer );
+CFIndex CFDataGetLength ( CFDataRef theData );
+
+CFTypeID CFArrayGetTypeID ( void );
+CFArrayRef CFArrayCreate ( CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks );
+CFIndex CFArrayGetCount ( CFArrayRef theArray );
+const void * CFArrayGetValueAtIndex ( CFArrayRef theArray, CFIndex idx );
+void CFArrayGetValues ( CFArrayRef theArray, CFRange range, const void **values );
+
+CFTypeID CFDictionaryGetTypeID ( void );
+CFDictionaryRef CFDictionaryCreate ( CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks );
+CFIndex CFDictionaryGetCount ( CFDictionaryRef theDict );
+void CFDictionaryGetKeysAndValues ( CFDictionaryRef theDict, const void **keys, const void **values );
+&quot;&quot;&quot;
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxframeworks_opendirectory_cffipy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_opendirectory_cffi.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_opendirectory_cffi.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_opendirectory_cffi.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,394 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+
+INCLUDES = &quot;&quot;&quot;
+#include &lt;OpenDirectory/OpenDirectory.h&gt;
+&quot;&quot;&quot;
+
+EXTRA_LINKS = [&quot;-framework&quot;, &quot;OpenDirectory&quot;]
+
+TYPES = &quot;&quot;&quot;
+typedef struct __ODSession *ODSessionRef;
+typedef struct __ODNode *ODNodeRef;
+typedef struct __ODRecord *ODRecordRef;
+typedef struct __ODQuery *ODQueryRef;
+typedef uint32_t ODMatchType;
+typedef const struct __ODContext *ODContextRef;
+
+typedef CFStringRef ODRecordType;
+typedef CFStringRef ODAttributeType;
+typedef CFStringRef ODAuthenticationType;
+
+enum {
+   kODMatchAny = 0x0001,
+   kODMatchEqualTo = 0x2001,
+   kODMatchBeginsWith = 0x2002,
+   kODMatchEndsWith = 0x2003,
+   kODMatchContains = 0x2004,
+   kODMatchGreaterThan = 0x2006,
+   kODMatchLessThan = 0x2007
+   //kODMatchInsensitiveEqualTo = 0x2101,
+   //kODMatchInsensitiveBeginsWith = 0x2102,
+   //kODMatchInsensitiveEndsWith = 0x2103,
+   //kODMatchInsensitiveContains = 0x2104
+};
+
+&quot;&quot;&quot;
+
+CONSTANTS = &quot;&quot;&quot;
+ODSessionRef kODSessionDefault;
+
+const ODRecordType kODRecordTypeAttributeTypes;
+const ODRecordType kODRecordTypeAFPServer;
+const ODRecordType kODRecordTypeAliases;
+const ODRecordType kODRecordTypeAugments;
+const ODRecordType kODRecordTypeAutomount;
+const ODRecordType kODRecordTypeAutomountMap;
+const ODRecordType kODRecordTypeAutoServerSetup;
+const ODRecordType kODRecordTypeBootp;
+const ODRecordType kODRecordTypeCertificateAuthorities;
+const ODRecordType kODRecordTypeComputerLists;
+const ODRecordType kODRecordTypeComputerGroups;
+const ODRecordType kODRecordTypeComputers;
+const ODRecordType kODRecordTypeConfiguration;
+const ODRecordType kODRecordTypeEthernets;
+const ODRecordType kODRecordTypeFileMakerServers;
+const ODRecordType kODRecordTypeFTPServer;
+const ODRecordType kODRecordTypeGroups;
+const ODRecordType kODRecordTypeHostServices;
+const ODRecordType kODRecordTypeHosts;
+const ODRecordType kODRecordTypeLDAPServer;
+const ODRecordType kODRecordTypeLocations;
+const ODRecordType kODRecordTypeMounts;
+const ODRecordType kODRecordTypeNFS;
+const ODRecordType kODRecordTypeNetDomains;
+const ODRecordType kODRecordTypeNetGroups;
+const ODRecordType kODRecordTypeNetworks;
+const ODRecordType kODRecordTypePeople;
+const ODRecordType kODRecordTypePresetComputers;
+const ODRecordType kODRecordTypePresetComputerGroups;
+const ODRecordType kODRecordTypePresetComputerLists;
+const ODRecordType kODRecordTypePresetGroups;
+const ODRecordType kODRecordTypePresetUsers;
+const ODRecordType kODRecordTypePrintService;
+const ODRecordType kODRecordTypePrintServiceUser;
+const ODRecordType kODRecordTypePrinters;
+const ODRecordType kODRecordTypeProtocols;
+const ODRecordType kODRecordTypeQTSServer;
+const ODRecordType kODRecordTypeQueryInformation;
+const ODRecordType kODRecordTypeRecordTypes;
+const ODRecordType kODRecordTypeResources;
+const ODRecordType kODRecordTypeRPC;
+const ODRecordType kODRecordTypeSMBServer;
+const ODRecordType kODRecordTypeServer;
+const ODRecordType kODRecordTypeServices;
+const ODRecordType kODRecordTypeSharePoints;
+const ODRecordType kODRecordTypeUsers;
+const ODRecordType kODRecordTypeWebServer;
+
+const ODAttributeType kODAttributeTypeAllAttributes;
+const ODAttributeType kODAttributeTypeStandardOnly;
+const ODAttributeType kODAttributeTypeNativeOnly;
+const ODAttributeType kODAttributeTypeMetaAmbiguousName;
+const ODAttributeType kODAttributeTypeMetaAugmentedAttributes;
+const ODAttributeType kODAttributeTypeMetaRecordName;
+const ODAttributeType kODAttributeTypeAdminLimits;
+const ODAttributeType kODAttributeTypeAltSecurityIdentities;
+const ODAttributeType kODAttributeTypeAuthenticationHint;
+const ODAttributeType kODAttributeTypeAllTypes;
+const ODAttributeType kODAttributeTypeAuthorityRevocationList;
+const ODAttributeType kODAttributeTypeBirthday;
+const ODAttributeType kODAttributeTypeCACertificate;
+const ODAttributeType kODAttributeTypeCapacity;
+const ODAttributeType kODAttributeTypeCertificateRevocationList;
+const ODAttributeType kODAttributeTypeComment;
+const ODAttributeType kODAttributeTypeContactGUID;
+const ODAttributeType kODAttributeTypeContactPerson;
+const ODAttributeType kODAttributeTypeCreationTimestamp;
+const ODAttributeType kODAttributeTypeCrossCertificatePair;
+const ODAttributeType kODAttributeTypeDataStamp;
+const ODAttributeType kODAttributeTypeFullName;
+const ODAttributeType kODAttributeTypeDNSDomain;
+const ODAttributeType kODAttributeTypeDNSNameServer;
+const ODAttributeType kODAttributeTypeENetAddress;
+const ODAttributeType kODAttributeTypeExpire;
+const ODAttributeType kODAttributeTypeFirstName;
+const ODAttributeType kODAttributeTypeGUID;
+const ODAttributeType kODAttributeTypeHardwareUUID;
+const ODAttributeType kODAttributeTypeHomeDirectoryQuota;
+const ODAttributeType kODAttributeTypeHomeDirectorySoftQuota;
+const ODAttributeType kODAttributeTypeHomeLocOwner;
+const ODAttributeType kODAttributeTypeInternetAlias;
+const ODAttributeType kODAttributeTypeKDCConfigData;
+const ODAttributeType kODAttributeTypeKerberosServices;
+const ODAttributeType kODAttributeTypeLastName;
+const ODAttributeType kODAttributeTypeLDAPSearchBaseSuffix;
+const ODAttributeType kODAttributeTypeLocation;
+const ODAttributeType kODAttributeTypeMapGUID;
+const ODAttributeType kODAttributeTypeMCXFlags;
+const ODAttributeType kODAttributeTypeMCXSettings;
+const ODAttributeType kODAttributeTypeMailAttribute;
+const ODAttributeType kODAttributeTypeMetaAutomountMap;
+const ODAttributeType kODAttributeTypeMiddleName;
+const ODAttributeType kODAttributeTypeModificationTimestamp;
+const ODAttributeType kODAttributeTypeNFSHomeDirectory;
+const ODAttributeType kODAttributeTypeNote;
+const ODAttributeType kODAttributeTypeOperatingSystem;
+const ODAttributeType kODAttributeTypeOperatingSystemVersion;
+const ODAttributeType kODAttributeTypeOwner;
+const ODAttributeType kODAttributeTypeOwnerGUID;
+const ODAttributeType kODAttributeTypePassword;
+const ODAttributeType kODAttributeTypePasswordPlus;
+const ODAttributeType kODAttributeTypePasswordPolicyOptions;
+const ODAttributeType kODAttributeTypePasswordServerList;
+const ODAttributeType kODAttributeTypePasswordServerLocation;
+const ODAttributeType kODAttributeTypePicture;
+const ODAttributeType kODAttributeTypePort;
+const ODAttributeType kODAttributeTypePresetUserIsAdmin;
+const ODAttributeType kODAttributeTypePrimaryComputerGUID;
+const ODAttributeType kODAttributeTypePrimaryComputerList;
+const ODAttributeType kODAttributeTypePrimaryGroupID;
+const ODAttributeType kODAttributeTypePrinter1284DeviceID;
+const ODAttributeType kODAttributeTypePrinterLPRHost;
+const ODAttributeType kODAttributeTypePrinterLPRQueue;
+const ODAttributeType kODAttributeTypePrinterMakeAndModel;
+const ODAttributeType kODAttributeTypePrinterType;
+const ODAttributeType kODAttributeTypePrinterURI;
+const ODAttributeType kODAttributeTypePrinterXRISupported;
+const ODAttributeType kODAttributeTypePrintServiceInfoText;
+const ODAttributeType kODAttributeTypePrintServiceInfoXML;
+const ODAttributeType kODAttributeTypePrintServiceUserData;
+const ODAttributeType kODAttributeTypeRealUserID;
+const ODAttributeType kODAttributeTypeRelativeDNPrefix;
+const ODAttributeType kODAttributeTypeSMBAcctFlags;
+const ODAttributeType kODAttributeTypeSMBGroupRID;
+const ODAttributeType kODAttributeTypeSMBHome;
+const ODAttributeType kODAttributeTypeSMBHomeDrive;
+const ODAttributeType kODAttributeTypeSMBKickoffTime;
+const ODAttributeType kODAttributeTypeSMBLogoffTime;
+const ODAttributeType kODAttributeTypeSMBLogonTime;
+const ODAttributeType kODAttributeTypeSMBPrimaryGroupSID;
+const ODAttributeType kODAttributeTypeSMBPWDLastSet;
+const ODAttributeType kODAttributeTypeSMBProfilePath;
+const ODAttributeType kODAttributeTypeSMBRID;
+const ODAttributeType kODAttributeTypeSMBScriptPath;
+const ODAttributeType kODAttributeTypeSMBSID;
+const ODAttributeType kODAttributeTypeSMBUserWorkstations;
+const ODAttributeType kODAttributeTypeServiceType;
+const ODAttributeType kODAttributeTypeSetupAdvertising;
+const ODAttributeType kODAttributeTypeSetupAutoRegister;
+const ODAttributeType kODAttributeTypeSetupLocation;
+const ODAttributeType kODAttributeTypeSetupOccupation;
+const ODAttributeType kODAttributeTypeTimeToLive;
+const ODAttributeType kODAttributeTypeTrustInformation;
+const ODAttributeType kODAttributeTypeUniqueID;
+const ODAttributeType kODAttributeTypeUserCertificate;
+const ODAttributeType kODAttributeTypeUserPKCS12Data;
+const ODAttributeType kODAttributeTypeUserShell;
+const ODAttributeType kODAttributeTypeUserSMIMECertificate;
+const ODAttributeType kODAttributeTypeVFSDumpFreq;
+const ODAttributeType kODAttributeTypeVFSLinkDir;
+const ODAttributeType kODAttributeTypeVFSPassNo;
+const ODAttributeType kODAttributeTypeVFSType;
+const ODAttributeType kODAttributeTypeWeblogURI;
+const ODAttributeType kODAttributeTypeXMLPlist;
+const ODAttributeType kODAttributeTypeProtocolNumber;
+const ODAttributeType kODAttributeTypeRPCNumber;
+const ODAttributeType kODAttributeTypeNetworkNumber;
+const ODAttributeType kODAttributeTypeAccessControlEntry;
+const ODAttributeType kODAttributeTypeAddressLine1;
+const ODAttributeType kODAttributeTypeAddressLine2;
+const ODAttributeType kODAttributeTypeAddressLine3;
+const ODAttributeType kODAttributeTypeAreaCode;
+const ODAttributeType kODAttributeTypeAuthenticationAuthority;
+const ODAttributeType kODAttributeTypeAutomountInformation;
+const ODAttributeType kODAttributeTypeBootParams;
+const ODAttributeType kODAttributeTypeBuilding;
+const ODAttributeType kODAttributeTypeServicesLocator;
+const ODAttributeType kODAttributeTypeCity;
+const ODAttributeType kODAttributeTypeCompany;
+const ODAttributeType kODAttributeTypeComputers;
+const ODAttributeType kODAttributeTypeCountry;
+const ODAttributeType kODAttributeTypeDepartment;
+const ODAttributeType kODAttributeTypeDNSName;
+const ODAttributeType kODAttributeTypeEMailAddress;
+const ODAttributeType kODAttributeTypeEMailContacts;
+const ODAttributeType kODAttributeTypeFaxNumber;
+const ODAttributeType kODAttributeTypeGroup;
+const ODAttributeType kODAttributeTypeGroupMembers;
+const ODAttributeType kODAttributeTypeGroupMembership;
+const ODAttributeType kODAttributeTypeGroupServices;
+const ODAttributeType kODAttributeTypeHomePhoneNumber;
+const ODAttributeType kODAttributeTypeHTML;
+const ODAttributeType kODAttributeTypeHomeDirectory;
+const ODAttributeType kODAttributeTypeIMHandle;
+const ODAttributeType kODAttributeTypeIPAddress;
+const ODAttributeType kODAttributeTypeIPAddressAndENetAddress;
+const ODAttributeType kODAttributeTypeIPv6Address;
+const ODAttributeType kODAttributeTypeJPEGPhoto;
+const ODAttributeType kODAttributeTypeJobTitle;
+const ODAttributeType kODAttributeTypeKDCAuthKey;
+const ODAttributeType kODAttributeTypeKeywords;
+const ODAttributeType kODAttributeTypeLDAPReadReplicas;
+const ODAttributeType kODAttributeTypeLDAPWriteReplicas;
+const ODAttributeType kODAttributeTypeMapCoordinates;
+const ODAttributeType kODAttributeTypeMapURI;
+const ODAttributeType kODAttributeTypeMIME;
+const ODAttributeType kODAttributeTypeMobileNumber;
+const ODAttributeType kODAttributeTypeNestedGroups;
+const ODAttributeType kODAttributeTypeNetGroups;
+const ODAttributeType kODAttributeTypeNickName;
+const ODAttributeType kODAttributeTypeOrganizationInfo;
+const ODAttributeType kODAttributeTypeOrganizationName;
+const ODAttributeType kODAttributeTypePagerNumber;
+const ODAttributeType kODAttributeTypePhoneContacts;
+const ODAttributeType kODAttributeTypePhoneNumber;
+const ODAttributeType kODAttributeTypePGPPublicKey;
+const ODAttributeType kODAttributeTypePostalAddress;
+const ODAttributeType kODAttributeTypePostalAddressContacts;
+const ODAttributeType kODAttributeTypePostalCode;
+const ODAttributeType kODAttributeTypeNamePrefix;
+const ODAttributeType kODAttributeTypeProfiles;
+const ODAttributeType kODAttributeTypeProfilesTimestamp;
+const ODAttributeType kODAttributeTypeProtocols;
+const ODAttributeType kODAttributeTypeRecordName;
+const ODAttributeType kODAttributeTypeRelationships;
+const ODAttributeType kODAttributeTypeResourceInfo;
+const ODAttributeType kODAttributeTypeResourceType;
+const ODAttributeType kODAttributeTypeState;
+const ODAttributeType kODAttributeTypeStreet;
+const ODAttributeType kODAttributeTypeNameSuffix;
+const ODAttributeType kODAttributeTypeURL;
+const ODAttributeType kODAttributeTypeVFSOpts;
+const ODAttributeType kODAttributeTypeAlias;
+const ODAttributeType kODAttributeTypeAuthCredential;
+const ODAttributeType kODAttributeTypeCopyTimestamp;
+const ODAttributeType kODAttributeTypeDateRecordCreated;
+const ODAttributeType kODAttributeTypeKerberosRealm;
+const ODAttributeType kODAttributeTypeNTDomainComputerAccount;
+const ODAttributeType kODAttributeTypeOriginalHomeDirectory;
+const ODAttributeType kODAttributeTypeOriginalNFSHomeDirectory;
+const ODAttributeType kODAttributeTypeOriginalNodeName;
+const ODAttributeType kODAttributeTypePrimaryNTDomain;
+const ODAttributeType kODAttributeTypePwdAgingPolicy;
+const ODAttributeType kODAttributeTypeReadOnlyNode;
+const ODAttributeType kODAttributeTypeTimePackage;
+const ODAttributeType kODAttributeTypeTotalSize;
+const ODAttributeType kODAttributeTypeAuthMethod;
+const ODAttributeType kODAttributeTypeMetaNodeLocation;
+const ODAttributeType kODAttributeTypeNodePath;
+const ODAttributeType kODAttributeTypePlugInInfo;
+const ODAttributeType kODAttributeTypeRecordType;
+const ODAttributeType kODAttributeTypeSchema;
+const ODAttributeType kODAttributeTypeSubNodes;
+const ODAttributeType kODAttributeTypeNetGroupTriplet;
+const ODAttributeType kODAttributeTypeSearchPath;
+const ODAttributeType kODAttributeTypeSearchPolicy;
+const ODAttributeType kODAttributeTypeAutomaticSearchPath;
+const ODAttributeType kODAttributeTypeLocalOnlySearchPath;
+const ODAttributeType kODAttributeTypeCustomSearchPath;
+const ODAttributeType kODAttributeTypeNodeOptions;
+//const ODAttributeType kODAttributeTypeNodeSASLRealm;
+const ODAttributeType kODAttributeTypeAdvertisedServices;
+const ODAttributeType kODAttributeTypeLocaleRelay;
+const ODAttributeType kODAttributeTypeLocaleSubnets;
+const ODAttributeType kODAttributeTypeNetworkInterfaces;
+const ODAttributeType kODAttributeTypeParentLocales;
+const ODAttributeType kODAttributeTypePrimaryLocale;
+const ODAttributeType kODAttributeTypeBuildVersion;
+const ODAttributeType kODAttributeTypeConfigAvailable;
+const ODAttributeType kODAttributeTypeConfigFile;
+const ODAttributeType kODAttributeTypeCoreFWVersion;
+const ODAttributeType kODAttributeTypeFunctionalState;
+const ODAttributeType kODAttributeTypeFWVersion;
+const ODAttributeType kODAttributeTypePluginIndex;
+const ODAttributeType kODAttributeTypeNumTableList;
+const ODAttributeType kODAttributeTypeVersion;
+const ODAttributeType kODAttributeTypePIDValue;
+const ODAttributeType kODAttributeTypeProcessName;
+const ODAttributeType kODAttributeTypeTotalRefCount;
+const ODAttributeType kODAttributeTypeDirRefCount;
+const ODAttributeType kODAttributeTypeNodeRefCount;
+const ODAttributeType kODAttributeTypeRecRefCount;
+const ODAttributeType kODAttributeTypeAttrListRefCount;
+const ODAttributeType kODAttributeTypeAttrListValueRefCount;
+const ODAttributeType kODAttributeTypeDirRefs;
+const ODAttributeType kODAttributeTypeNodeRefs;
+const ODAttributeType kODAttributeTypeRecRefs;
+const ODAttributeType kODAttributeTypeAttrListRefs;
+const ODAttributeType kODAttributeTypeAttrListValueRefs;
+
+const ODAuthenticationType kODAuthenticationType2WayRandom;
+const ODAuthenticationType kODAuthenticationType2WayRandomChangePasswd;
+const ODAuthenticationType kODAuthenticationTypeAPOP;
+const ODAuthenticationType kODAuthenticationTypeCRAM_MD5;
+const ODAuthenticationType kODAuthenticationTypeChangePasswd;
+const ODAuthenticationType kODAuthenticationTypeClearText;
+const ODAuthenticationType kODAuthenticationTypeCrypt;
+const ODAuthenticationType kODAuthenticationTypeDIGEST_MD5;
+const ODAuthenticationType kODAuthenticationTypeDeleteUser;
+const ODAuthenticationType kODAuthenticationTypeGetEffectivePolicy;
+const ODAuthenticationType kODAuthenticationTypeGetGlobalPolicy;
+const ODAuthenticationType kODAuthenticationTypeGetKerberosPrincipal;
+const ODAuthenticationType kODAuthenticationTypeGetPolicy;
+const ODAuthenticationType kODAuthenticationTypeGetUserData;
+const ODAuthenticationType kODAuthenticationTypeGetUserName;
+const ODAuthenticationType kODAuthenticationTypeKerberosTickets;
+const ODAuthenticationType kODAuthenticationTypeMPPEMasterKeys;
+const ODAuthenticationType kODAuthenticationTypeMSCHAP2;
+const ODAuthenticationType kODAuthenticationTypeNTLMv2;
+const ODAuthenticationType kODAuthenticationTypeNTLMv2WithSessionKey;
+const ODAuthenticationType kODAuthenticationTypeNewUser;
+const ODAuthenticationType kODAuthenticationTypeNewUserWithPolicy;
+const ODAuthenticationType kODAuthenticationTypeNodeNativeClearTextOK;
+const ODAuthenticationType kODAuthenticationTypeNodeNativeNoClearText;
+const ODAuthenticationType kODAuthenticationTypeReadSecureHash;
+const ODAuthenticationType kODAuthenticationTypeSMBNTv2UserSessionKey;
+const ODAuthenticationType kODAuthenticationTypeSMBWorkstationCredentialSessionKey;
+const ODAuthenticationType kODAuthenticationTypeSMB_LM_Key;
+const ODAuthenticationType kODAuthenticationTypeSMB_NT_Key;
+const ODAuthenticationType kODAuthenticationTypeSMB_NT_UserSessionKey;
+const ODAuthenticationType kODAuthenticationTypeSMB_NT_WithUserSessionKey;
+const ODAuthenticationType kODAuthenticationTypeSetGlobalPolicy;
+const ODAuthenticationType kODAuthenticationTypeSetLMHash;
+const ODAuthenticationType kODAuthenticationTypeSetNTHash;
+const ODAuthenticationType kODAuthenticationTypeSetPassword;
+const ODAuthenticationType kODAuthenticationTypeSetPasswordAsCurrent;
+const ODAuthenticationType kODAuthenticationTypeSetPolicy;
+const ODAuthenticationType kODAuthenticationTypeSetPolicyAsCurrent;
+const ODAuthenticationType kODAuthenticationTypeSetUserData;
+const ODAuthenticationType kODAuthenticationTypeSetUserName;
+const ODAuthenticationType kODAuthenticationTypeSetWorkstationPassword;
+const ODAuthenticationType kODAuthenticationTypeWithAuthorizationRef;
+const ODAuthenticationType kODAuthenticationTypeWriteSecureHash;&quot;&quot;&quot;
+
+FUNCTIONS = &quot;&quot;&quot;
+ODSessionRef ODSessionCreate ( CFAllocatorRef allocator, CFDictionaryRef options, CFErrorRef *error );
+CFArrayRef ODSessionCopyNodeNames ( CFAllocatorRef allocator, ODSessionRef session, CFErrorRef *error );
+
+ODNodeRef ODNodeCreateWithName ( CFAllocatorRef allocator, ODSessionRef session, CFStringRef nodeName, CFErrorRef *error );
+bool ODNodeSetCredentials ( ODNodeRef node, ODRecordType recordType, CFStringRef recordName, CFStringRef password, CFErrorRef *error );
+CFDictionaryRef ODNodeCopyDetails ( ODNodeRef node, CFArrayRef keys, CFErrorRef *error );
+ODRecordRef ODNodeCopyRecord ( ODNodeRef node, ODRecordType recordType, CFStringRef recordName, CFTypeRef attributes, CFErrorRef *error );
+
+CFDictionaryRef ODRecordCopyDetails ( ODRecordRef record, CFArrayRef attributes, CFErrorRef *error );
+bool ODRecordVerifyPassword ( ODRecordRef record, CFStringRef password, CFErrorRef *error );
+bool ODRecordVerifyPasswordExtended ( ODRecordRef record, ODAuthenticationType authType, CFArrayRef authItems, CFArrayRef *outAuthItems, ODContextRef *outContext, CFErrorRef *error );
+
+CFArrayRef ODQueryCopyResults ( ODQueryRef query, bool allowPartialResults, CFErrorRef *error );
+ODQueryRef ODQueryCreateWithNode ( CFAllocatorRef allocator, ODNodeRef node, CFTypeRef recordTypeOrList, ODAttributeType attribute, ODMatchType matchType, CFTypeRef queryValueOrList, CFTypeRef returnAttributeOrList, CFIndex maxResults, CFErrorRef *error );
+&quot;&quot;&quot;
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxframeworks_utils_cffipy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_utils_cffi.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_utils_cffi.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/frameworks/_utils_cffi.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,62 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+
+INCLUDES = &quot;&quot;&quot;
+#include &lt;CoreFoundation/CoreFoundation.h&gt;
+&quot;&quot;&quot;
+
+EXTRA_LINKS = []
+
+TYPES = &quot;&quot;&quot;
+typedef const struct __CFTimeZone *CFTimeZoneRef;
+
+typedef const struct __CFLocale *CFLocaleRef;
+
+typedef CFTypeRef CFPropertyListRef;
+
+typedef CFOptionFlags CFPropertyListMutabilityOptions;
+enum {
+    kCFPropertyListImmutable = 0,
+    kCFPropertyListMutableContainers,
+    kCFPropertyListMutableContainersAndLeaves
+};
+
+typedef CFIndex CFPropertyListFormat;
+enum {
+    kCFPropertyListOpenStepFormat = 1,
+    kCFPropertyListXMLFormat_v1_0 = 100,
+    kCFPropertyListBinaryFormat_v1_0 = 200
+};
+&quot;&quot;&quot;
+
+CONSTANTS = &quot;&quot;&quot;
+&quot;&quot;&quot;
+
+FUNCTIONS = &quot;&quot;&quot;
+// CFTimeZone
+CFTypeID CFTimeZoneGetTypeID ( void );
+CFTimeZoneRef CFTimeZoneCopyDefault ( void );
+CFStringRef CFTimeZoneGetName ( CFTimeZoneRef tz );
+
+// CFLocale
+CFTypeID CFLocaleGetTypeID ( void );
+CFLocaleRef CFLocaleCopyCurrent ( void );
+CFArrayRef CFLocaleCopyPreferredLanguages ( void );
+
+// CFPropertyList
+CFPropertyListRef CFPropertyListCreateWithData ( CFAllocatorRef allocator, CFDataRef data, CFOptionFlags options, CFPropertyListFormat *format, CFErrorRef *error );
+&quot;&quot;&quot;
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxopendirectorypy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/opendirectory.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/opendirectory.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/opendirectory.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,282 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+&quot;&quot;&quot;
+A set of Python classes that wrap common CFOpenDirectory classes.
+&quot;&quot;&quot;
+
+from ._corefoundation import ffi, lib as od
+from .corefoundation import CFObjectRef, CFErrorRef, \
+    CFArrayRef, CFStringRef, CFDictionaryRef
+
+
+class ODError(Exception):
+    &quot;&quot;&quot;
+    OpenDirectory error occurred.
+    &quot;&quot;&quot;
+
+
+
+class ODSession(CFObjectRef):
+    &quot;&quot;&quot;
+    Wraps a cffi/ODSession object.
+    &quot;&quot;&quot;
+
+    def __init__(self, session=None):
+        &quot;&quot;&quot;
+        Create an ODSession.
+        &quot;&quot;&quot;
+
+        if session is None:
+            error = ffi.new(&quot;CFErrorRef *&quot;)
+            session = od.ODSessionCreate(ffi.NULL, ffi.NULL, error)
+            if session == ffi.NULL:
+                error = CFErrorRef(error[0])
+                raise ODError(&quot;Unable to create ODSession: {}&quot;.format(error.error()))
+        super(ODSession, self).__init__(session)
+
+
+    @classmethod
+    def defaultSession(cls):
+        &quot;&quot;&quot;
+        Return the default session.
+
+        @return: the default session
+        @rtype: L{ODSession}
+        &quot;&quot;&quot;
+        return cls(session=od.kODSessionDefault)
+
+
+    def nodeNames(self):
+        &quot;&quot;&quot;
+        Return a list of all the nodes available to the current ODSession.
+
+        @return: list of node names
+        @rtype: L{list} of L{str}
+        &quot;&quot;&quot;
+
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        nodes = od.ODSessionCopyNodeNames(ffi.NULL, self.ref(), error)
+        if nodes == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise ODError(&quot;Unable to get node names from session: {}&quot;.format(error.error()))
+
+        nodes = CFArrayRef(nodes)
+        return nodes.toList()
+
+
+
+class ODNode(CFObjectRef):
+    &quot;&quot;&quot;
+    Wraps a cffi/ODNode object.
+    &quot;&quot;&quot;
+
+    def __init__(self, session, nodename):
+        &quot;&quot;&quot;
+        Create an ODNode with the specified name using an ODSession.
+
+        @param session: session to use
+        @type session: L{ODSession}
+        @param nodename: name
+        @type nodename: L{str}
+        &quot;&quot;&quot;
+        name = CFStringRef.fromString(nodename)
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        node = od.ODNodeCreateWithName(ffi.NULL, session.ref(), name.ref(), error)
+        if node == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise ODError(&quot;Unable to create ODNode: {} {}&quot;.format(nodename, error.error()))
+        super(ODNode, self).__init__(node)
+
+
+    def setCredentials(self, recordType, user, pswd):
+        &quot;&quot;&quot;
+        Authentication to this ODNode.
+
+        @param recordType: OD record type to use
+        @type recordType: L{str}
+        @param user: user record name to auth as
+        @type user: L{str}
+        @param pswd: password to use
+        @type pswd: L{str}
+
+        @return: whether or not the authentication worked
+        @rtype: L{bool}
+        &quot;&quot;&quot;
+
+        recordType = CFStringRef.fromString(recordType)
+        user = CFStringRef.fromString(user)
+        pswd = CFStringRef.fromString(pswd)
+
+        return od.ODNodeSetCredentials(self.ref(), recordType.ref(), user, pswd, ffi.NULL)
+
+
+    def details(self, keys):
+        &quot;&quot;&quot;
+        Return the node attributes as an L{dict}.
+
+        @param keys: specific attributes to retrieve
+        @type keys: L{list} of L{str}
+
+        @return: result
+        @rtype: L{dict}
+        &quot;&quot;&quot;
+
+        keys = CFArrayRef.fromList([CFStringRef.fromString(key) for key in keys])
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        details = od.ODNodeCopyDetails(self.ref(), keys.ref(), error)
+        if details == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise ODError(&quot;Unable to get node details: {}&quot;.format(error.error()))
+        details = CFDictionaryRef(details)
+        return details.toDict()
+
+
+    def record(self, recordType, recordName):
+        &quot;&quot;&quot;
+        Get a record of the specified type with the specified record name.
+
+        @param recordType: OD record type
+        @type recordType: L{str}
+        @param recordName: record name
+        @type recordName: L{str}
+
+        @return: record
+        @rtype: L{ODRecord} or L{None}
+        &quot;&quot;&quot;
+
+        recordType = CFStringRef.fromString(recordType)
+        recordName = CFStringRef.fromString(recordName)
+        record = od.ODNodeCopyRecord(self.ref(), recordType.ref(), recordName.ref(), ffi.NULL, ffi.NULL)
+        return ODRecord(record) if record != ffi.NULL else None
+
+
+
+class ODRecord(CFObjectRef):
+    &quot;&quot;&quot;
+    Wraps a cffi/ODRecord object.
+    &quot;&quot;&quot;
+
+    def details(self, attributes=None):
+        &quot;&quot;&quot;
+        Return the record attributes as an L{dict}.
+
+        @param attributes: specific attributes to retrieve
+        @type attributes: L{list} of L{ffi.ODAttributeType}
+
+        @return: result
+        @rtype: L{dict}
+        &quot;&quot;&quot;
+
+        attributes = CFArrayRef.fromList([CFStringRef.fromString(attribute) for attribute in attributes]) if attributes else CFObjectRef(None)
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        details = od.ODRecordCopyDetails(self.ref(), attributes.ref(), error)
+        if details == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise ODError(&quot;Unable to get record details: {}&quot;.format(error.error()))
+        details = CFDictionaryRef(details)
+        return details.toDict()
+
+
+    def verifyPassword(self, password):
+        &quot;&quot;&quot;
+        Verify the password associated with this record.
+
+        @param password: password to check
+        @type password: L{str}
+
+        @return: whether or not the verification worked
+        @rtype: L{bool}
+        &quot;&quot;&quot;
+
+        password = CFStringRef.fromString(password)
+        return od.ODRecordVerifyPassword(self.ref(), password.ref(), ffi.NULL)
+
+
+    def verifyPasswordExtended(self, authType, authItems):
+        &quot;&quot;&quot;
+        Verify the authentication details associated with this record, using an extended
+        authentication type.
+
+        @param authType: password to check
+        @type authType: L{str}
+        @param authItems: items needed by the auth type
+        @type authItems: L{list} of L{str}
+
+        @return: whether or not the verification worked
+        @rtype: L{bool}
+        &quot;&quot;&quot;
+        authType = CFStringRef.fromString(authType)
+        authItems = CFArrayRef.fromList([CFStringRef.fromString(item) for item in authItems])
+        return od.ODRecordVerifyPasswordExtended(self.ref(), authType.ref(), authItems.ref(), ffi.NULL, ffi.NULL, ffi.NULL)
+
+
+
+class ODQuery(CFObjectRef):
+    &quot;&quot;&quot;
+    Wraps a cffi/ODQuery object.
+    &quot;&quot;&quot;
+
+    @classmethod
+    def newQuery(cls, node, recordTypes, queryAttribute, matchType, queryValue, fetchAttributes, maxResults):
+        &quot;&quot;&quot;
+        Create a new query.
+
+        @param node: the node to query
+        @type node: L{ODNode}
+        @param recordTypes: list of record types
+        @type recordTypes: L{list} of L{str}
+        @param queryAttribute: attribute to query or L{None} for complex query
+        @type queryAttribute: L{str} or L{None}
+        @param matchType: type of match
+        @type matchType: L{lib.ODMatchType}
+        @param queryValue: value to query for, or complex query string
+        @type queryValue: L{str}
+        @param fetchAttributes: list of attributes to return in matched records
+        @type fetchAttributes: L{list} of L{lib.ODAttributeType}
+        @param maxResults: maximum number of results to return
+        @type maxResults: L{int}
+
+        @return; the query
+        @rtype: L{ODQuery}
+        &quot;&quot;&quot;
+        recordTypes = CFArrayRef.fromList([CFStringRef.fromString(recordType) for recordType in recordTypes])
+        queryAttribute = CFStringRef.fromString(queryAttribute) if queryAttribute is not None else CFObjectRef(None)
+        queryValue = CFStringRef.fromString(queryValue) if queryValue is not None else CFObjectRef(None)
+        fetchAttributes = CFArrayRef.fromList([CFStringRef.fromString(fetchAttribute) for fetchAttribute in fetchAttributes])
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        query = od.ODQueryCreateWithNode(
+            ffi.NULL, node.ref(), recordTypes.ref(),
+            queryAttribute.ref(), matchType, queryValue.ref(),
+            fetchAttributes.ref(), maxResults, error
+        )
+        if query == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise ODError(&quot;Unable to create query: {}&quot;.format(error.error()))
+        return ODQuery(query)
+
+
+    def results(self, allowPartial=False):
+        &quot;&quot;&quot;
+        Return a list of L{ODRecords} matching the query.
+        &quot;&quot;&quot;
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        results = od.ODQueryCopyResults(self.ref(), allowPartial, error)
+        if results == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise ODError(&quot;Unable to create query: {}&quot;.format(error.error()))
+        results = CFArrayRef(results)
+        return [ODRecord(result.ref(), owned=False) for result in results.toList()]
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtest__init__py"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/__init__.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/__init__.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/__init__.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtestdatabinary_fmtplist"></a>
<div class="binary"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/binary_fmt.plist</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/binary_fmt.plist
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtestdataxml_fmtplist"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/xml_fmt.plist (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/xml_fmt.plist                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/data/xml_fmt.plist        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+    &lt;dict&gt;
+        &lt;key&gt;English&lt;/key&gt;
+        &lt;string&gt;Hello World!&lt;/string&gt;
+        &lt;key&gt;Latin&lt;/key&gt;
+        &lt;string&gt;Salve Mundi!&lt;/string&gt;
+    &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfarraypy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfarray.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfarray.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfarray.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,87 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..corefoundation import CFArrayRef, CFStringRef
+
+
+&quot;&quot;&quot;
+CFArrayRef tests.
+&quot;&quot;&quot;
+
+class CFArrayRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFArrayRef}.
+    &quot;&quot;&quot;
+
+    def test_typeid(self):
+        &quot;&quot;&quot;
+        Make sure L{CFArrayRef.instanceTypeId} returns the right value.
+        &quot;&quot;&quot;
+
+        array = CFArrayRef.fromList((CFStringRef.fromString(&quot;abc&quot;), CFStringRef.fromString(&quot;def&quot;),))
+        self.assertEqual(array.instanceTypeId(), CFArrayRef.typeId())
+
+
+    def test_description(self):
+        &quot;&quot;&quot;
+        Make sure L{CFArrayRef.description} is the correct string.
+        &quot;&quot;&quot;
+
+        array = CFArrayRef.fromList((CFStringRef.fromString(&quot;abc&quot;), CFStringRef.fromString(&quot;def&quot;),))
+        self.assertTrue(&quot;CFArray&quot; in array.description(), msg=array.description())
+
+
+    def test_retain(self):
+        &quot;&quot;&quot;
+        Make sure L{CFArrayRef.retainCount} returns the correct value based on ownership.
+        &quot;&quot;&quot;
+
+        array1 = CFArrayRef.fromList((CFStringRef.fromString(&quot;abc&quot;), CFStringRef.fromString(&quot;def&quot;),))
+        self.assertEqual(array1.retainCount(), 1)
+        array2 = CFArrayRef(array1.ref(), owned=False)
+        self.assertEqual(array1.retainCount(), 2)
+        self.assertEqual(array2.retainCount(), 2)
+        del array1
+        self.assertEqual(array2.retainCount(), 1)
+
+
+    def test_to_from_list(self):
+        &quot;&quot;&quot;
+        Make sure L{CFArrayRef.fromString} and L{CFArrayRef.toString} work properly.
+        &quot;&quot;&quot;
+
+        array = CFArrayRef.fromList((CFStringRef.fromString(&quot;abc&quot;), CFStringRef.fromString(&quot;def&quot;),))
+        self.assertEqual(array.toList(), [&quot;abc&quot;, &quot;def&quot;, ])
+
+
+    def test_count(self):
+        &quot;&quot;&quot;
+        Make sure L{CFArrayRef.count} returns the right number.
+        &quot;&quot;&quot;
+
+        array = CFArrayRef.fromList((CFStringRef.fromString(&quot;abc&quot;), CFStringRef.fromString(&quot;def&quot;),))
+        self.assertEqual(array.count(), 2)
+
+
+    def test_valueAt(self):
+        &quot;&quot;&quot;
+        Make sure L{CFArrayRef.valueAtIndex} returns the right number.
+        &quot;&quot;&quot;
+
+        array = CFArrayRef.fromList((CFStringRef.fromString(&quot;abc&quot;), CFStringRef.fromString(&quot;def&quot;),))
+        self.assertEqual(array.valueAtIndex(0), &quot;abc&quot;)
+        self.assertEqual(array.valueAtIndex(1), &quot;def&quot;)
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfdatapy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdata.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdata.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdata.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,83 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..corefoundation import CFDataRef
+
+
+&quot;&quot;&quot;
+CFDataRef tests.
+&quot;&quot;&quot;
+
+class CFDataRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFDataRef}.
+    &quot;&quot;&quot;
+
+    def test_typeid(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDataRef.instanceTypeId} returns the right value.
+        &quot;&quot;&quot;
+
+        data = CFDataRef.fromString(&quot;abc&quot;)
+        self.assertEqual(data.instanceTypeId(), CFDataRef.typeId())
+
+
+    def test_description(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDataRef.description} is the correct string.
+        &quot;&quot;&quot;
+
+        data = CFDataRef.fromString(&quot;abc&quot;)
+        self.assertTrue(&quot;CFData&quot; in data.description())
+
+
+    def test_to_from_string(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDataRef.fromString} and L{CFDataRef.toString} work properly.
+        &quot;&quot;&quot;
+
+        data = CFDataRef.fromString(&quot;abc&quot;)
+        self.assertEqual(data.toString(), &quot;abc&quot;)
+
+
+    def test_count(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDataRef.count} returns correct length.
+        &quot;&quot;&quot;
+
+        data = CFDataRef.fromString(&quot;abc&quot;)
+        self.assertEqual(data.count(), 3)
+
+
+    def test_binary(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDataRef.fromString} and L{CFDataRef.toString} work properly.
+        &quot;&quot;&quot;
+
+        tmp = self.mktemp()
+        with open(tmp, &quot;w&quot;) as f:
+            for i in range(100):
+                f.write(chr(i))
+
+        binary = open(tmp, &quot;r&quot;).read()
+
+        data = CFDataRef.fromString(binary)
+        self.assertEqual(data.count(), 100)
+        result = data.toString()
+        self.assertEqual(len(result), 100)
+        for i in range(100):
+            self.assertEqual(result[i], chr(i))
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfdictionarypy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdictionary.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdictionary.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfdictionary.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,75 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..corefoundation import CFDictionaryRef, CFStringRef
+
+
+&quot;&quot;&quot;
+CFDictionaryRef tests.
+&quot;&quot;&quot;
+
+class CFDictionaryRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFDictionaryRef}.
+    &quot;&quot;&quot;
+
+    def test_typeid(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDictionaryRef.instanceTypeId} returns the right value.
+        &quot;&quot;&quot;
+
+        cfdict = CFDictionaryRef.fromDict({
+            CFStringRef.fromString(&quot;abc&quot;): CFStringRef.fromString(&quot;1&quot;),
+            CFStringRef.fromString(&quot;def&quot;): CFStringRef.fromString(&quot;2&quot;),
+        })
+        self.assertEqual(cfdict.instanceTypeId(), CFDictionaryRef.typeId())
+
+
+    def test_description(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDictionaryRef.description} is the correct string.
+        &quot;&quot;&quot;
+
+        cfdict = CFDictionaryRef.fromDict({
+            CFStringRef.fromString(&quot;abc&quot;): CFStringRef.fromString(&quot;1&quot;),
+            CFStringRef.fromString(&quot;def&quot;): CFStringRef.fromString(&quot;2&quot;),
+        })
+        self.assertTrue(&quot;dict&quot; in cfdict.description(), msg=cfdict.description())
+
+
+    def test_to_from_dict(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDictionaryRef.fromDict} and L{CFDictionaryRef.toDict} work properly.
+        &quot;&quot;&quot;
+
+        cfdict = CFDictionaryRef.fromDict({
+            CFStringRef.fromString(&quot;abc&quot;): CFStringRef.fromString(&quot;1&quot;),
+            CFStringRef.fromString(&quot;def&quot;): CFStringRef.fromString(&quot;2&quot;),
+        })
+        self.assertEqual(cfdict.toDict(), {&quot;abc&quot;: &quot;1&quot;, &quot;def&quot;: &quot;2&quot;, })
+
+
+    def test_count(self):
+        &quot;&quot;&quot;
+        Make sure L{CFDictionaryRef.count} returns the right number.
+        &quot;&quot;&quot;
+
+        cfdict = CFDictionaryRef.fromDict({
+            CFStringRef.fromString(&quot;abc&quot;): CFStringRef.fromString(&quot;1&quot;),
+            CFStringRef.fromString(&quot;def&quot;): CFStringRef.fromString(&quot;2&quot;),
+        })
+        self.assertEqual(cfdict.count(), 2)
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cflocalepy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cflocale.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cflocale.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cflocale.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..utils import CFLocaleRef
+
+
+&quot;&quot;&quot;
+CFLocaleRef tests.
+&quot;&quot;&quot;
+
+class CFLocaleRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFLocaleRef}.
+    &quot;&quot;&quot;
+
+    def test_typeid(self):
+        &quot;&quot;&quot;
+        Make sure L{CFLocaleRef.instanceTypeId} returns the right value.
+        &quot;&quot;&quot;
+
+        locale = CFLocaleRef.currentLocale()
+        self.assertEqual(locale.instanceTypeId(), CFLocaleRef.typeId())
+
+
+    def test_preferredLanguages(self):
+        &quot;&quot;&quot;
+        Make sure L{CFLocaleRef.description} is the correct string.
+        &quot;&quot;&quot;
+
+        langs = CFLocaleRef.preferredLanguages()
+        self.assertTrue(len(langs) &gt; 0)
+        self.assertTrue(all([isinstance(lang, str) for lang in langs]))
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfpropertylistpy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfpropertylist.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfpropertylist.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfpropertylist.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,130 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..corefoundation import CFArrayRef, CFDataRef, CFDictionaryRef, CFStringRef
+from ..utils import CFPropertyListRef
+
+import os
+
+&quot;&quot;&quot;
+CFPropertyListRef tests.
+&quot;&quot;&quot;
+
+class CFPropertyListRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFPropertyListRef}.
+    &quot;&quot;&quot;
+
+    def test_readStringData(self):
+        &quot;&quot;&quot;
+        Make sure L{CFPropertyListRef} can parse a string-only plist.
+        &quot;&quot;&quot;
+
+        data = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+    &lt;string&gt;Hello World!&lt;/string&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+        dataref = CFDataRef.fromString(data)
+        plist = CFPropertyListRef.createFromData(dataref)
+        self.assertEqual(plist.instanceTypeId(), CFStringRef.typeId())
+        self.assertEqual(plist.toString(), &quot;Hello World!&quot;)
+
+
+    def test_readArrayData(self):
+        &quot;&quot;&quot;
+        Make sure L{CFPropertyListRef} can parse an array plist.
+        &quot;&quot;&quot;
+
+        data = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+    &lt;array&gt;
+        &lt;string&gt;Hello World!&lt;/string&gt;
+        &lt;string&gt;Salve Mundi!&lt;/string&gt;
+    &lt;/array&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+        dataref = CFDataRef.fromString(data)
+        plist = CFPropertyListRef.createFromData(dataref)
+        self.assertEqual(plist.instanceTypeId(), CFArrayRef.typeId())
+        self.assertEqual(plist.count(), 2)
+        items = plist.toList()
+        self.assertEqual(len(items), 2)
+        self.assertEqual(items[0], &quot;Hello World!&quot;)
+        self.assertEqual(items[1], &quot;Salve Mundi!&quot;)
+
+
+    def test_readDictionaryData(self):
+        &quot;&quot;&quot;
+        Make sure L{CFPropertyListRef} can parse a dict plist.
+        &quot;&quot;&quot;
+
+        data = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+    &lt;dict&gt;
+        &lt;key&gt;English&lt;/key&gt;
+        &lt;string&gt;Hello World!&lt;/string&gt;
+        &lt;key&gt;Latin&lt;/key&gt;
+        &lt;string&gt;Salve Mundi!&lt;/string&gt;
+    &lt;/dict&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+        dataref = CFDataRef.fromString(data)
+        plist = CFPropertyListRef.createFromData(dataref)
+        self.assertEqual(plist.instanceTypeId(), CFDictionaryRef.typeId())
+        self.assertEqual(plist.count(), 2)
+        items = plist.toDict()
+        self.assertEqual(len(items), 2)
+        self.assertEqual(items[&quot;English&quot;], &quot;Hello World!&quot;)
+        self.assertEqual(items[&quot;Latin&quot;], &quot;Salve Mundi!&quot;)
+
+
+    dataDir = os.path.join(os.path.dirname(__file__), &quot;data&quot;)
+
+    def test_readDictionaryXMLFile(self):
+        &quot;&quot;&quot;
+        Make sure L{CFPropertyListRef} can parse a XML plist read from a file.
+        &quot;&quot;&quot;
+
+        data = open(os.path.join(self.dataDir, &quot;xml_fmt.plist&quot;)).read()
+        dataref = CFDataRef.fromString(data)
+        plist = CFPropertyListRef.createFromData(dataref)
+        self.assertEqual(plist.instanceTypeId(), CFDictionaryRef.typeId())
+        self.assertEqual(plist.count(), 2)
+        items = plist.toDict()
+        self.assertEqual(len(items), 2)
+        self.assertEqual(items[&quot;English&quot;], &quot;Hello World!&quot;)
+        self.assertEqual(items[&quot;Latin&quot;], &quot;Salve Mundi!&quot;)
+
+
+    def test_readDictionaryBinaryFile(self):
+        &quot;&quot;&quot;
+        Make sure L{CFPropertyListRef} can parse a binary plist read from a file.
+        &quot;&quot;&quot;
+
+        data = open(os.path.join(self.dataDir, &quot;binary_fmt.plist&quot;)).read()
+        dataref = CFDataRef.fromString(data)
+        plist = CFPropertyListRef.createFromData(dataref)
+        self.assertEqual(plist.instanceTypeId(), CFDictionaryRef.typeId())
+        self.assertEqual(plist.count(), 2)
+        items = plist.toDict()
+        self.assertEqual(len(items), 2)
+        self.assertEqual(items[&quot;English&quot;], &quot;Hello World!&quot;)
+        self.assertEqual(items[&quot;Latin&quot;], &quot;Salve Mundi!&quot;)
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cfstringpy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfstring.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfstring.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cfstring.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,88 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..corefoundation import CFStringRef
+
+
+&quot;&quot;&quot;
+CFStringRef tests.
+&quot;&quot;&quot;
+
+class CFStringRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFStringRef}.
+    &quot;&quot;&quot;
+
+    def test_typeid(self):
+        &quot;&quot;&quot;
+        Make sure L{CFStringRef.instanceTypeId} returns the right value.
+        &quot;&quot;&quot;
+
+        str = CFStringRef.fromString(&quot;abc&quot;)
+        self.assertEqual(str.instanceTypeId(), CFStringRef.typeId())
+
+
+    def test_description(self):
+        &quot;&quot;&quot;
+        Make sure L{CFStringRef.description} is the correct string.
+        &quot;&quot;&quot;
+
+        str = CFStringRef.fromString(&quot;abc&quot;)
+        self.assertTrue(&quot;CFString&quot; in str.description())
+
+
+    def test_retain(self):
+        &quot;&quot;&quot;
+        Make sure L{CFStringRef.retainCount} returns the correct value based on ownership.
+        &quot;&quot;&quot;
+
+        str1 = CFStringRef.fromString(&quot;abc&quot;)
+        self.assertEqual(str1.retainCount(), 1)
+
+        # In this case copy actually uses the same original object but bumps the retain count
+        str2 = str1.copy()
+        self.assertEqual(str1.retainCount(), 2)
+        self.assertEqual(str2.retainCount(), 2)
+        del str1
+        self.assertEqual(str2.retainCount(), 1)
+
+        str3 = CFStringRef.fromString(&quot;def&quot;)
+        self.assertEqual(str3.retainCount(), 1)
+        str4 = CFStringRef(str3.ref(), owned=False)
+        self.assertEqual(str3.retainCount(), 2)
+        self.assertEqual(str4.retainCount(), 2)
+        del str3
+        self.assertEqual(str4.retainCount(), 1)
+
+
+    def test_to_from_string(self):
+        &quot;&quot;&quot;
+        Make sure L{CFStringRef.fromString} and L{CFStringRef.toString} work properly.
+        &quot;&quot;&quot;
+
+        str = CFStringRef.fromString(&quot;abc&quot;)
+        self.assertEqual(str.toString(), &quot;abc&quot;)
+
+
+    def test_copy(self):
+        &quot;&quot;&quot;
+        Make sure L{CFStringRef.copy} properly copies a string.
+        &quot;&quot;&quot;
+
+        str = CFStringRef.fromString(&quot;abc&quot;)
+        str2 = str.copy()
+        self.assertEqual(str2.toString(), &quot;abc&quot;)
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_cftimezonepy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cftimezone.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cftimezone.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_cftimezone.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,47 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..utils import CFTimeZoneRef
+
+
+&quot;&quot;&quot;
+CFTimeZoneRef tests.
+&quot;&quot;&quot;
+
+class CFTimeZoneRefTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for L{CFTimeZoneRef}.
+    &quot;&quot;&quot;
+
+    def test_typeid(self):
+        &quot;&quot;&quot;
+        Make sure L{CFTimeZoneRef.instanceTypeId} returns the right value.
+        &quot;&quot;&quot;
+
+        tz = CFTimeZoneRef.defaultTimeZone()
+        self.assertEqual(tz.instanceTypeId(), CFTimeZoneRef.typeId())
+
+
+    def test_name(self):
+        &quot;&quot;&quot;
+        Make sure L{CFTimeZoneRef.description} is the correct string.
+        &quot;&quot;&quot;
+
+        tz = CFTimeZoneRef.defaultTimeZone()
+        name = tz.name()
+        self.assertTrue(isinstance(name, str))
+        self.assertEqual(name, CFTimeZoneRef.defaultTimeZoneName())
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxtesttest_opendirectorypy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_opendirectory.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_opendirectory.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/test/test_opendirectory.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,91 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+from twisted.trial import unittest
+from ..opendirectory import ODSession, ODNode, ODRecord, ODQuery
+from .._corefoundation import lib as od
+
+
+&quot;&quot;&quot;
+OpenDirectory tests.
+&quot;&quot;&quot;
+
+class OpenDirectoryTestCase(unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for OpenDirectory cffi wrappers.
+    &quot;&quot;&quot;
+
+    def test_session(self):
+        &quot;&quot;&quot;
+        Make sure L{ODSession} works.
+        &quot;&quot;&quot;
+
+        session = ODSession.defaultSession()
+        self.assertTrue(session is not None)
+
+
+    def test_nodeNames(self):
+        &quot;&quot;&quot;
+        Make sure L{ODSession.nodeNames} returns a set of nodes.
+        &quot;&quot;&quot;
+
+        session = ODSession.defaultSession()
+        names = session.nodeNames()
+        self.assertTrue(isinstance(names, list))
+        self.assertTrue(&quot;/Search&quot; in names)
+
+
+    def test_node(self):
+        &quot;&quot;&quot;
+        Make sure L{ODNode} returns a valid node.
+        &quot;&quot;&quot;
+
+        session = ODSession.defaultSession()
+        node = ODNode(session, &quot;/Search&quot;)
+        self.assertTrue(node is not None)
+        self.assertTrue(isinstance(node.details([&quot;dsAttrTypeStandard:RecordType&quot;, ]), dict))
+
+
+    def test_record(self):
+        &quot;&quot;&quot;
+        Make sure L{ODRecord} returns a valid node.
+        &quot;&quot;&quot;
+
+        session = ODSession.defaultSession()
+        node = ODNode(session, &quot;/Local/Default&quot;)
+        record = node.record(&quot;dsRecTypeStandard:Users&quot;, &quot;_www&quot;)
+        self.assertTrue(isinstance(record, ODRecord))
+        self.assertTrue(isinstance(record.details([&quot;dsAttrTypeStandard:RecordType&quot;, ]), dict))
+
+
+    def test_query(self):
+        &quot;&quot;&quot;
+        Make sure L{ODQuery} returns records.
+        &quot;&quot;&quot;
+
+        session = ODSession.defaultSession()
+        node = ODNode(session, &quot;/Local/Default&quot;)
+        query = ODQuery.newQuery(
+            node, [&quot;dsRecTypeStandard:Users&quot;], &quot;dsAttrTypeStandard:RealName&quot;,
+            od.kODMatchContains, &quot;_&quot;,
+            [&quot;dsAttrTypeStandard:UniqueID&quot;], 100
+        )
+        self.assertTrue(isinstance(query, ODQuery))
+
+        results = query.results()
+        self.assertTrue(isinstance(results, list))
+        self.assertTrue(len(results) &gt; 0)
+        self.assertTrue(isinstance(results[0], ODRecord), msg=results)
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextplatformosxutilspy"></a>
<div class="addfile"><h4>Added: twext/branches/users/cdaboo/cfod/twext/platform/osx/utils.py (0 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/platform/osx/utils.py                                (rev 0)
+++ twext/branches/users/cdaboo/cfod/twext/platform/osx/utils.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -0,0 +1,114 @@
</span><ins>+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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.
+##
+
+&quot;&quot;&quot;
+A set of Python classes that wrap miscellaneous CoreFoundation classes.
+&quot;&quot;&quot;
+
+from ._corefoundation import ffi, lib as cf
+from .corefoundation import CFObjectRef, CFStringRef, CFArrayRef, CFDictionaryRef, CFErrorRef, CFError
+
+
+class CFTimeZoneRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFTimeZoneRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFTimeZoneGetTypeID()
+
+
+    @classmethod
+    def defaultTimeZone(cls):
+        cftzref = cf.CFTimeZoneCopyDefault()
+        if cftzref == ffi.NULL:
+            raise CFError(&quot;Unable to create a CFTimeZoneRef&quot;)
+        return CFTimeZoneRef(cftzref)
+
+
+    @classmethod
+    def defaultTimeZoneName(cls):
+        tz = cls.defaultTimeZone()
+        return tz.name()
+
+
+    def name(self):
+        cfstr = CFStringRef(cf.CFTimeZoneGetName(self.ref()), owned=False)
+        return cfstr.toString()
+
+
+
+class CFLocaleRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFLocaleRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        return cf.CFLocaleGetTypeID()
+
+
+    @classmethod
+    def currentLocale(cls):
+        localeref = cf.CFLocaleCopyCurrent()
+        if localeref == ffi.NULL:
+            raise CFError(&quot;Unable to create a CFLocaleRef&quot;)
+        return CFLocaleRef(localeref)
+
+
+    @classmethod
+    def preferredLanguages(cls):
+        items = cf.CFLocaleCopyPreferredLanguages()
+        if items == ffi.NULL:
+            raise CFError(&quot;Unable to get CFLocale preferred languages&quot;)
+        items = CFArrayRef(items)
+        return items.toList()
+
+
+
+class CFPropertyListRef(CFObjectRef):
+    &quot;&quot;&quot;
+    A wrapper for CFPropertyListRef CoreFoundation objects.
+    &quot;&quot;&quot;
+
+    @staticmethod
+    def typeId():
+        # CFPropertyListRef is actually one of CFStringRef, CFArrayRef or CFDictionaryRef
+        return None
+
+
+    @classmethod
+    def createFromData(cls, data):
+        error = ffi.new(&quot;CFErrorRef *&quot;)
+        plist = cf.CFPropertyListCreateWithData(ffi.NULL, data.ref(), cf.kCFPropertyListImmutable, ffi.NULL, error)
+        if plist == ffi.NULL:
+            error = CFErrorRef(error[0])
+            raise CFError(&quot;Unable to create a CFPropertyListRef: {}&quot;.format(error.error()))
+
+        # Map to actual data type
+        plist = CFObjectRef(plist)
+        typeid = plist.instanceTypeId()
+        if typeid == CFStringRef.typeId():
+            plist = CFStringRef(plist.ref(), owned=False)
+        elif typeid == CFArrayRef.typeId():
+            plist = CFArrayRef(plist.ref(), owned=False)
+        elif typeid == CFDictionaryRef.typeId():
+            plist = CFDictionaryRef(plist.ref(), owned=False)
+        else:
+            raise CFError(&quot;Unknown CFPropertyListRef type id: {}&quot;.format(typeid))
+
+        return plist
</ins></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextwhoopendirectory_odframeworkpy"></a>
<div class="delfile"><h4>Deleted: twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_odframework.py (14898 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_odframework.py        2015-06-17 16:25:23 UTC (rev 14898)
+++ twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_odframework.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -1,30 +0,0 @@
</span><del>-##
-# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# 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 &quot;AS IS&quot; 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.
-##
-
-&quot;&quot;&quot;
-OpenDirectory.framework
-&quot;&quot;&quot;
-
-import objc as _objc
-
-__bundle__ = _objc.initFrameworkWrapper(
-    &quot;OpenDirectory&quot;,
-    frameworkIdentifier=&quot;com.apple.OpenDirectory&quot;,
-    frameworkPath=_objc.pathForFramework(
-        &quot;/System/Library/Frameworks/OpenDirectory.framework&quot;
-    ),
-    globals=globals()
-)
</del></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextwhoopendirectory_servicepy"></a>
<div class="modfile"><h4>Modified: twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_service.py (14898 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_service.py        2015-06-17 16:25:23 UTC (rev 14898)
+++ twext/branches/users/cdaboo/cfod/twext/who/opendirectory/_service.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -21,7 +21,6 @@
</span><span class="cx"> OpenDirectory directory service implementation.
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from time import time
</del><span class="cx"> from uuid import UUID
</span><span class="cx"> from zope.interface import implementer
</span><span class="cx"> 
</span><span class="lines">@@ -47,8 +46,7 @@
</span><span class="cx"> )
</span><span class="cx"> from ..util import ConstantsContainer, firstResult, uniqueResult
</span><span class="cx"> 
</span><del>-from Foundation import NSAutoreleasePool
-from ._odframework import ODSession, ODNode, ODQuery
</del><ins>+from ...platform.osx.opendirectory import ODSession, ODNode, ODQuery, ODError
</ins><span class="cx"> from ._constants import (
</span><span class="cx">     FieldName,
</span><span class="cx">     ODSearchPath, ODRecordType, ODAttribute, ODMatchType, ODAuthMethod,
</span><span class="lines">@@ -82,26 +80,6 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def wrapWithAutoreleasePool(f):
-    &quot;&quot;&quot;
-    A decorator which creates an autorelease pool and deletes it, causing it
-    to drain
-    &quot;&quot;&quot;
-    def wrapped(*args, **kwds):
-        pool = NSAutoreleasePool.alloc().init()
-        try:
-            return f(*args, **kwds)
-        finally:
-            del pool
-    return wrapped
-
-
-
-def deferToThreadWithAutoReleasePool(f, *args, **kwargs):
-    return deferToThread(wrapWithAutoreleasePool(f), *args, **kwargs)
-
-
-
</del><span class="cx"> #
</span><span class="cx"> # Exceptions
</span><span class="cx"> #
</span><span class="lines">@@ -165,10 +143,6 @@
</span><span class="cx"> 
</span><span class="cx">     fieldName = ConstantsContainer((BaseDirectoryService.fieldName, FieldName))
</span><span class="cx"> 
</span><del>-    # The auto release pool is a class attribute; if _poolDeletionRegistered
-    # is True, that means someone has already added a SystemEventTrigger
-    _poolDeletionRegistered = False
-
</del><span class="cx">     def __init__(
</span><span class="cx">         self,
</span><span class="cx">         nodeName=ODSearchPath.search.value,
</span><span class="lines">@@ -186,51 +160,6 @@
</span><span class="cx">         self._suppressSystemRecords = suppressSystemRecords
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-        # Create an autorelease pool which will get deleted when someone
-        # calls _maybeResetPool( ), but no more often than 60 seconds, hence
-        # the &quot;maybe&quot;
-        DirectoryService._resetAutoreleasePool()
-
-        # Register a pool delete to happen at shutdown
-        if not DirectoryService._poolDeletionRegistered:
-            from twisted.internet import reactor
-            DirectoryService._poolDeletionRegistered = True
-            reactor.addSystemEventTrigger(&quot;after&quot;, &quot;shutdown&quot;, DirectoryService._deletePool)
-
-
-    @classmethod
-    def _deletePool(cls):
-        &quot;&quot;&quot;
-        Delete the autorelease pool if we have one
-        &quot;&quot;&quot;
-        if hasattr(cls, &quot;_autoReleasePool&quot;):
-            del cls._autoReleasePool
-
-
-    @classmethod
-    def _resetAutoreleasePool(cls):
-        &quot;&quot;&quot;
-        Create an autorelease pool, deleting the old one if we had one.
-        &quot;&quot;&quot;
-        cls._deletePool()
-
-        cls._autoReleasePool = NSAutoreleasePool.alloc().init()
-        cls._poolCreationTime = time()
-
-
-    @classmethod
-    def _maybeResetPool(cls):
-        &quot;&quot;&quot;
-        If it's been at least 60 seconds since the last time we created the
-        pool, delete the pool (which drains it) and create a new one.
-        &quot;&quot;&quot;
-        poolCreationTime = getattr(cls, &quot;_poolCreationTime&quot;, 0)
-        now = time()
-        if (now - poolCreationTime) &gt; 60:
-            cls._resetAutoreleasePool()
-
-
-
</del><span class="cx">     @property
</span><span class="cx">     def nodeName(self):
</span><span class="cx">         return self._nodeName
</span><span class="lines">@@ -260,10 +189,9 @@
</span><span class="cx">         if not hasattr(self, &quot;_localNode&quot;):
</span><span class="cx"> 
</span><span class="cx">             if self.nodeName == ODSearchPath.search.value:
</span><del>-                details, error = self.node.nodeDetailsForKeys_error_(
-                    (ODAttribute.searchPath.value,), None
-                )
-                if error:
</del><ins>+                try:
+                    details = self.node.details((ODAttribute.searchPath.value,))
+                except ODError as error:
</ins><span class="cx">                     self.log.error(
</span><span class="cx">                         &quot;Error while examining Search path&quot;,
</span><span class="cx">                         error=error
</span><span class="lines">@@ -296,8 +224,7 @@
</span><span class="cx">         Get the underlying directory session.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if not hasattr(self, &quot;_session&quot;):
</span><del>-            session = ODSession.defaultSession()
-            self._session = session
</del><ins>+            self._session = ODSession.defaultSession()
</ins><span class="cx">         return self._session
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -313,11 +240,9 @@
</span><span class="cx">         @raises: L{OpenDirectoryConnectionError} if unable to connect.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        node, error = ODNode.nodeWithSession_name_error_(
-            self.session, nodeName, None
-        )
-
-        if error:
</del><ins>+        try:
+            node = ODNode(self.session, nodeName)
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while trying to connect to OpenDirectory node &quot;
</span><span class="cx">                 &quot;{source.nodeName!r}: {error}&quot;,
</span><span class="lines">@@ -542,18 +467,17 @@
</span><span class="cx">         else:
</span><span class="cx">             maxResults = limitResults
</span><span class="cx"> 
</span><del>-        query, error = ODQuery.queryWithNode_forRecordTypes_attribute_matchType_queryValues_returnAttributes_maximumResults_error_(
-            node,
-            scrubbedRecordTypes,
-            None,
-            matchType,
-            queryString,
-            self._getFetchAttributes(),
-            maxResults,
-            None
-        )
-
-        if error:
</del><ins>+        try:
+            query = ODQuery.newQuery(
+                node,
+                scrubbedRecordTypes,
+                None,
+                matchType,
+                queryString,
+                self._getFetchAttributes(),
+                maxResults,
+            )
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while forming OpenDirectory compound query: {error}&quot;,
</span><span class="cx">                 error=error
</span><span class="lines">@@ -676,18 +600,17 @@
</span><span class="cx">                 u&quot;,&quot;.join(r.name for r in recordTypes)
</span><span class="cx">             )
</span><span class="cx"> 
</span><del>-        query, error = ODQuery.queryWithNode_forRecordTypes_attribute_matchType_queryValues_returnAttributes_maximumResults_error_(
-            node,
-            scrubbedRecordTypes,
-            queryAttribute,
-            matchType | caseInsensitive,
-            queryValue,
-            self._getFetchAttributes(),
-            maxResults,
-            None
-        )
-
-        if error:
</del><ins>+        try:
+            query = ODQuery.newQuery(
+                node,
+                scrubbedRecordTypes,
+                queryAttribute,
+                matchType | caseInsensitive,
+                queryValue,
+                self._getFetchAttributes(),
+                maxResults,
+            )
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while forming OpenDirectory match query: {error}&quot;,
</span><span class="cx">                 error=error
</span><span class="lines">@@ -708,9 +631,10 @@
</span><span class="cx">         @return: True if system account record, False otherwise
</span><span class="cx">         @rtype: C{Boolean}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        details, error = odRecord.recordDetailsForAttributes_error_(None, None)
</del><span class="cx"> 
</span><del>-        if error:
</del><ins>+        try:
+            details = odRecord.details()
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while reading OpenDirectory record: {error}&quot;,
</span><span class="cx">                 error=error
</span><span class="lines">@@ -784,18 +708,17 @@
</span><span class="cx">         if query is None:
</span><span class="cx">             returnValue(())
</span><span class="cx"> 
</span><del>-        if DEFER_TO_THREAD:
-            odRecords, error = (
-                yield deferToThreadWithAutoReleasePool(
-                    query.resultsAllowingPartial_error_,
-                    False,
-                    None
</del><ins>+        try:
+            if DEFER_TO_THREAD:
+                odRecords = (
+                    yield deferToThread(
+                        query.results,
+                        False,
+                    )
</ins><span class="cx">                 )
</span><del>-            )
-        else:
-            odRecords, error = query.resultsAllowingPartial_error_(False, None)
-
-        if error:
</del><ins>+            else:
+                odRecords = query.results(False)
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while executing OpenDirectory query: {error}&quot;,
</span><span class="cx">                 error=error
</span><span class="lines">@@ -834,9 +757,6 @@
</span><span class="cx">         self, expression, recordTypes=None, records=None,
</span><span class="cx">         limitResults=None, timeoutSeconds=None
</span><span class="cx">     ):
</span><del>-        DirectoryService._maybeResetPool()
-
-
</del><span class="cx">         if isinstance(expression, MatchExpression):
</span><span class="cx">             self.log.debug(&quot;OD call: {}&quot;.format(expression))
</span><span class="cx">             try:
</span><span class="lines">@@ -874,8 +794,6 @@
</span><span class="cx">         CompoundExpression up into MatchExpressions for sending to the local
</span><span class="cx">         node.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        DirectoryService._maybeResetPool()
-
</del><span class="cx">         try:
</span><span class="cx">             self.log.debug(&quot;OD call: {}&quot;.format(expression))
</span><span class="cx">             query = self._queryFromCompoundExpression(
</span><span class="lines">@@ -984,10 +902,9 @@
</span><span class="cx"> 
</span><span class="cx">         @return: ODRecord, or None
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        record, error = self.node.recordWithRecordType_name_attributes_error_(
-            ODRecordType.user.value, username, None, None
-        )
-        if error:
</del><ins>+        try:
+            record = self.node.record(ODRecordType.user.value, username)
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while looking up user: {error}&quot;,
</span><span class="cx">                 error=error
</span><span class="lines">@@ -1037,7 +954,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> @implementer(IPlaintextPasswordVerifier, IHTTPDigestVerifier)
</span><span class="cx"> class DirectoryRecord(BaseDirectoryRecord):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -1051,9 +967,10 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, service, odRecord):
</span><del>-        details, error = odRecord.recordDetailsForAttributes_error_(None, None)
</del><span class="cx"> 
</span><del>-        if error:
</del><ins>+        try:
+            details = odRecord.details()
+        except ODError as error:
</ins><span class="cx">             self.log.error(
</span><span class="cx">                 &quot;Error while reading OpenDirectory record: {error}&quot;,
</span><span class="cx">                 error=error
</span><span class="lines">@@ -1104,7 +1021,7 @@
</span><span class="cx">             if type(values) is bytes:
</span><span class="cx">                 values = (coerceType(fieldName, values),)
</span><span class="cx">             else:
</span><del>-                values = tuple(coerceType(fieldName, v) for v in values)
</del><ins>+                values = tuple(coerceType(fieldName, v.decode(&quot;utf-8&quot;)) for v in values)
</ins><span class="cx"> 
</span><span class="cx">             if service.fieldName.isMultiValue(fieldName):
</span><span class="cx">                 fields[fieldName] = values
</span><span class="lines">@@ -1146,20 +1063,19 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def verifyPlaintextPassword(self, password):
</span><span class="cx"> 
</span><del>-        if DEFER_TO_THREAD:
-            result, error = (
-                yield deferToThreadWithAutoReleasePool(
-                    self._odRecord.verifyPassword_error_,
</del><ins>+        try:
+            if DEFER_TO_THREAD:
+                result = (
+                    yield deferToThread(
+                        self._odRecord.verifyPassword,
+                        password,
+                    )
+                )
+            else:
+                result = self._odRecord.verifyPassword(
</ins><span class="cx">                     password,
</span><del>-                    None
</del><span class="cx">                 )
</span><del>-            )
-        else:
-            result, error = self._odRecord.verifyPassword_error_(
-                password, None
-            )
-
-        if error:
</del><ins>+        except ODError:
</ins><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><span class="cx">         returnValue(result)
</span><span class="lines">@@ -1199,23 +1115,21 @@
</span><span class="cx">             response=response
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        if DEFER_TO_THREAD:
-            result, _ignore_m1, _ignore_m2, error = (
-                yield deferToThreadWithAutoReleasePool(
-                    self._odRecord.verifyExtendedWithAuthenticationType_authenticationItems_continueItems_context_error_,
</del><ins>+        try:
+            if DEFER_TO_THREAD:
+                result = (
+                    yield deferToThread(
+                        self._odRecord.verifyPasswordExtended,
+                        ODAuthMethod.digestMD5.value,
+                        [username, challenge, responseArg, method],
+                    )
+                )
+            else:
+                result = self._odRecord.verifyPasswordExtended(
</ins><span class="cx">                     ODAuthMethod.digestMD5.value,
</span><span class="cx">                     [username, challenge, responseArg, method],
</span><del>-                    None, None, None
</del><span class="cx">                 )
</span><del>-            )
-        else:
-            result, _ignore_m1, _ignore_m2, error = self._odRecord.verifyExtendedWithAuthenticationType_authenticationItems_continueItems_context_error_(
-                ODAuthMethod.digestMD5.value,
-                [username, challenge, responseArg, method],
-                None, None, None
-            )
-
-        if error:
</del><ins>+        except ODError:
</ins><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><span class="cx">         returnValue(result)
</span></span></pre></div>
<a id="twextbranchesuserscdaboocfodtwextwhoopendirectorytesttest_servicepy"></a>
<div class="modfile"><h4>Modified: twext/branches/users/cdaboo/cfod/twext/who/opendirectory/test/test_service.py (14898 => 14899)</h4>
<pre class="diff"><span>
<span class="info">--- twext/branches/users/cdaboo/cfod/twext/who/opendirectory/test/test_service.py        2015-06-17 16:25:23 UTC (rev 14898)
+++ twext/branches/users/cdaboo/cfod/twext/who/opendirectory/test/test_service.py        2015-06-18 14:58:26 UTC (rev 14899)
</span><span class="lines">@@ -38,10 +38,7 @@
</span><span class="cx">     def setUp(self):
</span><span class="cx">         self.service = DirectoryService()
</span><span class="cx"> 
</span><del>-    def tearDown(self):
-        self.service._deletePool()
</del><span class="cx"> 
</span><del>-
</del><span class="cx">     def test_queryFromMatchExpression_recordType(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Make sure queryFromMatchExpression handles recordType correctly
</span></span></pre>
</div>
</div>

</body>
</html>