<!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>[12835] CalendarServer/branches/users/sagen/move2who</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/12835">12835</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2014-03-06 17:22:21 -0800 (Thu, 06 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Halfway through rewrite of calendarserver_manage_principals.  Added recordsMatchingTokens()</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertapcaldavpy">CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertoolsprincipalspy">CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertoolsutilpy">CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavdpsclientpy">CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavdpscommandspy">CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavdpsserverpy">CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavdpstesttest_clientpy">CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -549,8 +549,8 @@
</span><span class="cx">             self.monitor.addProcessObject(process, PARENT_ENVIRONMENT)
</span><span class="cx"> 
</span><span class="cx">         if (
</span><del>-           config.DirectoryProxy.Enabled and
-           config.DirectoryProxy.SocketPath != &quot;&quot;
</del><ins>+            config.DirectoryProxy.Enabled and
+            config.DirectoryProxy.SocketPath != &quot;&quot;
</ins><span class="cx">         ):
</span><span class="cx">             log.info(&quot;Adding directory proxy service&quot;)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -26,17 +26,16 @@
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><span class="cx"> from txdav.xml import element as davxml
</span><ins>+from txdav.who.delegates import addDelegate, removeDelegate
</ins><span class="cx"> 
</span><del>-from txdav.xml.base import decodeXMLName, encodeXMLName
</del><span class="cx"> 
</span><span class="cx"> from twistedcaldav.config import config
</span><del>-from twistedcaldav.directory.directory import UnknownRecordTypeError, DirectoryError
</del><ins>+from twistedcaldav.directory.directory import UnknownRecordTypeError
</ins><span class="cx"> from txdav.who.groups import schedulePolledGroupCachingUpdate
</span><span class="cx"> 
</span><span class="cx"> from calendarserver.tools.util import (
</span><del>-    booleanArgument, proxySubprincipal, action_addProxyPrincipal,
-    principalForPrincipalID, prettyPrincipal, ProxyError,
-    action_removeProxyPrincipal
</del><ins>+    booleanArgument, proxySubprincipal,
+    recordForPrincipalID, prettyPrincipal, prettyRecord, ProxyError
</ins><span class="cx"> )
</span><span class="cx"> from twistedcaldav.directory.augment import allowedAutoScheduleModes
</span><span class="cx"> 
</span><span class="lines">@@ -74,10 +73,10 @@
</span><span class="cx">     print(&quot;  --search &lt;search-string&gt;: search for matching principals&quot;)
</span><span class="cx">     print(&quot;  --list-principal-types: list all of the known principal types&quot;)
</span><span class="cx">     print(&quot;  --list-principals type: list all principals of the given type&quot;)
</span><del>-    print(&quot;  --read-property=property: read DAV property (eg.: {DAV:}group-member-set)&quot;)
</del><span class="cx">     print(&quot;  --list-read-proxies: list proxies with read-only access&quot;)
</span><span class="cx">     print(&quot;  --list-write-proxies: list proxies with read-write access&quot;)
</span><span class="cx">     print(&quot;  --list-proxies: list all proxies&quot;)
</span><ins>+    print(&quot;  --list-proxy-for: principals this principal is a proxy for&quot;)
</ins><span class="cx">     print(&quot;  --add-read-proxy=principal: add a read-only proxy&quot;)
</span><span class="cx">     print(&quot;  --add-write-proxy=principal: add a read-write proxy&quot;)
</span><span class="cx">     print(&quot;  --remove-proxy=principal: remove a proxy&quot;)
</span><span class="lines">@@ -118,33 +117,32 @@
</span><span class="cx">         resource, directory, store, and whatever has been assigned to &quot;params&quot;.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if self.function is not None:
</span><del>-            rootResource = self.rootResource()
-            directory = rootResource.getDirectory()
-            yield self.function(rootResource, directory, self.store, *self.params)
</del><ins>+            yield self.function(self.store, *self.params)
</ins><span class="cx"> 
</span><span class="cx"> attrMap = {
</span><del>-    'GeneratedUID' : { 'attr' : 'guid', },
-    'RealName' : { 'attr' : 'fullName', },
-    'RecordName' : { 'attr' : 'shortNames', },
-    'AutoSchedule' : { 'attr' : 'autoSchedule', },
-    'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
</del><ins>+    'GeneratedUID': {'attr': 'guid', },
+    'RealName': {'attr': 'fullName', },
+    'RecordName': {'attr': 'shortNames', },
+    'AutoSchedule': {'attr': 'autoSchedule', },
+    'AutoAcceptGroup': {'attr': 'autoAcceptGroup', },
</ins><span class="cx"> 
</span><del>-    'Comment' : { 'extras' : True, 'attr' : 'comment', },
-    'Description' : { 'extras' : True, 'attr' : 'description', },
-    'Type' : { 'extras' : True, 'attr' : 'type', },
</del><ins>+    'Comment': {'extras': True, 'attr': 'comment', },
+    'Description': {'extras': True, 'attr': 'description', },
+    'Type': {'extras': True, 'attr': 'type', },
</ins><span class="cx"> 
</span><span class="cx">     # For &quot;Locations&quot;, i.e. scheduled spaces
</span><del>-    'Capacity' : { 'extras' : True, 'attr' : 'capacity', },
-    'Floor' : { 'extras' : True, 'attr' : 'floor', },
-    'AssociatedAddress' : { 'extras' : True, 'attr' : 'associatedAddress', },
</del><ins>+    'Capacity': {'extras': True, 'attr': 'capacity', },
+    'Floor': {'extras': True, 'attr': 'floor', },
+    'AssociatedAddress': {'extras': True, 'attr': 'associatedAddress', },
</ins><span class="cx"> 
</span><span class="cx">     # For &quot;Addresses&quot;, i.e. nonscheduled areas containing Locations
</span><del>-    'AbbreviatedName' : { 'extras' : True, 'attr' : 'abbreviatedName', },
-    'StreetAddress' : { 'extras' : True, 'attr' : 'streetAddress', },
-    'Geo' : { 'extras' : True, 'attr' : 'geo', },
</del><ins>+    'AbbreviatedName': {'extras': True, 'attr': 'abbreviatedName', },
+    'StreetAddress': {'extras': True, 'attr': 'streetAddress', },
+    'Geo': {'extras': True, 'attr': 'geo', },
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def main():
</span><span class="cx">     try:
</span><span class="cx">         (optargs, args) = getopt(
</span><span class="lines">@@ -156,10 +154,10 @@
</span><span class="cx">                 &quot;search=&quot;,
</span><span class="cx">                 &quot;list-principal-types&quot;,
</span><span class="cx">                 &quot;list-principals=&quot;,
</span><del>-                &quot;read-property=&quot;,
</del><span class="cx">                 &quot;list-read-proxies&quot;,
</span><span class="cx">                 &quot;list-write-proxies&quot;,
</span><span class="cx">                 &quot;list-proxies&quot;,
</span><ins>+                &quot;list-proxy-for&quot;,
</ins><span class="cx">                 &quot;add-read-proxy=&quot;,
</span><span class="cx">                 &quot;add-write-proxy=&quot;,
</span><span class="cx">                 &quot;remove-proxy=&quot;,
</span><span class="lines">@@ -185,7 +183,7 @@
</span><span class="cx">     # Get configuration
</span><span class="cx">     #
</span><span class="cx">     configFileName = None
</span><del>-    addType = None
</del><ins>+    # addType = None
</ins><span class="cx">     listPrincipalTypes = False
</span><span class="cx">     listPrincipals = None
</span><span class="cx">     searchPrincipals = None
</span><span class="lines">@@ -202,11 +200,11 @@
</span><span class="cx">         elif opt in (&quot;-f&quot;, &quot;--config&quot;):
</span><span class="cx">             configFileName = arg
</span><span class="cx"> 
</span><del>-        elif opt in (&quot;-a&quot;, &quot;--add&quot;):
-            addType = arg
</del><ins>+        # elif opt in (&quot;-a&quot;, &quot;--add&quot;):
+        #     addType = arg
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;-r&quot;, &quot;--remove&quot;):
-            principalActions.append((action_removePrincipal,))
</del><ins>+        # elif opt in (&quot;-r&quot;, &quot;--remove&quot;):
+        #     principalActions.append((action_removePrincipal,))
</ins><span class="cx"> 
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--list-principal-types&quot;):
</span><span class="cx">             listPrincipalTypes = True
</span><span class="lines">@@ -217,13 +215,6 @@
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--search&quot;):
</span><span class="cx">             searchPrincipals = arg
</span><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--read-property&quot;):
-            try:
-                qname = decodeXMLName(arg)
-            except ValueError, e:
-                abort(e)
-            principalActions.append((action_readProperty, qname))
-
</del><span class="cx">         elif opt in (&quot;&quot;, &quot;--list-read-proxies&quot;):
</span><span class="cx">             principalActions.append((action_listProxies, &quot;read&quot;))
</span><span class="cx"> 
</span><span class="lines">@@ -233,6 +224,9 @@
</span><span class="cx">         elif opt in (&quot;-L&quot;, &quot;--list-proxies&quot;):
</span><span class="cx">             principalActions.append((action_listProxies, &quot;read&quot;, &quot;write&quot;))
</span><span class="cx"> 
</span><ins>+        elif opt in (&quot;--list-proxy-for&quot;):
+            principalActions.append((action_listProxyFor, &quot;read&quot;, &quot;write&quot;))
+
</ins><span class="cx">         elif opt in (&quot;--add-read-proxy&quot;, &quot;--add-write-proxy&quot;):
</span><span class="cx">             if &quot;read&quot; in opt:
</span><span class="cx">                 proxyType = &quot;read&quot;
</span><span class="lines">@@ -242,7 +236,7 @@
</span><span class="cx">                 raise AssertionError(&quot;Unknown proxy type&quot;)
</span><span class="cx"> 
</span><span class="cx">             try:
</span><del>-                principalForPrincipalID(arg, checkOnly=True)
</del><ins>+                yield recordForPrincipalID(arg, checkOnly=True)
</ins><span class="cx">             except ValueError, e:
</span><span class="cx">                 abort(e)
</span><span class="cx"> 
</span><span class="lines">@@ -250,64 +244,64 @@
</span><span class="cx"> 
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--remove-proxy&quot;):
</span><span class="cx">             try:
</span><del>-                principalForPrincipalID(arg, checkOnly=True)
</del><ins>+                yield recordForPrincipalID(arg, checkOnly=True)
</ins><span class="cx">             except ValueError, e:
</span><span class="cx">                 abort(e)
</span><span class="cx"> 
</span><span class="cx">             principalActions.append((action_removeProxy, arg))
</span><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--set-auto-schedule&quot;):
-            try:
-                autoSchedule = booleanArgument(arg)
-            except ValueError, e:
-                abort(e)
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--set-auto-schedule&quot;):
+        #     try:
+        #         autoSchedule = booleanArgument(arg)
+        #     except ValueError, e:
+        #         abort(e)
</ins><span class="cx"> 
</span><del>-            principalActions.append((action_setAutoSchedule, autoSchedule))
</del><ins>+        #     principalActions.append((action_setAutoSchedule, autoSchedule))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--get-auto-schedule&quot;):
-            principalActions.append((action_getAutoSchedule,))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--get-auto-schedule&quot;):
+        #     principalActions.append((action_getAutoSchedule,))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--set-auto-schedule-mode&quot;):
-            try:
-                if arg not in allowedAutoScheduleModes:
-                    raise ValueError(&quot;Unknown auto-schedule mode: %s&quot; % (arg,))
-                autoScheduleMode = arg
-            except ValueError, e:
-                abort(e)
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--set-auto-schedule-mode&quot;):
+        #     try:
+        #         if arg not in allowedAutoScheduleModes:
+        #             raise ValueError(&quot;Unknown auto-schedule mode: %s&quot; % (arg,))
+        #         autoScheduleMode = arg
+        #     except ValueError, e:
+        #         abort(e)
</ins><span class="cx"> 
</span><del>-            principalActions.append((action_setAutoScheduleMode, autoScheduleMode))
</del><ins>+        #     principalActions.append((action_setAutoScheduleMode, autoScheduleMode))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--get-auto-schedule-mode&quot;):
-            principalActions.append((action_getAutoScheduleMode,))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--get-auto-schedule-mode&quot;):
+        #     principalActions.append((action_getAutoScheduleMode,))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--set-auto-accept-group&quot;):
-            try:
-                principalForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--set-auto-accept-group&quot;):
+        #     try:
+        #         yield recordForPrincipalID(arg, checkOnly=True)
+        #     except ValueError, e:
+        #         abort(e)
</ins><span class="cx"> 
</span><del>-            principalActions.append((action_setAutoAcceptGroup, arg))
</del><ins>+        #     principalActions.append((action_setAutoAcceptGroup, arg))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--get-auto-accept-group&quot;):
-            principalActions.append((action_getAutoAcceptGroup,))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--get-auto-accept-group&quot;):
+        #     principalActions.append((action_getAutoAcceptGroup,))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--set-geo&quot;):
-            principalActions.append((action_setValue, &quot;Geo&quot;, arg))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--set-geo&quot;):
+        #     principalActions.append((action_setValue, &quot;Geo&quot;, arg))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--get-geo&quot;):
-            principalActions.append((action_getValue, &quot;Geo&quot;))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--get-geo&quot;):
+        #     principalActions.append((action_getValue, &quot;Geo&quot;))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--set-street-address&quot;):
-            principalActions.append((action_setValue, &quot;StreetAddress&quot;, arg))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--set-street-address&quot;):
+        #     principalActions.append((action_setValue, &quot;StreetAddress&quot;, arg))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--get-street-address&quot;):
-            principalActions.append((action_getValue, &quot;StreetAddress&quot;))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--get-street-address&quot;):
+        #     principalActions.append((action_getValue, &quot;StreetAddress&quot;))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--set-address&quot;):
-            principalActions.append((action_setValue, &quot;AssociatedAddress&quot;, arg))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--set-address&quot;):
+        #     principalActions.append((action_setValue, &quot;AssociatedAddress&quot;, arg))
</ins><span class="cx"> 
</span><del>-        elif opt in (&quot;&quot;, &quot;--get-address&quot;):
-            principalActions.append((action_getValue, &quot;AssociatedAddress&quot;))
</del><ins>+        # elif opt in (&quot;&quot;, &quot;--get-address&quot;):
+        #     principalActions.append((action_getValue, &quot;AssociatedAddress&quot;))
</ins><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             raise NotImplementedError(opt)
</span><span class="lines">@@ -322,32 +316,34 @@
</span><span class="cx">         function = runListPrincipalTypes
</span><span class="cx">         params = ()
</span><span class="cx"> 
</span><del>-    elif addType:
</del><ins>+    # elif addType:
</ins><span class="cx"> 
</span><del>-        try:
-            addType = matchStrings(addType, [&quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;])
-        except ValueError, e:
-            print(e)
-            return
</del><ins>+    #     try:
+    #         addType = matchStrings(addType, [&quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;])
+    #     except ValueError, e:
+    #         print(e)
+    #         return
</ins><span class="cx"> 
</span><del>-        try:
-            fullName, shortName, guid = parseCreationArgs(args)
-        except ValueError, e:
-            print(e)
-            return
</del><ins>+    #     try:
+    #         fullName, shortName, guid = parseCreationArgs(args)
+    #     except ValueError, e:
+    #         print(e)
+    #         return
</ins><span class="cx"> 
</span><del>-        if shortName is not None:
-            shortNames = [shortName]
-        else:
-            shortNames = ()
</del><ins>+    #     if shortName is not None:
+    #         shortNames = [shortName]
+    #     else:
+    #         shortNames = ()
</ins><span class="cx"> 
</span><del>-        function = runAddPrincipal
-        params = (addType, guid, shortNames, fullName)
</del><ins>+    #     function = runAddPrincipal
+    #     params = (addType, guid, shortNames, fullName)
</ins><span class="cx"> 
</span><span class="cx">     elif listPrincipals:
</span><span class="cx">         try:
</span><del>-            listPrincipals = matchStrings(listPrincipals, [&quot;users&quot;, &quot;groups&quot;,
-                &quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;])
</del><ins>+            listPrincipals = matchStrings(
+                listPrincipals,
+                [&quot;users&quot;, &quot;groups&quot;, &quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;]
+            )
</ins><span class="cx">         except ValueError, e:
</span><span class="cx">             print(e)
</span><span class="cx">             return
</span><span class="lines">@@ -372,7 +368,7 @@
</span><span class="cx"> 
</span><span class="cx">         for arg in args:
</span><span class="cx">             try:
</span><del>-                principalForPrincipalID(arg, checkOnly=True)
</del><ins>+                yield recordForPrincipalID(arg, checkOnly=True)
</ins><span class="cx">             except ValueError, e:
</span><span class="cx">                 abort(e)
</span><span class="cx"> 
</span><span class="lines">@@ -385,17 +381,20 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def runListPrincipalTypes(service, rootResource, directory, store):
</del><ins>+def runListPrincipalTypes(service, store):
+    directory = store.directoryService()
</ins><span class="cx">     for recordType in directory.recordTypes():
</span><del>-        print(recordType)
</del><ins>+        print(directory.recordTypeToOldString(recordType))
</ins><span class="cx">     return succeed(None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def runListPrincipals(service, rootResource, directory, store, listPrincipals):
</del><ins>+def runListPrincipals(service, store, listPrincipals):
+    directory = store.directoryService()
+    recordType = directory.oldNameToRecordType(listPrincipals)
</ins><span class="cx">     try:
</span><del>-        records = list((yield directory.listRecords(listPrincipals)))
</del><ins>+        records = list((yield directory.recordsWithRecordType(recordType)))
</ins><span class="cx">         if records:
</span><span class="cx">             printRecordList(records)
</span><span class="cx">         else:
</span><span class="lines">@@ -407,51 +406,48 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def runPrincipalActions(service, rootResource, directory, store, principalIDs,
-    actions):
</del><ins>+def runPrincipalActions(service, store, principalIDs, actions):
+    directory = store.directoryService()
</ins><span class="cx">     for principalID in principalIDs:
</span><del>-        # Resolve the given principal IDs to principals
</del><ins>+        # Resolve the given principal IDs to records
</ins><span class="cx">         try:
</span><del>-            principal = yield principalForPrincipalID(principalID, directory=directory)
</del><ins>+            record = yield recordForPrincipalID(
+                principalID, directory=directory
+            )
</ins><span class="cx">         except ValueError:
</span><del>-            principal = None
</del><ins>+            record = None
</ins><span class="cx"> 
</span><del>-        if principal is None:
</del><ins>+        if record is None:
</ins><span class="cx">             sys.stderr.write(&quot;Invalid principal ID: %s\n&quot; % (principalID,))
</span><span class="cx">             continue
</span><span class="cx"> 
</span><span class="cx">         # Performs requested actions
</span><span class="cx">         for action in actions:
</span><del>-            (yield action[0](rootResource, directory, store, principal,
-                *action[1:]))
</del><ins>+            (yield action[0](store, record, *action[1:]))
</ins><span class="cx">             print(&quot;&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def runSearch(service, rootResource, directory, store, searchTerm):
-
</del><ins>+def runSearch(service, store, searchTerm):
+    directory = store.directoryService()
</ins><span class="cx">     fields = []
</span><del>-    for fieldName in (&quot;fullName&quot;, &quot;firstName&quot;, &quot;lastName&quot;, &quot;emailAddresses&quot;):
</del><ins>+    for fieldName in (&quot;fullNames&quot;, &quot;emailAddresses&quot;):
</ins><span class="cx">         fields.append((fieldName, searchTerm, True, &quot;contains&quot;))
</span><span class="cx"> 
</span><span class="cx">     records = list((yield directory.recordsMatchingTokens(searchTerm.strip().split())))
</span><span class="cx">     if records:
</span><del>-        records.sort(key=operator.attrgetter('fullName'))
</del><ins>+        records.sort(key=operator.attrgetter('fullNames'))
</ins><span class="cx">         print(&quot;%d matches found:&quot; % (len(records),))
</span><span class="cx">         for record in records:
</span><del>-            print(&quot;\n%s (%s)&quot; % (record.fullName,
-                {&quot;users&quot; : &quot;User&quot;,
-                 &quot;groups&quot; : &quot;Group&quot;,
-                 &quot;locations&quot; : &quot;Place&quot;,
-                 &quot;resources&quot; : &quot;Resource&quot;,
-                 &quot;addresses&quot; : &quot;Address&quot;,
-                }.get(record.recordType),
-            ))
-            print(&quot;   GUID: %s&quot; % (record.guid,))
</del><ins>+            print(
+                &quot;\n{d} {rt}&quot;.format(
+                    d=record.displayName,
+                    rt=record.recordType.name
+                )
+            )
+            print(&quot;   UID: %s&quot; % (record.uid,))
</ins><span class="cx">             print(&quot;   Record name(s): %s&quot; % (&quot;, &quot;.join(record.shortNames),))
</span><del>-            if record.authIDs:
-                print(&quot;   Auth ID(s): %s&quot; % (&quot;, &quot;.join(record.authIDs),))
</del><span class="cx">             if record.emailAddresses:
</span><span class="cx">                 print(&quot;   Email(s): %s&quot; % (&quot;, &quot;.join(record.emailAddresses),))
</span><span class="cx">     else:
</span><span class="lines">@@ -461,292 +457,318 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-@inlineCallbacks
-def runAddPrincipal(service, rootResource, directory, store, addType, guid,
-    shortNames, fullName):
-    try:
-        yield updateRecord(True, directory, addType, guid=guid,
-            shortNames=shortNames, fullName=fullName)
-        print(&quot;Added '%s'&quot; % (fullName,))
-    except DirectoryError, e:
-        print(e)
</del><ins>+# @inlineCallbacks
+# def runAddPrincipal(service, store, addType, guid, shortNames, fullName):
+#     directory = store.directoryService()
+#     try:
+#         # FIXME STOP USING GUID
+#         yield updateRecord(
+#             True, directory, addType, guid=guid,
+#             shortNames=shortNames, fullName=fullName
+#         )
+#         print(&quot;Added '%s'&quot; % (fullName,))
+#     except DirectoryError, e:
+#         print(e)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def action_removePrincipal(rootResource, directory, store, principal):
-    record = principal.record
-    fullName = record.fullName
-    shortName = record.shortNames[0]
-    guid = record.guid
</del><ins>+# def action_removePrincipal(store, record):
+#     directory = store.directoryService()
+#     fullName = record.displayName
+#     shortName = record.shortNames[0]
</ins><span class="cx"> 
</span><del>-    directory.destroyRecord(record.recordType, guid=guid)
-    print(&quot;Removed '%s' %s %s&quot; % (fullName, shortName, guid))
</del><ins>+#     yield directory.destroyRecord(record.recordType, uid=record.uid)
+#     print(&quot;Removed '%s' %s %s&quot; % (fullName, shortName, record.uid))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def action_readProperty(rootResource, directory, store, resource, qname):
-    property = (yield resource.readProperty(qname, None))
-    print(&quot;%r on %s:&quot; % (encodeXMLName(*qname), resource))
-    print(&quot;&quot;)
-    print(property.toxml())
</del><ins>+def action_listProxies(store, record, *proxyTypes):
+    directory = store.directoryService()
+    for proxyType in proxyTypes:
</ins><span class="cx"> 
</span><ins>+        groupRecordType = {
+            &quot;read&quot;: directory.recordType.readDelegateGroup,
+            &quot;write&quot;: directory.recordType.writeDelegateGroup,
+        }.get(proxyType)
</ins><span class="cx"> 
</span><ins>+        pseudoGroup = yield directory.recordWithShortName(
+            groupRecordType,
+            record.uid
+        )
+        proxies = yield pseudoGroup.members()
+        if proxies:
+            print(&quot;%s proxies for %s:&quot; % (
+                {&quot;read&quot;: &quot;Read-only&quot;, &quot;write&quot;: &quot;Read/write&quot;}[proxyType],
+                prettyRecord(record)
+            ))
+            printRecordList(proxies)
+            print(&quot;&quot;)
+        else:
+            print(&quot;No %s proxies for %s&quot; % (proxyType, prettyRecord(record)))
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def action_listProxies(rootResource, directory, store, principal, *proxyTypes):
</del><ins>+def action_listProxyFor(store, record, *proxyTypes):
+    directory = store.directoryService()
</ins><span class="cx">     for proxyType in proxyTypes:
</span><del>-        subPrincipal = proxySubprincipal(principal, proxyType)
-        if subPrincipal is None:
-            print(&quot;No %s proxies for %s&quot; % (proxyType,
-                prettyPrincipal(principal)))
-            continue
</del><span class="cx"> 
</span><del>-        membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
</del><ins>+        groupRecordType = {
+            &quot;read&quot;: directory.recordType.readDelegatorGroup,
+            &quot;write&quot;: directory.recordType.writeDelegatorGroup,
+        }.get(proxyType)
</ins><span class="cx"> 
</span><del>-        if membersProperty.children:
-            print(&quot;%s proxies for %s:&quot; % (
-                {&quot;read&quot;: &quot;Read-only&quot;, &quot;write&quot;: &quot;Read/write&quot;}[proxyType],
-                prettyPrincipal(principal)
</del><ins>+        pseudoGroup = yield directory.recordWithShortName(
+            groupRecordType,
+            record.uid
+        )
+        proxies = yield pseudoGroup.members()
+        if proxies:
+            print(&quot;%s is a %s proxy for:&quot; % (
+                prettyRecord(record),
+                {&quot;read&quot;: &quot;Read-only&quot;, &quot;write&quot;: &quot;Read/write&quot;}[proxyType]
</ins><span class="cx">             ))
</span><del>-            records = []
-            for member in membersProperty.children:
-                proxyPrincipal = principalForPrincipalID(str(member),
-                    directory=directory)
-                records.append(proxyPrincipal.record)
-
-            printRecordList(records)
-            print
</del><ins>+            printRecordList(proxies)
+            print(&quot;&quot;)
</ins><span class="cx">         else:
</span><del>-            print(&quot;No %s proxies for %s&quot; % (proxyType,
-                prettyPrincipal(principal)))
</del><ins>+            print(
+                &quot;{r} is not a {t} proxy for anyone&quot;.format(
+                    r=prettyRecord(record),
+                    t={&quot;read&quot;: &quot;Read-only&quot;, &quot;write&quot;: &quot;Read/write&quot;}[proxyType]
+                )
+            )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> @inlineCallbacks
</span><del>-def action_addProxy(rootResource, directory, store, principal, proxyType, *proxyIDs):
</del><ins>+def _addRemoveProxy(fn, store, record, proxyType, *proxyIDs):
+    directory = store.directoryService()
+    readWrite = (proxyType == &quot;write&quot;)
</ins><span class="cx">     for proxyID in proxyIDs:
</span><del>-        proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
-        if proxyPrincipal is None:
</del><ins>+        proxyRecord = yield recordForPrincipalID(proxyID, directory=directory)
+        if proxyRecord is None:
</ins><span class="cx">             print(&quot;Invalid principal ID: %s&quot; % (proxyID,))
</span><span class="cx">         else:
</span><del>-            (yield action_addProxyPrincipal(rootResource, directory, store,
-                principal, proxyType, proxyPrincipal))
</del><ins>+            txn = store.newTransaction()
+            yield fn(txn, record, proxyRecord, readWrite)
+            yield txn.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def action_addProxy(store, record, proxyType, *proxyIDs):
+    return _addRemoveProxy(addDelegate, store, record, proxyType, *proxyIDs)
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def setProxies(store, principal, readProxyPrincipals, writeProxyPrincipals, directory=None):
-    &quot;&quot;&quot;
-    Set read/write proxies en masse for a principal
-    @param principal: DirectoryPrincipalResource
-    @param readProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
-    @param writeProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
-    &quot;&quot;&quot;
</del><ins>+def action_removeProxy(store, record, *proxyIDs):
+    # Write
+    yield _addRemoveProxy(removeDelegate, store, record, &quot;write&quot;, *proxyIDs)
+    # Read
+    yield _addRemoveProxy(removeDelegate, store, record, &quot;read&quot;, *proxyIDs)
</ins><span class="cx"> 
</span><del>-    proxyTypes = [
-        (&quot;read&quot;, readProxyPrincipals),
-        (&quot;write&quot;, writeProxyPrincipals),
-    ]
-    for proxyType, proxyIDs in proxyTypes:
-        if proxyIDs is None:
-            continue
-        subPrincipal = proxySubprincipal(principal, proxyType)
-        if subPrincipal is None:
-            raise ProxyError(&quot;Unable to edit %s proxies for %s\n&quot; % (proxyType,
-                prettyPrincipal(principal)))
-        memberURLs = []
-        for proxyID in proxyIDs:
-            proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
-            proxyURL = proxyPrincipal.url()
-            memberURLs.append(davxml.HRef(proxyURL))
-        membersProperty = davxml.GroupMemberSet(*memberURLs)
-        yield subPrincipal.writeProperty(membersProperty, None)
-        if store is not None:
-            # Schedule work the PeerConnectionPool will pick up as overdue
-            yield schedulePolledGroupCachingUpdate(store)
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+# @inlineCallbacks
+# def setProxies(store, principal, readProxyPrincipals, writeProxyPrincipals, directory=None):
+#     &quot;&quot;&quot;
+#     Set read/write proxies en masse for a principal
+#     @param principal: DirectoryPrincipalResource
+#     @param readProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
+#     @param writeProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
+#     &quot;&quot;&quot;
</ins><span class="cx"> 
</span><del>-@inlineCallbacks
-def getProxies(principal, directory=None):
-    &quot;&quot;&quot;
-    Returns a tuple containing the GUIDs for read proxies and write proxies
-    of the given principal
-    &quot;&quot;&quot;
</del><ins>+#     proxyTypes = [
+#         (&quot;read&quot;, readProxyPrincipals),
+#         (&quot;write&quot;, writeProxyPrincipals),
+#     ]
+#     for proxyType, proxyIDs in proxyTypes:
+#         if proxyIDs is None:
+#             continue
+#         subPrincipal = proxySubprincipal(principal, proxyType)
+#         if subPrincipal is None:
+#             raise ProxyError(&quot;Unable to edit %s proxies for %s\n&quot; % (proxyType,
+#                 prettyPrincipal(principal)))
+#         memberURLs = []
+#         for proxyID in proxyIDs:
+#             proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
+#             proxyURL = proxyPrincipal.url()
+#             memberURLs.append(davxml.HRef(proxyURL))
+#         membersProperty = davxml.GroupMemberSet(*memberURLs)
+#         yield subPrincipal.writeProperty(membersProperty, None)
+#         if store is not None:
+#             # Schedule work the PeerConnectionPool will pick up as overdue
+#             yield schedulePolledGroupCachingUpdate(store)
</ins><span class="cx"> 
</span><del>-    proxies = {
-        &quot;read&quot; : [],
-        &quot;write&quot; : [],
-    }
-    for proxyType in proxies.iterkeys():
-        subPrincipal = proxySubprincipal(principal, proxyType)
-        if subPrincipal is not None:
-            membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
-            if membersProperty.children:
-                for member in membersProperty.children:
-                    proxyPrincipal = yield principalForPrincipalID(str(member), directory=directory)
-                    proxies[proxyType].append(proxyPrincipal.record.guid)
</del><span class="cx"> 
</span><del>-    returnValue((proxies['read'], proxies['write']))
</del><span class="cx"> 
</span><ins>+# @inlineCallbacks
+# def getProxies(principal, directory=None):
+#     &quot;&quot;&quot;
+#     Returns a tuple containing the GUIDs for read proxies and write proxies
+#     of the given principal
+#     &quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+#     proxies = {
+#         &quot;read&quot;: [],
+#         &quot;write&quot;: [],
+#     }
+#     for proxyType in proxies.iterkeys():
+#         subPrincipal = proxySubprincipal(principal, proxyType)
+#         if subPrincipal is not None:
+#             membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+#             if membersProperty.children:
+#                 for member in membersProperty.children:
+#                     proxyPrincipal = yield principalForPrincipalID(str(member), directory=directory)
+#                     proxies[proxyType].append(proxyPrincipal.record.guid)
</ins><span class="cx"> 
</span><del>-@inlineCallbacks
-def action_removeProxy(rootResource, directory, store, principal, *proxyIDs, **kwargs):
-    for proxyID in proxyIDs:
-        proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
-        if proxyPrincipal is None:
-            print(&quot;Invalid principal ID: %s&quot; % (proxyID,))
-        else:
-            (yield action_removeProxyPrincipal(rootResource, directory, store,
-                principal, proxyPrincipal, **kwargs))
</del><ins>+#     returnValue((proxies['read'], proxies['write']))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-@inlineCallbacks
-def action_setAutoSchedule(rootResource, directory, store, principal, autoSchedule):
-    if principal.record.recordType == &quot;groups&quot;:
-        print(&quot;Enabling auto-schedule for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-    elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
-        print(&quot;Enabling auto-schedule for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-    else:
-        print(&quot;Setting auto-schedule to %s for %s&quot; % (
-            {True: &quot;true&quot;, False: &quot;false&quot;}[autoSchedule],
-            prettyPrincipal(principal),
-        ))
</del><ins>+# @inlineCallbacks
+# def action_setAutoSchedule(rootResource, directory, store, principal, autoSchedule):
+#     if principal.record.recordType == &quot;groups&quot;:
+#         print(&quot;Enabling auto-schedule for %s is not allowed.&quot; % (principal,))
</ins><span class="cx"> 
</span><del>-        (yield updateRecord(False, directory,
-            principal.record.recordType,
-            guid=principal.record.guid,
-            shortNames=principal.record.shortNames,
-            fullName=principal.record.fullName,
-            autoSchedule=autoSchedule,
-            **principal.record.extras
-        ))
</del><ins>+#     elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+#         print(&quot;Enabling auto-schedule for %s is not allowed.&quot; % (principal,))
</ins><span class="cx"> 
</span><ins>+#     else:
+#         print(&quot;Setting auto-schedule to %s for %s&quot; % (
+#             {True: &quot;true&quot;, False: &quot;false&quot;}[autoSchedule],
+#             prettyPrincipal(principal),
+#         ))
</ins><span class="cx"> 
</span><ins>+#         (yield updateRecord(False, directory,
+#             principal.record.recordType,
+#             guid=principal.record.guid,
+#             shortNames=principal.record.shortNames,
+#             fullName=principal.record.fullName,
+#             autoSchedule=autoSchedule,
+#             **principal.record.extras
+#         ))
</ins><span class="cx"> 
</span><del>-def action_getAutoSchedule(rootResource, directory, store, principal):
-    autoSchedule = principal.getAutoSchedule()
-    print(&quot;Auto-schedule for %s is %s&quot; % (
-        prettyPrincipal(principal),
-        {True: &quot;true&quot;, False: &quot;false&quot;}[autoSchedule],
-    ))
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+# def action_getAutoSchedule(rootResource, directory, store, principal):
+#     autoSchedule = principal.getAutoSchedule()
+#     print(&quot;Auto-schedule for %s is %s&quot; % (
+#         prettyPrincipal(principal),
+#         {True: &quot;true&quot;, False: &quot;false&quot;}[autoSchedule],
+#     ))
</ins><span class="cx"> 
</span><del>-@inlineCallbacks
-def action_setAutoScheduleMode(rootResource, directory, store, principal, autoScheduleMode):
-    if principal.record.recordType == &quot;groups&quot;:
-        print(&quot;Setting auto-schedule mode for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-    elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
-        print(&quot;Setting auto-schedule mode for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-    else:
-        print(&quot;Setting auto-schedule mode to %s for %s&quot; % (
-            autoScheduleMode,
-            prettyPrincipal(principal),
-        ))
</del><ins>+# @inlineCallbacks
+# def action_setAutoScheduleMode(rootResource, directory, store, principal, autoScheduleMode):
+#     if principal.record.recordType == &quot;groups&quot;:
+#         print(&quot;Setting auto-schedule mode for %s is not allowed.&quot; % (principal,))
</ins><span class="cx"> 
</span><del>-        (yield updateRecord(False, directory,
-            principal.record.recordType,
-            guid=principal.record.guid,
-            shortNames=principal.record.shortNames,
-            fullName=principal.record.fullName,
-            autoScheduleMode=autoScheduleMode,
-            **principal.record.extras
-        ))
</del><ins>+#     elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+#         print(&quot;Setting auto-schedule mode for %s is not allowed.&quot; % (principal,))
</ins><span class="cx"> 
</span><ins>+#     else:
+#         print(&quot;Setting auto-schedule mode to %s for %s&quot; % (
+#             autoScheduleMode,
+#             prettyPrincipal(principal),
+#         ))
</ins><span class="cx"> 
</span><ins>+#         (yield updateRecord(False, directory,
+#             principal.record.recordType,
+#             guid=principal.record.guid,
+#             shortNames=principal.record.shortNames,
+#             fullName=principal.record.fullName,
+#             autoScheduleMode=autoScheduleMode,
+#             **principal.record.extras
+#         ))
</ins><span class="cx"> 
</span><del>-def action_getAutoScheduleMode(rootResource, directory, store, principal):
-    autoScheduleMode = principal.getAutoScheduleMode()
-    if not autoScheduleMode:
-        autoScheduleMode = &quot;automatic&quot;
-    print(&quot;Auto-schedule mode for %s is %s&quot; % (
-        prettyPrincipal(principal),
-        autoScheduleMode,
-    ))
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+# def action_getAutoScheduleMode(rootResource, directory, store, principal):
+#     autoScheduleMode = principal.getAutoScheduleMode()
+#     if not autoScheduleMode:
+#         autoScheduleMode = &quot;automatic&quot;
+#     print(&quot;Auto-schedule mode for %s is %s&quot; % (
+#         prettyPrincipal(principal),
+#         autoScheduleMode,
+#     ))
</ins><span class="cx"> 
</span><del>-@inlineCallbacks
-def action_setAutoAcceptGroup(rootResource, directory, store, principal, autoAcceptGroup):
-    if principal.record.recordType == &quot;groups&quot;:
-        print(&quot;Setting auto-accept-group for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-    elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
-        print(&quot;Setting auto-accept-group for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-    else:
-        groupPrincipal = yield principalForPrincipalID(autoAcceptGroup, directory=directory)
-        if groupPrincipal is None or groupPrincipal.record.recordType != &quot;groups&quot;:
-            print(&quot;Invalid principal ID: %s&quot; % (autoAcceptGroup,))
-        else:
-            print(&quot;Setting auto-accept-group to %s for %s&quot; % (
-                prettyPrincipal(groupPrincipal),
-                prettyPrincipal(principal),
-            ))
</del><ins>+# @inlineCallbacks
+# def action_setAutoAcceptGroup(rootResource, directory, store, principal, autoAcceptGroup):
+#     if principal.record.recordType == &quot;groups&quot;:
+#         print(&quot;Setting auto-accept-group for %s is not allowed.&quot; % (principal,))
</ins><span class="cx"> 
</span><del>-            (yield updateRecord(False, directory,
-                principal.record.recordType,
-                guid=principal.record.guid,
-                shortNames=principal.record.shortNames,
-                fullName=principal.record.fullName,
-                autoAcceptGroup=groupPrincipal.record.guid,
-                **principal.record.extras
-            ))
</del><ins>+#     elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+#         print(&quot;Setting auto-accept-group for %s is not allowed.&quot; % (principal,))
</ins><span class="cx"> 
</span><ins>+#     else:
+#         groupPrincipal = yield principalForPrincipalID(autoAcceptGroup, directory=directory)
+#         if groupPrincipal is None or groupPrincipal.record.recordType != &quot;groups&quot;:
+#             print(&quot;Invalid principal ID: %s&quot; % (autoAcceptGroup,))
+#         else:
+#             print(&quot;Setting auto-accept-group to %s for %s&quot; % (
+#                 prettyPrincipal(groupPrincipal),
+#                 prettyPrincipal(principal),
+#             ))
</ins><span class="cx"> 
</span><ins>+#             (yield updateRecord(False, directory,
+#                 principal.record.recordType,
+#                 guid=principal.record.guid,
+#                 shortNames=principal.record.shortNames,
+#                 fullName=principal.record.fullName,
+#                 autoAcceptGroup=groupPrincipal.record.guid,
+#                 **principal.record.extras
+#             ))
</ins><span class="cx"> 
</span><del>-def action_getAutoAcceptGroup(rootResource, directory, store, principal):
-    autoAcceptGroup = principal.getAutoAcceptGroup()
-    if autoAcceptGroup:
-        record = yield directory.recordWithGUID(autoAcceptGroup)
-        if record is not None:
-            groupPrincipal = yield directory.principalCollection.principalForUID(record.uid)
-            if groupPrincipal is not None:
-                print(&quot;Auto-accept-group for %s is %s&quot; % (
-                    prettyPrincipal(principal),
-                    prettyPrincipal(groupPrincipal),
-                ))
-                return
-        print(&quot;Invalid auto-accept-group assigned: %s&quot; % (autoAcceptGroup,))
-    else:
-        print(&quot;No auto-accept-group assigned to %s&quot; % (prettyPrincipal(principal),))
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+# def action_getAutoAcceptGroup(rootResource, directory, store, principal):
+#     autoAcceptGroup = principal.getAutoAcceptGroup()
+#     if autoAcceptGroup:
+#         record = yield directory.recordWithGUID(autoAcceptGroup)
+#         if record is not None:
+#             groupPrincipal = yield directory.principalCollection.principalForUID(record.uid)
+#             if groupPrincipal is not None:
+#                 print(&quot;Auto-accept-group for %s is %s&quot; % (
+#                     prettyPrincipal(principal),
+#                     prettyPrincipal(groupPrincipal),
+#                 ))
+#                 return
+#         print(&quot;Invalid auto-accept-group assigned: %s&quot; % (autoAcceptGroup,))
+#     else:
+#         print(&quot;No auto-accept-group assigned to %s&quot; % (prettyPrincipal(principal),))
</ins><span class="cx"> 
</span><del>-@inlineCallbacks
-def action_setValue(rootResource, directory, store, principal, name, value):
-    print(&quot;Setting %s to %s for %s&quot; % (
-        name, value, prettyPrincipal(principal),
-    ))
</del><span class="cx"> 
</span><del>-    principal.record.extras[attrMap[name][&quot;attr&quot;]] = value
-    (yield updateRecord(False, directory,
-        principal.record.recordType,
-        guid=principal.record.guid,
-        shortNames=principal.record.shortNames,
-        fullName=principal.record.fullName,
-        **principal.record.extras
-    ))
</del><span class="cx"> 
</span><ins>+# @inlineCallbacks
+# def action_setValue(rootResource, directory, store, principal, name, value):
+#     print(&quot;Setting %s to %s for %s&quot; % (
+#         name, value, prettyPrincipal(principal),
+#     ))
</ins><span class="cx"> 
</span><ins>+#     principal.record.extras[attrMap[name][&quot;attr&quot;]] = value
+#     (yield updateRecord(False, directory,
+#         principal.record.recordType,
+#         guid=principal.record.guid,
+#         shortNames=principal.record.shortNames,
+#         fullName=principal.record.fullName,
+#         **principal.record.extras
+#     ))
</ins><span class="cx"> 
</span><del>-def action_getValue(rootResource, directory, store, principal, name):
-    print(&quot;%s for %s is %s&quot; % (
-        name,
-        prettyPrincipal(principal),
-        principal.record.extras[attrMap[name][&quot;attr&quot;]]
-    ))
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+# def action_getValue(rootResource, directory, store, principal, name):
+#     print(&quot;%s for %s is %s&quot; % (
+#         name,
+#         prettyPrincipal(principal),
+#         principal.record.extras[attrMap[name][&quot;attr&quot;]]
+#     ))
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> def abort(msg, status=1):
</span><span class="cx">     sys.stdout.write(&quot;%s\n&quot; % (msg,))
</span><span class="cx">     try:
</span><span class="lines">@@ -804,14 +826,16 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> def printRecordList(records):
</span><del>-    results = [(record.fullName, record.shortNames[0], record.guid)
-        for record in records]
</del><ins>+    results = [
+        (record.displayName, record.recordType.name, record.uid, record.shortNames)
+        for record in records
+    ]
</ins><span class="cx">     results.sort()
</span><del>-    format = &quot;%-22s %-17s %s&quot;
-    print(format % (&quot;Full name&quot;, &quot;Record name&quot;, &quot;UUID&quot;))
-    print(format % (&quot;---------&quot;, &quot;-----------&quot;, &quot;----&quot;))
-    for fullName, shortName, guid in results:
-        print(format % (fullName, shortName, guid))
</del><ins>+    format = &quot;%-22s %-10s %-20s %s&quot;
+    print(format % (&quot;Full name&quot;, &quot;Type&quot;, &quot;UID&quot;, &quot;Short names&quot;))
+    print(format % (&quot;---------&quot;, &quot;----&quot;, &quot;---&quot;, &quot;-----------&quot;))
+    for fullName, recordType, uid, shortNames in results:
+        print(format % (fullName, recordType, uid, u&quot;, &quot;.join(shortNames)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertoolsutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -334,6 +334,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def principalForPrincipalID(principalID, checkOnly=False, directory=None):
</span><span class="cx"> 
</span><span class="cx">     # Allow a directory parameter to be passed in, but default to config.directory
</span><span class="lines">@@ -351,16 +352,16 @@
</span><span class="cx">             raise ValueError(&quot;Can't resolve all paths yet&quot;)
</span><span class="cx"> 
</span><span class="cx">         if checkOnly:
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx"> 
</span><del>-        return directory.principalCollection.principalForUID(uid)
</del><ins>+        returnValue((yield directory.principalCollection.principalForUID(uid)))
</ins><span class="cx"> 
</span><span class="cx">     if principalID.startswith(&quot;(&quot;):
</span><span class="cx">         try:
</span><span class="cx">             i = principalID.index(&quot;)&quot;)
</span><span class="cx"> 
</span><span class="cx">             if checkOnly:
</span><del>-                return None
</del><ins>+                returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">             recordType = principalID[1:i]
</span><span class="cx">             shortName = principalID[i + 1:]
</span><span class="lines">@@ -368,34 +369,93 @@
</span><span class="cx">             if not recordType or not shortName or &quot;(&quot; in recordType:
</span><span class="cx">                 raise ValueError()
</span><span class="cx"> 
</span><del>-            return directory.principalCollection.principalForShortName(recordType, shortName)
</del><ins>+            returnValue((yield directory.principalCollection.principalForShortName(recordType, shortName)))
</ins><span class="cx"> 
</span><span class="cx">         except ValueError:
</span><span class="cx">             pass
</span><span class="cx"> 
</span><span class="cx">     if &quot;:&quot; in principalID:
</span><span class="cx">         if checkOnly:
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">         recordType, shortName = principalID.split(&quot;:&quot;, 1)
</span><span class="cx"> 
</span><del>-        return directory.principalCollection.principalForShortName(recordType, shortName)
</del><ins>+        returnValue((yield directory.principalCollection.principalForShortName(recordType, shortName)))
</ins><span class="cx"> 
</span><span class="cx">     try:
</span><span class="cx">         UUID(principalID)
</span><span class="cx"> 
</span><span class="cx">         if checkOnly:
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx"> 
</span><del>-        x = directory.principalCollection.principalForUID(principalID)
-        return x
</del><ins>+        returnValue((yield directory.principalCollection.principalForUID(principalID)))
</ins><span class="cx">     except ValueError:
</span><span class="cx">         pass
</span><span class="cx"> 
</span><span class="cx">     raise ValueError(&quot;Invalid principal identifier: %s&quot; % (principalID,))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
+def recordForPrincipalID(principalID, checkOnly=False, directory=None):
</ins><span class="cx"> 
</span><ins>+    # Allow a directory parameter to be passed in, but default to config.directory
+    # But config.directory isn't set right away, so only use it when we're doing more
+    # than checking.
+    if not checkOnly and not directory:
+        directory = config.directory
+
+    if principalID.startswith(&quot;/&quot;):
+        segments = principalID.strip(&quot;/&quot;).split(&quot;/&quot;)
+        if (len(segments) == 3 and
+            segments[0] == &quot;principals&quot; and segments[1] == &quot;__uids__&quot;):
+            uid = segments[2]
+        else:
+            raise ValueError(&quot;Can't resolve all paths yet&quot;)
+
+        if checkOnly:
+            returnValue(None)
+
+        returnValue((yield directory.recordWithUID(uid)))
+
+    if principalID.startswith(&quot;(&quot;):
+        try:
+            i = principalID.index(&quot;)&quot;)
+
+            if checkOnly:
+                returnValue(None)
+
+            recordType = directory.oldNameToRecordType(principalID[1:i])
+            shortName = principalID[i + 1:]
+
+            if not recordType or not shortName or &quot;(&quot; in recordType:
+                raise ValueError()
+
+            returnValue((yield directory.recordWithShortName(recordType, shortName)))
+
+        except ValueError:
+            pass
+
+    if &quot;:&quot; in principalID:
+        if checkOnly:
+            returnValue(None)
+
+        recordType, shortName = principalID.split(&quot;:&quot;, 1)
+        recordType = directory.oldNameToRecordType(recordType)
+
+        returnValue((yield directory.recordWithShortName(recordType, shortName)))
+
+    try:
+        if checkOnly:
+            returnValue(None)
+
+        returnValue((yield directory.recordWithUID(principalID)))
+    except ValueError:
+        pass
+
+    raise ValueError(&quot;Invalid principal identifier: %s&quot; % (principalID,))
+
+
+
</ins><span class="cx"> def proxySubprincipal(principal, proxyType):
</span><span class="cx">     return principal.getChild(&quot;calendar-proxy-&quot; + proxyType)
</span><span class="cx"> 
</span><span class="lines">@@ -501,12 +561,19 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> def prettyPrincipal(principal):
</span><del>-    record = principal.record
-    return &quot;\&quot;%s\&quot; (%s:%s)&quot; % (record.fullName, record.recordType,
-        record.shortNames[0])
</del><ins>+    prettyRecord(principal.record)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def prettyRecord(record):
+    return &quot;\&quot;{d}\&quot; {uid} ({rt}) {sn}&quot;.format(
+        d=record.displayName,
+        rt=record.recordType.name,
+        uid=record.uid,
+        sn=(&quot;, &quot;.join(record.shortNames))
+    )
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class ProxyError(Exception):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Raised when proxy assignments cannot be performed
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavdpsclientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -29,12 +29,12 @@
</span><span class="cx"> from twisted.internet.protocol import ClientCreator
</span><span class="cx"> from twisted.protocols import amp
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
</del><span class="cx"> from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryRecord
</span><span class="cx"> from txdav.common.idirectoryservice import IStoreDirectoryService
</span><span class="cx"> from txdav.dps.commands import (
</span><span class="cx">     RecordWithShortNameCommand, RecordWithUIDCommand, RecordWithGUIDCommand,
</span><span class="cx">     RecordsWithRecordTypeCommand, RecordsWithEmailAddressCommand,
</span><ins>+    RecordsMatchingTokensCommand,
</ins><span class="cx">     MembersCommand, GroupsCommand, SetMembersCommand,
</span><span class="cx">     VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand
</span><span class="cx"> )
</span><span class="lines">@@ -239,13 +239,15 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def listRecords(self, recordType):
-        # MOVE2WHO
-        return []
</del><ins>+    # def listRecords(self, recordType):
+    #     # MOVE2WHO
+    #     return []
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def recordWithCalendarUserAddress(self, address):
</span><ins>+        # FIXME: Circular
+        from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
</ins><span class="cx">         address = normalizeCUAddr(address)
</span><span class="cx">         record = None
</span><span class="cx">         if address.startswith(&quot;urn:uuid:&quot;):
</span><span class="lines">@@ -270,18 +272,27 @@
</span><span class="cx">         returnValue(record if record and record.hasCalendars else None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @inlineCallbacks
</del><span class="cx">     def recordsMatchingTokens(self, tokens, context=None, limitResults=50,
</span><span class="cx">                               timeoutSeconds=10):
</span><del>-        rec = yield self.recordWithShortName(
-            twext.who.idirectory.RecordType.user,
-            u&quot;wsanchez&quot;
</del><ins>+        return self._call(
+            RecordsMatchingTokensCommand,
+            self._processMultipleRecords,
+            tokens=[t.encode(&quot;utf-8&quot;) for t in tokens],
+            context=context
</ins><span class="cx">         )
</span><del>-        returnValue([rec])
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    # FIXME: Existing code assumes record type names are plural. Is there any
+    # reason to maintain backwards compatibility?  I suppose there could be
+    # scripts referring to record type of &quot;users&quot;, &quot;locations&quot;
+    def recordTypeToOldName(self, recordType):
+        return recordType.name + u&quot;s&quot;
</ins><span class="cx"> 
</span><ins>+    def oldNameToRecordType(self, oldName):
+        return self.recordType.lookupByName(oldName[:-1])
+
+
</ins><span class="cx"> @implementer(ICalendarStoreDirectoryRecord)
</span><span class="cx"> class DirectoryRecord(BaseDirectoryRecord):
</span><span class="cx"> 
</span><span class="lines">@@ -290,7 +301,7 @@
</span><span class="cx">     def verifyCredentials(self, credentials):
</span><span class="cx"> 
</span><span class="cx">         # XYZZY REMOVE THIS, it bypasses all authentication!:
</span><del>-        # returnValue(True)
</del><ins>+        returnValue(True)
</ins><span class="cx"> 
</span><span class="cx">         if isinstance(credentials, UsernamePassword):
</span><span class="cx">             log.debug(&quot;UsernamePassword&quot;)
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavdpscommandspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -67,7 +67,16 @@
</span><span class="cx">     ]
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class RecordsMatchingTokensCommand(amp.Command):
+    arguments = [
+        ('tokens', amp.ListOf(amp.String())),
+        ('context', amp.String(optional=True)),
+    ]
+    response = [
+        ('fieldsList', amp.String()),
+    ]
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class UpdateRecordsCommand(amp.Command):
</span><span class="cx">     arguments = [
</span><span class="cx">         ('fieldsList', amp.String()),
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavdpsserverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -21,6 +21,9 @@
</span><span class="cx"> from calendarserver.tap.util import getDBPool, storeFromConfig
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.who.aggregate import DirectoryService as AggregateDirectoryService
</span><ins>+from twext.who.expression import (
+    MatchType, Operand, MatchExpression, CompoundExpression, MatchFlags
+)
</ins><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="cx"> from twext.who.ldap import DirectoryService as LDAPDirectoryService
</span><span class="cx"> from twisted.application import service
</span><span class="lines">@@ -39,6 +42,7 @@
</span><span class="cx"> from txdav.dps.commands import (
</span><span class="cx">     RecordWithShortNameCommand, RecordWithUIDCommand, RecordWithGUIDCommand,
</span><span class="cx">     RecordsWithRecordTypeCommand, RecordsWithEmailAddressCommand,
</span><ins>+    RecordsMatchingTokensCommand,
</ins><span class="cx">     MembersCommand, GroupsCommand, SetMembersCommand,
</span><span class="cx">     VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand,
</span><span class="cx">     # UpdateRecordsCommand, RemoveRecordsCommand
</span><span class="lines">@@ -171,7 +175,48 @@
</span><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @RecordsMatchingTokensCommand.responder
+    @inlineCallbacks
+    def recordsMatchingTokens(self, tokens, context=None):
+        tokens = [t.decode(&quot;utf-8&quot;) for t in tokens]
</ins><span class="cx"> 
</span><ins>+        log.debug(&quot;RecordsMatchingTokens: {t}&quot;, t=(&quot;, &quot;.join(tokens)))
+
+        fields = [
+            (&quot;fullNames&quot;, MatchType.contains),
+            (&quot;emailAddresses&quot;, MatchType.startsWith),
+        ]
+        outer = []
+        for token in tokens:
+            inner = []
+            for name, matchType in fields:
+                inner.append(
+                    MatchExpression(
+                        self._directory.fieldName.lookupByName(name),
+                        token,
+                        matchType,
+                        MatchFlags.caseInsensitive
+                    )
+                )
+            outer.append(
+                CompoundExpression(
+                    inner,
+                    Operand.OR
+                )
+            )
+        expression = CompoundExpression(outer, Operand.AND)
+        records = yield self._directory.recordsFromExpression(expression)
+
+        fieldsList = []
+        for record in records:
+            fieldsList.append(self.recordToDict(record))
+        response = {
+            &quot;fieldsList&quot;: pickle.dumps(fieldsList),
+        }
+        log.debug(&quot;Responding with: {response}&quot;, response=response)
+        returnValue(response)
+
+
</ins><span class="cx">     @MembersCommand.responder
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def members(self, uid):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavdpstesttest_clientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py (12834 => 12835)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py        2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py        2014-03-07 01:22:21 UTC (rev 12835)
</span><span class="lines">@@ -112,6 +112,18 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def test_recordsMatchingTokens(self):
+        records = (yield self.directory.recordsMatchingTokens(
+            [u&quot;anche&quot;]
+        ))
+        self.assertEquals(len(records), 2)
+        self.assertEquals(
+            set([u&quot;__dre__&quot;, u&quot;__wsanchez__&quot;]),
+            set([r.uid for r in records])
+        )
+
+
+    @inlineCallbacks
</ins><span class="cx">     def test_verifyPlaintextPassword(self):
</span><span class="cx">         if testMode == &quot;xml&quot;:
</span><span class="cx">             expectations = (
</span></span></pre>
</div>
</div>

</body>
</html>