<!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>[12861] CalendarServer/branches/users/sagen/move2who-2</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/12861">12861</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2014-03-07 18:19:19 -0800 (Fri, 07 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge from trunk</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarserveraccesslogpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/accesslog.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarserverprovisionrootpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/provision/root.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarserverpushapplepushpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/push/applepush.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertapcaldavpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertaptesttest_utilpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertaputilpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertoolscalverifypy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/calverify.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertoolsprincipalspy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertoolsshelldirectorypy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/shell/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertoolstesttest_principalspy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/test/test_principals.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarservertoolsutilpy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2calendarserverwebcalresourcepy">CalendarServer/branches/users/sagen/move2who-2/calendarserver/webcal/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavcustomxmlpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/customxml.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectoryaddressbookpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/addressbook.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectoryaugmentpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/augment.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorycalendarpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendar.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorycalendaruserproxypy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendaruserproxy.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorycommonpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/common.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorydirectoryprincipalresourcehtml">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory-principal-resource.html</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorydirectorypy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectoryprincipalpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorybackedaddressbookpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directorybackedaddressbook.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavextensionspy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/extensions.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavfreebusyurlpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/freebusyurl.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavicalpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/ical.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavresourcepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavscheduling_storecaldavresourcepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/scheduling_store/caldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavsharingpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/sharing.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavstorebridgepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavtesttest_icalendarpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_icalendar.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavtesttest_upgradepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavtestutilpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavtimezoneservicepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezoneservice.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavtimezonestdservicepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezonestdservice.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavupgradepy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2twistedcaldavutilpy">CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingcaldavschedulerpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/caldav/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingfreebusypy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/freebusy.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingimipinboundpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/imip/inbound.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingimplicitpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/implicit.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingischeduledeliverypy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/delivery.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingischeduleresourcepy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingischeduleschedulerpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingprocessingpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/processing.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingschedulerpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingworkpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/work.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoresqlpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoretesttest_attachmentspy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/test_attachments.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoretestutilpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreutilpy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcaldavicalendardirectoryservicepy">CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/icalendardirectoryservice.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcommondatastorepoddingconduitpy">CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/conduit.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcommondatastorepoddingresourcepy">CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcommondatastorepoddingtesttest_conduitpy">CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/test/test_conduit.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcommondatastoresqlpy">CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavcommondatastoretestutilpy">CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavdpsclientpy">CalendarServer/branches/users/sagen/move2who-2/txdav/dps/client.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavdpscommandspy">CalendarServer/branches/users/sagen/move2who-2/txdav/dps/commands.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavdpsserverpy">CalendarServer/branches/users/sagen/move2who-2/txdav/dps/server.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavdpstesttestxml">CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test.xml</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavdpstesttest_clientpy">CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test_client.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhodelegatespy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhogroupspy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/groups.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhotestaccountsaccountsxml">CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/accounts/accounts.xml</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhotesttest_delegatespy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhotesttest_groupspy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/test_groups.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhoxmlpy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/xml.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txweb2davresourcepy">CalendarServer/branches/users/sagen/move2who-2/txweb2/dav/resource.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhoaugmentpy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/augment.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who2txdavwhodirectorypy">CalendarServer/branches/users/sagen/move2who-2/txdav/who/directory.py</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2who2">CalendarServer/branches/users/sagen/move2who-2/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagenmove2who2"></a>
<div class="propset"><h4>Property changes: CalendarServer/branches/users/sagen/move2who-2</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.2-dev:11972,12357-12358,12794,12814
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cross-pod-sharing:12038-12191
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/json:11622-11912
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
</span><span class="cx">/CalendarServer/branches/users/cdaboo/scheduling-queue-refresh:11783-12557
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/sharing-in-the-store:11935-12016
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/cleanrevisions:12152-12334
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroupfixes:12120-12142
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">   + /CalDAVTester/trunk:11193-11198
</span><span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.2-dev:11972,12357-12358,12794,12814
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cross-pod-sharing:12038-12191
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/json:11622-11912
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
</span><span class="cx">/CalendarServer/branches/users/cdaboo/scheduling-queue-refresh:11783-12557
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/sharing-in-the-store:11935-12016
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/cleanrevisions:12152-12334
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroupfixes:12120-12142
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/move2who:12819-12860
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><a id="CalendarServerbranchesuserssagenmove2who2calendarserveraccesslogpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/accesslog.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/accesslog.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/accesslog.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -103,10 +103,15 @@
</span><span class="cx">                         else:
</span><span class="cx">                             return uid
</span><span class="cx"> 
</span><del>-                    uidn = convertUIDtoShortName(uidn)
-                    if uidz:
-                        uidz = convertUIDtoShortName(uidz)
</del><ins>+                    # MOVE2WHO
+                    # Better to stick the records directly on the request at
+                    # an earlier point, since we can't do anything deferred
+                    # in here.
</ins><span class="cx"> 
</span><ins>+                    # uidn = convertUIDtoShortName(uidn)
+                    # if uidz:
+                    #     uidz = convertUIDtoShortName(uidz)
+
</ins><span class="cx">                     if uidn and uidz:
</span><span class="cx">                         uid = '&quot;%s as %s&quot;' % (uidn, uidz,)
</span><span class="cx">                     else:
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarserverprovisionrootpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/provision/root.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/provision/root.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/provision/root.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -27,7 +27,7 @@
</span><span class="cx"> from txweb2.http import HTTPError, StatusResponse, RedirectResponse
</span><span class="cx"> 
</span><span class="cx"> from twisted.cred.error import LoginFailed, UnauthorizedLogin
</span><del>-from twisted.internet.defer import inlineCallbacks, returnValue
</del><ins>+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</ins><span class="cx"> from twisted.python.reflect import namedClass
</span><span class="cx"> from twisted.web.xmlrpc import Proxy
</span><span class="cx"> from twisted.web.error import Error as WebError
</span><span class="lines">@@ -110,7 +110,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return config.RootResourceACL
</del><ins>+        return succeed(config.RootResourceACL)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarserverpushapplepushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/push/applepush.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/push/applepush.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/push/applepush.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -820,23 +820,25 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return davxml.ACL(
-            # DAV:Read for authenticated principals
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
</del><ins>+        return succeed(
+            davxml.ACL(
+                # DAV:Read for authenticated principals
+                davxml.ACE(
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                    ),
+                    davxml.Protected(),
</ins><span class="cx">                 ),
</span><del>-                davxml.Protected(),
-            ),
-            # DAV:Write for authenticated principals
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Write()),
</del><ins>+                # DAV:Write for authenticated principals
+                davxml.ACE(
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Write()),
+                    ),
+                    davxml.Protected(),
</ins><span class="cx">                 ),
</span><del>-                davxml.Protected(),
-            ),
</del><ins>+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/caldav.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/caldav.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/caldav.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -86,7 +86,7 @@
</span><span class="cx"> from txdav.common.datastore.work.revision_cleanup import (
</span><span class="cx">     scheduleFirstFindMinRevision
</span><span class="cx"> )
</span><del>-from txdav.dps.server import DirectoryProxyServiceMaker
</del><ins>+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> from txdav.dps.client import DirectoryService as DirectoryProxyClientService
</span><span class="cx"> from txdav.who.groups import GroupCacher as NewGroupCacher
</span><span class="cx"> 
</span><span class="lines">@@ -551,8 +551,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 class="lines">@@ -929,10 +929,10 @@
</span><span class="cx">         CalDAV and CardDAV requests.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         pool, txnFactory = getDBPool(config)
</span><del>-        store = storeFromConfig(config, txnFactory)
</del><ins>+        directory = DirectoryProxyClientService(&quot;FIXME&quot;)
+        store = storeFromConfig(config, txnFactory, directory)
</ins><span class="cx">         logObserver = AMPCommonAccessLoggingObserver()
</span><span class="cx">         result = self.requestProcessingService(options, store, logObserver)
</span><del>-        directory = store.directoryService()
</del><span class="cx"> 
</span><span class="cx">         if pool is not None:
</span><span class="cx">             pool.setServiceParent(result)
</span><span class="lines">@@ -1013,14 +1013,17 @@
</span><span class="cx">                 namespace=config.GroupCaching.MemcachedPool,
</span><span class="cx">                 useExternalProxies=config.GroupCaching.UseExternalProxies,
</span><span class="cx">             )
</span><ins>+            newGroupCacher = NewGroupCacher(directory)
</ins><span class="cx">         else:
</span><span class="cx">             groupCacher = None
</span><ins>+            newGroupCacher = None
</ins><span class="cx"> 
</span><span class="cx">         def decorateTransaction(txn):
</span><span class="cx">             txn._pushDistributor = pushDistributor
</span><span class="cx">             txn._rootResource = result.rootResource
</span><span class="cx">             txn._mailRetriever = mailRetriever
</span><span class="cx">             txn._groupCacher = groupCacher
</span><ins>+            txn._newGroupCacher = newGroupCacher
</ins><span class="cx"> 
</span><span class="cx">         store.callWithNewTransactions(decorateTransaction)
</span><span class="cx"> 
</span><span class="lines">@@ -1311,6 +1314,13 @@
</span><span class="cx">             if store is None:
</span><span class="cx">                 raise StoreNotAvailable()
</span><span class="cx"> 
</span><ins>+            # Create a Directory Proxy &quot;Server&quot; service and hand it to the
+            # store.
+            # FIXME: right now the store passed *to* the directory is the
+            # calendar/contacts data store, but for a multi-server deployment
+            # it will need its own separate store.
+            store.setDirectoryService(directoryFromConfig(config, store=store))
+
</ins><span class="cx">             result = self.requestProcessingService(options, store, logObserver)
</span><span class="cx"> 
</span><span class="cx">             # Optionally set up push notifications
</span><span class="lines">@@ -1356,9 +1366,7 @@
</span><span class="cx">                     namespace=config.GroupCaching.MemcachedPool,
</span><span class="cx">                     useExternalProxies=config.GroupCaching.UseExternalProxies
</span><span class="cx">                 )
</span><del>-                newGroupCacher = NewGroupCacher(
-                    DirectoryProxyClientService(None)
-                )
</del><ins>+                newGroupCacher = NewGroupCacher(directory)
</ins><span class="cx">             else:
</span><span class="cx">                 groupCacher = None
</span><span class="cx">                 newGroupCacher = None
</span><span class="lines">@@ -1392,11 +1400,6 @@
</span><span class="cx">                         &quot;manhole_tap could not be imported&quot;
</span><span class="cx">                     )
</span><span class="cx"> 
</span><del>-            # Optionally enable Directory Proxy
-            if config.DirectoryProxy.Enabled:
-                dps = DirectoryProxyServiceMaker().makeService(None)
-                dps.setServiceParent(result)
-
</del><span class="cx">             def decorateTransaction(txn):
</span><span class="cx">                 txn._pushDistributor = pushDistributor
</span><span class="cx">                 txn._rootResource = result.rootResource
</span><span class="lines">@@ -1442,7 +1445,7 @@
</span><span class="cx">                 Popen(memcachedArgv)
</span><span class="cx"> 
</span><span class="cx">         return self.storageService(
</span><del>-            slaveSvcCreator, logObserver, uid=uid, gid=gid
</del><ins>+            slaveSvcCreator, logObserver, uid=uid, gid=gid, directory=None
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1455,10 +1458,17 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         def toolServiceCreator(pool, store, ignored, storageService):
</span><ins>+            # Create a Directory Proxy &quot;Server&quot; service and hand it to the
+            # store
+            # FIXME: right now the store passed *to* the directory is the
+            # calendar/contacts data store, but for a multi-server deployment
+            # it will need its own separate store.
+            store.setDirectoryService(directoryFromConfig(config, store=store))
</ins><span class="cx">             return config.UtilityServiceClass(store)
</span><span class="cx"> 
</span><span class="cx">         uid, gid = getSystemIDs(config.UserName, config.GroupName)
</span><del>-        return self.storageService(toolServiceCreator, None, uid=uid, gid=gid)
</del><ins>+        return self.storageService(toolServiceCreator, None, uid=uid, gid=gid,
+                                   directory=None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def makeService_Agent(self, options):
</span><span class="lines">@@ -1506,7 +1516,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def storageService(
</span><del>-        self, createMainService, logObserver, uid=None, gid=None
</del><ins>+        self, createMainService, logObserver, uid=None, gid=None, directory=None
</ins><span class="cx">     ):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         If necessary, create a service to be started used for storage; for
</span><span class="lines">@@ -1532,6 +1542,9 @@
</span><span class="cx">             running as root (also the gid to chown Attachments to).
</span><span class="cx">         @type gid: C{int}
</span><span class="cx"> 
</span><ins>+        @param directory: The directory service to use.
+        @type directory: L{IStoreDirectoryService} or None
+
</ins><span class="cx">         @return: the appropriate a service to start.
</span><span class="cx">         @rtype: L{IService}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -1546,7 +1559,7 @@
</span><span class="cx">                     maxConnections=config.MaxDBConnectionsPerPool
</span><span class="cx">                 )
</span><span class="cx">                 cp.setServiceParent(ms)
</span><del>-                store = storeFromConfig(config, cp.connection)
</del><ins>+                store = storeFromConfig(config, cp.connection, directory)
</ins><span class="cx"> 
</span><span class="cx">                 pps = PreProcessingService(
</span><span class="cx">                     createMainService, cp, store, logObserver, storageService
</span><span class="lines">@@ -1671,7 +1684,7 @@
</span><span class="cx">                     &quot;Unknown database type {}&quot;.format(config.DBType)
</span><span class="cx">                 )
</span><span class="cx">         else:
</span><del>-            store = storeFromConfig(config, None)
</del><ins>+            store = storeFromConfig(config, None, directory)
</ins><span class="cx">             return createMainService(None, store, logObserver, None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1938,20 +1951,26 @@
</span><span class="cx">                     namespace=config.GroupCaching.MemcachedPool,
</span><span class="cx">                     useExternalProxies=config.GroupCaching.UseExternalProxies
</span><span class="cx">                 )
</span><ins>+                newGroupCacher = NewGroupCacher(directory)
</ins><span class="cx">             else:
</span><span class="cx">                 groupCacher = None
</span><ins>+                newGroupCacher = None
</ins><span class="cx"> 
</span><span class="cx">             def decorateTransaction(txn):
</span><span class="cx">                 txn._pushDistributor = None
</span><span class="cx">                 txn._rootResource = rootResource
</span><span class="cx">                 txn._mailRetriever = mailRetriever
</span><span class="cx">                 txn._groupCacher = groupCacher
</span><ins>+                txn._newGroupCacher = newGroupCacher
</ins><span class="cx"> 
</span><span class="cx">             store.callWithNewTransactions(decorateTransaction)
</span><span class="cx"> 
</span><span class="cx">             return multi
</span><span class="cx"> 
</span><del>-        ssvc = self.storageService(spawnerSvcCreator, None, uid, gid)
</del><ins>+        ssvc = self.storageService(
+            spawnerSvcCreator, None, uid, gid,
+            directory=DirectoryProxyClientService(&quot;FIXME&quot;)
+        )
</ins><span class="cx">         ssvc.setServiceParent(s)
</span><span class="cx">         return s
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertaptesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/test/test_util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/test/test_util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/test/test_util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -14,13 +14,14 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from calendarserver.tap.util import directoryFromConfig, MemoryLimitService, Stepper
</del><ins>+from calendarserver.tap.util import MemoryLimitService, Stepper
</ins><span class="cx"> from twistedcaldav.util import computeProcessCount
</span><span class="cx"> from twistedcaldav.test.util import TestCase
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.directory.augment import AugmentXMLDB
</span><span class="cx"> from twisted.internet.task import Clock
</span><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks
</span><ins>+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><span class="cx"> class ProcessCountTestCase(TestCase):
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertaputilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tap/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -100,7 +100,12 @@
</span><span class="cx"> from urllib import quote
</span><span class="cx"> from twisted.python.usage import UsageError
</span><span class="cx"> 
</span><ins>+from txdav.dps.client import DirectoryService as DirectoryProxyClientService
</ins><span class="cx"> 
</span><ins>+from twext.who.checker import UsernamePasswordCredentialChecker
+from twext.who.checker import HTTPDigestCredentialChecker
+from twisted.cred.error import UnauthorizedLogin
+from txweb2.dav.auth import IPrincipalCredentials
</ins><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -218,7 +223,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def storeFromConfig(config, txnFactory, directoryService=None):
</del><ins>+def storeFromConfig(config, txnFactory, directoryService):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Produce an L{IDataStore} from the given configuration, transaction factory,
</span><span class="cx">     and notifier factory.
</span><span class="lines">@@ -236,9 +241,6 @@
</span><span class="cx">     if config.EnableResponseCache and config.Memcached.Pools.Default.ClientEnabled:
</span><span class="cx">         notifierFactories[&quot;cache&quot;] = CacheStoreNotifierFactory()
</span><span class="cx"> 
</span><del>-    if directoryService is None:
-        directoryService = directoryFromConfig(config)
-
</del><span class="cx">     quota = config.UserQuota
</span><span class="cx">     if quota == 0:
</span><span class="cx">         quota = None
</span><span class="lines">@@ -281,10 +283,11 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def directoryFromConfig(config):
</del><ins>+def REMOVEMEdirectoryFromConfig(config):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Create an L{AggregateDirectoryService} from the given configuration.
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+
</ins><span class="cx">     #
</span><span class="cx">     # Setup the Augment Service
</span><span class="cx">     #
</span><span class="lines">@@ -370,7 +373,56 @@
</span><span class="cx">     return directory
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+# MOVE2WHO -- should we move this class somewhere else?
+class PrincipalCredentialChecker(object):
+    credentialInterfaces = (IPrincipalCredentials,)
</ins><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def requestAvatarId(self, credentials):
+        credentials = IPrincipalCredentials(credentials)
+
+        if credentials.authnPrincipal is None:
+            raise UnauthorizedLogin(&quot;No such user: %s&quot; % (credentials.credentials.username,))
+
+        # See if record is enabledForLogin
+        if not credentials.authnPrincipal.record.isLoginEnabled():
+            raise UnauthorizedLogin(
+                &quot;User not allowed to log in: {user}&quot;.format(
+                    user=credentials.credentials.username
+                )
+            )
+
+        # Handle Kerberos as a separate behavior
+        try:
+            from twistedcaldav.authkerb import NegotiateCredentials
+        except ImportError:
+            NegotiateCredentials = None
+
+        if NegotiateCredentials and isinstance(credentials.credentials,
+                                               NegotiateCredentials):
+            # If we get here with Kerberos, then authentication has already succeeded
+            returnValue(
+                (
+                    credentials.authnPrincipal.principalURL(),
+                    credentials.authzPrincipal.principalURL(),
+                    credentials.authnPrincipal,
+                    credentials.authzPrincipal,
+                )
+            )
+        else:
+            if (yield credentials.authnPrincipal.record.verifyCredentials(credentials.credentials)):
+                returnValue(
+                    (
+                        credentials.authnPrincipal.principalURL(),
+                        credentials.authzPrincipal.principalURL(),
+                        credentials.authnPrincipal,
+                        credentials.authzPrincipal,
+                    )
+                )
+            else:
+                raise UnauthorizedLogin(&quot;Incorrect credentials for %s&quot; % (credentials.credentials.username,))
+
+
</ins><span class="cx"> def getRootResource(config, newStore, resources=None):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Set up directory service and resource hierarchy based on config.
</span><span class="lines">@@ -407,22 +459,26 @@
</span><span class="cx">     addressBookResourceClass = DirectoryAddressBookHomeProvisioningResource
</span><span class="cx">     directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookResource
</span><span class="cx">     apnSubscriptionResourceClass = APNSubscriptionResource
</span><ins>+    principalResourceClass = DirectoryPrincipalProvisioningResource
</ins><span class="cx"> 
</span><span class="cx">     directory = newStore.directoryService()
</span><ins>+    principalCollection = principalResourceClass(&quot;/principals/&quot;, directory)
</ins><span class="cx"> 
</span><span class="cx">     #
</span><span class="cx">     # Setup the ProxyDB Service
</span><span class="cx">     #
</span><del>-    proxydbClass = namedClass(config.ProxyDBService.type)
</del><span class="cx"> 
</span><del>-    log.info(&quot;Configuring proxydb service of type: {cls}&quot;, cls=proxydbClass)
</del><ins>+    # MOVE2WHO
+    # proxydbClass = namedClass(config.ProxyDBService.type)
</ins><span class="cx"> 
</span><del>-    try:
-        calendaruserproxy.ProxyDBService = proxydbClass(**config.ProxyDBService.params)
-    except IOError:
-        log.error(&quot;Could not start proxydb service&quot;)
-        raise
</del><ins>+    # log.info(&quot;Configuring proxydb service of type: {cls}&quot;, cls=proxydbClass)
</ins><span class="cx"> 
</span><ins>+    # try:
+    #     calendaruserproxy.ProxyDBService = proxydbClass(**config.ProxyDBService.params)
+    # except IOError:
+    #     log.error(&quot;Could not start proxydb service&quot;)
+    #     raise
+
</ins><span class="cx">     #
</span><span class="cx">     # Configure the Site and Wrappers
</span><span class="cx">     #
</span><span class="lines">@@ -431,7 +487,9 @@
</span><span class="cx"> 
</span><span class="cx">     portal = Portal(auth.DavRealm())
</span><span class="cx"> 
</span><del>-    portal.registerChecker(directory)
</del><ins>+    portal.registerChecker(UsernamePasswordCredentialChecker(directory))
+    portal.registerChecker(HTTPDigestCredentialChecker(directory))
+    portal.registerChecker(PrincipalCredentialChecker())
</ins><span class="cx"> 
</span><span class="cx">     realm = directory.realmName or &quot;&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -491,7 +549,7 @@
</span><span class="cx">     #
</span><span class="cx">     log.info(&quot;Setting up document root at: {root}&quot;, root=config.DocumentRoot)
</span><span class="cx"> 
</span><del>-    principalCollection = directory.principalCollection
</del><ins>+    # principalCollection = directory.principalCollection
</ins><span class="cx"> 
</span><span class="cx">     if config.EnableCalDAV:
</span><span class="cx">         log.info(&quot;Setting up calendar collection: {cls}&quot;, cls=calendarResourceClass)
</span><span class="lines">@@ -712,6 +770,7 @@
</span><span class="cx">     #
</span><span class="cx">     # Configure ancillary data
</span><span class="cx">     #
</span><ins>+    # MOVE2WHO
</ins><span class="cx">     log.info(&quot;Configuring authentication wrapper&quot;)
</span><span class="cx"> 
</span><span class="cx">     overrides = {}
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertoolscalverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/calverify.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/calverify.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/calverify.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -431,7 +431,7 @@
</span><span class="cx">         configuration, creating one first if necessary.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if self._directory is None:
</span><del>-            self._directory = getDirectory(self.config) #directoryFromConfig(self.config)
</del><ins>+            self._directory = getDirectory(self.config)
</ins><span class="cx">         return self._directory
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/principals.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/principals.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/principals.py        2014-03-08 02:19:19 UTC (rev 12861)
</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,30 +117,29 @@
</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><ins>+
</ins><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><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">@@ -193,6 +191,10 @@
</span><span class="cx">     verbose = False
</span><span class="cx"> 
</span><span class="cx">     for opt, arg in optargs:
</span><ins>+
+        # Args come in as encoded bytes
+        arg = arg.decode(&quot;utf-8&quot;)
+
</ins><span class="cx">         if opt in (&quot;-h&quot;, &quot;--help&quot;):
</span><span class="cx">             usage()
</span><span class="cx"> 
</span><span class="lines">@@ -202,11 +204,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 +219,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 +228,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">@@ -240,74 +238,63 @@
</span><span class="cx">                 proxyType = &quot;write&quot;
</span><span class="cx">             else:
</span><span class="cx">                 raise AssertionError(&quot;Unknown proxy type&quot;)
</span><del>-
-            try:
-                principalForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
-
</del><span class="cx">             principalActions.append((action_addProxy, proxyType, arg))
</span><span class="cx"> 
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--remove-proxy&quot;):
</span><del>-            try:
-                principalForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
-
</del><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 +309,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">@@ -363,21 +352,19 @@
</span><span class="cx">         params = (searchPrincipals,)
</span><span class="cx"> 
</span><span class="cx">     else:
</span><del>-        #
-        # Do a quick sanity check that arguments look like principal
-        # identifiers.
-        #
</del><span class="cx">         if not args:
</span><span class="cx">             usage(&quot;No principals specified.&quot;)
</span><span class="cx"> 
</span><del>-        for arg in args:
-            try:
-                principalForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
</del><ins>+        # We don't have a directory yet
+        # for arg in args:
+        #     try:
+        #         yield recordForPrincipalID(arg, checkOnly=True)
+        #     except ValueError, e:
+        #         abort(e)
</ins><span class="cx"> 
</span><ins>+        unicodeArgs = [a.decode(&quot;utf-8&quot;) for a in args]
</ins><span class="cx">         function = runPrincipalActions
</span><del>-        params = (args, principalActions)
</del><ins>+        params = (unicodeArgs, principalActions)
</ins><span class="cx"> 
</span><span class="cx">     PrincipalService.function = function
</span><span class="cx">     PrincipalService.params = params
</span><span class="lines">@@ -385,74 +372,86 @@
</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><del>-def runListPrincipals(service, rootResource, directory, store, listPrincipals):
</del><ins>+@inlineCallbacks
+def runListPrincipals(service, store, listPrincipals):
+    directory = store.directoryService()
+    recordType = directory.oldNameToRecordType(listPrincipals)
</ins><span class="cx">     try:
</span><del>-        records = list(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="cx">             print(&quot;No records of type %s&quot; % (listPrincipals,))
</span><span class="cx">     except UnknownRecordTypeError, e:
</span><span class="cx">         usage(e)
</span><del>-    return succeed(None)
</del><ins>+    returnValue(None)
</ins><span class="cx"> 
</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 = principalForPrincipalID(principalID, directory=directory)
</del><ins>+            record = yield recordForPrincipalID(directory, principalID)
</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'))
-        print(&quot;%d matches found:&quot; % (len(records),))
</del><ins>+        records.sort(key=operator.attrgetter('fullNames'))
+        print(&quot;{n} matches found:&quot;.format(n=len(records)))
</ins><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,))
-            print(&quot;   Record name(s): %s&quot; % (&quot;, &quot;.join(record.shortNames),))
-            if record.authIDs:
-                print(&quot;   Auth ID(s): %s&quot; % (&quot;, &quot;.join(record.authIDs),))
-            if record.emailAddresses:
-                print(&quot;   Email(s): %s&quot; % (&quot;, &quot;.join(record.emailAddresses),))
</del><ins>+            print(
+                &quot;\n{d} ({rt})&quot;.format(
+                    d=record.displayName,
+                    rt=record.recordType.name
+                )
+            )
+            print(&quot;   UID: {u}&quot;.format(u=record.uid,))
+            print(
+                &quot;   Record name{plural}: {names}&quot;.format(
+                    plural=(&quot;s&quot; if len(record.shortNames) &gt; 1 else &quot;&quot;),
+                    names=(&quot;, &quot;.join(record.shortNames))
+                )
+            )
+            try:
+                if record.emailAddresses:
+                    print(
+                        &quot;   Email{plural}: {emails}&quot;.format(
+                            plural=(&quot;s&quot; if len(record.emailAddresses) &gt; 1 else &quot;&quot;),
+                            emails=(&quot;, &quot;.join(record.emailAddresses))
+                        )
+                    )
+            except AttributeError:
+                pass
</ins><span class="cx">     else:
</span><span class="cx">         print(&quot;No matches found&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -460,292 +459,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 = principalForPrincipalID(proxyID, directory=directory)
-        if proxyPrincipal is None:
</del><ins>+        proxyRecord = yield recordForPrincipalID(directory, proxyID)
+        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 = 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 = 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 = 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 = 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 = directory.recordWithGUID(autoAcceptGroup)
-        if record is not None:
-            groupPrincipal = 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">@@ -803,14 +828,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 class="lines">@@ -859,16 +886,16 @@
</span><span class="cx">             kwargs[key] = newValue
</span><span class="cx"> 
</span><span class="cx">     if create:
</span><del>-        record = directory.createRecord(recordType, **kwargs)
</del><ins>+        record = yield directory.createRecord(recordType, **kwargs)
</ins><span class="cx">         kwargs['guid'] = record.guid
</span><span class="cx">     else:
</span><span class="cx">         try:
</span><del>-            record = directory.updateRecord(recordType, **kwargs)
</del><ins>+            record = yield directory.updateRecord(recordType, **kwargs)
</ins><span class="cx">         except NotImplementedError:
</span><span class="cx">             # Updating of directory information is not supported by underlying
</span><span class="cx">             # directory implementation, but allow augment information to be
</span><span class="cx">             # updated
</span><del>-            record = directory.recordWithGUID(kwargs[&quot;guid&quot;])
</del><ins>+            record = yield directory.recordWithGUID(kwargs[&quot;guid&quot;])
</ins><span class="cx">             pass
</span><span class="cx"> 
</span><span class="cx">     augmentService = directory.serviceForRecordType(recordType).augmentService
</span><span class="lines">@@ -882,7 +909,7 @@
</span><span class="cx">         augmentRecord.autoAcceptGroup = autoAcceptGroup
</span><span class="cx">     (yield augmentService.addAugmentRecords([augmentRecord]))
</span><span class="cx">     try:
</span><del>-        directory.updateRecord(recordType, **kwargs)
</del><ins>+        yield directory.updateRecord(recordType, **kwargs)
</ins><span class="cx">     except NotImplementedError:
</span><span class="cx">         # Updating of directory information is not supported by underlying
</span><span class="cx">         # directory implementation, but allow augment information to be
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertoolsshelldirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/shell/directory.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/shell/directory.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/shell/directory.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -83,11 +83,17 @@
</span><span class="cx">     add(&quot;First Name&quot;, record.firstName)
</span><span class="cx">     add(&quot;Last Name&quot; , record.lastName )
</span><span class="cx"> 
</span><del>-    for email in record.emailAddresses:
-        add(&quot;Email Address&quot;, email)
</del><ins>+    try:
+        for email in record.emailAddresses:
+            add(&quot;Email Address&quot;, email)
+    except AttributeError:
+        pass
</ins><span class="cx"> 
</span><del>-    for cua in record.calendarUserAddresses:
-        add(&quot;Calendar User Address&quot;, cua)
</del><ins>+    try:
+        for cua in record.calendarUserAddresses:
+            add(&quot;Calendar User Address&quot;, cua)
+    except AttributeError:
+        pass
</ins><span class="cx"> 
</span><span class="cx">     add(&quot;Server ID&quot;           , record.serverID)
</span><span class="cx">     add(&quot;Enabled&quot;             , record.enabled)
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertoolstesttest_principalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/test/test_principals.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/test/test_principals.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/test/test_principals.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -17,22 +17,23 @@
</span><span class="cx"> import os
</span><span class="cx"> import sys
</span><span class="cx"> 
</span><ins>+from calendarserver.tools.principals import (
+    parseCreationArgs, matchStrings,
+    updateRecord, principalForPrincipalID, getProxies, setProxies
+)
</ins><span class="cx"> from twext.python.filepath import CachingFilePath as FilePath
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
</span><del>-
</del><span class="cx"> from twistedcaldav.config import config
</span><ins>+from twistedcaldav.directory import calendaruserproxy
</ins><span class="cx"> from twistedcaldav.directory.directory import DirectoryError
</span><del>-from twistedcaldav.directory import calendaruserproxy
</del><ins>+from twistedcaldav.test.util import (
+    TestCase, CapturingProcessProtocol, ErrorOutput
+)
+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><del>-from twistedcaldav.test.util import TestCase, CapturingProcessProtocol, \
-    ErrorOutput
</del><span class="cx"> 
</span><del>-from calendarserver.tap.util import directoryFromConfig
-from calendarserver.tools.principals import (parseCreationArgs, matchStrings,
-    updateRecord, principalForPrincipalID, getProxies, setProxies)
</del><span class="cx"> 
</span><del>-
</del><span class="cx"> class ManagePrincipalsTestCase(TestCase):
</span><span class="cx"> 
</span><span class="cx">     def setUp(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2calendarservertoolsutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/tools/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</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,87 @@
</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(directory, principalID, checkOnly=False):
</ins><span class="cx"> 
</span><ins>+    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 +555,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="CalendarServerbranchesuserssagenmove2who2calendarserverwebcalresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/calendarserver/webcal/resource.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/calendarserver/webcal/resource.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/calendarserver/webcal/resource.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -48,15 +48,17 @@
</span><span class="cx"> class WebCalendarResource (ReadOnlyResourceMixIn, DAVFile):
</span><span class="cx"> 
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return davxml.ACL(
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
</del><ins>+        return succeed(
+            davxml.ACL(
+                davxml.ACE(
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                    ),
+                    davxml.Protected(),
+                    TwistedACLInheritable(),
</ins><span class="cx">                 ),
</span><del>-                davxml.Protected(),
-                TwistedACLInheritable(),
-            ),
</del><ins>+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavcustomxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/customxml.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/customxml.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/customxml.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -1456,6 +1456,8 @@
</span><span class="cx"> 
</span><span class="cx"> ResourceType.calendarproxyread = ResourceType(Principal(), Collection(), CalendarProxyRead())
</span><span class="cx"> ResourceType.calendarproxywrite = ResourceType(Principal(), Collection(), CalendarProxyWrite())
</span><ins>+ResourceType.calendarproxyreadfor = ResourceType(Principal(), Collection(), CalendarProxyReadFor())
+ResourceType.calendarproxywritefor = ResourceType(Principal(), Collection(), CalendarProxyWriteFor())
</ins><span class="cx"> 
</span><span class="cx"> ResourceType.timezones = ResourceType(Timezones())
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectoryaddressbookpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/addressbook.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/addressbook.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/addressbook.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -65,7 +65,7 @@
</span><span class="cx">     DAVResource,
</span><span class="cx"> ):
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return config.ProvisioningResourceACL
</del><ins>+        return succeed(config.ProvisioningResourceACL)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def etag(self):
</span><span class="lines">@@ -93,7 +93,8 @@
</span><span class="cx"> 
</span><span class="cx">         super(DirectoryAddressBookHomeProvisioningResource, self).__init__()
</span><span class="cx"> 
</span><del>-        self.directory = IDirectoryService(directory)
</del><ins>+        # MOVE2WHO
+        self.directory = directory  # IDirectoryService(directory)
</ins><span class="cx">         self._url = url
</span><span class="cx">         self._newStore = store
</span><span class="cx"> 
</span><span class="lines">@@ -103,7 +104,7 @@
</span><span class="cx">         #
</span><span class="cx">         # Create children
</span><span class="cx">         #
</span><del>-        for recordType in self.directory.recordTypes():
</del><ins>+        for recordType in [r.name for r in self.directory.recordTypes()]:
</ins><span class="cx">             self.putChild(recordType, DirectoryAddressBookHomeTypeProvisioningResource(self, recordType))
</span><span class="cx"> 
</span><span class="cx">         self.putChild(uidsResourceName, DirectoryAddressBookHomeUIDProvisioningResource(self))
</span><span class="lines">@@ -114,7 +115,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><del>-        return self.directory.recordTypes()
</del><ins>+        return [r.name for r in self.directory.recordTypes()]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def principalCollections(self):
</span><span class="lines">@@ -129,12 +130,13 @@
</span><span class="cx">         return self.directory.principalCollection.principalForRecord(record)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def homeForDirectoryRecord(self, record, request):
</span><del>-        uidResource = self.getChild(uidsResourceName)
</del><ins>+        uidResource = yield self.getChild(uidsResourceName)
</ins><span class="cx">         if uidResource is None:
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx">         else:
</span><del>-            return uidResource.homeResourceForRecord(record, request)
</del><ins>+            returnValue((yield uidResource.homeResourceForRecord(record, request)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     ##
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectoryaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/augment.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/augment.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/augment.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -46,6 +46,7 @@
</span><span class="cx">     &quot;automatic&quot;,
</span><span class="cx"> ))
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class AugmentRecord(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Augmented directory record information
</span><span class="lines">@@ -75,13 +76,14 @@
</span><span class="cx">         self.clonedFromDefault = False
</span><span class="cx"> 
</span><span class="cx"> recordTypesMap = {
</span><del>-    &quot;users&quot; : &quot;User&quot;,
-    &quot;groups&quot; : &quot;Group&quot;,
-    &quot;locations&quot; : &quot;Location&quot;,
-    &quot;resources&quot; : &quot;Resource&quot;,
-    &quot;addresses&quot; : &quot;Address&quot;,
</del><ins>+    &quot;users&quot;: &quot;User&quot;,
+    &quot;groups&quot;: &quot;Group&quot;,
+    &quot;locations&quot;: &quot;Location&quot;,
+    &quot;resources&quot;: &quot;Resource&quot;,
+    &quot;addresses&quot;: &quot;Address&quot;,
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class AugmentDB(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Abstract base class for an augment record database.
</span><span class="lines">@@ -128,7 +130,6 @@
</span><span class="cx"> 
</span><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-
</del><span class="cx">         recordType = recordTypesMap[recordType]
</span><span class="cx"> 
</span><span class="cx">         result = (yield self._lookupAugmentRecord(uid))
</span><span class="lines">@@ -266,9 +267,9 @@
</span><span class="cx">         self.xmlFiles = [fullServerPath(config.DataRoot, path) for path in xmlFiles]
</span><span class="cx">         self.xmlFileStats = {}
</span><span class="cx">         for path in self.xmlFiles:
</span><del>-            self.xmlFileStats[path] = (0, 0) # mtime, size
</del><ins>+            self.xmlFileStats[path] = (0, 0)  # mtime, size
</ins><span class="cx"> 
</span><del>-        self.statSeconds = statSeconds # Don't stat more often than this value
</del><ins>+        self.statSeconds = statSeconds  # Don't stat more often than this value
</ins><span class="cx">         self.lastCached = 0
</span><span class="cx">         self.db = {}
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorycalendarpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendar.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendar.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendar.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -65,7 +65,7 @@
</span><span class="cx">     DAVResource,
</span><span class="cx"> ):
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return config.ProvisioningResourceACL
</del><ins>+        return succeed(config.ProvisioningResourceACL)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def etag(self):
</span><span class="lines">@@ -91,7 +91,8 @@
</span><span class="cx"> 
</span><span class="cx">         super(DirectoryCalendarHomeProvisioningResource, self).__init__()
</span><span class="cx"> 
</span><del>-        self.directory = IDirectoryService(directory)
</del><ins>+        # MOVE2WHO
+        self.directory = directory  # IDirectoryService(directory)
</ins><span class="cx">         self._url = url
</span><span class="cx">         self._newStore = store
</span><span class="cx"> 
</span><span class="lines">@@ -101,8 +102,9 @@
</span><span class="cx">         #
</span><span class="cx">         # Create children
</span><span class="cx">         #
</span><del>-        for recordType in self.directory.recordTypes():
-            self.putChild(recordType, DirectoryCalendarHomeTypeProvisioningResource(self, recordType))
</del><ins>+        # MOVE2WHO
+        for name, recordType in [(r.name + &quot;s&quot;, r) for r in self.directory.recordTypes()]:
+            self.putChild(name, DirectoryCalendarHomeTypeProvisioningResource(self, name, recordType))
</ins><span class="cx"> 
</span><span class="cx">         self.putChild(uidsResourceName, DirectoryCalendarHomeUIDProvisioningResource(self))
</span><span class="cx"> 
</span><span class="lines">@@ -112,7 +114,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><del>-        return self.directory.recordTypes()
</del><ins>+        # MOVE2WHO
+        return [r.name + &quot;s&quot; for r in self.directory.recordTypes()]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def principalCollections(self):
</span><span class="lines">@@ -127,12 +130,13 @@
</span><span class="cx">         return self.directory.principalCollection.principalForRecord(record)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def homeForDirectoryRecord(self, record, request):
</span><del>-        uidResource = self.getChild(uidsResourceName)
</del><ins>+        uidResource = yield self.getChild(uidsResourceName)
</ins><span class="cx">         if uidResource is None:
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx">         else:
</span><del>-            return uidResource.homeResourceForRecord(record, request)
</del><ins>+            returnValue((yield uidResource.homeResourceForRecord(record, request)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     ##
</span><span class="lines">@@ -156,23 +160,25 @@
</span><span class="cx">     Resource which provisions calendar home collections of a specific
</span><span class="cx">     record type as needed.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    def __init__(self, parent, recordType):
</del><ins>+    def __init__(self, parent, name, recordType):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param parent: the parent of this resource
</span><span class="cx">         @param recordType: the directory record type to provision.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         assert parent is not None
</span><ins>+        assert name is not None
</ins><span class="cx">         assert recordType is not None
</span><span class="cx"> 
</span><span class="cx">         super(DirectoryCalendarHomeTypeProvisioningResource, self).__init__()
</span><span class="cx"> 
</span><span class="cx">         self.directory = parent.directory
</span><ins>+        self.name = name
</ins><span class="cx">         self.recordType = recordType
</span><span class="cx">         self._parent = parent
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def url(self):
</span><del>-        return joinURL(self._parent.url(), self.recordType)
</del><ins>+        return joinURL(self._parent.url(), self.name)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><span class="lines">@@ -203,7 +209,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def displayName(self):
</span><del>-        return self.recordType
</del><ins>+        return self.name
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     ##
</span><span class="lines">@@ -258,7 +264,7 @@
</span><span class="cx">             else:
</span><span class="cx">                 # ...otherwise permissions are fixed, and are not subject to
</span><span class="cx">                 # inheritance rules, etc.
</span><del>-                return succeed(self.defaultAccessControlList())
</del><ins>+                return self.defaultAccessControlList()
</ins><span class="cx"> 
</span><span class="cx">         d = getWikiACL(self, request)
</span><span class="cx">         d.addCallback(gotACL)
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorycalendaruserproxypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendaruserproxy.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendaruserproxy.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/calendaruserproxy.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -29,37 +29,40 @@
</span><span class="cx"> 
</span><span class="cx"> import itertools
</span><span class="cx"> import time
</span><ins>+import uuid
</ins><span class="cx"> 
</span><ins>+from twext.python.log import Logger
+from twext.who.idirectory import RecordType as BaseRecordType
</ins><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks, returnValue
</span><del>-from txweb2 import responsecode
-from txweb2.http import HTTPError, StatusResponse
-from txdav.xml import element as davxml
-from txdav.xml.base import dav_namespace
-from txweb2.dav.util import joinURL
-from txweb2.dav.noneprops import NonePropertyStore
-
-from twext.python.log import Logger
-
</del><ins>+from twisted.python.modules import getModule
</ins><span class="cx"> from twisted.web.template import XMLFile, Element, renderer
</span><del>-from twisted.python.modules import getModule
-from twistedcaldav.extensions import DirectoryElement
</del><ins>+from twistedcaldav.config import config, fullServerPath
+from twistedcaldav.database import (
+    AbstractADBAPIDatabase, ADBAPISqliteMixin, ADBAPIPostgreSQLMixin
+)
</ins><span class="cx"> from twistedcaldav.directory.principal import formatLink
</span><span class="cx"> from twistedcaldav.directory.principal import formatLinks
</span><span class="cx"> from twistedcaldav.directory.principal import formatPrincipals
</span><del>-
</del><span class="cx"> from twistedcaldav.directory.util import normalizeUUID
</span><del>-from twistedcaldav.config import config, fullServerPath
-from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin, \
-    ADBAPIPostgreSQLMixin
-from twistedcaldav.extensions import DAVPrincipalResource, \
-    DAVResourceWithChildrenMixin
</del><ins>+from twistedcaldav.extensions import (
+    DAVPrincipalResource, DAVResourceWithChildrenMixin
+)
+from twistedcaldav.extensions import DirectoryElement
</ins><span class="cx"> from twistedcaldav.extensions import ReadOnlyWritePropertiesResourceMixIn
</span><span class="cx"> from twistedcaldav.memcacher import Memcacher
</span><span class="cx"> from twistedcaldav.resource import CalDAVComplianceMixIn
</span><ins>+from txdav.who.delegates import RecordType as DelegateRecordType
+from txdav.xml import element as davxml
+from txdav.xml.base import dav_namespace
+from txweb2 import responsecode
+from txweb2.dav.noneprops import NonePropertyStore
+from txweb2.dav.util import joinURL
+from txweb2.http import HTTPError, StatusResponse
</ins><span class="cx"> 
</span><span class="cx"> thisModule = getModule(__name__)
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class PermissionsMixIn (ReadOnlyWritePropertiesResourceMixIn):
</span><span class="cx">     def defaultAccessControlList(self):
</span><span class="cx">         aces = (
</span><span class="lines">@@ -86,13 +89,13 @@
</span><span class="cx">             for principal in config.AdminPrincipals
</span><span class="cx">         ))
</span><span class="cx"> 
</span><del>-        return davxml.ACL(*aces)
</del><ins>+        return succeed(davxml.ACL(*aces))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def accessControlList(self, request, inheritance=True, expanding=False,
</span><span class="cx">                           inherited_aces=None):
</span><span class="cx">         # Permissions here are fixed, and are not subject to inheritance rules, etc.
</span><del>-        return succeed(self.defaultAccessControlList())
</del><ins>+        return self.defaultAccessControlList()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -119,13 +122,20 @@
</span><span class="cx">         record = self.resource.parent.record
</span><span class="cx">         resource = self.resource
</span><span class="cx">         parent = self.resource.parent
</span><ins>+        try:
+            if isinstance(record.guid, uuid.UUID):
+                guid = str(record.guid).upper()
+            else:
+                guid = record.guid
+        except AttributeError:
+            guid = &quot;&quot;
</ins><span class="cx">         return tag.fillSlots(
</span><span class="cx">             directoryGUID=record.service.guid,
</span><span class="cx">             realm=record.service.realmName,
</span><del>-            guid=record.guid,
-            recordType=record.recordType,
</del><ins>+            guid=guid,
+            recordType=record.recordType.name + &quot;s&quot;,  # MOVE2WHO need mapping
</ins><span class="cx">             shortNames=record.shortNames,
</span><del>-            fullName=record.fullName,
</del><ins>+            fullName=record.displayName,
</ins><span class="cx">             principalUID=parent.principalUID(),
</span><span class="cx">             principalURL=formatLink(parent.principalURL()),
</span><span class="cx">             proxyPrincipalUID=resource.principalUID(),
</span><span class="lines">@@ -209,9 +219,13 @@
</span><span class="cx"> 
</span><span class="cx">     def resourceType(self):
</span><span class="cx">         if self.proxyType == &quot;calendar-proxy-read&quot;:
</span><del>-            return davxml.ResourceType.calendarproxyread #@UndefinedVariable
</del><ins>+            return davxml.ResourceType.calendarproxyread  # @UndefinedVariable
</ins><span class="cx">         elif self.proxyType == &quot;calendar-proxy-write&quot;:
</span><del>-            return davxml.ResourceType.calendarproxywrite #@UndefinedVariable
</del><ins>+            return davxml.ResourceType.calendarproxywrite  # @UndefinedVariable
+        elif self.proxyType == &quot;calendar-proxy-read-for&quot;:
+            return davxml.ResourceType.calendarproxyreadfor  # @UndefinedVariable
+        elif self.proxyType == &quot;calendar-proxy-write-for&quot;:
+            return davxml.ResourceType.calendarproxywritefor  # @UndefinedVariable
</ins><span class="cx">         else:
</span><span class="cx">             return super(CalendarUserProxyPrincipalResource, self).resourceType()
</span><span class="cx"> 
</span><span class="lines">@@ -270,7 +284,7 @@
</span><span class="cx">         principals = []
</span><span class="cx">         newUIDs = set()
</span><span class="cx">         for uri in members:
</span><del>-            principal = self.pcollection._principalForURI(uri)
</del><ins>+            principal = yield self.pcollection._principalForURI(uri)
</ins><span class="cx">             # Invalid principals MUST result in an error.
</span><span class="cx">             if principal is None or principal.principalURL() != uri:
</span><span class="cx">                 raise HTTPError(StatusResponse(
</span><span class="lines">@@ -282,7 +296,9 @@
</span><span class="cx">             newUIDs.add(principal.principalUID())
</span><span class="cx"> 
</span><span class="cx">         # Get the old set of UIDs
</span><del>-        oldUIDs = (yield self._index().getMembers(self.uid))
</del><ins>+        # oldUIDs = (yield self._index().getMembers(self.uid))
+        oldPrincipals = yield self.groupMembers()
+        oldUIDs = [p.principalUID() for p in oldPrincipals]
</ins><span class="cx"> 
</span><span class="cx">         # Change membership
</span><span class="cx">         yield self.setGroupMemberSetPrincipals(principals)
</span><span class="lines">@@ -293,19 +309,24 @@
</span><span class="cx"> 
</span><span class="cx">         changedUIDs = newUIDs.symmetric_difference(oldUIDs)
</span><span class="cx">         for uid in changedUIDs:
</span><del>-            principal = self.pcollection.principalForUID(uid)
</del><ins>+            principal = yield self.pcollection.principalForUID(uid)
</ins><span class="cx">             if principal:
</span><span class="cx">                 yield principal.cacheNotifier.changed()
</span><span class="cx"> 
</span><span class="cx">         returnValue(True)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def setGroupMemberSetPrincipals(self, principals):
</span><del>-        # Map the principals to UIDs.
-        return self._index().setGroupMembers(
-            self.uid,
-            [p.principalUID() for p in principals],
</del><ins>+
+        # Find our pseudo-record
+        record = yield self.parent.record.service.recordWithShortName(
+            self._recordTypeFromProxyType(),
+            self.parent.principalUID()
</ins><span class="cx">         )
</span><ins>+        # Set the members
+        memberRecords = [p.record for p in principals]
+        yield record.setMembers(memberRecords)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     ##
</span><span class="lines">@@ -349,7 +370,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def _expandMemberUIDs(self, uid=None, relatives=None, uids=None, infinity=False):
</del><ins>+    def _expandMemberPrincipals(self, uid=None, relatives=None, uids=None, infinity=False):
</ins><span class="cx">         if uid is None:
</span><span class="cx">             uid = self.principalUID()
</span><span class="cx">         if relatives is None:
</span><span class="lines">@@ -360,14 +381,14 @@
</span><span class="cx">         if uid not in uids:
</span><span class="cx">             from twistedcaldav.directory.principal import DirectoryPrincipalResource
</span><span class="cx">             uids.add(uid)
</span><del>-            principal = self.pcollection.principalForUID(uid)
</del><ins>+            principal = yield self.pcollection.principalForUID(uid)
</ins><span class="cx">             if isinstance(principal, CalendarUserProxyPrincipalResource):
</span><span class="cx">                 members = yield self._directGroupMembers()
</span><span class="cx">                 for member in members:
</span><span class="cx">                     if member.principalUID() not in uids:
</span><span class="cx">                         relatives.add(member)
</span><span class="cx">                         if infinity:
</span><del>-                            yield self._expandMemberUIDs(member.principalUID(), relatives, uids, infinity=infinity)
</del><ins>+                            yield self._expandMemberPrincipals(member.principalUID(), relatives, uids, infinity=infinity)
</ins><span class="cx">             elif isinstance(principal, DirectoryPrincipalResource):
</span><span class="cx">                 if infinity:
</span><span class="cx">                     members = yield principal.expandedGroupMembers()
</span><span class="lines">@@ -378,30 +399,45 @@
</span><span class="cx">         returnValue(relatives)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _recordTypeFromProxyType(self):
+        return {
+            &quot;calendar-proxy-read&quot;: DelegateRecordType.readDelegateGroup,
+            &quot;calendar-proxy-write&quot;: DelegateRecordType.writeDelegateGroup,
+            &quot;calendar-proxy-read-for&quot;: DelegateRecordType.readDelegatorGroup,
+            &quot;calendar-proxy-write-for&quot;: DelegateRecordType.writeDelegatorGroup,
+        }.get(self.proxyType)
+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _directGroupMembers(self):
</span><del>-        # Get member UIDs from database and map to principal resources
-        members = yield self._index().getMembers(self.uid)
-        found = []
-        for uid in members:
-            p = self.pcollection.principalForUID(uid)
-            if p:
-                # Only principals enabledForLogin can be a delegate
-                # (and groups as well)
-                if (p.record.enabledForLogin or
-                    p.record.recordType == p.record.service.recordType_groups):
-                    found.append(p)
-                # Make sure any outstanding deletion timer entries for
-                # existing principals are removed
-                yield self._index().refreshPrincipal(uid)
-            else:
-                self.log.warn(&quot;Delegate is missing from directory: %s&quot; % (uid,))
</del><ins>+        &quot;&quot;&quot;
+        Fault in the record representing the sub principal for this proxy type
+        (either read-only or read-write), then fault in the direct members of
+        that record.
+        &quot;&quot;&quot;
+        memberPrincipals = []
+        record = yield self.parent.record.service.recordWithShortName(
+            self._recordTypeFromProxyType(),
+            self.parent.principalUID()
+        )
+        if record is not None:
+            memberRecords = yield record.members()
+            for record in memberRecords:
+                if record is not None:
+                    principal = yield self.pcollection.principalForRecord(
+                        record
+                    )
+                    if principal is not None:
+                        if (
+                            principal.record.loginAllowed or
+                            principal.record.recordType is BaseRecordType.group
+                        ):
+                            memberPrincipals.append(principal)
+        returnValue(memberPrincipals)
</ins><span class="cx"> 
</span><del>-        returnValue(found)
</del><span class="cx"> 
</span><del>-
</del><span class="cx">     def groupMembers(self):
</span><del>-        return self._expandMemberUIDs()
</del><ins>+        return self._expandMemberPrincipals()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -410,18 +446,12 @@
</span><span class="cx">         Return the complete, flattened set of principals belonging to this
</span><span class="cx">         group.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        returnValue((yield self._expandMemberUIDs(infinity=True)))
</del><ins>+        returnValue((yield self._expandMemberPrincipals(infinity=True)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def groupMemberships(self):
</span><del>-        # Get membership UIDs and map to principal resources
-        d = self._index().getMemberships(self.uid)
-        d.addCallback(lambda memberships: [
-            p for p
-            in [self.pcollection.principalForUID(uid) for uid in memberships]
-            if p
-        ])
-        return d
</del><ins>+        # Unlikely to ever want to put a subprincipal into a group
+        return succeed([])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -437,7 +467,7 @@
</span><span class="cx">         @return: True if principal is a proxy (of the correct type) of our parent
</span><span class="cx">         @rtype: C{boolean}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        readWrite = self.isProxyType(True) # is read-write
</del><ins>+        readWrite = self.isProxyType(True)  # is read-write
</ins><span class="cx">         if principal and self.parent in (yield principal.proxyFor(readWrite)):
</span><span class="cx">             returnValue(True)
</span><span class="cx">         returnValue(False)
</span><span class="lines">@@ -630,7 +660,7 @@
</span><span class="cx"> 
</span><span class="cx">             overdue = yield self._memcacher.checkDeletionTimer(principalUID)
</span><span class="cx"> 
</span><del>-            if overdue == False:
</del><ins>+            if overdue is False:
</ins><span class="cx">                 # Do nothing
</span><span class="cx">                 returnValue(None)
</span><span class="cx"> 
</span><span class="lines">@@ -855,9 +885,9 @@
</span><span class="cx">         )
</span><span class="cx">         if alreadyDone is None:
</span><span class="cx">             for (groupname, member) in (
</span><del>-                    (yield self._db_all_values_for_sql(
-                        &quot;select GROUPNAME, MEMBER from GROUPS&quot;))
-                ):
</del><ins>+                (yield self._db_all_values_for_sql(
+                    &quot;select GROUPNAME, MEMBER from GROUPS&quot;))
+            ):
</ins><span class="cx">                 grouplist = groupname.split(&quot;#&quot;)
</span><span class="cx">                 grouplist[0] = normalizeUUID(grouplist[0])
</span><span class="cx">                 newGroupName = &quot;#&quot;.join(grouplist)
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorycommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/common.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/common.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/common.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -71,10 +71,11 @@
</span><span class="cx">             log.debug(&quot;No directory record with GUID %r&quot; % (name,))
</span><span class="cx">             returnValue(None)
</span><span class="cx"> 
</span><del>-        if not getattr(record, self.enabledAttribute):
-            log.debug(&quot;Directory record %r is not enabled for %s&quot; % (
-                record, self.homeResourceTypeName))
-            returnValue(None)
</del><ins>+        # MOVE2WHO
+        # if not getattr(record, self.enabledAttribute):
+        #     log.debug(&quot;Directory record %r is not enabled for %s&quot; % (
+        #         record, self.homeResourceTypeName))
+        #     returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">         assert len(name) &gt; 4, &quot;Directory record has an invalid GUID: %r&quot; % (
</span><span class="cx">             name,)
</span><span class="lines">@@ -94,7 +95,7 @@
</span><span class="cx">         if name == &quot;&quot;:
</span><span class="cx">             returnValue((self, ()))
</span><span class="cx"> 
</span><del>-        record = self.directory.recordWithUID(name)
</del><ins>+        record = yield self.directory.recordWithUID(name)
</ins><span class="cx">         if record:
</span><span class="cx">             child = yield self.homeResourceForRecord(record, request)
</span><span class="cx">             returnValue((child, segments[1:]))
</span><span class="lines">@@ -149,7 +150,7 @@
</span><span class="cx">         if name == &quot;&quot;:
</span><span class="cx">             returnValue((self, segments[1:]))
</span><span class="cx"> 
</span><del>-        record = self.directory.recordWithShortName(self.recordType, name)
</del><ins>+        record = yield self.directory.recordWithShortName(self.recordType, name)
</ins><span class="cx">         if record is None:
</span><span class="cx">             returnValue(
</span><span class="cx">                 (NotFoundResource(principalCollections=self._parent.principalCollections()), [])
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorydirectoryprincipalresourcehtml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory-principal-resource.html (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory-principal-resource.html        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory-principal-resource.html        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -11,10 +11,7 @@
</span><span class="cx"> GUID: &lt;t:slot name=&quot;principalGUID&quot;/&gt;
</span><span class="cx"> Record type: &lt;t:slot name=&quot;recordType&quot;/&gt;
</span><span class="cx"> Short names: &lt;t:slot name=&quot;shortNames&quot;/&gt;
</span><del>-Security Identities: &lt;t:slot name=&quot;securityIDs&quot;/&gt;
</del><span class="cx"> Full name: &lt;t:slot name=&quot;fullName&quot;/&gt;
</span><del>-First name: &lt;t:slot name=&quot;firstName&quot;/&gt;
-Last name: &lt;t:slot name=&quot;lastName&quot;/&gt;
</del><span class="cx"> Email addresses:
</span><span class="cx"> &lt;t:slot name=&quot;emailAddresses&quot; /&gt;Principal UID: &lt;t:slot name=&quot;principalUID&quot;/&gt;
</span><span class="cx"> Principal URL: &lt;t:slot name=&quot;principalURL&quot;/&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorydirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/directory.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -239,7 +239,7 @@
</span><span class="cx">                 else:
</span><span class="cx">                     record = self.recordWithShortName(parts[2], parts[3])
</span><span class="cx"> 
</span><del>-        return record if record and record.enabledForCalendaring else None
</del><ins>+        return record if record and record.hasCalendars else None
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordWithCachedGroupsAlias(self, recordType, alias):
</span><span class="lines">@@ -531,7 +531,7 @@
</span><span class="cx">         )
</span><span class="cx">         for record in resources:
</span><span class="cx">             guid = record.guid
</span><del>-            if record.enabledForCalendaring:
</del><ins>+            if record.hasCalendars:
</ins><span class="cx">                 assignments.append((&quot;%s#calendar-proxy-write&quot; % (guid,),
</span><span class="cx">                                    record.externalProxies()))
</span><span class="cx">                 assignments.append((&quot;%s#calendar-proxy-read&quot; % (guid,),
</span><span class="lines">@@ -937,7 +937,8 @@
</span><span class="cx"> 
</span><span class="cx">             self.log.info(&quot;Retrieving list of all proxies&quot;)
</span><span class="cx">             # This is always a set of guids:
</span><del>-            delegatedGUIDs = set((yield self.proxyDB.getAllMembers()))
</del><ins>+            # MOVE2WHO
+            delegatedGUIDs = set() # set((yield self.proxyDB.getAllMembers()))
</ins><span class="cx">             self.log.info(&quot;There are %d proxies&quot; % (len(delegatedGUIDs),))
</span><span class="cx">             self.log.info(&quot;Retrieving group hierarchy from directory&quot;)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/principal.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/principal.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directory/principal.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx">     &quot;DirectoryCalendarPrincipalResource&quot;,
</span><span class="cx"> ]
</span><span class="cx"> 
</span><ins>+import uuid
</ins><span class="cx"> from urllib import unquote
</span><span class="cx"> from urlparse import urlparse
</span><span class="cx"> 
</span><span class="lines">@@ -76,7 +77,7 @@
</span><span class="cx"> 
</span><span class="cx"> class PermissionsMixIn (ReadOnlyResourceMixIn):
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return authReadACL
</del><ins>+        return succeed(authReadACL)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -94,7 +95,7 @@
</span><span class="cx">         else:
</span><span class="cx">             # ...otherwise permissions are fixed, and are not subject to
</span><span class="cx">             # inheritance rules, etc.
</span><del>-            returnValue(self.defaultAccessControlList())
</del><ins>+            returnValue((yield self.defaultAccessControlList()))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -150,18 +151,21 @@
</span><span class="cx">         CalendarPrincipalCollectionResource.__init__(self, url)
</span><span class="cx">         DAVResourceWithChildrenMixin.__init__(self)
</span><span class="cx"> 
</span><del>-        self.directory = IDirectoryService(directory)
</del><ins>+        # MOVE2WHO
+        # self.directory = IDirectoryService(directory)
+        self.directory = directory
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def __repr__(self):
</span><span class="cx">         return &quot;&lt;%s: %s %s&gt;&quot; % (self.__class__.__name__, self.directory, self._url)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def locateChild(self, req, segments):
</span><del>-        child = self.getChild(segments[0])
</del><ins>+        child = (yield self.getChild(segments[0]))
</ins><span class="cx">         if child is not None:
</span><del>-            return (child, segments[1:])
-        return (NotFoundResource(principalCollections=self.principalCollections()), ())
</del><ins>+            returnValue((child, segments[1:]))
+        returnValue((NotFoundResource(principalCollections=self.principalCollections()), ()))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def deadProperties(self):
</span><span class="lines">@@ -174,12 +178,14 @@
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def principalForShortName(self, recordType, name):
</span><del>-        return self.principalForRecord(self.directory.recordWithShortName(recordType, name))
</del><ins>+        record = (yield self.directory.recordWithShortName(recordType, name))
+        returnValue((yield self.principalForRecord(record)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def principalForUser(self, user):
</span><del>-        return self.principalForShortName(DirectoryService.recordType_users, user)
</del><ins>+        return self.principalForShortName(self.directory.recordType.lookupByName(&quot;user&quot;), user)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def principalForAuthID(self, user):
</span><span class="lines">@@ -207,7 +213,7 @@
</span><span class="cx"> 
</span><span class="cx">     def principalForRecord(self, record):
</span><span class="cx">         if record is None or not record.enabled:
</span><del>-            return None
</del><ins>+            return succeed(None)
</ins><span class="cx">         return self.principalForUID(record.uid)
</span><span class="cx"> 
</span><span class="cx">     ##
</span><span class="lines">@@ -281,16 +287,21 @@
</span><span class="cx">         #
</span><span class="cx">         # Create children
</span><span class="cx">         #
</span><del>-        for recordType in self.directory.recordTypes():
-            self.putChild(recordType, DirectoryPrincipalTypeProvisioningResource(self, recordType))
</del><ins>+        # MOVE2WHO - hack: appending &quot;s&quot; -- need mapping
+        for name, recordType in [(r.name + &quot;s&quot;, r) for r in self.directory.recordTypes()]:
+            self.putChild(name, DirectoryPrincipalTypeProvisioningResource(self,
+                name, recordType))
</ins><span class="cx"> 
</span><span class="cx">         self.putChild(uidsResourceName, DirectoryPrincipalUIDProvisioningResource(self))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def principalForUID(self, uid):
</span><del>-        return self.getChild(uidsResourceName).getChild(uid)
</del><ins>+        child = (yield self.getChild(uidsResourceName))
+        returnValue((yield child.getChild(uid)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _principalForURI(self, uri):
</span><span class="cx">         scheme, netloc, path, _ignore_params, _ignore_query, _ignore_fragment = urlparse(uri)
</span><span class="cx"> 
</span><span class="lines">@@ -312,56 +323,62 @@
</span><span class="cx"> 
</span><span class="cx">             if (host != config.ServerHostName and
</span><span class="cx">                 host not in config.Scheduling.Options.PrincipalHostAliases):
</span><del>-                return None
</del><ins>+                returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">             if port != {
</span><span class="cx">                 &quot;http&quot; : config.HTTPPort,
</span><span class="cx">                 &quot;https&quot;: config.SSLPort,
</span><span class="cx">             }[scheme]:
</span><del>-                return None
</del><ins>+                returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">         elif scheme == &quot;urn&quot;:
</span><span class="cx">             if path.startswith(&quot;uuid:&quot;):
</span><del>-                return self.principalForUID(path[5:])
</del><ins>+                returnValue((yield self.principalForUID(path[5:])))
</ins><span class="cx">             else:
</span><del>-                return None
</del><ins>+                returnValue(None)
</ins><span class="cx">         else:
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">         if not path.startswith(self._url):
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">         path = path[len(self._url) - 1:]
</span><span class="cx"> 
</span><span class="cx">         segments = [unquote(s) for s in path.rstrip(&quot;/&quot;).split(&quot;/&quot;)]
</span><span class="cx">         if segments[0] == &quot;&quot; and len(segments) == 3:
</span><del>-            typeResource = self.getChild(segments[1])
</del><ins>+            typeResource = yield self.getChild(segments[1])
</ins><span class="cx">             if typeResource is not None:
</span><del>-                principalResource = typeResource.getChild(segments[2])
</del><ins>+                principalResource = yield typeResource.getChild(segments[2])
</ins><span class="cx">                 if principalResource:
</span><del>-                    return principalResource
</del><ins>+                    returnValue(principalResource)
</ins><span class="cx"> 
</span><del>-        return None
</del><ins>+        returnValue(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def principalForCalendarUserAddress(self, address):
</span><span class="cx">         # First see if the address is a principal URI
</span><del>-        principal = self._principalForURI(address)
</del><ins>+        principal = yield self._principalForURI(address)
</ins><span class="cx">         if principal:
</span><del>-            if isinstance(principal, DirectoryCalendarPrincipalResource) and principal.record.enabledForCalendaring:
-                return principal
</del><ins>+            if (
+                isinstance(principal, DirectoryCalendarPrincipalResource) and
+                principal.record.hasCalendars
+            ):
+                returnValue(principal)
</ins><span class="cx">         else:
</span><span class="cx">             # Next try looking it up in the directory
</span><del>-            record = self.directory.recordWithCalendarUserAddress(address)
-            if record is not None and record.enabled and record.enabledForCalendaring:
-                return self.principalForRecord(record)
</del><ins>+            record = yield self.directory.recordWithCalendarUserAddress(address)
+            if record is not None and record.hasCalendars:
+                returnValue((yield self.principalForRecord(record)))
</ins><span class="cx"> 
</span><span class="cx">         log.debug(&quot;No principal for calendar user address: %r&quot; % (address,))
</span><del>-        return None
</del><ins>+        returnValue(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def principalForRecord(self, record):
</span><del>-        return self.getChild(uidsResourceName).principalForRecord(record)
</del><ins>+        child = (yield self.getChild(uidsResourceName))
+        returnValue((yield child.principalForRecord(record)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     ##
</span><span class="lines">@@ -375,13 +392,14 @@
</span><span class="cx"> 
</span><span class="cx">     def getChild(self, name):
</span><span class="cx">         if name == &quot;&quot;:
</span><del>-            return self
</del><ins>+            return succeed(self)
</ins><span class="cx">         else:
</span><del>-            return self.putChildren.get(name, None)
</del><ins>+            return succeed(self.putChildren.get(name, None))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><del>-        return self.directory.recordTypes()
</del><ins>+        # MOVE2WHO hack
+        return [r.name + &quot;s&quot; for r in self.directory.recordTypes()]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     ##
</span><span class="lines">@@ -421,14 +439,14 @@
</span><span class="cx">     Collection resource which provisions directory principals of a
</span><span class="cx">     specific type as its children, indexed by short name.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    def __init__(self, parent, recordType):
</del><ins>+    def __init__(self, parent, name, recordType):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param parent: the parent L{DirectoryPrincipalProvisioningResource}.
</span><span class="cx">         @param recordType: the directory record type to provision.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         DirectoryProvisioningResource.__init__(
</span><span class="cx">             self,
</span><del>-            joinURL(parent.principalCollectionURL(), recordType) + &quot;/&quot;,
</del><ins>+            joinURL(parent.principalCollectionURL(), name) + &quot;/&quot;,
</ins><span class="cx">             parent.directory
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -459,7 +477,7 @@
</span><span class="cx"> 
</span><span class="cx">     def getChild(self, name):
</span><span class="cx">         if name == &quot;&quot;:
</span><del>-            return self
</del><ins>+            return succeed(self)
</ins><span class="cx">         else:
</span><span class="cx">             return self.principalForShortName(self.recordType, name)
</span><span class="cx"> 
</span><span class="lines">@@ -517,16 +535,18 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def principalForRecord(self, record):
</span><del>-        if record is None or not record.enabled:
-            return None
</del><ins>+        # MOVE2WHO
+        if record is None: #  or not record.enabled:
+            return succeed(None)
</ins><span class="cx"> 
</span><del>-        if record.enabledForCalendaring or record.enabledForAddressBooks:
</del><ins>+        # MOVE2WHO
+        if record.hasCalendars or record.hasContacts:
</ins><span class="cx">             # XXX these are different features and one should not automatically
</span><span class="cx">             # imply the other...
</span><span class="cx">             principal = DirectoryCalendarPrincipalResource(self, record)
</span><span class="cx">         else:
</span><span class="cx">             principal = DirectoryPrincipalResource(self, record)
</span><del>-        return principal
</del><ins>+        return succeed(principal)
</ins><span class="cx"> 
</span><span class="cx">     ##
</span><span class="cx">     # Static
</span><span class="lines">@@ -538,9 +558,10 @@
</span><span class="cx">         raise HTTPError(responsecode.NOT_FOUND)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def getChild(self, name):
</span><span class="cx">         if name == &quot;&quot;:
</span><del>-            return self
</del><ins>+            returnValue(self)
</ins><span class="cx"> 
</span><span class="cx">         if &quot;#&quot; in name:
</span><span class="cx">             # This UID belongs to a sub-principal
</span><span class="lines">@@ -549,16 +570,16 @@
</span><span class="cx">             primaryUID = name
</span><span class="cx">             subType = None
</span><span class="cx"> 
</span><del>-        record = self.directory.recordWithUID(primaryUID)
-        primaryPrincipal = self.principalForRecord(record)
</del><ins>+        record = (yield self.directory.recordWithUID(primaryUID))
+        primaryPrincipal = (yield self.principalForRecord(record))
</ins><span class="cx">         if primaryPrincipal is None:
</span><span class="cx">             log.info(&quot;No principal found for UID: %s&quot; % (name,))
</span><del>-            return None
</del><ins>+            returnValue(None)
</ins><span class="cx"> 
</span><span class="cx">         if subType is None:
</span><del>-            return primaryPrincipal
</del><ins>+            returnValue(primaryPrincipal)
</ins><span class="cx">         else:
</span><del>-            return primaryPrincipal.getChild(subType)
</del><ins>+            returnValue((yield primaryPrincipal.getChild(subType)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><span class="lines">@@ -610,17 +631,31 @@
</span><span class="cx">         Top-level renderer in the template.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         record = self.resource.record
</span><ins>+        try:
+            if isinstance(record.guid, uuid.UUID):
+                guid = str(record.guid).upper()
+            else:
+                guid = record.guid
+        except AttributeError:
+            guid = &quot;&quot;
+        try:
+            emailAddresses = record.emailAddresses
+        except AttributeError:
+            emailAddresses = []
</ins><span class="cx">         return tag.fillSlots(
</span><span class="cx">             directoryGUID=str(record.service.guid),
</span><span class="cx">             realm=str(record.service.realmName),
</span><del>-            principalGUID=str(record.guid),
-            recordType=str(record.recordType),
</del><ins>+            principalGUID=guid,
+            recordType=record.recordType.name + &quot;s&quot;,  # MOVE2WHO need mapping
</ins><span class="cx">             shortNames=&quot;,&quot;.join(record.shortNames),
</span><del>-            securityIDs=&quot;,&quot;.join(record.authIDs),
-            fullName=str(record.fullName),
-            firstName=str(record.firstName),
-            lastName=str(record.lastName),
-            emailAddresses=formatList(record.emailAddresses),
</del><ins>+            # MOVE2WHO: need this?
+            # securityIDs=&quot;,&quot;.join(record.authIDs),
+            fullName=str(record.displayName),
+            # MOVE2WHO: need this?
+            # firstName=str(record.firstName),
+            # MOVE2WHO: need this?
+            # lastName=str(record.lastName),
+            emailAddresses=formatList(emailAddresses),
</ins><span class="cx">             principalUID=str(self.resource.principalUID()),
</span><span class="cx">             principalURL=formatLink(self.resource.principalURL()),
</span><span class="cx">             alternateURIs=formatLinks(self.resource.alternateURIs()),
</span><span class="lines">@@ -697,7 +732,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         resource = self.resource
</span><span class="cx">         record = resource.record
</span><del>-        if record.enabledForCalendaring:
</del><ins>+        if record.hasCalendars:
</ins><span class="cx">             return tag.fillSlots(
</span><span class="cx">                 calendarUserAddresses=formatLinks(
</span><span class="cx">                     sorted(resource.calendarUserAddresses())
</span><span class="lines">@@ -715,7 +750,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         resource = self.resource
</span><span class="cx">         record = resource.record
</span><del>-        if record.enabledForAddressBooks:
</del><ins>+        if record.hasContacts:
</ins><span class="cx">             return tag.fillSlots(
</span><span class="cx">                 addressBookHomes=formatLinks(resource.addressBookHomeURLs())
</span><span class="cx">             )
</span><span class="lines">@@ -750,7 +785,8 @@
</span><span class="cx">             (calendarserver_namespace, &quot;first-name&quot;),
</span><span class="cx">             (calendarserver_namespace, &quot;last-name&quot;),
</span><span class="cx">             (calendarserver_namespace, &quot;email-address-set&quot;),
</span><del>-            davxml.ResourceID.qname(),
</del><ins>+            # MOVE2WHO
+            # davxml.ResourceID.qname(),
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">     cacheNotifierFactory = DisabledCacheNotifier
</span><span class="lines">@@ -778,8 +814,9 @@
</span><span class="cx">         url = joinURL(parent.principalCollectionURL(), self.principalUID()) + slash
</span><span class="cx">         self._url = url
</span><span class="cx"> 
</span><ins>+        # MOVE2WHO - hack: just adding an &quot;s&quot; using recordType.name (need a mapping)
</ins><span class="cx">         self._alternate_urls = tuple([
</span><del>-            joinURL(parent.parent.principalCollectionURL(), record.recordType, shortName) + slash for shortName in record.shortNames
</del><ins>+            joinURL(parent.parent.principalCollectionURL(), record.recordType.name+&quot;s&quot;, shortName) + slash for shortName in record.shortNames
</ins><span class="cx">         ])
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -811,24 +848,27 @@
</span><span class="cx"> 
</span><span class="cx">         namespace, name = qname
</span><span class="cx"> 
</span><del>-        if qname == davxml.ResourceID.qname():
-            returnValue(davxml.ResourceID(davxml.HRef.fromString(&quot;urn:uuid:%s&quot; % (self.record.guid,))))
-        elif namespace == calendarserver_namespace:
-            if name == &quot;first-name&quot;:
-                firstName = self.record.firstName
-                if firstName is not None:
-                    returnValue(customxml.FirstNameProperty(firstName))
-                else:
-                    returnValue(None)
</del><ins>+        # MOVE2WHO -- does principal need ResourceID ?
+        # if qname == davxml.ResourceID.qname():
+        #     returnValue(davxml.ResourceID(davxml.HRef.fromString(&quot;urn:uuid:%s&quot; % (self.record.guid,))))
+        if namespace == calendarserver_namespace:
</ins><span class="cx"> 
</span><del>-            elif name == &quot;last-name&quot;:
-                lastName = self.record.lastName
-                if lastName is not None:
-                    returnValue(customxml.LastNameProperty(lastName))
-                else:
-                    returnValue(None)
</del><ins>+            # MOVE2WHO
+            # if name == &quot;first-name&quot;:
+            #     firstName = self.record.firstName
+            #     if firstName is not None:
+            #         returnValue(customxml.FirstNameProperty(firstName))
+            #     else:
+            #         returnValue(None)
</ins><span class="cx"> 
</span><del>-            elif name == &quot;email-address-set&quot;:
</del><ins>+            # elif name == &quot;last-name&quot;:
+            #     lastName = self.record.lastName
+            #     if lastName is not None:
+            #         returnValue(customxml.LastNameProperty(lastName))
+            #     else:
+            #         returnValue(None)
+
+            if name == &quot;email-address-set&quot;:
</ins><span class="cx">                 returnValue(customxml.EmailAddressSet(
</span><span class="cx">                     *[customxml.EmailAddressProperty(addr) for addr in sorted(self.record.emailAddresses)]
</span><span class="cx">                 ))
</span><span class="lines">@@ -867,7 +907,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def displayName(self):
</span><del>-        return self.record.displayName()
</del><ins>+        return self.record.displayName
</ins><span class="cx"> 
</span><span class="cx">     ##
</span><span class="cx">     # ACL
</span><span class="lines">@@ -939,51 +979,40 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def proxyFor(self, read_write, resolve_memberships=True):
</del><ins>+    def proxyFor(self, readWrite):
+        &quot;&quot;&quot;
+        Returns the set of principals currently delegating to this principal
+        with the access indicated by the readWrite argument.  If readWrite is
+        True, then write-access delegators are returned, otherwise the read-
+        only-access delegators are returned.
</ins><span class="cx"> 
</span><ins>+        @param readWrite: Whether to look up read-write delegators, or
+            read-only delegators
+        @type readWrite: C{bool}
+
+        @return: A Deferred firing with a set of principals
+        &quot;&quot;&quot;
</ins><span class="cx">         proxyFors = set()
</span><span class="cx"> 
</span><del>-        if resolve_memberships:
-            cache = getattr(self.record.service, &quot;groupMembershipCache&quot;, None)
-            if cache:
-                log.debug(&quot;proxyFor is using groupMembershipCache&quot;)
-                guids = (yield self.record.cachedGroups())
-                memberships = set()
-                for guid in guids:
-                    principal = self.parent.principalForUID(guid)
-                    if principal:
-                        memberships.add(principal)
-            else:
-                memberships = self._getRelatives(&quot;groups&quot;, infinity=True)
-
-            for membership in memberships:
-                results = (yield membership.proxyFor(read_write, False))
-                proxyFors.update(results)
-
</del><span class="cx">         if config.EnableProxyPrincipals:
</span><del>-            # Get proxy group UIDs and map to principal resources
-            proxies = []
-            memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
-            for uid in memberships:
-                subprincipal = self.parent.principalForUID(uid)
-                if subprincipal:
-                    if subprincipal.isProxyType(read_write):
-                        proxies.append(subprincipal.parent)
-                else:
-                    yield self._calendar_user_proxy_index().removeGroup(uid)
</del><ins>+            childName = &quot;calendar-proxy-{rw}-for&quot;.format(
+                rw=(&quot;write&quot; if readWrite else &quot;read&quot;)
+            )
+            proxyForGroup = yield self.getChild(childName)
+            if proxyForGroup:
+                proxyFors = yield proxyForGroup.groupMembers()
</ins><span class="cx"> 
</span><del>-            proxyFors.update(proxies)
</del><ins>+                uids = set()
+                for principal in tuple(proxyFors):
+                    if principal.principalUID() in uids:
+                        proxyFors.remove(principal)
+                    else:
+                        uids.add(principal.principalUID())
</ins><span class="cx"> 
</span><del>-        uids = set()
-        for principal in tuple(proxyFors):
-            if principal.principalUID() in uids:
-                proxyFors.remove(principal)
-            else:
-                uids.add(principal.principalUID())
-
</del><span class="cx">         returnValue(proxyFors)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _getRelatives(self, method, record=None, relatives=None, records=None, proxy=None, infinity=False):
</span><span class="cx">         if record is None:
</span><span class="cx">             record = self.record
</span><span class="lines">@@ -994,61 +1023,62 @@
</span><span class="cx"> 
</span><span class="cx">         if record not in records:
</span><span class="cx">             records.add(record)
</span><del>-            for relative in getattr(record, method)():
</del><ins>+            for relative in (yield getattr(record, method)()):
</ins><span class="cx">                 if relative not in records:
</span><del>-                    found = self.parent.principalForRecord(relative)
</del><ins>+                    found = (yield self.parent.principalForRecord(relative))
</ins><span class="cx">                     if found is None:
</span><span class="cx">                         log.error(&quot;No principal found for directory record: %r&quot; % (relative,))
</span><span class="cx">                     else:
</span><span class="cx">                         if proxy:
</span><span class="cx">                             if proxy == &quot;read-write&quot;:
</span><del>-                                found = found.getChild(&quot;calendar-proxy-write&quot;)
</del><ins>+                                found = (yield found.getChild(&quot;calendar-proxy-write&quot;))
</ins><span class="cx">                             else:
</span><del>-                                found = found.getChild(&quot;calendar-proxy-read&quot;)
</del><ins>+                                found = (yield found.getChild(&quot;calendar-proxy-read&quot;))
</ins><span class="cx">                         relatives.add(found)
</span><span class="cx"> 
</span><span class="cx">                     if infinity:
</span><del>-                        self._getRelatives(method, relative, relatives, records,
</del><ins>+                        yield self._getRelatives(method, relative, relatives, records,
</ins><span class="cx">                             infinity=infinity)
</span><span class="cx"> 
</span><del>-        return relatives
</del><ins>+        returnValue(relatives)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def groupMembers(self):
</span><del>-        return succeed(self._getRelatives(&quot;members&quot;))
</del><ins>+        return self._getRelatives(&quot;members&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def expandedGroupMembers(self):
</span><del>-        return succeed(self._getRelatives(&quot;members&quot;, infinity=True))
</del><ins>+        return self._getRelatives(&quot;members&quot;, infinity=True)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def groupMemberships(self, infinity=False):
</span><span class="cx"> 
</span><del>-        cache = getattr(self.record.service, &quot;groupMembershipCache&quot;, None)
-        if cache:
-            log.debug(&quot;groupMemberships is using groupMembershipCache&quot;)
-            guids = (yield self.record.cachedGroups())
-            groups = set()
-            for guid in guids:
-                principal = self.parent.principalForUID(guid)
-                if principal:
-                    groups.add(principal)
-        else:
-            groups = self._getRelatives(&quot;groups&quot;, infinity=infinity)
</del><ins>+        # cache = getattr(self.record.service, &quot;groupMembershipCache&quot;, None)
+        # if cache:
+        #     log.debug(&quot;groupMemberships is using groupMembershipCache&quot;)
+        #     guids = (yield self.record.cachedGroups())
+        #     groups = set()
+        #     for guid in guids:
+        #         principal = yield self.parent.principalForUID(guid)
+        #         if principal:
+        #             groups.add(principal)
+        # else:
+        groups = yield self._getRelatives(&quot;groups&quot;, infinity=infinity)
</ins><span class="cx"> 
</span><del>-        if config.EnableProxyPrincipals:
-            # Get proxy group UIDs and map to principal resources
-            proxies = []
-            memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
-            for uid in memberships:
-                subprincipal = self.parent.principalForUID(uid)
-                if subprincipal:
-                    proxies.append(subprincipal)
-                else:
-                    yield self._calendar_user_proxy_index().removeGroup(uid)
</del><ins>+        # MOVE2WHO
+        # if config.EnableProxyPrincipals:
+        #     # Get proxy group UIDs and map to principal resources
+        #     proxies = []
+        #     memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
+        #     for uid in memberships:
+        #         subprincipal = yield self.parent.principalForUID(uid)
+        #         if subprincipal:
+        #             proxies.append(subprincipal)
+        #         else:
+        #             yield self._calendar_user_proxy_index().removeGroup(uid)
</ins><span class="cx"> 
</span><del>-            groups.update(proxies)
</del><ins>+        #     groups.update(proxies)
</ins><span class="cx"> 
</span><span class="cx">         returnValue(groups)
</span><span class="cx"> 
</span><span class="lines">@@ -1099,7 +1129,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getAutoSchedule(self):
</span><del>-        return self.record.autoSchedule
</del><ins>+        # MOVE2WHO
+        return True
+        # return self.record.autoSchedule
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def canAutoSchedule(self, organizer=None):
</span><span class="lines">@@ -1187,18 +1219,19 @@
</span><span class="cx">         raise HTTPError(responsecode.NOT_FOUND)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def locateChild(self, req, segments):
</span><del>-        child = self.getChild(segments[0])
</del><ins>+        child = (yield self.getChild(segments[0]))
</ins><span class="cx">         if child is not None:
</span><del>-            return (child, segments[1:])
-        return (None, ())
</del><ins>+            returnValue((child, segments[1:]))
+        returnValue((None, ()))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getChild(self, name):
</span><span class="cx">         if name == &quot;&quot;:
</span><del>-            return self
</del><ins>+            return succeed(self)
</ins><span class="cx"> 
</span><del>-        return None
</del><ins>+        return succeed(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><span class="lines">@@ -1221,7 +1254,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def addressBooksEnabled(self):
</span><del>-        return config.EnableCardDAV and self.record.enabledForAddressBooks
</del><ins>+        return config.EnableCardDAV and self.record.hasContacts
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1288,7 +1321,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def calendarHomeURLs(self):
</span><del>-        if self.record.enabledForCalendaring:
</del><ins>+        if self.record.hasCalendars:
</ins><span class="cx">             homeURL = self._homeChildURL(None)
</span><span class="cx">         else:
</span><span class="cx">             homeURL = &quot;&quot;
</span><span class="lines">@@ -1318,7 +1351,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def addressBookHomeURLs(self):
</span><del>-        if self.record.enabledForAddressBooks:
</del><ins>+        if self.record.hasContacts:
</ins><span class="cx">             homeURL = self._addressBookHomeChildURL(None)
</span><span class="cx">         else:
</span><span class="cx">             homeURL = &quot;&quot;
</span><span class="lines">@@ -1391,22 +1424,27 @@
</span><span class="cx"> 
</span><span class="cx">     def getChild(self, name):
</span><span class="cx">         if name == &quot;&quot;:
</span><del>-            return self
</del><ins>+            return succeed(self)
</ins><span class="cx"> 
</span><del>-        if config.EnableProxyPrincipals and name in (&quot;calendar-proxy-read&quot;,
-                                                     &quot;calendar-proxy-write&quot;):
</del><ins>+        if config.EnableProxyPrincipals and name in (
+            &quot;calendar-proxy-read&quot;, &quot;calendar-proxy-write&quot;,
+            &quot;calendar-proxy-read-for&quot;, &quot;calendar-proxy-write-for&quot;,
+            ):
</ins><span class="cx">             # name is required to be str
</span><span class="cx">             from twistedcaldav.directory.calendaruserproxy import (
</span><span class="cx">                 CalendarUserProxyPrincipalResource
</span><span class="cx">             )
</span><del>-            return CalendarUserProxyPrincipalResource(self, str(name))
</del><ins>+            return succeed(CalendarUserProxyPrincipalResource(self, str(name)))
</ins><span class="cx">         else:
</span><del>-            return None
</del><ins>+            return succeed(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def listChildren(self):
</span><span class="cx">         if config.EnableProxyPrincipals:
</span><del>-            return (&quot;calendar-proxy-read&quot;, &quot;calendar-proxy-write&quot;)
</del><ins>+            return (
+                &quot;calendar-proxy-read&quot;, &quot;calendar-proxy-write&quot;,
+                &quot;calendar-proxy-read-for&quot;, &quot;calendar-proxy-write-for&quot;,
+            )
</ins><span class="cx">         else:
</span><span class="cx">             return ()
</span><span class="cx"> 
</span><span class="lines">@@ -1442,7 +1480,7 @@
</span><span class="cx"> 
</span><span class="cx">     def describe(principal):
</span><span class="cx">         if hasattr(principal, &quot;record&quot;):
</span><del>-            return &quot; - %s&quot; % (principal.record.fullName,)
</del><ins>+            return &quot; - %s&quot; % (principal.record.displayName,)
</ins><span class="cx">         else:
</span><span class="cx">             return &quot;&quot;
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavdirectorybackedaddressbookpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directorybackedaddressbook.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directorybackedaddressbook.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/directorybackedaddressbook.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -112,16 +112,18 @@
</span><span class="cx">             # DAV:Read for all authenticated principals (does not include anonymous)
</span><span class="cx">             accessPrincipal = davxml.Authenticated()
</span><span class="cx"> 
</span><del>-        return davxml.ACL(
-            davxml.ACE(
-                davxml.Principal(accessPrincipal),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
-                    davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet())
-                                ),
-                davxml.Protected(),
-                TwistedACLInheritable(),
-           ),
</del><ins>+        return succeed(
+            davxml.ACL(
+                davxml.ACE(
+                    davxml.Principal(accessPrincipal),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet())
+                                    ),
+                    davxml.Protected(),
+                    TwistedACLInheritable(),
+               ),
+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -160,7 +162,7 @@
</span><span class="cx"> 
</span><span class="cx">     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
</span><span class="cx">         # Permissions here are fixed, and are not subject to inheritance rules, etc.
</span><del>-        return succeed(self.defaultAccessControlList())
</del><ins>+        return self.defaultAccessControlList()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavextensionspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/extensions.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/extensions.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/extensions.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -299,7 +299,7 @@
</span><span class="cx">         records = (yield dir.recordsMatchingTokens(tokens, context=context))
</span><span class="cx"> 
</span><span class="cx">         for record in records:
</span><del>-            resource = principalCollection.principalForRecord(record)
</del><ins>+            resource = yield principalCollection.principalForRecord(record)
</ins><span class="cx">             if resource:
</span><span class="cx">                 matchingResources.append(resource)
</span><span class="cx"> 
</span><span class="lines">@@ -420,9 +420,9 @@
</span><span class="cx">                 f.trap(HTTPError)
</span><span class="cx">                 code = f.value.response.code
</span><span class="cx">                 if code == responsecode.NOT_FOUND:
</span><del>-                    log.error(&quot;Property %s was returned by listProperties() &quot;
-                              &quot;but does not exist for resource %s.&quot;
-                              % (name, self.resource))
</del><ins>+                    log.error(&quot;Property {p} was returned by listProperties() &quot;
+                              &quot;but does not exist for resource {r}.&quot;,
+                              p=name, r=self.resource)
</ins><span class="cx">                     return (name, None)
</span><span class="cx">                 if code == responsecode.UNAUTHORIZED:
</span><span class="cx">                     return (name, accessDeniedValue)
</span><span class="lines">@@ -721,7 +721,8 @@
</span><span class="cx"> 
</span><span class="cx">             elif name == &quot;record-type&quot;:
</span><span class="cx">                 if hasattr(self, &quot;record&quot;):
</span><del>-                    returnValue(customxml.RecordType(self.record.recordType))
</del><ins>+                    # MOVE2WHO -- need mapping
+                    returnValue(customxml.RecordType(self.record.recordType.name + &quot;s&quot;))
</ins><span class="cx">                 else:
</span><span class="cx">                     raise HTTPError(StatusResponse(
</span><span class="cx">                         responsecode.NOT_FOUND,
</span><span class="lines">@@ -848,7 +849,7 @@
</span><span class="cx">     ):
</span><span class="cx">         # Permissions here are fixed, and are not subject to
</span><span class="cx">         # inheritance rules, etc.
</span><del>-        return succeed(self.defaultAccessControlList())
</del><ins>+        return self.defaultAccessControlList()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavfreebusyurlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/freebusyurl.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/freebusyurl.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/freebusyurl.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -102,7 +102,7 @@
</span><span class="cx">                     davxml.Protected(),
</span><span class="cx">                 ),
</span><span class="cx">             )
</span><del>-        return davxml.ACL(*aces)
</del><ins>+        return succeed(davxml.ACL(*aces))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def resourceType(self):
</span><span class="lines">@@ -243,7 +243,7 @@
</span><span class="cx">         # TODO: We should probably verify that the actual time-range is within sensible bounds (e.g. not too far in the past or future and not too long)
</span><span class="cx"> 
</span><span class="cx">         # Now lookup the principal details for the targeted user
</span><del>-        principal = self.parent.principalForRecord()
</del><ins>+        principal = (yield self.parent.principalForRecord())
</ins><span class="cx"> 
</span><span class="cx">         # Pick the first mailto cu address or the first other type
</span><span class="cx">         cuaddr = None
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavicalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/ical.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/ical.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/ical.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> import itertools
</span><span class="cx"> import uuid
</span><span class="cx"> 
</span><ins>+from twisted.internet.defer import inlineCallbacks, returnValue
</ins><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from txweb2.stream import IStream
</span><span class="cx"> from txweb2.dav.util import allDataFromStream
</span><span class="lines">@@ -3239,6 +3240,7 @@
</span><span class="cx">                         self.removeProperty(attachment)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def normalizeCalendarUserAddresses(self, lookupFunction, principalFunction,
</span><span class="cx">         toUUID=True):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -3259,7 +3261,7 @@
</span><span class="cx">                 # Check that we can lookup this calendar user address - if not
</span><span class="cx">                 # we cannot do anything with it
</span><span class="cx">                 cuaddr = normalizeCUAddr(prop.value())
</span><del>-                name, guid, cuaddrs = lookupFunction(cuaddr, principalFunction, config)
</del><ins>+                name, guid, cuaddrs = yield lookupFunction(cuaddr, principalFunction, config)
</ins><span class="cx">                 if guid is None:
</span><span class="cx">                     continue
</span><span class="cx"> 
</span><span class="lines">@@ -3275,7 +3277,9 @@
</span><span class="cx"> 
</span><span class="cx">                 if toUUID:
</span><span class="cx">                     # Always re-write value to urn:uuid
</span><del>-                    prop.setValue(&quot;urn:uuid:%s&quot; % (guid,))
</del><ins>+                    if isinstance(guid, uuid.UUID):
+                        guid = unicode(guid).upper()
+                    prop.setValue(&quot;urn:uuid:{guid}&quot;.format(guid=guid))
</ins><span class="cx"> 
</span><span class="cx">                 # If it is already a non-UUID address leave it be
</span><span class="cx">                 elif cuaddr.startswith(&quot;urn:uuid:&quot;):
</span><span class="lines">@@ -3353,7 +3357,7 @@
</span><span class="cx"> 
</span><span class="cx">             # For VPOLL also do immediate children
</span><span class="cx">             if component.name() == &quot;VPOLL&quot;:
</span><del>-                component.normalizeCalendarUserAddresses(lookupFunction, principalFunction, toUUID)
</del><ins>+                yield component.normalizeCalendarUserAddresses(lookupFunction, principalFunction, toUUID)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def allPerUserUIDs(self):
</span><span class="lines">@@ -3563,15 +3567,16 @@
</span><span class="cx"> # Utilities
</span><span class="cx"> # #
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def normalizeCUAddress(cuaddr, lookupFunction, principalFunction, toUUID=True):
</span><span class="cx">     # Check that we can lookup this calendar user address - if not
</span><span class="cx">     # we cannot do anything with it
</span><del>-    _ignore_name, guid, cuaddrs = lookupFunction(normalizeCUAddr(cuaddr), principalFunction, config)
</del><ins>+    _ignore_name, guid, cuaddrs = (yield lookupFunction(normalizeCUAddr(cuaddr), principalFunction, config))
</ins><span class="cx"> 
</span><span class="cx">     if toUUID:
</span><span class="cx">         # Always re-write value to urn:uuid
</span><span class="cx">         if guid:
</span><del>-            return &quot;urn:uuid:%s&quot; % (guid,)
</del><ins>+            returnValue(&quot;urn:uuid:%s&quot; % (guid,))
</ins><span class="cx"> 
</span><span class="cx">     # If it is already a non-UUID address leave it be
</span><span class="cx">     elif cuaddr.startswith(&quot;urn:uuid:&quot;):
</span><span class="lines">@@ -3610,9 +3615,9 @@
</span><span class="cx"> 
</span><span class="cx">         # Make the change
</span><span class="cx">         if newaddr:
</span><del>-            return newaddr
</del><ins>+            returnValue(newaddr)
</ins><span class="cx"> 
</span><del>-    return cuaddr
</del><ins>+    returnValue(cuaddr)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/resource.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/resource.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/resource.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -863,7 +863,8 @@
</span><span class="cx">                 home = self._newStoreObject.parentCollection().ownerHome()
</span><span class="cx">             else:
</span><span class="cx">                 home = self._newStoreObject.ownerHome()
</span><del>-            returnValue(element.HRef(self.principalForUID(home.uid()).principalURL()))
</del><ins>+            principal = (yield self.principalForUID(home.uid()))
+            returnValue(element.HRef(principal.principalURL()))
</ins><span class="cx">         else:
</span><span class="cx">             parent = (yield self.locateParent(request, request.urlForResource(self)))
</span><span class="cx">         if parent and isinstance(parent, CalDAVResource):
</span><span class="lines">@@ -883,7 +884,7 @@
</span><span class="cx">                 home = self._newStoreObject.parentCollection().ownerHome()
</span><span class="cx">             else:
</span><span class="cx">                 home = self._newStoreObject.ownerHome()
</span><del>-            returnValue(self.principalForUID(home.uid()))
</del><ins>+            returnValue((yield self.principalForUID(home.uid())))
</ins><span class="cx">         else:
</span><span class="cx">             parent = (yield self.locateParent(request, request.urlForResource(self)))
</span><span class="cx">         if parent and isinstance(parent, CalDAVResource):
</span><span class="lines">@@ -933,8 +934,8 @@
</span><span class="cx">             return None
</span><span class="cx"> 
</span><span class="cx">         if 'record' in dir(self):
</span><del>-            if self.record.fullName:
-                return self.record.fullName
</del><ins>+            if self.record.fullNames:
+                return self.record.fullNames[0]
</ins><span class="cx">             elif self.record.shortNames:
</span><span class="cx">                 return self.record.shortNames[0]
</span><span class="cx">             else:
</span><span class="lines">@@ -1063,6 +1064,7 @@
</span><span class="cx">         returnValue(PerUserDataFilter(accessUID).filter(caldata))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    # MOVE2WHO returns Deferred
</ins><span class="cx">     def iCalendarAddressDoNormalization(self, ical):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Normalize calendar user addresses in the supplied iCalendar object into their
</span><span class="lines">@@ -1071,24 +1073,26 @@
</span><span class="cx">         @param ical: calendar object to normalize.
</span><span class="cx">         @type ical: L{Component}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        ical.normalizeCalendarUserAddresses(normalizationLookup,
</del><ins>+        return ical.normalizeCalendarUserAddresses(normalizationLookup,
</ins><span class="cx">             self.principalForCalendarUserAddress)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def principalForCalendarUserAddress(self, address):
</span><span class="cx">         for principalCollection in self.principalCollections():
</span><del>-            principal = principalCollection.principalForCalendarUserAddress(address)
</del><ins>+            principal = (yield principalCollection.principalForCalendarUserAddress(address))
</ins><span class="cx">             if principal is not None:
</span><del>-                return principal
-        return None
</del><ins>+                returnValue(principal)
+        returnValue(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def principalForUID(self, principalUID):
</span><span class="cx">         for principalCollection in self.principalCollections():
</span><del>-            principal = principalCollection.principalForUID(principalUID)
</del><ins>+            principal = (yield principalCollection.principalForUID(principalUID))
</ins><span class="cx">             if principal is not None:
</span><del>-                return principal
-        return None
</del><ins>+                returnValue(principal)
+        returnValue(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1874,7 +1878,7 @@
</span><span class="cx"> 
</span><span class="cx">             elif name == &quot;auto-schedule-mode&quot; and self.calendarsEnabled():
</span><span class="cx">                 autoScheduleMode = self.getAutoScheduleMode()
</span><del>-                returnValue(customxml.AutoScheduleMode(autoScheduleMode if autoScheduleMode else &quot;default&quot;))
</del><ins>+                returnValue(customxml.AutoScheduleMode(autoScheduleMode.description if autoScheduleMode else &quot;default&quot;))
</ins><span class="cx"> 
</span><span class="cx">         elif namespace == carddav_namespace and self.addressBooksEnabled():
</span><span class="cx">             if name == &quot;addressbook-home-set&quot;:
</span><span class="lines">@@ -2302,20 +2306,23 @@
</span><span class="cx">     # ACL
</span><span class="cx">     ##
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def owner(self, request):
</span><del>-        return succeed(element.HRef(self.principalForRecord().principalURL()))
</del><ins>+        principal = yield self.principalForRecord()
+        returnValue(element.HRef(principal.principalURL()))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def ownerPrincipal(self, request):
</span><del>-        return succeed(self.principalForRecord())
</del><ins>+        return self.principalForRecord()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def resourceOwnerPrincipal(self, request):
</span><del>-        return succeed(self.principalForRecord())
</del><ins>+        return self.principalForRecord()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def defaultAccessControlList(self):
</span><del>-        myPrincipal = self.principalForRecord()
</del><ins>+        myPrincipal = yield self.principalForRecord()
</ins><span class="cx"> 
</span><span class="cx">         # Server may be read only
</span><span class="cx">         if config.EnableReadOnlyServer:
</span><span class="lines">@@ -2342,12 +2349,12 @@
</span><span class="cx">         # Give all access to config.AdminPrincipals
</span><span class="cx">         aces += config.AdminACEs
</span><span class="cx"> 
</span><del>-        return element.ACL(*aces)
</del><ins>+        returnValue(element.ACL(*aces))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
</span><span class="cx">         # Permissions here are fixed, and are not subject to inheritance rules, etc.
</span><del>-        return succeed(self.defaultAccessControlList())
</del><ins>+        return self.defaultAccessControlList()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def principalCollections(self):
</span><span class="lines">@@ -2555,9 +2562,10 @@
</span><span class="cx">         return config.Sharing.Enabled and config.Sharing.Calendars.Enabled and self.exists()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _otherPrincipalHomeURL(self, otherUID):
</span><del>-        ownerPrincipal = self.principalForUID(otherUID)
-        return ownerPrincipal.calendarHomeURLs()[0]
</del><ins>+        ownerPrincipal = (yield self.principalForUID(otherUID))
+        returnValue(ownerPrincipal.calendarHomeURLs()[0])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -2584,8 +2592,9 @@
</span><span class="cx">         return self._newStoreHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object._newStoreObject, mode)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def defaultAccessControlList(self):
</span><del>-        myPrincipal = self.principalForRecord()
</del><ins>+        myPrincipal = yield self.principalForRecord()
</ins><span class="cx"> 
</span><span class="cx">         # Server may be read only
</span><span class="cx">         if config.EnableReadOnlyServer:
</span><span class="lines">@@ -2652,7 +2661,7 @@
</span><span class="cx">                 ),
</span><span class="cx">             )
</span><span class="cx"> 
</span><del>-        return element.ACL(*aces)
</del><ins>+        returnValue(element.ACL(*aces))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -2808,9 +2817,10 @@
</span><span class="cx">         return config.Sharing.Enabled and config.Sharing.AddressBooks.Enabled and self.exists()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _otherPrincipalHomeURL(self, otherUID):
</span><del>-        ownerPrincipal = self.principalForUID(otherUID)
-        return ownerPrincipal.addressBookHomeURLs()[0]
</del><ins>+        ownerPrincipal = (yield self.principalForUID(otherUID))
+        returnValue(ownerPrincipal.addressBookHomeURLs()[0])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavscheduling_storecaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/scheduling_store/caldav/resource.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/scheduling_store/caldav/resource.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/scheduling_store/caldav/resource.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -373,12 +373,14 @@
</span><span class="cx">         if config.Scheduling.CalDAV.OldDraftCompatibility:
</span><span class="cx">             privs += (davxml.Privilege(caldavxml.Schedule()),)
</span><span class="cx"> 
</span><del>-        return davxml.ACL(
-            # CalDAV:schedule-deliver for any authenticated user
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(*privs),
-            ),
</del><ins>+        return succeed(
+            davxml.ACL(
+                # CalDAV:schedule-deliver for any authenticated user
+                davxml.ACE(
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(*privs),
+                ),
+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -532,9 +534,10 @@
</span><span class="cx">         return succeed(sendSchedulePrivilegeSet)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def defaultAccessControlList(self):
</span><span class="cx">         if config.EnableProxyPrincipals:
</span><del>-            myPrincipal = self.parent.principalForRecord()
</del><ins>+            myPrincipal = yield self.parent.principalForRecord()
</ins><span class="cx"> 
</span><span class="cx">             privs = (
</span><span class="cx">                 davxml.Privilege(caldavxml.ScheduleSend()),
</span><span class="lines">@@ -542,16 +545,18 @@
</span><span class="cx">             if config.Scheduling.CalDAV.OldDraftCompatibility:
</span><span class="cx">                 privs += (davxml.Privilege(caldavxml.Schedule()),)
</span><span class="cx"> 
</span><del>-            return davxml.ACL(
-                # CalDAV:schedule for associated write proxies
-                davxml.ACE(
-                    davxml.Principal(davxml.HRef(joinURL(myPrincipal.principalURL(), &quot;calendar-proxy-write&quot;))),
-                    davxml.Grant(*privs),
-                    davxml.Protected(),
-                ),
</del><ins>+            returnValue(
+                davxml.ACL(
+                    # CalDAV:schedule for associated write proxies
+                    davxml.ACE(
+                        davxml.Principal(davxml.HRef(joinURL(myPrincipal.principalURL(), &quot;calendar-proxy-write&quot;))),
+                        davxml.Grant(*privs),
+                        davxml.Protected(),
+                    ),
+                )
</ins><span class="cx">             )
</span><span class="cx">         else:
</span><del>-            return super(ScheduleOutboxResource, self).defaultAccessControlList()
</del><ins>+            returnValue(super(ScheduleOutboxResource, self).defaultAccessControlList())
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def report_urn_ietf_params_xml_ns_caldav_calendar_query(self, request, calendar_query):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavsharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/sharing.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/sharing.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/sharing.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -792,7 +792,7 @@
</span><span class="cx">         Set shared state and check access control.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if child._newStoreObject is not None and not child._newStoreObject.owned():
</span><del>-            ownerHomeURL = self._otherPrincipalHomeURL(child._newStoreObject.ownerHome().uid())
</del><ins>+            ownerHomeURL = (yield self._otherPrincipalHomeURL(child._newStoreObject.ownerHome().uid()))
</ins><span class="cx">             ownerView = yield child._newStoreObject.ownerView()
</span><span class="cx">             child.setShare(joinURL(ownerHomeURL, ownerView.name()))
</span><span class="cx">             access = yield child._checkAccessControl()
</span><span class="lines">@@ -802,6 +802,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def _otherPrincipalHomeURL(self, otherUID):
</span><ins>+        # Is this only meant to be overridden?
</ins><span class="cx">         pass
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/storebridge.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/storebridge.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/storebridge.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -1763,11 +1763,12 @@
</span><span class="cx">         return succeed(davPrivilegeSet)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def defaultAccessControlList(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Only read privileges allowed for managed attachments.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        myPrincipal = self.parent.principalForRecord()
</del><ins>+        myPrincipal = yield self.parent.principalForRecord()
</ins><span class="cx"> 
</span><span class="cx">         read_privs = (
</span><span class="cx">             davxml.Privilege(davxml.Read()),
</span><span class="lines">@@ -1808,12 +1809,12 @@
</span><span class="cx">                 ),
</span><span class="cx">             )
</span><span class="cx"> 
</span><del>-        return davxml.ACL(*aces)
</del><ins>+        returnValue(davxml.ACL(*aces))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
</span><span class="cx">         # Permissions here are fixed, and are not subject to inheritance rules, etc.
</span><del>-        return succeed(self.defaultAccessControlList())
</del><ins>+        return self.defaultAccessControlList()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavtesttest_icalendarpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_icalendar.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_icalendar.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_icalendar.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -19,6 +19,7 @@
</span><span class="cx"> import itertools
</span><span class="cx"> 
</span><span class="cx"> from twisted.trial.unittest import SkipTest
</span><ins>+from twisted.internet.defer import inlineCallbacks, succeed
</ins><span class="cx"> 
</span><span class="cx"> from twistedcaldav.ical import Component, Property, InvalidICalendarDataError, \
</span><span class="cx">     normalizeCUAddress, normalize_iCalStr
</span><span class="lines">@@ -32,6 +33,8 @@
</span><span class="cx"> from twistedcaldav.dateops import normalizeForExpand
</span><span class="cx"> from pycalendar.value import Value
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class iCalendar (twistedcaldav.test.util.TestCase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     iCalendar support tests
</span><span class="lines">@@ -7497,6 +7500,7 @@
</span><span class="cx">             self.assertEquals(expected, ical.hasInstancesAfter(cutoff))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCalendarUserAddressesFromUUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure mailto is preferred, followed by path form, then http form.
</span><span class="lines">@@ -7520,25 +7524,27 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         def lookupFunction(cuaddr, ignored1, ignored2):
</span><del>-            return {
-                &quot;urn:uuid:foo&quot; : (
-                    &quot;Foo&quot;,
-                    &quot;foo&quot;,
-                    (&quot;urn:uuid:foo&quot;, &quot;http://example.com/foo&quot;, &quot;/foo&quot;)
-                ),
-                &quot;urn:uuid:bar&quot; : (
-                    &quot;Bar&quot;,
-                    &quot;bar&quot;,
-                    (&quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;, &quot;http://example.com/bar&quot;, &quot;/bar&quot;)
-                ),
-                &quot;urn:uuid:baz&quot; : (
-                    &quot;Baz&quot;,
-                    &quot;baz&quot;,
-                    (&quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;)
-                ),
-            }[cuaddr]
</del><ins>+            return succeed(
+                {
+                    &quot;urn:uuid:foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        (&quot;urn:uuid:foo&quot;, &quot;http://example.com/foo&quot;, &quot;/foo&quot;)
+                    ),
+                    &quot;urn:uuid:bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        (&quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;, &quot;http://example.com/bar&quot;, &quot;/bar&quot;)
+                    ),
+                    &quot;urn:uuid:baz&quot; : (
+                        &quot;Baz&quot;,
+                        &quot;baz&quot;,
+                        (&quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;)
+                    ),
+                }[cuaddr]
+            )
</ins><span class="cx"> 
</span><del>-        component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=False)
</del><ins>+        yield component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=False)
</ins><span class="cx"> 
</span><span class="cx">         self.assertEquals(&quot;mailto:bar@example.com&quot;,
</span><span class="cx">             component.getAttendeeProperty((&quot;mailto:bar@example.com&quot;,)).value())
</span><span class="lines">@@ -7548,6 +7554,7 @@
</span><span class="cx">             component.getAttendeeProperty((&quot;http://example.com/baz&quot;,)).value())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCalendarUserAddressesAndLocationChange(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure http(s) and /path CUA values are tucked away into the property
</span><span class="lines">@@ -7573,25 +7580,27 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         def lookupFunction(cuaddr, ignored1, ignored2):
</span><del>-            return {
-                &quot;/principals/users/foo&quot; : (
-                    &quot;Foo&quot;,
-                    &quot;foo&quot;,
-                    (&quot;urn:uuid:foo&quot;,)
-                ),
-                &quot;http://example.com/principals/users/bar&quot; : (
-                    &quot;Bar&quot;,
-                    &quot;bar&quot;,
-                    (&quot;urn:uuid:bar&quot;,)
-                ),
-                &quot;http://example.com/principals/locations/buzz&quot; : (
-                    &quot;{Restricted} Buzz&quot;,
-                    &quot;buzz&quot;,
-                    (&quot;urn:uuid:buzz&quot;,)
-                ),
-            }[cuaddr]
</del><ins>+            return succeed(
+                {
+                    &quot;/principals/users/foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        (&quot;urn:uuid:foo&quot;,)
+                    ),
+                    &quot;http://example.com/principals/users/bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        (&quot;urn:uuid:bar&quot;,)
+                    ),
+                    &quot;http://example.com/principals/locations/buzz&quot; : (
+                        &quot;{Restricted} Buzz&quot;,
+                        &quot;buzz&quot;,
+                        (&quot;urn:uuid:buzz&quot;,)
+                    ),
+                }[cuaddr]
+            )
</ins><span class="cx"> 
</span><del>-        component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=True)
</del><ins>+        yield component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=True)
</ins><span class="cx"> 
</span><span class="cx">         # Location value changed
</span><span class="cx">         prop = component.mainComponent().getProperty(&quot;LOCATION&quot;)
</span><span class="lines">@@ -7601,6 +7610,7 @@
</span><span class="cx">         self.assertEquals(prop.parameterValue(&quot;CN&quot;), &quot;{Restricted} Buzz&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCalendarUserAddressesAndLocationNoChange(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure http(s) and /path CUA values are tucked away into the property
</span><span class="lines">@@ -7626,25 +7636,27 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         def lookupFunction(cuaddr, ignored1, ignored2):
</span><del>-            return {
-                &quot;/principals/users/foo&quot; : (
-                    &quot;Foo&quot;,
-                    &quot;foo&quot;,
-                    (&quot;urn:uuid:foo&quot;,)
-                ),
-                &quot;http://example.com/principals/users/bar&quot; : (
-                    &quot;Bar&quot;,
-                    &quot;bar&quot;,
-                    (&quot;urn:uuid:bar&quot;,)
-                ),
-                &quot;http://example.com/principals/locations/buzz&quot; : (
-                    &quot;{Restricted} Buzz&quot;,
-                    &quot;buzz&quot;,
-                    (&quot;urn:uuid:buzz&quot;,)
-                ),
-            }[cuaddr]
</del><ins>+            return succeed(
+                {
+                    &quot;/principals/users/foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        (&quot;urn:uuid:foo&quot;,)
+                    ),
+                    &quot;http://example.com/principals/users/bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        (&quot;urn:uuid:bar&quot;,)
+                    ),
+                    &quot;http://example.com/principals/locations/buzz&quot; : (
+                        &quot;{Restricted} Buzz&quot;,
+                        &quot;buzz&quot;,
+                        (&quot;urn:uuid:buzz&quot;,)
+                    ),
+                }[cuaddr]
+            )
</ins><span class="cx"> 
</span><del>-        component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=True)
</del><ins>+        yield component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=True)
</ins><span class="cx"> 
</span><span class="cx">         # Location value changed
</span><span class="cx">         prop = component.mainComponent().getProperty(&quot;LOCATION&quot;)
</span><span class="lines">@@ -7654,6 +7666,7 @@
</span><span class="cx">         self.assertEquals(prop.parameterValue(&quot;CN&quot;), &quot;{Restricted} Buzz&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCalendarUserAddressesAndLocationNoChangeOtherCUType(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure http(s) and /path CUA values are tucked away into the property
</span><span class="lines">@@ -7679,25 +7692,27 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         def lookupFunction(cuaddr, ignored1, ignored2):
</span><del>-            return {
-                &quot;/principals/users/foo&quot; : (
-                    &quot;Foo&quot;,
-                    &quot;foo&quot;,
-                    (&quot;urn:uuid:foo&quot;,)
-                ),
-                &quot;http://example.com/principals/users/bar&quot; : (
-                    &quot;Bar&quot;,
-                    &quot;bar&quot;,
-                    (&quot;urn:uuid:bar&quot;,)
-                ),
-                &quot;http://example.com/principals/locations/buzz&quot; : (
-                    &quot;{Restricted} Buzz&quot;,
-                    &quot;buzz&quot;,
-                    (&quot;urn:uuid:buzz&quot;,)
-                ),
-            }[cuaddr]
</del><ins>+            return succeed(
+                {
+                    &quot;/principals/users/foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        (&quot;urn:uuid:foo&quot;,)
+                    ),
+                    &quot;http://example.com/principals/users/bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        (&quot;urn:uuid:bar&quot;,)
+                    ),
+                    &quot;http://example.com/principals/locations/buzz&quot; : (
+                        &quot;{Restricted} Buzz&quot;,
+                        &quot;buzz&quot;,
+                        (&quot;urn:uuid:buzz&quot;,)
+                    ),
+                }[cuaddr]
+            )
</ins><span class="cx"> 
</span><del>-        component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=True)
</del><ins>+        yield component.normalizeCalendarUserAddresses(lookupFunction, None, toUUID=True)
</ins><span class="cx"> 
</span><span class="cx">         # Location value changed
</span><span class="cx">         prop = component.mainComponent().getProperty(&quot;LOCATION&quot;)
</span><span class="lines">@@ -8404,6 +8419,7 @@
</span><span class="cx">             self.assertEqual(changed, result_changed)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCUAddressFromUUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure mailto is preferred, followed by path form, then http form.
</span><span class="lines">@@ -8418,34 +8434,37 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         def lookupFunction(cuaddr, ignored1, ignored2):
</span><del>-            return {
-                &quot;urn:uuid:foo&quot; : (
-                    &quot;Foo&quot;,
-                    &quot;foo&quot;,
-                    (&quot;urn:uuid:foo&quot;, &quot;http://example.com/foo&quot;, &quot;/foo&quot;)
-                ),
-                &quot;urn:uuid:bar&quot; : (
-                    &quot;Bar&quot;,
-                    &quot;bar&quot;,
-                    (&quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;, &quot;http://example.com/bar&quot;, &quot;/bar&quot;)
-                ),
-                &quot;urn:uuid:baz&quot; : (
-                    &quot;Baz&quot;,
-                    &quot;baz&quot;,
-                    (&quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;)
-                ),
-                &quot;urn:uuid:buz&quot; : (
-                    &quot;Buz&quot;,
-                    &quot;buz&quot;,
-                    (&quot;urn:uuid:buz&quot;,)
-                ),
-            }[cuaddr]
</del><ins>+            return succeed(
+                {
+                    &quot;urn:uuid:foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        (&quot;urn:uuid:foo&quot;, &quot;http://example.com/foo&quot;, &quot;/foo&quot;)
+                    ),
+                    &quot;urn:uuid:bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        (&quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;, &quot;http://example.com/bar&quot;, &quot;/bar&quot;)
+                    ),
+                    &quot;urn:uuid:baz&quot; : (
+                        &quot;Baz&quot;,
+                        &quot;baz&quot;,
+                        (&quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;)
+                    ),
+                    &quot;urn:uuid:buz&quot; : (
+                        &quot;Buz&quot;,
+                        &quot;buz&quot;,
+                        (&quot;urn:uuid:buz&quot;,)
+                    ),
+                }[cuaddr]
+            )
</ins><span class="cx"> 
</span><span class="cx">         for cuaddr, result in data:
</span><del>-            new_cuaddr = normalizeCUAddress(cuaddr, lookupFunction, None, toUUID=False)
</del><ins>+            new_cuaddr = yield normalizeCUAddress(cuaddr, lookupFunction, None, toUUID=False)
</ins><span class="cx">             self.assertEquals(new_cuaddr, result)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCUAddressToUUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure http(s) and /path CUA values are tucked away into the property
</span><span class="lines">@@ -8459,21 +8478,23 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         def lookupFunction(cuaddr, ignored1, ignored2):
</span><del>-            return {
-                &quot;/principals/users/foo&quot; : (
-                    &quot;Foo&quot;,
-                    &quot;foo&quot;,
-                    (&quot;urn:uuid:foo&quot;,)
-                ),
-                &quot;http://example.com/principals/users/buz&quot; : (
-                    &quot;Buz&quot;,
-                    &quot;buz&quot;,
-                    (&quot;urn:uuid:buz&quot;,)
-                ),
-            }[cuaddr]
</del><ins>+            return succeed(
+                {
+                    &quot;/principals/users/foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        (&quot;urn:uuid:foo&quot;,)
+                    ),
+                    &quot;http://example.com/principals/users/buz&quot; : (
+                        &quot;Buz&quot;,
+                        &quot;buz&quot;,
+                        (&quot;urn:uuid:buz&quot;,)
+                    ),
+                }[cuaddr]
+            )
</ins><span class="cx"> 
</span><span class="cx">         for cuaddr, result in data:
</span><del>-            new_cuaddr = normalizeCUAddress(cuaddr, lookupFunction, None, toUUID=True)
</del><ins>+            new_cuaddr = yield normalizeCUAddress(cuaddr, lookupFunction, None, toUUID=True)
</ins><span class="cx">             self.assertEquals(new_cuaddr, result)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavtesttest_upgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_upgrade.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_upgrade.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/test_upgrade.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -1536,6 +1536,7 @@
</span><span class="cx">         self.assertFalse(changed)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def test_normalizeCUAddrs(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure that calendar user addresses (CUAs) are cached so we can
</span><span class="lines">@@ -1577,8 +1578,8 @@
</span><span class="cx"> 
</span><span class="cx">         directory = StubDirectory()
</span><span class="cx">         cuaCache = {}
</span><del>-        normalizeCUAddrs(normalizeEvent, directory, cuaCache)
-        normalizeCUAddrs(normalizeEvent, directory, cuaCache)
</del><ins>+        yield normalizeCUAddrs(normalizeEvent, directory, cuaCache)
+        yield normalizeCUAddrs(normalizeEvent, directory, cuaCache)
</ins><span class="cx"> 
</span><span class="cx">         # Ensure we only called principalForCalendarUserAddress 3 times.  It
</span><span class="cx">         # would have been 8 times without the cuaCache.
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavtestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/test/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -50,11 +50,12 @@
</span><span class="cx"> from calendarserver.provision.root import RootResource
</span><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><del>-from txdav.caldav.datastore.test.util import buildCalendarStore
-from calendarserver.tap.util import getRootResource, directoryFromConfig
</del><ins>+from calendarserver.tap.util import getRootResource
</ins><span class="cx"> from txweb2.dav.test.util import SimpleRequest
</span><span class="cx"> from twistedcaldav.directory.util import transactionFromRequest
</span><span class="cx"> from twistedcaldav.directory.directory import DirectoryService
</span><ins>+from txdav.caldav.datastore.test.util import buildCalendarStore
+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavtimezoneservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezoneservice.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezoneservice.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezoneservice.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -86,15 +86,17 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return davxml.ACL(
-            # DAV:Read for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
</del><ins>+        return succeed(
+            davxml.ACL(
+                # DAV:Read for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                    ),
+                    davxml.Protected(),
</ins><span class="cx">                 ),
</span><del>-                davxml.Protected(),
-            ),
</del><ins>+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavtimezonestdservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezonestdservice.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezonestdservice.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/timezonestdservice.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -146,15 +146,17 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def defaultAccessControlList(self):
</span><del>-        return davxml.ACL(
-            # DAV:Read for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(
-                    davxml.Privilege(davxml.Read()),
</del><ins>+        return succeed(
+            davxml.ACL(
+                # DAV:Read for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                    ),
+                    davxml.Protected(),
</ins><span class="cx">                 ),
</span><del>-                davxml.Protected(),
-            ),
</del><ins>+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/upgrade.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/upgrade.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/upgrade.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -60,7 +60,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twisted.protocols.amp import AMP, Command, String, Boolean
</span><span class="cx"> 
</span><del>-from calendarserver.tap.util import getRootResource, FakeRequest, directoryFromConfig
</del><ins>+from calendarserver.tap.util import getRootResource, FakeRequest
</ins><span class="cx"> from calendarserver.tools.util import getDirectory
</span><span class="cx"> 
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.mailgateway import migrateTokensToStore
</span><span class="lines">@@ -121,6 +121,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def upgradeCalendarCollection(calPath, directory, cuaCache):
</span><span class="cx">     errorOccurred = False
</span><span class="cx">     collectionUpdated = False
</span><span class="lines">@@ -164,7 +165,7 @@
</span><span class="cx">                 continue
</span><span class="cx"> 
</span><span class="cx">             try:
</span><del>-                data, fixed = normalizeCUAddrs(data, directory, cuaCache)
</del><ins>+                data, fixed = (yield normalizeCUAddrs(data, directory, cuaCache))
</ins><span class="cx">                 if fixed:
</span><span class="cx">                     log.debug(&quot;Normalized CUAddrs in %s&quot; % (resPath,))
</span><span class="cx">                     needsRewrite = True
</span><span class="lines">@@ -207,10 +208,11 @@
</span><span class="cx">         except:
</span><span class="cx">             raise
</span><span class="cx"> 
</span><del>-    return errorOccurred
</del><ins>+    returnValue(errorOccurred)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def upgradeCalendarHome(homePath, directory, cuaCache):
</span><span class="cx"> 
</span><span class="cx">     errorOccurred = False
</span><span class="lines">@@ -229,7 +231,7 @@
</span><span class="cx">                 rmdir(calPath)
</span><span class="cx">                 continue
</span><span class="cx">             log.debug(&quot;Upgrading calendar: %s&quot; % (calPath,))
</span><del>-            if not upgradeCalendarCollection(calPath, directory, cuaCache):
</del><ins>+            if not (yield upgradeCalendarCollection(calPath, directory, cuaCache)):
</ins><span class="cx">                 errorOccurred = True
</span><span class="cx"> 
</span><span class="cx">             # Change the calendar-free-busy-set xattrs of the inbox to the
</span><span class="lines">@@ -254,7 +256,7 @@
</span><span class="cx">         log.error(&quot;Failed to upgrade calendar home %s: %s&quot; % (homePath, e))
</span><span class="cx">         raise
</span><span class="cx"> 
</span><del>-    return errorOccurred
</del><ins>+    returnValue(errorOccurred)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -288,9 +290,10 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @UpgradeOneHome.responder
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def upgradeOne(self, path):
</span><del>-        result = upgradeCalendarHome(path, self.directory, self.cuaCache)
-        return dict(succeeded=result)
</del><ins>+        result = yield upgradeCalendarHome(path, self.directory, self.cuaCache)
+        returnValue(dict(succeeded=result))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -543,9 +546,9 @@
</span><span class="cx">                                         # Skip non-directories
</span><span class="cx">                                         continue
</span><span class="cx"> 
</span><del>-                                    if not upgradeCalendarHome(
</del><ins>+                                    if not (yield upgradeCalendarHome(
</ins><span class="cx">                                         homePath, directory, cuaCache
</span><del>-                                    ):
</del><ins>+                                    )):
</ins><span class="cx">                                         setError()
</span><span class="cx"> 
</span><span class="cx">                                     count += 1
</span><span class="lines">@@ -564,6 +567,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def normalizeCUAddrs(data, directory, cuaCache):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Normalize calendar user addresses to urn:uuid: form.
</span><span class="lines">@@ -583,23 +587,24 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     cal = Component.fromString(data)
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def lookupFunction(cuaddr, principalFunction, config):
</span><span class="cx"> 
</span><span class="cx">         # Return cached results, if any.
</span><span class="cx">         if cuaddr in cuaCache:
</span><del>-            return cuaCache[cuaddr]
</del><ins>+            returnValue(cuaCache[cuaddr])
</ins><span class="cx"> 
</span><del>-        result = normalizationLookup(cuaddr, principalFunction, config)
</del><ins>+        result = yield normalizationLookup(cuaddr, principalFunction, config)
</ins><span class="cx"> 
</span><span class="cx">         # Cache the result
</span><span class="cx">         cuaCache[cuaddr] = result
</span><del>-        return result
</del><ins>+        returnValue(result)
</ins><span class="cx"> 
</span><del>-    cal.normalizeCalendarUserAddresses(lookupFunction,
</del><ins>+    yield cal.normalizeCalendarUserAddresses(lookupFunction,
</ins><span class="cx">         directory.principalForCalendarUserAddress)
</span><span class="cx"> 
</span><span class="cx">     newData = str(cal)
</span><del>-    return newData, not newData == data
</del><ins>+    returnValue(newData, not newData == data)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -997,7 +1002,9 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Execute the step.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        return self.doUpgrade()
</del><ins>+        return succeed(None)
+        # MOVE2WHO
+        # return self.doUpgrade()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1025,7 +1032,7 @@
</span><span class="cx">     def stepWithResult(self, result):
</span><span class="cx">         if self.doPostImport:
</span><span class="cx"> 
</span><del>-            directory = directoryFromConfig(self.config)
</del><ins>+            directory = self.store.directoryService()
</ins><span class="cx"> 
</span><span class="cx">             # Load proxy assignments from XML if specified
</span><span class="cx">             if self.config.ProxyLoadFromFile:
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2twistedcaldavutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/twistedcaldav/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -23,6 +23,7 @@
</span><span class="cx"> from hashlib import md5, sha1
</span><span class="cx"> 
</span><span class="cx"> from twisted.internet import ssl, reactor
</span><ins>+from twisted.internet.defer import inlineCallbacks, returnValue
</ins><span class="cx"> from twisted.web import client
</span><span class="cx"> from twisted.python import failure
</span><span class="cx"> from twext.python.log import Logger
</span><span class="lines">@@ -495,6 +496,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def normalizationLookup(cuaddr, principalFunction, config):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Lookup function to be passed to ical.normalizeCalendarUserAddresses.
</span><span class="lines">@@ -503,13 +505,13 @@
</span><span class="cx">     principal for the cuaddr.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     try:
</span><del>-        principal = principalFunction(cuaddr)
</del><ins>+        principal = yield principalFunction(cuaddr)
</ins><span class="cx">     except Exception, e:
</span><span class="cx">         log.debug(&quot;Lookup of %s failed: %s&quot; % (cuaddr, e))
</span><span class="cx">         principal = None
</span><span class="cx"> 
</span><span class="cx">     if principal is None:
</span><del>-        return (None, None, None)
</del><ins>+        returnValue((None, None, None))
</ins><span class="cx">     else:
</span><span class="cx">         rec = principal.record
</span><span class="cx"> 
</span><span class="lines">@@ -520,9 +522,9 @@
</span><span class="cx">         # to single-quotes.
</span><span class="cx">         fullName = rec.fullName.replace('&quot;', &quot;'&quot;)
</span><span class="cx"> 
</span><del>-        cuas = principal.record.calendarUserAddresses
</del><ins>+        cuas = principal.record.calendarUserAddresses()
</ins><span class="cx"> 
</span><del>-        return (fullName, rec.guid, cuas)
</del><ins>+        returnValue((fullName, rec.guid, cuas))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingcaldavschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/caldav/scheduler.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/caldav/scheduler.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/caldav/scheduler.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -85,13 +85,14 @@
</span><span class="cx">             ))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def checkOriginator(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Check the validity of the Originator header. Extract the corresponding principal.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # Verify that Originator is a valid calendar user
</span><del>-        originatorPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
</del><ins>+        originatorPrincipal = yield self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
</ins><span class="cx">         if originatorPrincipal is None:
</span><span class="cx">             # Local requests MUST have a principal.
</span><span class="cx">             log.error(&quot;Could not find principal for originator: %s&quot; % (self.originator,))
</span><span class="lines">@@ -122,7 +123,7 @@
</span><span class="cx">         results = []
</span><span class="cx">         for recipient in self.recipients:
</span><span class="cx">             # Get the principal resource for this recipient
</span><del>-            principal = self.txn.directoryService().recordWithCalendarUserAddress(recipient)
</del><ins>+            principal = yield self.txn.directoryService().recordWithCalendarUserAddress(recipient)
</ins><span class="cx"> 
</span><span class="cx">             # If no principal we may have a remote recipient but we should check whether
</span><span class="cx">             # the address is one that ought to be on our server and treat that as a missing
</span><span class="lines">@@ -161,7 +162,7 @@
</span><span class="cx">         # Verify that the ORGANIZER's cu address maps to a valid user
</span><span class="cx">         organizer = self.calendar.getOrganizer()
</span><span class="cx">         if organizer:
</span><del>-            organizerPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(organizer)
</del><ins>+            organizerPrincipal = yield self.txn.directoryService().recordWithCalendarUserAddress(organizer)
</ins><span class="cx">             if organizerPrincipal:
</span><span class="cx">                 if organizerPrincipal.calendarsEnabled():
</span><span class="cx"> 
</span><span class="lines">@@ -225,6 +226,7 @@
</span><span class="cx">             ))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def checkAttendeeAsOriginator(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Check the validity of the ATTENDEE value as this is the originator of the iTIP message.
</span><span class="lines">@@ -232,7 +234,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # Attendee's Outbox MUST be the request URI
</span><del>-        attendeePrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
</del><ins>+        attendeePrincipal = yield self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
</ins><span class="cx">         if attendeePrincipal:
</span><span class="cx">             if self.doingPOST is not None and attendeePrincipal.uid != self.originator_uid:
</span><span class="cx">                 log.error(&quot;ATTENDEE in calendar data does not match owner of Outbox: %s&quot; % (self.calendar,))
</span><span class="lines">@@ -257,11 +259,11 @@
</span><span class="cx"> 
</span><span class="cx">         # Prevent spoofing of ORGANIZER with specific METHODs when local
</span><span class="cx">         if self.isiTIPRequest:
</span><del>-            self.checkOrganizerAsOriginator()
</del><ins>+            return self.checkOrganizerAsOriginator()
</ins><span class="cx"> 
</span><span class="cx">         # Prevent spoofing when doing reply-like METHODs
</span><span class="cx">         else:
</span><del>-            self.checkAttendeeAsOriginator()
</del><ins>+            return self.checkAttendeeAsOriginator()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def finalChecks(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingfreebusypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/freebusy.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/freebusy.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/freebusy.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -212,12 +212,12 @@
</span><span class="cx">     # TODO: actually we by pass altogether by assuming anyone can check anyone else's freebusy
</span><span class="cx"> 
</span><span class="cx">     # May need organizer principal
</span><del>-    organizer_record = calresource.directoryService().recordWithCalendarUserAddress(organizer) if organizer else None
</del><ins>+    organizer_record = (yield calresource.directoryService().recordWithCalendarUserAddress(organizer)) if organizer else None
</ins><span class="cx">     organizer_uid = organizer_record.uid if organizer_record else &quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     # Free busy is per-user
</span><span class="cx">     attendee_uid = calresource.viewerHome().uid()
</span><del>-    attendee_record = calresource.directoryService().recordWithUID(attendee_uid)
</del><ins>+    attendee_record = yield calresource.directoryService().recordWithUID(attendee_uid)
</ins><span class="cx"> 
</span><span class="cx">     # Get the timezone property from the collection.
</span><span class="cx">     tz = calresource.getTimezone()
</span><span class="lines">@@ -237,7 +237,7 @@
</span><span class="cx">         authz_record = organizer_record
</span><span class="cx">         if hasattr(calresource._txn, &quot;_authz_uid&quot;) and calresource._txn._authz_uid != organizer_uid:
</span><span class="cx">             authz_uid = calresource._txn._authz_uid
</span><del>-            authz_record = calresource.directoryService().recordWithUID(authz_uid)
</del><ins>+            authz_record = yield calresource.directoryService().recordWithUID(unicode(authz_uid))
</ins><span class="cx"> 
</span><span class="cx">         # Check if attendee is also the organizer or the delegate doing the request
</span><span class="cx">         if attendee_uid in (organizer_uid, authz_uid):
</span><span class="lines">@@ -335,7 +335,7 @@
</span><span class="cx">                 if excludeuid:
</span><span class="cx">                     # See if we have a UID match
</span><span class="cx">                     if (excludeuid == uid):
</span><del>-                        test_record = calresource.directoryService().recordWithCalendarUserAddress(test_organizer) if test_organizer else None
</del><ins>+                        test_record = (yield calresource.directoryService().recordWithCalendarUserAddress(test_organizer)) if test_organizer else None
</ins><span class="cx">                         test_uid = test_record.uid if test_record else &quot;&quot;
</span><span class="cx"> 
</span><span class="cx">                         # Check that ORGANIZER's match (security requirement)
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingimipinboundpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/imip/inbound.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/imip/inbound.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/imip/inbound.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -329,9 +329,12 @@
</span><span class="cx">                 toAddr = organizer[7:]
</span><span class="cx">             elif organizer.startswith(&quot;urn:uuid:&quot;):
</span><span class="cx">                 guid = organizer[9:]
</span><del>-                record = self.directory.recordWithGUID(guid)
-                if record and record.emailAddresses:
-                    toAddr = list(record.emailAddresses)[0]
</del><ins>+                record = yield self.directory.recordWithGUID(guid)
+                try:
+                    if record and record.emailAddresses:
+                        toAddr = list(record.emailAddresses)[0]
+                except AttributeError:
+                    pass
</ins><span class="cx"> 
</span><span class="cx">             if toAddr is None:
</span><span class="cx">                 log.error(&quot;Don't have an email address for the organizer; &quot;
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingimplicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/implicit.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/implicit.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/implicit.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -297,7 +297,7 @@
</span><span class="cx">         organizer_scheduling = (yield self.isOrganizerScheduling())
</span><span class="cx">         if organizer_scheduling:
</span><span class="cx">             self.state = &quot;organizer&quot;
</span><del>-        elif self.isAttendeeScheduling():
</del><ins>+        elif (yield self.isAttendeeScheduling()):
</ins><span class="cx">             self.state = &quot;attendee&quot;
</span><span class="cx">         elif self.organizer:
</span><span class="cx">             # There is an ORGANIZER that is not this user but no ATTENDEE property for
</span><span class="lines">@@ -365,7 +365,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Get some useful information from the calendar
</span><span class="cx">         yield self.extractCalendarData()
</span><del>-        self.organizerPrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
</del><ins>+        self.organizerPrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
</ins><span class="cx">         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
</span><span class="cx"> 
</span><span class="cx">         # Originator is the organizer in this case
</span><span class="lines">@@ -447,7 +447,7 @@
</span><span class="cx">             self.calendar = calendar_old
</span><span class="cx"> 
</span><span class="cx">         yield self.extractCalendarData()
</span><del>-        self.organizerPrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
</del><ins>+        self.organizerPrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
</ins><span class="cx">         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
</span><span class="cx"> 
</span><span class="cx">         # Originator is the organizer in this case
</span><span class="lines">@@ -479,7 +479,7 @@
</span><span class="cx">         # Get some useful information from the calendar
</span><span class="cx">         yield self.extractCalendarData()
</span><span class="cx"> 
</span><del>-        self.attendeePrincipal = self.calendar_home.directoryService().recordWithUID(self.calendar_home.uid())
</del><ins>+        self.attendeePrincipal = yield self.calendar_home.directoryService().recordWithUID(self.calendar_home.uid())
</ins><span class="cx">         self.originator = self.attendee = self.attendeePrincipal.canonicalCalendarUserAddress()
</span><span class="cx"> 
</span><span class="cx">         result = (yield self.scheduleWithOrganizer())
</span><span class="lines">@@ -491,7 +491,7 @@
</span><span class="cx">     def extractCalendarData(self):
</span><span class="cx"> 
</span><span class="cx">         # Get the originator who is the owner of the calendar resource being modified
</span><del>-        self.originatorPrincipal = self.calendar_home.directoryService().recordWithUID(self.calendar_home.uid())
</del><ins>+        self.originatorPrincipal = yield self.calendar_home.directoryService().recordWithUID(self.calendar_home.uid())
</ins><span class="cx"> 
</span><span class="cx">         # Pick the canonical CUA:
</span><span class="cx">         self.originator = self.originatorPrincipal.canonicalCalendarUserAddress()
</span><span class="lines">@@ -555,7 +555,7 @@
</span><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><span class="cx">         # Organizer must map to a valid principal
</span><del>-        self.organizerPrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
</del><ins>+        self.organizerPrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
</ins><span class="cx">         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
</span><span class="cx">         if not self.organizerPrincipal:
</span><span class="cx">             returnValue(False)
</span><span class="lines">@@ -567,21 +567,22 @@
</span><span class="cx">         returnValue(True)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def isAttendeeScheduling(self):
</span><span class="cx"> 
</span><span class="cx">         # First must have organizer property
</span><span class="cx">         if not self.organizer:
</span><del>-            return False
</del><ins>+            returnValue(False)
</ins><span class="cx"> 
</span><span class="cx">         # Check to see whether any attendee is the owner
</span><span class="cx">         for attendee in self.attendees:
</span><del>-            attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</del><ins>+            attendeePrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</ins><span class="cx">             if attendeePrincipal and attendeePrincipal.uid == self.calendar_home.uid():
</span><span class="cx">                 self.attendee = attendee
</span><span class="cx">                 self.attendeePrincipal = attendeePrincipal
</span><del>-                return True
</del><ins>+                returnValue(True)
</ins><span class="cx"> 
</span><del>-        return False
</del><ins>+        returnValue(False)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def makeScheduler(self):
</span><span class="lines">@@ -1015,7 +1016,7 @@
</span><span class="cx">             if attendee.parameterValue(&quot;SCHEDULE-AGENT&quot;, &quot;SERVER&quot;).upper() == &quot;CLIENT&quot;:
</span><span class="cx">                 cuaddr = attendee.value()
</span><span class="cx">                 if cuaddr not in coerced:
</span><del>-                    attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(cuaddr)
</del><ins>+                    attendeePrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(cuaddr)
</ins><span class="cx">                     attendeeAddress = (yield addressmapping.mapper.getCalendarUser(cuaddr, attendeePrincipal))
</span><span class="cx">                     local_attendee = type(attendeeAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
</span><span class="cx">                     coerced[cuaddr] = local_attendee
</span><span class="lines">@@ -1078,7 +1079,7 @@
</span><span class="cx"> 
</span><span class="cx">             # Handle split by not scheduling local attendees
</span><span class="cx">             if self.split_details is not None:
</span><del>-                attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</del><ins>+                attendeePrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</ins><span class="cx">                 attendeeAddress = (yield addressmapping.mapper.getCalendarUser(attendee, attendeePrincipal))
</span><span class="cx">                 if type(attendeeAddress) is LocalCalendarUser:
</span><span class="cx">                     continue
</span><span class="lines">@@ -1135,7 +1136,7 @@
</span><span class="cx"> 
</span><span class="cx">             # Handle split by not scheduling local attendees
</span><span class="cx">             if self.split_details is not None:
</span><del>-                attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</del><ins>+                attendeePrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</ins><span class="cx">                 attendeeAddress = (yield addressmapping.mapper.getCalendarUser(attendee, attendeePrincipal))
</span><span class="cx">                 if type(attendeeAddress) is LocalCalendarUser:
</span><span class="cx">                     continue
</span><span class="lines">@@ -1195,7 +1196,7 @@
</span><span class="cx"> 
</span><span class="cx">             # Handle split by not scheduling local attendees
</span><span class="cx">             if self.split_details is not None:
</span><del>-                attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</del><ins>+                attendeePrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</ins><span class="cx">                 attendeeAddress = (yield addressmapping.mapper.getCalendarUser(attendee, attendeePrincipal))
</span><span class="cx">                 if type(attendeeAddress) is LocalCalendarUser:
</span><span class="cx">                     continue
</span><span class="lines">@@ -1260,7 +1261,7 @@
</span><span class="cx"> 
</span><span class="cx">             # Handle split by not scheduling local attendees
</span><span class="cx">             if self.split_details is not None:
</span><del>-                attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</del><ins>+                attendeePrincipal = yield self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
</ins><span class="cx">                 attendeeAddress = (yield addressmapping.mapper.getCalendarUser(attendee, attendeePrincipal))
</span><span class="cx">                 if type(attendeeAddress) is LocalCalendarUser:
</span><span class="cx">                     continue
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingischeduledeliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/delivery.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -228,7 +228,7 @@
</span><span class="cx">             # Loop over at most 3 redirects
</span><span class="cx">             ssl, host, port, path = self.server.details()
</span><span class="cx">             for _ignore in xrange(3):
</span><del>-                self._prepareRequest(host, port)
</del><ins>+                yield self._prepareRequest(host, port)
</ins><span class="cx">                 response = (yield self._processRequest(ssl, host, port, path))
</span><span class="cx">                 if response.code not in (responsecode.MOVED_PERMANENTLY, responsecode.TEMPORARY_REDIRECT,):
</span><span class="cx">                     break
</span><span class="lines">@@ -334,16 +334,18 @@
</span><span class="cx">         returnValue(iostr.getvalue())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _prepareRequest(self, host, port):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Setup the request for sending. We might need to do this several times
</span><span class="cx">         whilst following redirects.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        component, method = self._prepareData()
-        self._prepareHeaders(host, port, component, method)
</del><ins>+        component, method = (yield self._prepareData())
+        yield self._prepareHeaders(host, port, component, method)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _prepareHeaders(self, host, port, component, method):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Always generate a new set of headers because the Host may varying during redirects,
</span><span class="lines">@@ -357,7 +359,7 @@
</span><span class="cx">         # The Originator must be the ORGANIZER (for a request) or ATTENDEE (for a reply)
</span><span class="cx">         originator = self.scheduler.organizer.cuaddr if self.scheduler.isiTIPRequest else self.scheduler.attendee
</span><span class="cx">         if self.server.unNormalizeAddresses:
</span><del>-            originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
</del><ins>+            originator = yield normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
</ins><span class="cx">         self.headers.addRawHeader(&quot;Originator&quot;, utf8String(originator))
</span><span class="cx">         self.sign_headers.append(&quot;Originator&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -399,6 +401,7 @@
</span><span class="cx">             self.sign_headers.append(&quot;Authorization&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _prepareData(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Prepare data via normalization etc. Only need to do this once even when
</span><span class="lines">@@ -411,7 +414,7 @@
</span><span class="cx">             normalizedCalendar = self.scheduler.calendar.duplicate()
</span><span class="cx">             self.original_organizer = normalizedCalendar.getOrganizer()
</span><span class="cx">             if self.server.unNormalizeAddresses:
</span><del>-                normalizedCalendar.normalizeCalendarUserAddresses(
</del><ins>+                yield normalizedCalendar.normalizeCalendarUserAddresses(
</ins><span class="cx">                     normalizationLookup,
</span><span class="cx">                     self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
</span><span class="cx">                     toUUID=False)
</span><span class="lines">@@ -423,12 +426,12 @@
</span><span class="cx">             component = normalizedCalendar.mainType()
</span><span class="cx">             method = normalizedCalendar.propertyValue(&quot;METHOD&quot;)
</span><span class="cx">             self.data = str(normalizedCalendar)
</span><del>-            return component, method
</del><ins>+            returnValue(component, method)
</ins><span class="cx">         else:
</span><span class="cx">             cal = Component.fromString(self.data)
</span><span class="cx">             component = cal.mainType()
</span><span class="cx">             method = cal.propertyValue(&quot;METHOD&quot;)
</span><del>-            return component, method
</del><ins>+            returnValue(component, method)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingischeduleresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/resource.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/resource.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/resource.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -353,11 +353,13 @@
</span><span class="cx">             davxml.Privilege(caldavxml.ScheduleDeliver()),
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        return davxml.ACL(
-            # DAV:Read, CalDAV:schedule-deliver for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(*privs),
-                davxml.Protected(),
-            ),
</del><ins>+        return succeed(
+            davxml.ACL(
+                # DAV:Read, CalDAV:schedule-deliver for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(*privs),
+                    davxml.Protected(),
+                ),
+            )
</ins><span class="cx">         )
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingischeduleschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/scheduler.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/scheduler.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/ischedule/scheduler.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -186,7 +186,7 @@
</span><span class="cx">         # Normalize recipient addresses
</span><span class="cx">         results = []
</span><span class="cx">         for recipient in recipients:
</span><del>-            normalized = normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
</del><ins>+            normalized = yield normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
</ins><span class="cx">             self.recipientsNormalizationMap[normalized] = recipient
</span><span class="cx">             results.append(normalized)
</span><span class="cx">         recipients = results
</span><span class="lines">@@ -205,7 +205,7 @@
</span><span class="cx">         if not self.checkForFreeBusy():
</span><span class="cx">             # Need to normalize the calendar data and recipient values to keep those in sync,
</span><span class="cx">             # as we might later try to match them
</span><del>-            self.calendar.normalizeCalendarUserAddresses(normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
</del><ins>+            return self.calendar.normalizeCalendarUserAddresses(normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def checkAuthorization(self):
</span><span class="lines">@@ -226,7 +226,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # For remote requests we do not allow the originator to be a local user or one within our domain.
</span><del>-        originatorPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
</del><ins>+        originatorPrincipal = (yield self.txn.directoryService().recordWithCalendarUserAddress(self.originator))
</ins><span class="cx">         localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.originator))
</span><span class="cx">         if originatorPrincipal or localUser:
</span><span class="cx">             if originatorPrincipal.thisServer():
</span><span class="lines">@@ -367,7 +367,7 @@
</span><span class="cx">         # Verify that the ORGANIZER's cu address does not map to a valid user
</span><span class="cx">         organizer = self.calendar.getOrganizer()
</span><span class="cx">         if organizer:
</span><del>-            organizerPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(organizer)
</del><ins>+            organizerPrincipal = yield self.txn.directoryService().recordWithCalendarUserAddress(organizer)
</ins><span class="cx">             if organizerPrincipal:
</span><span class="cx">                 if organizerPrincipal.thisServer():
</span><span class="cx">                     log.error(&quot;Invalid ORGANIZER in calendar data: %s&quot; % (self.calendar,))
</span><span class="lines">@@ -408,7 +408,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # Attendee cannot be local.
</span><del>-        attendeePrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
</del><ins>+        attendeePrincipal = yield self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
</ins><span class="cx">         if attendeePrincipal:
</span><span class="cx">             if attendeePrincipal.thisServer():
</span><span class="cx">                 log.error(&quot;Invalid ATTENDEE in calendar data: %s&quot; % (self.calendar,))
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingprocessingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/processing.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/processing.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/processing.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.work import ScheduleRefreshWork, \
</span><span class="cx">     ScheduleAutoReplyWork
</span><span class="cx"> from txdav.caldav.icalendarstore import ComponentUpdateState, ComponentRemoveState
</span><ins>+from txdav.who.idirectory import AutoScheduleMode
</ins><span class="cx"> 
</span><span class="cx"> import collections
</span><span class="cx"> import hashlib
</span><span class="lines">@@ -58,6 +59,8 @@
</span><span class="cx"> 
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class ImplicitProcessorException(Exception):
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, msg):
</span><span class="lines">@@ -604,19 +607,28 @@
</span><span class="cx">         @param calendar: the iTIP message to process
</span><span class="cx">         @type calendar: L{Component}
</span><span class="cx">         @param automode: the auto-schedule mode for the recipient
</span><del>-        @type automode: C{str}
</del><ins>+        @type automode: L{txdav.who.idirectory.AutoScheduleMode}
</ins><span class="cx"> 
</span><span class="cx">         @return: C{tuple} of C{bool}, C{bool}, C{str} indicating whether changes were made, whether the inbox item
</span><span class="cx">             should be added, and the new PARTSTAT.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-
</del><span class="cx">         # First ignore the none mode
</span><del>-        if automode == &quot;none&quot;:
</del><ins>+        if automode == AutoScheduleMode.none:
</ins><span class="cx">             returnValue((False, True, &quot;&quot;,))
</span><del>-        elif not automode or automode == &quot;default&quot;:
-            automode = config.Scheduling.Options.AutoSchedule.DefaultMode
</del><ins>+        elif not automode:
+            automode = {
+                &quot;none&quot;: AutoScheduleMode.none,
+                &quot;accept-always&quot;: AutoScheduleMode.accept,
+                &quot;decline-always&quot;: AutoScheduleMode.decline,
+                &quot;accept-if-free&quot;: AutoScheduleMode.acceptIfFree,
+                &quot;decline-if-busy&quot;: AutoScheduleMode.declineIfBusy,
+                &quot;automatic&quot;: AutoScheduleMode.acceptIfFreeDeclineIfBusy,
+            }.get(
+                config.Scheduling.Options.AutoSchedule.DefaultMode,
+                &quot;automatic&quot;
+            )
</ins><span class="cx"> 
</span><del>-        log.debug(&quot;ImplicitProcessing - recipient '%s' processing UID: '%s' - checking for auto-reply with mode: %s&quot; % (self.recipient.cuaddr, self.uid, automode,))
</del><ins>+        log.debug(&quot;ImplicitProcessing - recipient '%s' processing UID: '%s' - checking for auto-reply with mode: %s&quot; % (self.recipient.cuaddr, self.uid, automode.name,))
</ins><span class="cx"> 
</span><span class="cx">         cuas = self.recipient.principal.calendarUserAddresses
</span><span class="cx"> 
</span><span class="lines">@@ -704,13 +716,19 @@
</span><span class="cx">         partstat_counts = collections.defaultdict(int)
</span><span class="cx">         for instance in instances.instances.itervalues():
</span><span class="cx">             if instance.partstat == &quot;NEEDS-ACTION&quot; and instance.active:
</span><del>-                if automode == &quot;accept-always&quot;:
</del><ins>+                if automode == AutoScheduleMode.accept:
</ins><span class="cx">                     freePartstat = busyPartstat = &quot;ACCEPTED&quot;
</span><del>-                elif automode == &quot;decline-always&quot;:
</del><ins>+                elif automode == AutoScheduleMode.decline:
</ins><span class="cx">                     freePartstat = busyPartstat = &quot;DECLINED&quot;
</span><span class="cx">                 else:
</span><del>-                    freePartstat = &quot;ACCEPTED&quot; if automode in (&quot;accept-if-free&quot;, &quot;automatic&quot;,) else &quot;NEEDS-ACTION&quot;
-                    busyPartstat = &quot;DECLINED&quot; if automode in (&quot;decline-if-busy&quot;, &quot;automatic&quot;,) else &quot;NEEDS-ACTION&quot;
</del><ins>+                    freePartstat = &quot;ACCEPTED&quot; if automode in (
+                        AutoScheduleMode.acceptIfFree,
+                        AutoScheduleMode.acceptIfFreeDeclineIfBusy,
+                    ) else &quot;NEEDS-ACTION&quot;
+                    busyPartstat = &quot;DECLINED&quot; if automode in (
+                        AutoScheduleMode.declineIfBusy,
+                        AutoScheduleMode.acceptIfFreeDeclineIfBusy,
+                    ) else &quot;NEEDS-ACTION&quot;
</ins><span class="cx">                 instance.partstat = freePartstat if instance.free else busyPartstat
</span><span class="cx">             partstat_counts[instance.partstat] += 1
</span><span class="cx"> 
</span><span class="lines">@@ -901,7 +919,7 @@
</span><span class="cx"> 
</span><span class="cx">         # We only need to fix data that already exists
</span><span class="cx">         if recipient_resource is not None:
</span><del>-            if originator_calendar.mainType() != None:
</del><ins>+            if originator_calendar.mainType() is not None:
</ins><span class="cx">                 yield self.writeCalendarResource(None, recipient_resource, originator_calendar)
</span><span class="cx">             else:
</span><span class="cx">                 yield self.deleteCalendarResource(recipient_resource)
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/scheduler.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/scheduler.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/scheduler.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -162,7 +162,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         self.calendar = calendar
</span><del>-        self.preProcessCalendarData()
</del><ins>+        yield self.preProcessCalendarData()
</ins><span class="cx"> 
</span><span class="cx">         if self.logItems is not None:
</span><span class="cx">             self.logItems[&quot;recipients&quot;] = len(recipients)
</span><span class="lines">@@ -550,7 +550,7 @@
</span><span class="cx">         results = []
</span><span class="cx">         for recipient in self.recipients:
</span><span class="cx">             # Get the principal resource for this recipient
</span><del>-            principal = self.txn.directoryService().recordWithCalendarUserAddress(recipient)
</del><ins>+            principal = yield self.txn.directoryService().recordWithCalendarUserAddress(recipient)
</ins><span class="cx"> 
</span><span class="cx">             # If no principal we may have a remote recipient but we should check whether
</span><span class="cx">             # the address is one that ought to be on our server and treat that as a missing
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreschedulingworkpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/work.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/work.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/scheduling/work.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -189,7 +189,7 @@
</span><span class="cx">         try:
</span><span class="cx">             home = (yield self.transaction.calendarHomeWithResourceID(self.homeResourceID))
</span><span class="cx">             resource = (yield home.objectResourceWithID(self.resourceID))
</span><del>-            organizerPrincipal = home.directoryService().recordWithUID(home.uid())
</del><ins>+            organizerPrincipal = yield home.directoryService().recordWithUID(home.uid())
</ins><span class="cx">             organizer = organizerPrincipal.canonicalCalendarUserAddress()
</span><span class="cx">             calendar_old = Component.fromString(self.icalendarTextOld) if self.icalendarTextOld else None
</span><span class="cx">             calendar_new = Component.fromString(self.icalendarTextNew) if self.icalendarTextNew else None
</span><span class="lines">@@ -311,7 +311,7 @@
</span><span class="cx">         try:
</span><span class="cx">             home = (yield self.transaction.calendarHomeWithResourceID(self.homeResourceID))
</span><span class="cx">             resource = (yield home.objectResourceWithID(self.resourceID))
</span><del>-            attendeePrincipal = home.directoryService().recordWithUID(home.uid())
</del><ins>+            attendeePrincipal = yield home.directoryService().recordWithUID(home.uid())
</ins><span class="cx">             attendee = attendeePrincipal.canonicalCalendarUserAddress()
</span><span class="cx">             calendar = (yield resource.componentForUser())
</span><span class="cx">             organizer = calendar.validOrganizerForScheduling()
</span><span class="lines">@@ -336,6 +336,7 @@
</span><span class="cx">             self._dequeued()
</span><span class="cx"> 
</span><span class="cx">         except Exception, e:
</span><ins>+            # FIXME: calendar may not be set here!
</ins><span class="cx">             log.debug(&quot;ScheduleReplyWork - exception ID: {id}, UID: '{uid}', {err}&quot;, id=self.workID, uid=calendar.resourceUID(), err=str(e))
</span><span class="cx">             raise
</span><span class="cx">         except:
</span><span class="lines">@@ -381,7 +382,7 @@
</span><span class="cx"> 
</span><span class="cx">         try:
</span><span class="cx">             home = (yield self.transaction.calendarHomeWithResourceID(self.homeResourceID))
</span><del>-            attendeePrincipal = home.directoryService().recordWithUID(home.uid())
</del><ins>+            attendeePrincipal = yield home.directoryService().recordWithUID(home.uid())
</ins><span class="cx">             attendee = attendeePrincipal.canonicalCalendarUserAddress()
</span><span class="cx">             calendar = Component.fromString(self.icalendarText)
</span><span class="cx">             organizer = calendar.validOrganizerForScheduling()
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/sql.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/sql.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/sql.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -1915,14 +1915,14 @@
</span><span class="cx"> 
</span><span class="cx">             # Normalize the calendar user addresses once we know we have valid
</span><span class="cx">             # calendar data
</span><del>-            component.normalizeCalendarUserAddresses(normalizationLookup, self.directoryService().recordWithCalendarUserAddress)
</del><ins>+            yield component.normalizeCalendarUserAddresses(normalizationLookup, self.directoryService().recordWithCalendarUserAddress)
</ins><span class="cx"> 
</span><span class="cx">         # Possible timezone stripping
</span><span class="cx">         if config.EnableTimezonesByReference:
</span><span class="cx">             component.stripKnownTimezones()
</span><span class="cx"> 
</span><span class="cx">         # Check location/resource organizer requirement
</span><del>-        self.validLocationResourceOrganizer(component, inserting, internal_state)
</del><ins>+        yield self.validLocationResourceOrganizer(component, inserting, internal_state)
</ins><span class="cx"> 
</span><span class="cx">         # Check access
</span><span class="cx">         if config.EnablePrivateEvents:
</span><span class="lines">@@ -1986,13 +1986,14 @@
</span><span class="cx">                     raise TooManyAttendeesError(&quot;Attendee list size %d is larger than allowed limit %d&quot; % (attendeeListLength, config.MaxAttendeesPerInstance))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def validLocationResourceOrganizer(self, component, inserting, internal_state):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         If the calendar owner is a location or resource, check whether an ORGANIZER property is required.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         if internal_state == ComponentUpdateState.NORMAL:
</span><del>-            originatorPrincipal = self.calendar().ownerHome().directoryRecord()
</del><ins>+            originatorPrincipal = yield self.calendar().ownerHome().directoryRecord()
</ins><span class="cx">             cutype = originatorPrincipal.getCUType() if originatorPrincipal is not None else &quot;INDIVIDUAL&quot;
</span><span class="cx">             organizer = component.getOrganizer()
</span><span class="cx"> 
</span><span class="lines">@@ -2009,10 +2010,10 @@
</span><span class="cx"> 
</span><span class="cx">                 # Find current principal and update modified by details
</span><span class="cx">                 if hasattr(self._txn, &quot;_authz_uid&quot;):
</span><del>-                    authz = self.directoryService().recordWithUID(self._txn._authz_uid)
</del><ins>+                    authz = yield self.directoryService().recordWithUID(self._txn._authz_uid)
</ins><span class="cx">                     prop = Property(&quot;X-CALENDARSERVER-MODIFIED-BY&quot;, authz.canonicalCalendarUserAddress())
</span><span class="cx">                     prop.setParameter(&quot;CN&quot;, authz.displayName())
</span><del>-                    for candidate in authz.calendarUserAddresses:
</del><ins>+                    for candidate in authz.calendarUserAddresses():
</ins><span class="cx">                         if candidate.startswith(&quot;mailto:&quot;):
</span><span class="cx">                             prop.setParameter(&quot;EMAIL&quot;, candidate[7:])
</span><span class="cx">                             break
</span><span class="lines">@@ -2108,7 +2109,7 @@
</span><span class="cx">                 log.debug(&quot;Organizer and attendee properties were entirely removed by the client. Restoring existing properties.&quot;)
</span><span class="cx"> 
</span><span class="cx">                 # Get the originator who is the owner of the calendar resource being modified
</span><del>-                originatorPrincipal = self.calendar().ownerHome().directoryRecord()
</del><ins>+                originatorPrincipal = yield self.calendar().ownerHome().directoryRecord()
</ins><span class="cx">                 originatorAddresses = originatorPrincipal.calendarUserAddresses
</span><span class="cx"> 
</span><span class="cx">                 for component in calendar.subcomponents():
</span><span class="lines">@@ -2145,7 +2146,7 @@
</span><span class="cx">                 log.debug(&quot;Sync COMPLETED property change.&quot;)
</span><span class="cx"> 
</span><span class="cx">                 # Get the originator who is the owner of the calendar resource being modified
</span><del>-                originatorPrincipal = self.calendar().ownerHome().directoryRecord()
</del><ins>+                originatorPrincipal = yield self.calendar().ownerHome().directoryRecord()
</ins><span class="cx">                 originatorAddresses = originatorPrincipal.calendarUserAddresses
</span><span class="cx"> 
</span><span class="cx">                 for component in calendar.subcomponents():
</span><span class="lines">@@ -2264,6 +2265,7 @@
</span><span class="cx">             self._componentChanged = True
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def addStructuredLocation(self, component):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Scan the component for ROOM attendees; if any are associated with an
</span><span class="lines">@@ -2277,12 +2279,12 @@
</span><span class="cx">                     value = attendee.value()
</span><span class="cx">                     if value.startswith(&quot;urn:uuid:&quot;):
</span><span class="cx">                         guid = value[9:]
</span><del>-                        loc = self.directoryService().recordWithGUID(guid)
</del><ins>+                        loc = yield self.directoryService().recordWithGUID(guid)
</ins><span class="cx">                         if loc is not None:
</span><span class="cx">                             guid = loc.extras.get(&quot;associatedAddress&quot;,
</span><span class="cx">                                 None)
</span><span class="cx">                             if guid is not None:
</span><del>-                                addr = self.directoryService().recordWithGUID(guid)
</del><ins>+                                addr = yield self.directoryService().recordWithGUID(guid)
</ins><span class="cx">                                 if addr is not None:
</span><span class="cx">                                     street = addr.extras.get(&quot;streetAddress&quot;, &quot;&quot;)
</span><span class="cx">                                     geo = addr.extras.get(&quot;geo&quot;, &quot;&quot;)
</span><span class="lines">@@ -2539,7 +2541,7 @@
</span><span class="cx">             self.processAlarms(component, inserting)
</span><span class="cx"> 
</span><span class="cx">             # Process structured location
</span><del>-            self.addStructuredLocation(component)
</del><ins>+            yield self.addStructuredLocation(component)
</ins><span class="cx"> 
</span><span class="cx">             # Do scheduling
</span><span class="cx">             implicit_result = (yield self.doImplicitScheduling(component, inserting, internal_state))
</span><span class="lines">@@ -3760,9 +3762,9 @@
</span><span class="cx">             raise InvalidSplit()
</span><span class="cx"> 
</span><span class="cx">         # Cannot be attendee
</span><del>-        ownerPrincipal = self.calendar().ownerHome().directoryRecord()
</del><ins>+        ownerPrincipal = yield self.calendar().ownerHome().directoryRecord()
</ins><span class="cx">         organizer = component.getOrganizer()
</span><del>-        organizerPrincipal = self.directoryService().recordWithCalendarUserAddress(organizer) if organizer else None
</del><ins>+        organizerPrincipal = (yield self.directoryService().recordWithCalendarUserAddress(organizer)) if organizer else None
</ins><span class="cx">         if organizer is not None and organizerPrincipal.uid != ownerPrincipal.uid:
</span><span class="cx">             raise InvalidSplit()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoretesttest_attachmentspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/test_attachments.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/test_attachments.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/test_attachments.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from calendarserver.tap.util import directoryFromConfig
</del><ins>+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><span class="cx"> from pycalendar.datetime import DateTime
</span><span class="cx"> from pycalendar.value import Value
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/test/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> ##
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx"> from twext.python.clsprop import classproperty
</span><del>-from twisted.internet.defer import inlineCallbacks
</del><ins>+from twisted.internet.defer import inlineCallbacks, succeed
</ins><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Store test utility functions
</span><span class="lines">@@ -40,7 +40,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordWithCalendarUserAddress(self, cuaddr):
</span><del>-        return self.recordsByCUA.get(cuaddr)
</del><ins>+        return succeed(self.recordsByCUA.get(cuaddr))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def addRecord(self, record):
</span><span class="lines">@@ -117,7 +117,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def isProxyFor(self, other):
</span><del>-        return False
</del><ins>+        return succeed(False)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavdatastoreutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/datastore/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -100,6 +100,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def normalizationLookup(cuaddr, recordFunction, config):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Lookup function to be passed to ical.normalizeCalendarUserAddresses.
</span><span class="lines">@@ -108,23 +109,25 @@
</span><span class="cx">     record for the cuaddr.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     try:
</span><del>-        record = recordFunction(cuaddr)
</del><ins>+        record = yield recordFunction(cuaddr)
</ins><span class="cx">     except Exception, e:
</span><span class="cx">         log.debug(&quot;Lookup of %s failed: %s&quot; % (cuaddr, e))
</span><span class="cx">         record = None
</span><span class="cx"> 
</span><span class="cx">     if record is None:
</span><del>-        return (None, None, None)
</del><ins>+        returnValue((None, None, None))
</ins><span class="cx">     else:
</span><span class="cx">         # RFC5545 syntax does not allow backslash escaping in
</span><span class="cx">         # parameter values. A double-quote is thus not allowed
</span><span class="cx">         # in a parameter value except as the start/end delimiters.
</span><span class="cx">         # Single quotes are allowed, so we convert any double-quotes
</span><span class="cx">         # to single-quotes.
</span><del>-        return (
-            record.fullName.replace('&quot;', &quot;'&quot;),
-            record.uid,
-            record.calendarUserAddresses,
</del><ins>+        returnValue(
+            (
+                record.displayName.replace('&quot;', &quot;'&quot;),
+                record.uid,
+                record.calendarUserAddresses,
+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcaldavicalendardirectoryservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/icalendardirectoryservice.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/icalendardirectoryservice.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/caldav/icalendardirectoryservice.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -37,8 +37,8 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Return the record for the specified calendar user address.
</span><span class="cx"> 
</span><del>-        @return: the record.
-        @rtype: L{ICalendarStoreDirectoryRecord}
</del><ins>+        @return: Deferred resulting in the record.
+        @rtype: L{Deferred} resulting in L{ICalendarStoreDirectoryRecord}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcommondatastorepoddingconduitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/conduit.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/conduit.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/conduit.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -76,7 +76,8 @@
</span><span class="cx">         self.store = store
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def validRequst(self, source_guid, destination_guid):
</del><ins>+    @inlineCallbacks
+    def validRequest(self, source_guid, destination_guid):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Verify that the specified GUIDs are valid for the request and return the
</span><span class="cx">         matching directory records.
</span><span class="lines">@@ -86,22 +87,22 @@
</span><span class="cx">         @param destination_guid: GUID for the user to whom the request is being sent
</span><span class="cx">         @type destination_guid: C{str}
</span><span class="cx"> 
</span><del>-        @return: C{tuple} of L{IStoreDirectoryRecord}
</del><ins>+        @return: L{Deferred} resulting in C{tuple} of L{IStoreDirectoryRecord}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        source = self.store.directoryService().recordWithUID(source_guid)
</del><ins>+        source = yield self.store.directoryService().recordWithUID(source_guid)
</ins><span class="cx">         if source is None:
</span><span class="cx">             raise DirectoryRecordNotFoundError(&quot;Cross-pod source: {}&quot;.format(source_guid))
</span><span class="cx">         if not source.thisServer():
</span><span class="cx">             raise FailedCrossPodRequestError(&quot;Cross-pod source not on this server: {}&quot;.format(source_guid))
</span><span class="cx"> 
</span><del>-        destination = self.store.directoryService().recordWithUID(destination_guid)
</del><ins>+        destination = yield self.store.directoryService().recordWithUID(destination_guid)
</ins><span class="cx">         if destination is None:
</span><span class="cx">             raise DirectoryRecordNotFoundError(&quot;Cross-pod destination: {}&quot;.format(destination_guid))
</span><span class="cx">         if destination.thisServer():
</span><span class="cx">             raise FailedCrossPodRequestError(&quot;Cross-pod destination on this server: {}&quot;.format(destination_guid))
</span><span class="cx"> 
</span><del>-        return (source, destination,)
</del><ins>+        returnValue((source, destination,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -186,7 +187,7 @@
</span><span class="cx">         @type supported_components: C{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        _ignore_sender, recipient = self.validRequst(ownerUID, shareeUID)
</del><ins>+        _ignore_sender, recipient = yield self.validRequest(ownerUID, shareeUID)
</ins><span class="cx"> 
</span><span class="cx">         action = {
</span><span class="cx">             &quot;action&quot;: &quot;shareinvite&quot;,
</span><span class="lines">@@ -260,7 +261,7 @@
</span><span class="cx">         @type shareUID: C{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        _ignore_sender, recipient = self.validRequst(ownerUID, shareeUID)
</del><ins>+        _ignore_sender, recipient = yield self.validRequest(ownerUID, shareeUID)
</ins><span class="cx"> 
</span><span class="cx">         action = {
</span><span class="cx">             &quot;action&quot;: &quot;shareuninvite&quot;,
</span><span class="lines">@@ -325,7 +326,7 @@
</span><span class="cx">         @type summary: C{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        _ignore_sender, recipient = self.validRequst(shareeUID, ownerUID)
</del><ins>+        _ignore_sender, recipient = yield self.validRequest(shareeUID, ownerUID)
</ins><span class="cx"> 
</span><span class="cx">         action = {
</span><span class="cx">             &quot;action&quot;: &quot;sharereply&quot;,
</span><span class="lines">@@ -398,7 +399,7 @@
</span><span class="cx"> 
</span><span class="cx">         actionName = &quot;add-attachment&quot;
</span><span class="cx">         shareeView = objectResource._parentCollection
</span><del>-        action, recipient = self._send(actionName, shareeView, objectResource)
</del><ins>+        action, recipient = yield self._send(actionName, shareeView, objectResource)
</ins><span class="cx">         action[&quot;rids&quot;] = rids
</span><span class="cx">         action[&quot;filename&quot;] = filename
</span><span class="cx">         result = yield self.sendRequest(shareeView._txn, recipient, action, stream, content_type)
</span><span class="lines">@@ -458,7 +459,7 @@
</span><span class="cx"> 
</span><span class="cx">         actionName = &quot;update-attachment&quot;
</span><span class="cx">         shareeView = objectResource._parentCollection
</span><del>-        action, recipient = self._send(actionName, shareeView, objectResource)
</del><ins>+        action, recipient = yield self._send(actionName, shareeView, objectResource)
</ins><span class="cx">         action[&quot;managedID&quot;] = managed_id
</span><span class="cx">         action[&quot;filename&quot;] = filename
</span><span class="cx">         result = yield self.sendRequest(shareeView._txn, recipient, action, stream, content_type)
</span><span class="lines">@@ -514,7 +515,7 @@
</span><span class="cx"> 
</span><span class="cx">         actionName = &quot;remove-attachment&quot;
</span><span class="cx">         shareeView = objectResource._parentCollection
</span><del>-        action, recipient = self._send(actionName, shareeView, objectResource)
</del><ins>+        action, recipient = yield self._send(actionName, shareeView, objectResource)
</ins><span class="cx">         action[&quot;rids&quot;] = rids
</span><span class="cx">         action[&quot;managedID&quot;] = managed_id
</span><span class="cx">         result = yield self.sendRequest(shareeView._txn, recipient, action)
</span><span class="lines">@@ -557,6 +558,7 @@
</span><span class="cx">     # Sharer data access related apis
</span><span class="cx">     #
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def _send(self, action, parent, child=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Base behavior for an operation on a L{CommonHomeChild}.
</span><span class="lines">@@ -570,7 +572,7 @@
</span><span class="cx">         ownerID = parent.external_id()
</span><span class="cx">         shareeUID = parent.viewerHome().uid()
</span><span class="cx"> 
</span><del>-        _ignore_sender, recipient = self.validRequst(shareeUID, ownerUID)
</del><ins>+        _ignore_sender, recipient = yield self.validRequest(shareeUID, ownerUID)
</ins><span class="cx"> 
</span><span class="cx">         result = {
</span><span class="cx">             &quot;action&quot;: action,
</span><span class="lines">@@ -581,7 +583,7 @@
</span><span class="cx">         }
</span><span class="cx">         if child is not None:
</span><span class="cx">             result[&quot;resource_id&quot;] = child.id()
</span><del>-        return result, recipient
</del><ins>+        returnValue((result, recipient))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -644,7 +646,7 @@
</span><span class="cx">         @type kwargs: C{dict}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        action, recipient = self._send(actionName, shareeView, objectResource)
</del><ins>+        action, recipient = yield self._send(actionName, shareeView, objectResource)
</ins><span class="cx">         if args is not None:
</span><span class="cx">             action[&quot;arguments&quot;] = args
</span><span class="cx">         if kwargs is not None:
</span><span class="lines">@@ -710,7 +712,7 @@
</span><span class="cx">         servertoserver,
</span><span class="cx">         event_details,
</span><span class="cx">     ):
</span><del>-        action, recipient = self._send(&quot;freebusy&quot;, calresource)
</del><ins>+        action, recipient = yield self._send(&quot;freebusy&quot;, calresource)
</ins><span class="cx">         action[&quot;timerange&quot;] = [timerange.start.getText(), timerange.end.getText()]
</span><span class="cx">         action[&quot;matchtotal&quot;] = matchtotal
</span><span class="cx">         action[&quot;excludeuid&quot;] = excludeuid
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcommondatastorepoddingresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/resource.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/resource.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/resource.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -179,11 +179,13 @@
</span><span class="cx">             davxml.Privilege(davxml.Read()),
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        return davxml.ACL(
-            # DAV:Read for all principals (includes anonymous)
-            davxml.ACE(
-                davxml.Principal(davxml.All()),
-                davxml.Grant(*privs),
-                davxml.Protected(),
-            ),
</del><ins>+        return succeed(
+            davxml.ACL(
+                # DAV:Read for all principals (includes anonymous)
+                davxml.ACE(
+                    davxml.Principal(davxml.All()),
+                    davxml.Grant(*privs),
+                    davxml.Protected(),
+                ),
+            )
</ins><span class="cx">         )
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcommondatastorepoddingtesttest_conduitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/test/test_conduit.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/test/test_conduit.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/podding/test/test_conduit.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -123,29 +123,41 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def test_validRequst(self):
</del><ins>+    @inlineCallbacks
+    def test_validRequest(self):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Cross-pod request fails when there is no shared secret header present.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         conduit = PoddingConduit(self.storeUnderTest())
</span><del>-        r1, r2 = conduit.validRequst(&quot;user01&quot;, &quot;puser02&quot;)
</del><ins>+        r1, r2 = yield conduit.validRequest(&quot;user01&quot;, &quot;puser02&quot;)
</ins><span class="cx">         self.assertTrue(r1 is not None)
</span><span class="cx">         self.assertTrue(r2 is not None)
</span><span class="cx"> 
</span><del>-        self.assertRaises(DirectoryRecordNotFoundError, conduit.validRequst, &quot;bogus01&quot;, &quot;user02&quot;)
-        self.assertRaises(DirectoryRecordNotFoundError, conduit.validRequst, &quot;user01&quot;, &quot;bogus02&quot;)
-        self.assertRaises(FailedCrossPodRequestError, conduit.validRequst, &quot;user01&quot;, &quot;user02&quot;)
</del><ins>+        self.assertFailure(
+            conduit.validRequest(&quot;bogus01&quot;, &quot;user02&quot;),
+            DirectoryRecordNotFoundError
+        )
</ins><span class="cx"> 
</span><ins>+        self.assertFailure(
+            conduit.validRequest(&quot;user01&quot;, &quot;bogus02&quot;),
+            DirectoryRecordNotFoundError
+        )
</ins><span class="cx"> 
</span><ins>+        self.assertFailure(
+            conduit.validRequest(&quot;user01&quot;, &quot;user02&quot;),
+            FailedCrossPodRequestError
+        )
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class TestConduitToConduit(MultiStoreConduitTest):
</span><span class="cx"> 
</span><span class="cx">     class FakeConduit(PoddingConduit):
</span><span class="cx"> 
</span><span class="cx">         @inlineCallbacks
</span><span class="cx">         def send_fake(self, txn, ownerUID, shareeUID):
</span><del>-            _ignore_owner, sharee = self.validRequst(ownerUID, shareeUID)
</del><ins>+            _ignore_owner, sharee = yield self.validRequest(ownerUID, shareeUID)
</ins><span class="cx">             action = {
</span><span class="cx">                 &quot;action&quot;: &quot;fake&quot;,
</span><span class="cx">                 &quot;echo&quot;: &quot;bravo&quot;
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/sql.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/sql.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/sql.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -211,6 +211,10 @@
</span><span class="cx">         return self._directoryService
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def setDirectoryService(self, directoryService):
+        self._directoryService = directoryService
+
+
</ins><span class="cx">     def callWithNewTransactions(self, callback):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Registers a method to be called whenever a new transaction is
</span><span class="lines">@@ -922,102 +926,164 @@
</span><span class="cx">     @classproperty
</span><span class="cx">     def _addGroupQuery(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><del>-        return Insert({gr.NAME: Parameter(&quot;name&quot;),
-                       gr.GROUP_GUID: Parameter(&quot;groupGUID&quot;),
-                       gr.MEMBERSHIP_HASH: Parameter(&quot;membershipHash&quot;)},
-                       Return=gr.GROUP_ID)
</del><ins>+        return Insert(
+            {
+                gr.NAME: Parameter(&quot;name&quot;),
+                gr.GROUP_GUID: Parameter(&quot;groupUID&quot;),
+                gr.MEMBERSHIP_HASH: Parameter(&quot;membershipHash&quot;)
+            },
+            Return=gr.GROUP_ID
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="cx">     def _updateGroupQuery(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><del>-        return Update({gr.MEMBERSHIP_HASH: Parameter(&quot;membershipHash&quot;),
-            gr.NAME: Parameter(&quot;name&quot;), gr.MODIFIED: Parameter(&quot;timestamp&quot;)},
-            Where=(gr.GROUP_GUID == Parameter(&quot;groupGUID&quot;)))
</del><ins>+        return Update(
+            {
+                gr.MEMBERSHIP_HASH: Parameter(&quot;membershipHash&quot;),
+                gr.NAME: Parameter(&quot;name&quot;),
+                gr.MODIFIED:
+                Parameter(&quot;timestamp&quot;)
+            },
+            Where=(gr.GROUP_GUID == Parameter(&quot;groupUID&quot;))
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><del>-    def _groupByGUID(cls):
</del><ins>+    def _groupByUID(cls):
</ins><span class="cx">         gr = schema.GROUPS
</span><del>-        return Select([gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH], From=gr,
-                Where=(
-                    gr.GROUP_GUID == Parameter(&quot;groupGUID&quot;)
-                )
-            )
</del><ins>+        return Select(
+            [gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH],
+            From=gr,
+            Where=(gr.GROUP_GUID == Parameter(&quot;groupUID&quot;))
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="cx">     def _groupByID(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><del>-        return Select([gr.GROUP_GUID, gr.NAME, gr.MEMBERSHIP_HASH], From=gr,
-                Where=(
-                    gr.GROUP_ID == Parameter(&quot;groupID&quot;)
-                )
-            )
</del><ins>+        return Select(
+            [gr.GROUP_GUID, gr.NAME, gr.MEMBERSHIP_HASH],
+            From=gr,
+            Where=(gr.GROUP_ID == Parameter(&quot;groupID&quot;))
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="cx">     def _deleteGroup(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><del>-        return Delete(From=gr,
-              Where=(gr.GROUP_ID == Parameter(&quot;groupID&quot;)))
</del><ins>+        return Delete(
+            From=gr,
+            Where=(gr.GROUP_ID == Parameter(&quot;groupID&quot;))
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def addGroup(self, groupGUID, name, membershipHash):
</del><ins>+    def addGroup(self, groupUID, name, membershipHash):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        @type groupGUID: C{UUID}
</del><ins>+        @type groupUID: C{unicode}
+        @type name: C{unicode}
+        @type membershipHash: C{str}
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        return self._addGroupQuery.on(self, name=name,
-            groupGUID=str(groupGUID), membershipHash=membershipHash)
</del><ins>+        return self._addGroupQuery.on(
+            self,
+            name=name.encode(&quot;utf-8&quot;),
+            groupUID=groupUID.encode(&quot;utf-8&quot;),
+            membershipHash=membershipHash
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def updateGroup(self, groupGUID, name, membershipHash):
</del><ins>+    def updateGroup(self, groupUID, name, membershipHash):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        @type groupGUID: C{UUID}
</del><ins>+        @type groupUID: C{unicode}
+        @type name: C{unicode}
+        @type membershipHash: C{str}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         timestamp = datetime.datetime.utcnow()
</span><del>-        return self._updateGroupQuery.on(self, name=name,
-            groupGUID=str(groupGUID), timestamp=timestamp,
-            membershipHash=membershipHash)
</del><ins>+        return self._updateGroupQuery.on(
+            self,
+            name=name.encode(&quot;utf-8&quot;),
+            groupUID=groupUID.encode(&quot;utf-8&quot;),
+            timestamp=timestamp,
+            membershipHash=membershipHash
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def groupByGUID(self, groupGUID):
</del><ins>+    def groupByUID(self, groupUID):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        @type groupGUID: C{UUID}
</del><ins>+        Return or create a record for the group UID.
+
+        @type groupUID: C{unicode}
+
+        @return: Deferred firing with tuple of group ID C{str}, group name
+            C{unicode}, and membership hash C{str}
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        results = (yield self._groupByGUID.on(self, groupGUID=str(groupGUID)))
</del><ins>+        results = (
+            yield self._groupByUID.on(
+                self, groupUID=groupUID.encode(&quot;utf-8&quot;)
+            )
+        )
</ins><span class="cx">         if results:
</span><del>-            returnValue(results[0])
</del><ins>+            returnValue((
+                results[0][0],  # group id
+                results[0][1].decode(&quot;utf-8&quot;),  # name
+                results[0][2],  # membership hash
+            ))
</ins><span class="cx">         else:
</span><del>-            savepoint = SavepointAction(&quot;groupByGUID&quot;)
</del><ins>+            savepoint = SavepointAction(&quot;groupByUID&quot;)
</ins><span class="cx">             yield savepoint.acquire(self)
</span><span class="cx">             try:
</span><del>-                yield self.addGroup(groupGUID, &quot;&quot;, &quot;&quot;)
</del><ins>+                yield self.addGroup(groupUID, u&quot;&quot;, &quot;&quot;)
</ins><span class="cx">             except Exception:
</span><span class="cx">                 yield savepoint.rollback(self)
</span><del>-                results = (yield self._groupByGUID.on(self,
-                    groupGUID=str(groupGUID)))
</del><ins>+                results = (
+                    yield self._groupByUID.on(
+                        self, groupUID=groupUID.encode(&quot;utf-8&quot;)
+                    )
+                )
</ins><span class="cx">                 if results:
</span><del>-                    returnValue(results[0])
</del><ins>+                    returnValue((
+                        results[0][0],  # group id
+                        results[0][1].decode(&quot;utf-8&quot;),  # name
+                        results[0][2],  # membership hash
+                    ))
</ins><span class="cx">                 else:
</span><span class="cx">                     raise
</span><span class="cx">             else:
</span><span class="cx">                 yield savepoint.release(self)
</span><del>-                results = (yield self._groupByGUID.on(self,
-                    groupGUID=str(groupGUID)))
</del><ins>+                results = (
+                    yield self._groupByUID.on(
+                        self, groupUID=groupUID.encode(&quot;utf-8&quot;)
+                    )
+                )
</ins><span class="cx">                 if results:
</span><del>-                    returnValue(results[0])
</del><ins>+                    returnValue((
+                        results[0][0],  # group id
+                        results[0][1].decode(&quot;utf-8&quot;),  # name
+                        results[0][2],  # membership hash
+                    ))
</ins><span class="cx">                 else:
</span><span class="cx">                     raise
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def groupByID(self, groupID):
</span><ins>+        &quot;&quot;&quot;
+        Given a group ID, return the group UID, or raise NotFoundError
+
+        @type groupID: C{str}
+        @return: Deferred firing with a tuple of group UID C{unicode},
+            group name C{unicode}, and membership hash C{str}
+        &quot;&quot;&quot;
</ins><span class="cx">         try:
</span><span class="cx">             results = (yield self._groupByID.on(self, groupID=groupID))[0]
</span><span class="cx">             if results:
</span><del>-                results = [UUID(&quot;urn:uuid:&quot; + results[0])] + results[1:]
</del><ins>+                results = (
+                    results[0].decode(&quot;utf-8&quot;),
+                    results[1].decode(&quot;utf-8&quot;),
+                    results[2]
+                )
</ins><span class="cx">             returnValue(results)
</span><span class="cx">         except IndexError:
</span><span class="cx">             raise NotFoundError
</span><span class="lines">@@ -1037,7 +1103,7 @@
</span><span class="cx">         return Insert(
</span><span class="cx">             {
</span><span class="cx">                 gm.GROUP_ID: Parameter(&quot;groupID&quot;),
</span><del>-                gm.MEMBER_GUID: Parameter(&quot;memberGUID&quot;)
</del><ins>+                gm.MEMBER_GUID: Parameter(&quot;memberUID&quot;)
</ins><span class="cx">             }
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -1050,7 +1116,7 @@
</span><span class="cx">             Where=(
</span><span class="cx">                 gm.GROUP_ID == Parameter(&quot;groupID&quot;)
</span><span class="cx">             ).And(
</span><del>-                gm.MEMBER_GUID == Parameter(&quot;memberGUID&quot;)
</del><ins>+                gm.MEMBER_GUID == Parameter(&quot;memberUID&quot;)
</ins><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -1069,25 +1135,35 @@
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="cx">     def _selectGroupsForQuery(cls):
</span><ins>+        gr = schema.GROUPS
</ins><span class="cx">         gm = schema.GROUP_MEMBERSHIP
</span><ins>+
</ins><span class="cx">         return Select(
</span><del>-            [gm.GROUP_ID],
-            From=gm,
</del><ins>+            [gr.GROUP_GUID],
+            From=gr,
</ins><span class="cx">             Where=(
</span><del>-                gm.MEMBER_GUID == Parameter(&quot;guid&quot;)
</del><ins>+                gr.GROUP_ID.In(
+                    Select(
+                        [gm.GROUP_ID],
+                        From=gm,
+                        Where=(
+                            gm.MEMBER_GUID == Parameter(&quot;uid&quot;)
+                        )
+                    )
+                )
</ins><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def addMemberToGroup(self, memberGUID, groupID):
</del><ins>+    def addMemberToGroup(self, memberUID, groupID):
</ins><span class="cx">         return self._addMemberToGroupQuery.on(
</span><del>-            self, groupID=groupID, memberGUID=str(memberGUID)
</del><ins>+            self, groupID=groupID, memberUID=memberUID.encode(&quot;utf-8&quot;)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def removeMemberFromGroup(self, memberGUID, groupID):
</del><ins>+    def removeMemberFromGroup(self, memberUID, groupID):
</ins><span class="cx">         return self._removeMemberFromGroupQuery.on(
</span><del>-            self, groupID=groupID, memberGUID=str(memberGUID)
</del><ins>+            self, groupID=groupID, memberUID=memberUID.encode(&quot;utf-8&quot;)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1107,25 +1183,29 @@
</span><span class="cx">         members = set()
</span><span class="cx">         results = (yield self._selectGroupMembersQuery.on(self, groupID=groupID))
</span><span class="cx">         for row in results:
</span><del>-            members.add(UUID(&quot;urn:uuid:&quot; + row[0]))
</del><ins>+            members.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx">         returnValue(members)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def groupsFor(self, guid):
</del><ins>+    def groupsFor(self, uid):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        Returns the cached set of GUIDs for the groups this given guid is
</del><ins>+        Returns the cached set of UIDs for the groups this given uid is
</ins><span class="cx">         a member of.
</span><span class="cx"> 
</span><del>-        @param guid: the guid
-        @type guid: C{UUID}
</del><ins>+        @param uid: the uid
+        @type uid: C{unicode}
</ins><span class="cx">         @return: the set of group IDs
</span><span class="cx">         @rtype: a Deferred which fires with a set() of C{int} group IDs
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         groups = set()
</span><del>-        results = (yield self._selectGroupsForQuery.on(self, guid=str(guid)))
</del><ins>+        results = (
+            yield self._selectGroupsForQuery.on(
+                self, uid=uid.encode(&quot;utf-8&quot;)
+            )
+        )
</ins><span class="cx">         for row in results:
</span><del>-            groups.add(row[0])
</del><ins>+            groups.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx">         returnValue(groups)
</span><span class="cx"> 
</span><span class="cx">     # End of Group Members
</span><span class="lines">@@ -1168,6 +1248,19 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><ins>+    def _removeDelegatesQuery(cls):
+        de = schema.DELEGATES
+        return Delete(
+            From=de,
+            Where=(
+                de.DELEGATOR == Parameter(&quot;delegator&quot;)
+            ).And(
+                de.READ_WRITE == Parameter(&quot;readWrite&quot;)
+            )
+        )
+
+
+    @classproperty
</ins><span class="cx">     def _removeDelegateGroupQuery(cls):
</span><span class="cx">         ds = schema.DELEGATE_GROUPS
</span><span class="cx">         return Delete(
</span><span class="lines">@@ -1183,6 +1276,19 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><ins>+    def _removeDelegateGroupsQuery(cls):
+        ds = schema.DELEGATE_GROUPS
+        return Delete(
+            From=ds,
+            Where=(
+                ds.DELEGATOR == Parameter(&quot;delegator&quot;)
+            ).And(
+                ds.READ_WRITE == Parameter(&quot;readWrite&quot;)
+            )
+        )
+
+
+    @classproperty
</ins><span class="cx">     def _selectDelegatesQuery(cls):
</span><span class="cx">         de = schema.DELEGATES
</span><span class="cx">         return Select(
</span><span class="lines">@@ -1199,13 +1305,23 @@
</span><span class="cx">     @classproperty
</span><span class="cx">     def _selectDelegateGroupsQuery(cls):
</span><span class="cx">         ds = schema.DELEGATE_GROUPS
</span><ins>+        gr = schema.GROUPS
+
</ins><span class="cx">         return Select(
</span><del>-            [ds.GROUP_ID],
-            From=ds,
</del><ins>+            [gr.GROUP_GUID],
+            From=gr,
</ins><span class="cx">             Where=(
</span><del>-                ds.DELEGATOR == Parameter(&quot;delegator&quot;)
-            ).And(
-                ds.READ_WRITE == Parameter(&quot;readWrite&quot;)
</del><ins>+                gr.GROUP_ID.In(
+                    Select(
+                        [ds.GROUP_ID],
+                        From=ds,
+                        Where=(
+                            ds.DELEGATOR == Parameter(&quot;delegator&quot;)
+                        ).And(
+                            ds.READ_WRITE == Parameter(&quot;readWrite&quot;)
+                        )
+                    )
+                )
</ins><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -1317,18 +1433,18 @@
</span><span class="cx">         Adds a row to the DELEGATES table.  The delegate should not be a
</span><span class="cx">         group.  To delegate to a group, call addDelegateGroup() instead.
</span><span class="cx"> 
</span><del>-        @param delegator: the GUID of the delegator
-        @type delegator: C{UUID}
-        @param delegate: the GUID of the delegate
-        @type delegate: C{UUID}
</del><ins>+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
+        @param delegate: the UID of the delegate
+        @type delegate: C{unicode}
</ins><span class="cx">         @param readWrite: grant read and write access if True, otherwise
</span><span class="cx">             read-only access
</span><span class="cx">         @type readWrite: C{boolean}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return self._addDelegateQuery.on(
</span><span class="cx">             self,
</span><del>-            delegator=str(delegator),
-            delegate=str(delegate),
</del><ins>+            delegator=delegator.encode(&quot;utf-8&quot;),
+            delegate=delegate.encode(&quot;utf-8&quot;),
</ins><span class="cx">             readWrite=1 if readWrite else 0
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -1339,8 +1455,8 @@
</span><span class="cx">         Adds a row to the DELEGATE_GROUPS table.  The delegate should be a
</span><span class="cx">         group.  To delegate to a person, call addDelegate() instead.
</span><span class="cx"> 
</span><del>-        @param delegator: the GUID of the delegator
-        @type delegator: C{UUID}
</del><ins>+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
</ins><span class="cx">         @param delegateGroupID: the GROUP_ID of the delegate group
</span><span class="cx">         @type delegateGroupID: C{int}
</span><span class="cx">         @param readWrite: grant read and write access if True, otherwise
</span><span class="lines">@@ -1349,7 +1465,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return self._addDelegateGroupQuery.on(
</span><span class="cx">             self,
</span><del>-            delegator=str(delegator),
</del><ins>+            delegator=delegator.encode(&quot;utf-8&quot;),
</ins><span class="cx">             groupID=delegateGroupID,
</span><span class="cx">             readWrite=1 if readWrite else 0,
</span><span class="cx">             isExternal=1 if isExternal else 0
</span><span class="lines">@@ -1361,29 +1477,47 @@
</span><span class="cx">         Removes a row from the DELEGATES table.  The delegate should not be a
</span><span class="cx">         group.  To remove a delegate group, call removeDelegateGroup() instead.
</span><span class="cx"> 
</span><del>-        @param delegator: the GUID of the delegator
-        @type delegator: C{UUID}
-        @param delegate: the GUID of the delegate
-        @type delegate: C{UUID}
</del><ins>+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
+        @param delegate: the UID of the delegate
+        @type delegate: C{unicode}
</ins><span class="cx">         @param readWrite: remove read and write access if True, otherwise
</span><span class="cx">             read-only access
</span><span class="cx">         @type readWrite: C{boolean}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return self._removeDelegateQuery.on(
</span><span class="cx">             self,
</span><del>-            delegator=str(delegator),
-            delegate=str(delegate),
</del><ins>+            delegator=delegator.encode(&quot;utf-8&quot;),
+            delegate=delegate.encode(&quot;utf-8&quot;),
</ins><span class="cx">             readWrite=1 if readWrite else 0
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def removeDelegates(self, delegator, readWrite):
+        &quot;&quot;&quot;
+        Removes all rows for this delegator/readWrite combination from the
+        DELEGATES table.
+
+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
+        @param readWrite: remove read and write access if True, otherwise
+            read-only access
+        @type readWrite: C{boolean}
+        &quot;&quot;&quot;
+        return self._removeDelegatesQuery.on(
+            self,
+            delegator=delegator.encode(&quot;utf-8&quot;),
+            readWrite=1 if readWrite else 0
+        )
+
+
</ins><span class="cx">     def removeDelegateGroup(self, delegator, delegateGroupID, readWrite):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Removes a row from the DELEGATE_GROUPS table.  The delegate should be a
</span><span class="cx">         group.  To remove a delegate person, call removeDelegate() instead.
</span><span class="cx"> 
</span><del>-        @param delegator: the GUID of the delegator
-        @type delegator: C{UUID}
</del><ins>+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
</ins><span class="cx">         @param delegateGroupID: the GROUP_ID of the delegate group
</span><span class="cx">         @type delegateGroupID: C{int}
</span><span class="cx">         @param readWrite: remove read and write access if True, otherwise
</span><span class="lines">@@ -1392,26 +1526,44 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return self._removeDelegateGroupQuery.on(
</span><span class="cx">             self,
</span><del>-            delegator=str(delegator),
</del><ins>+            delegator=delegator.encode(&quot;utf-8&quot;),
</ins><span class="cx">             groupID=delegateGroupID,
</span><span class="cx">             readWrite=1 if readWrite else 0
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def removeDelegateGroups(self, delegator, readWrite):
+        &quot;&quot;&quot;
+        Removes all rows for this delegator/readWrite combination from the
+        DELEGATE_GROUPS table.
+
+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
+        @param readWrite: remove read and write access if True, otherwise
+            read-only access
+        @type readWrite: C{boolean}
+        &quot;&quot;&quot;
+        return self._removeDelegateGroupsQuery.on(
+            self,
+            delegator=delegator.encode(&quot;utf-8&quot;),
+            readWrite=1 if readWrite else 0
+        )
+
+
</ins><span class="cx">     @inlineCallbacks
</span><del>-    def delegates(self, delegator, readWrite):
</del><ins>+    def delegates(self, delegator, readWrite, expanded=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        Returns the GUIDs of all delegates for the given delegator.  If
-        delegate access was granted to any groups, those groups' members
-        (flattened) will be included. No GUIDs of the groups themselves
-        will be returned.
</del><ins>+        Returns the UIDs of all delegates for the given delegator.  If
+        expanded is False, only the direct delegates (users and groups)
+        are returned.  If expanded is True, the expanded membmership is
+        returned, not including the groups themselves.
</ins><span class="cx"> 
</span><del>-        @param delegator: the GUID of the delegator
-        @type delegator: C{UUID}
</del><ins>+        @param delegator: the UID of the delegator
+        @type delegator: C{unicode}
</ins><span class="cx">         @param readWrite: the access-type to check for; read and write
</span><span class="cx">             access if True, otherwise read-only access
</span><span class="cx">         @type readWrite: C{boolean}
</span><del>-        @returns: the GUIDs of the delegates (for the specified access
</del><ins>+        @returns: the UIDs of the delegates (for the specified access
</ins><span class="cx">             type)
</span><span class="cx">         @rtype: a Deferred resulting in a set
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -1421,35 +1573,48 @@
</span><span class="cx">         results = (
</span><span class="cx">             yield self._selectDelegatesQuery.on(
</span><span class="cx">                 self,
</span><del>-                delegator=str(delegator),
</del><ins>+                delegator=delegator.encode(&quot;utf-8&quot;),
</ins><span class="cx">                 readWrite=1 if readWrite else 0
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx">         for row in results:
</span><del>-            delegates.add(UUID(&quot;urn:uuid:&quot; + row[0]))
</del><ins>+            delegates.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx"> 
</span><del>-        # Finally get those who are in groups which have been delegated to
-        results = (
-            yield self._selectIndirectDelegatesQuery.on(
-                self,
-                delegator=str(delegator),
-                readWrite=1 if readWrite else 0
</del><ins>+        if expanded:
+            # Get those who are in groups which have been delegated to
+            results = (
+                yield self._selectIndirectDelegatesQuery.on(
+                    self,
+                    delegator=delegator.encode(&quot;utf-8&quot;),
+                    readWrite=1 if readWrite else 0
+                )
</ins><span class="cx">             )
</span><del>-        )
-        for row in results:
-            delegates.add(UUID(&quot;urn:uuid:&quot; + row[0]))
</del><ins>+            for row in results:
+                delegates.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx"> 
</span><ins>+        else:
+            # Get the directly-delegated-to groups
+            results = (
+                yield self._selectDelegateGroupsQuery.on(
+                    self,
+                    delegator=delegator.encode(&quot;utf-8&quot;),
+                    readWrite=1 if readWrite else 0
+                )
+            )
+            for row in results:
+                delegates.add(row[0].decode(&quot;utf-8&quot;))
+
</ins><span class="cx">         returnValue(delegates)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def delegators(self, delegate, readWrite):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Returns the GUIDs of all delegators which have granted access to
</del><ins>+        Returns the UIDs of all delegators which have granted access to
</ins><span class="cx">         the given delegate, either directly or indirectly via groups.
</span><span class="cx"> 
</span><del>-        @param delegate: the GUID of the delegate
-        @type delegate: C{UUID}
</del><ins>+        @param delegate: the UID of the delegate
+        @type delegate: C{unicode}
</ins><span class="cx">         @param readWrite: the access-type to check for; read and write
</span><span class="cx">             access if True, otherwise read-only access
</span><span class="cx">         @type readWrite: C{boolean}
</span><span class="lines">@@ -1463,24 +1628,24 @@
</span><span class="cx">         results = (
</span><span class="cx">             yield self._selectDirectDelegatorsQuery.on(
</span><span class="cx">                 self,
</span><del>-                delegate=str(delegate),
</del><ins>+                delegate=delegate.encode(&quot;utf-8&quot;),
</ins><span class="cx">                 readWrite=1 if readWrite else 0
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx">         for row in results:
</span><del>-            delegators.add(UUID(&quot;urn:uuid:&quot; + row[0]))
</del><ins>+            delegators.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx"> 
</span><span class="cx">         # Finally get those who have delegated to groups the delegate
</span><span class="cx">         # is a member of
</span><span class="cx">         results = (
</span><span class="cx">             yield self._selectIndirectDelegatorsQuery.on(
</span><span class="cx">                 self,
</span><del>-                delegate=str(delegate),
</del><ins>+                delegate=delegate.encode(&quot;utf-8&quot;),
</ins><span class="cx">                 readWrite=1 if readWrite else 0
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx">         for row in results:
</span><del>-            delegators.add(UUID(&quot;urn:uuid:&quot; + row[0]))
</del><ins>+            delegators.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx"> 
</span><span class="cx">         returnValue(delegators)
</span><span class="cx"> 
</span><span class="lines">@@ -1488,11 +1653,11 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def allGroupDelegates(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Return the GUIDs of all groups which have been delegated to.  Useful
</del><ins>+        Return the UIDs of all groups which have been delegated to.  Useful
</ins><span class="cx">         for obtaining the set of groups which need to be synchronized from
</span><span class="cx">         the directory.
</span><span class="cx"> 
</span><del>-        @returns: the GUIDs of all delegated-to groups
</del><ins>+        @returns: the UIDs of all delegated-to groups
</ins><span class="cx">         @rtype: a Deferred resulting in a set
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         gr = schema.GROUPS
</span><span class="lines">@@ -1505,7 +1670,7 @@
</span><span class="cx">         ).on(self))
</span><span class="cx">         delegates = set()
</span><span class="cx">         for row in results:
</span><del>-            delegates.add(UUID(&quot;urn:uuid:&quot; + row[0]))
</del><ins>+            delegates.add(row[0].decode(&quot;utf-8&quot;))
</ins><span class="cx"> 
</span><span class="cx">         returnValue(delegates)
</span><span class="cx"> 
</span><span class="lines">@@ -1513,22 +1678,22 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def externalDelegates(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Returns a dictionary mapping delegate GUIDs to (read-group, write-group)
</del><ins>+        Returns a dictionary mapping delegate UIDs to (read-group, write-group)
</ins><span class="cx">         tuples, including only those assignments that originated from the
</span><span class="cx">         directory.
</span><span class="cx"> 
</span><del>-        @returns: dictionary mapping delegator guid to (readDelegateGUID,
-            writeDelegateGUID) tuples
</del><ins>+        @returns: dictionary mapping delegator uid to (readDelegateUID,
+            writeDelegateUID) tuples
</ins><span class="cx">         @rtype: a Deferred resulting in a dictionary
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         delegates = {}
</span><span class="cx"> 
</span><span class="cx">         # Get the externally managed delegates (which are all groups)
</span><span class="cx">         results = (yield self._selectExternalDelegateGroupsQuery.on(self))
</span><del>-        for delegator, readDelegateGUID, writeDelegateGUID in results:
-            delegates[UUID(delegator)] = (
-                UUID(readDelegateGUID) if readDelegateGUID else None,
-                UUID(writeDelegateGUID) if writeDelegateGUID else None
</del><ins>+        for delegator, readDelegateUID, writeDelegateUID in results:
+            delegates[delegator.encode(&quot;utf-8&quot;)] = (
+                readDelegateUID.encode(&quot;utf-8&quot;) if readDelegateUID else None,
+                writeDelegateUID.encode(&quot;utf-8&quot;) if writeDelegateUID else None
</ins><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">         returnValue(delegates)
</span><span class="lines">@@ -1537,7 +1702,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def assignExternalDelegates(
</span><span class="cx">         self, delegator, readDelegateGroupID, writeDelegateGroupID,
</span><del>-        readDelegateGUID, writeDelegateGUID
</del><ins>+        readDelegateUID, writeDelegateUID
</ins><span class="cx">     ):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Update the external delegate group table so we can quickly identify
</span><span class="lines">@@ -1560,12 +1725,12 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         # Store new assignments in the external comparison table
</span><del>-        if readDelegateGUID or writeDelegateGUID:
</del><ins>+        if readDelegateUID or writeDelegateUID:
</ins><span class="cx">             readDelegateForDB = (
</span><del>-                str(readDelegateGUID) if readDelegateGUID else &quot;&quot;
</del><ins>+                readDelegateUID.encode(&quot;utf-8&quot;) if readDelegateUID else &quot;&quot;
</ins><span class="cx">             )
</span><span class="cx">             writeDelegateForDB = (
</span><del>-                str(writeDelegateGUID) if writeDelegateGUID else &quot;&quot;
</del><ins>+                writeDelegateUID.encode(&quot;utf-8&quot;) if writeDelegateUID else &quot;&quot;
</ins><span class="cx">             )
</span><span class="cx">             yield self._storeExternalDelegateGroupsPairQuery.on(
</span><span class="cx">                 self,
</span><span class="lines">@@ -2750,7 +2915,7 @@
</span><span class="cx">                 returnValue(None)
</span><span class="cx"> 
</span><span class="cx">             # Determine if the user is local or external
</span><del>-            record = txn.directoryService().recordWithUID(uid)
</del><ins>+            record = yield txn.directoryService().recordWithUID(uid)
</ins><span class="cx">             if record is None:
</span><span class="cx">                 raise DirectoryRecordNotFoundError(&quot;Cannot create home for UID since no directory record exists: {}&quot;.format(uid))
</span><span class="cx"> 
</span><span class="lines">@@ -6751,7 +6916,7 @@
</span><span class="cx">             created = False
</span><span class="cx">         elif create:
</span><span class="cx">             # Determine if the user is local or external
</span><del>-            record = txn.directoryService().recordWithUID(uid)
</del><ins>+            record = yield txn.directoryService().recordWithUID(uid)
</ins><span class="cx">             if record is None:
</span><span class="cx">                 raise DirectoryRecordNotFoundError(&quot;Cannot create home for UID since no directory record exists: {}&quot;.format(uid))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavcommondatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/test/util.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/test/util.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/common/datastore/test/util.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -46,7 +46,7 @@
</span><span class="cx"> from twisted.application.service import Service
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import Deferred, inlineCallbacks
</span><del>-from twisted.internet.defer import returnValue
</del><ins>+from twisted.internet.defer import returnValue, succeed
</ins><span class="cx"> from twisted.internet.task import deferLater
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx"> 
</span><span class="lines">@@ -110,14 +110,14 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordWithUID(self, uid):
</span><del>-        return self.records.get(uid)
</del><ins>+        return succeed(self.records.get(uid))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordWithGUID(self, guid):
</span><span class="cx">         for record in self.records.itervalues():
</span><span class="cx">             if record.guid == guid:
</span><del>-                return record
-        return None
</del><ins>+                return succeed(record)
+        return succeed(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def addRecord(self, record):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavdpsclientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/dps/client.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/dps/client.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/dps/client.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -15,53 +15,95 @@
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> import cPickle as pickle
</span><ins>+import uuid
</ins><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.who.directory import DirectoryRecord as BaseDirectoryRecord
</span><span class="cx"> from twext.who.directory import DirectoryService as BaseDirectoryService
</span><del>-from twext.who.idirectory import RecordType
</del><ins>+from twext.who.idirectory import RecordType, IDirectoryService
</ins><span class="cx"> import twext.who.idirectory
</span><span class="cx"> from twext.who.util import ConstantsContainer
</span><ins>+from twisted.cred.credentials import UsernamePassword
</ins><span class="cx"> from twisted.internet import reactor
</span><del>-from twisted.internet.defer import inlineCallbacks, returnValue
</del><ins>+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</ins><span class="cx"> from twisted.internet.protocol import ClientCreator
</span><span class="cx"> from twisted.protocols import amp
</span><ins>+from twisted.python.constants import Names, NamedConstant
+from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryRecord
+from txdav.common.idirectoryservice import IStoreDirectoryService
</ins><span class="cx"> from txdav.dps.commands import (
</span><span class="cx">     RecordWithShortNameCommand, RecordWithUIDCommand, RecordWithGUIDCommand,
</span><span class="cx">     RecordsWithRecordTypeCommand, RecordsWithEmailAddressCommand,
</span><ins>+    RecordsMatchingTokensCommand,
+    MembersCommand, GroupsCommand, SetMembersCommand,
</ins><span class="cx">     VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand
</span><span class="cx"> )
</span><ins>+from txdav.who.directory import (
+    CalendarDirectoryRecordMixin, CalendarDirectoryServiceMixin
+)
+import txdav.who.delegates
</ins><span class="cx"> import txdav.who.idirectory
</span><ins>+from txweb2.auth.digest import DigestedCredentials
</ins><span class="cx"> from zope.interface import implementer
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> ##
</span><span class="cx"> ## Client implementation of Directory Proxy Service
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-@implementer(twext.who.idirectory.IDirectoryService)
-class DirectoryService(BaseDirectoryService):
</del><ins>+
+## MOVE2WHO TODOs:
+## augmented service
+## configuration of aggregate services
+## hooking up delegates
+## calverify needs deferreds, including:
+##    component.normalizeCalendarUserAddresses
+
+@implementer(IDirectoryService, IStoreDirectoryService)
+class DirectoryService(BaseDirectoryService, CalendarDirectoryServiceMixin):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Client side of directory proxy
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     recordType = ConstantsContainer(
</span><span class="cx">         (twext.who.idirectory.RecordType,
</span><del>-         txdav.who.idirectory.RecordType)
</del><ins>+         txdav.who.idirectory.RecordType,
+         txdav.who.delegates.RecordType)
</ins><span class="cx">     )
</span><span class="cx"> 
</span><ins>+    fieldName = ConstantsContainer(
+        (twext.who.idirectory.FieldName,
+         txdav.who.idirectory.FieldName)
+    )
</ins><span class="cx"> 
</span><ins>+    # def __init__(self, fieldNames, recordTypes):
+    #     self.fieldName = fieldNames
+    #     self.recordType = recordTypes
+
+    # MOVE2WHO
+    def getGroups(self, guids=None):
+        return succeed(set())
+
+
+    guid = &quot;1332A615-4D3A-41FE-B636-FBE25BFB982E&quot;
+
+    # END MOVE2WHO
+
+
+
+
</ins><span class="cx">     def _dictToRecord(self, serializedFields):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        This to be replaced by something awesome
</del><ins>+        Turn a dictionary of fields sent from the server into a directory
+        record
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if not serializedFields:
</span><span class="cx">             return None
</span><span class="cx"> 
</span><ins>+        # print(&quot;FIELDS&quot;, serializedFields)
+
</ins><span class="cx">         fields = {}
</span><span class="cx">         for fieldName, value in serializedFields.iteritems():
</span><span class="cx">             try:
</span><span class="lines">@@ -70,8 +112,21 @@
</span><span class="cx">                 # unknown field
</span><span class="cx">                 pass
</span><span class="cx">             else:
</span><del>-                fields[field] = value
-        fields[self.fieldName.recordType] = self.recordType.user
</del><ins>+                valueType = self.fieldName.valueType(field)
+                if valueType in (unicode, bool):
+                    fields[field] = value
+                elif valueType is uuid.UUID:
+                    fields[field] = uuid.UUID(value)
+                elif issubclass(valueType, Names):
+                    if value is not None:
+                        fields[field] = field.valueType.lookupByName(value)
+                    else:
+                        fields[field] = None
+                elif issubclass(valueType, NamedConstant):
+                    if fieldName == &quot;recordType&quot;:  # Is there a better way?
+                        fields[field] = self.recordType.lookupByName(value)
+
+        # print(&quot;AFTER:&quot;, fields)
</ins><span class="cx">         return DirectoryRecord(self, fields)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -127,10 +182,14 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordWithShortName(self, recordType, shortName):
</span><ins>+        # MOVE2WHO
+        # temporary hack until we can fix all callers not to pass strings:
+        if isinstance(recordType, (str, unicode)):
+            recordType = self.recordType.lookupByName(recordType)
</ins><span class="cx">         return self._call(
</span><span class="cx">             RecordWithShortNameCommand,
</span><span class="cx">             self._processSingleRecord,
</span><del>-            recordType=recordType.description.encode(&quot;utf-8&quot;),
</del><ins>+            recordType=recordType.name.encode(&quot;utf-8&quot;),
</ins><span class="cx">             shortName=shortName.encode(&quot;utf-8&quot;)
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -147,7 +206,7 @@
</span><span class="cx">         return self._call(
</span><span class="cx">             RecordWithGUIDCommand,
</span><span class="cx">             self._processSingleRecord,
</span><del>-            guid=guid.encode(&quot;utf-8&quot;)
</del><ins>+            guid=str(guid)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -155,7 +214,7 @@
</span><span class="cx">         return self._call(
</span><span class="cx">             RecordsWithRecordTypeCommand,
</span><span class="cx">             self._processMultipleRecords,
</span><del>-            recordType=recordType.description.encode(&quot;utf-8&quot;)
</del><ins>+            recordType=recordType.name.encode(&quot;utf-8&quot;)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -167,9 +226,55 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def recordsMatchingTokens(self, tokens, context=None, limitResults=50,
+                              timeoutSeconds=10):
+        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>-class DirectoryRecord(BaseDirectoryRecord):
</del><span class="cx"> 
</span><ins>+
+
+
+
+
+@implementer(ICalendarStoreDirectoryRecord)
+class DirectoryRecord(BaseDirectoryRecord, CalendarDirectoryRecordMixin):
+
+
+    @inlineCallbacks
+    def verifyCredentials(self, credentials):
+
+        # XYZZY REMOVE THIS, it bypasses all authentication!:
+        returnValue(True)
+
+        if isinstance(credentials, UsernamePassword):
+            log.debug(&quot;UsernamePassword&quot;)
+            returnValue(
+                (yield self.verifyPlaintextPassword(credentials.password))
+            )
+
+        elif isinstance(credentials, DigestedCredentials):
+            log.debug(&quot;DigestedCredentials&quot;)
+            returnValue(
+                (yield self.verifyHTTPDigest(
+                    self.shortNames[0],
+                    self.service.realmName,
+                    credentials.fields[&quot;uri&quot;],
+                    credentials.fields[&quot;nonce&quot;],
+                    credentials.fields.get(&quot;cnonce&quot;, &quot;&quot;),
+                    credentials.fields[&quot;algorithm&quot;],
+                    credentials.fields.get(&quot;nc&quot;, &quot;&quot;),
+                    credentials.fields.get(&quot;qop&quot;, &quot;&quot;),
+                    credentials.fields[&quot;response&quot;],
+                    credentials.method
+                ))
+            )
+
+
</ins><span class="cx">     def verifyPlaintextPassword(self, password):
</span><span class="cx">         return self.service._call(
</span><span class="cx">             VerifyPlaintextPasswordCommand,
</span><span class="lines">@@ -200,7 +305,52 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def members(self):
+        return self.service._call(
+            MembersCommand,
+            self.service._processMultipleRecords,
+            uid=self.uid.encode(&quot;utf-8&quot;)
+        )
</ins><span class="cx"> 
</span><ins>+
+    def groups(self):
+        return self.service._call(
+            GroupsCommand,
+            self.service._processMultipleRecords,
+            uid=self.uid.encode(&quot;utf-8&quot;)
+        )
+
+
+    def setMembers(self, members):
+        log.debug(&quot;DPS Client setMembers&quot;)
+        memberUIDs = [m.uid.encode(&quot;utf-8&quot;) for m in members]
+        return self.service._call(
+            SetMembersCommand,
+            lambda x: x['success'],
+            uid=self.uid.encode(&quot;utf-8&quot;),
+            memberUIDs=memberUIDs
+        )
+
+
+
+
+    # For scheduling/freebusy
+    # FIXME: doesn't this need to happen in the DPS?
+    @inlineCallbacks
+    def isProxyFor(self, other):
+        for recordType in (
+            txdav.who.delegates.RecordType.readDelegatorGroup,
+            txdav.who.delegates.RecordType.writeDelegatorGroup,
+        ):
+            delegatorGroup = yield self.service.recordWithShortName(
+                recordType, self.uid
+            )
+            if delegatorGroup:
+                if other in (yield delegatorGroup.members()):
+                    returnValue(True)
+
+
+
</ins><span class="cx"> # Test client:
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavdpscommandspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/dps/commands.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/dps/commands.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/dps/commands.py        2014-03-08 02:19:19 UTC (rev 12861)
</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 class="lines">@@ -89,6 +98,37 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class MembersCommand(amp.Command):
+    arguments = [
+        ('uid', amp.String()),
+    ]
+    response = [
+        ('fieldsList', amp.String()),
+    ]
+
+
+
+class GroupsCommand(amp.Command):
+    arguments = [
+        ('uid', amp.String()),
+    ]
+    response = [
+        ('fieldsList', amp.String()),
+    ]
+
+
+
+class SetMembersCommand(amp.Command):
+    arguments = [
+        ('uid', amp.String()),
+        ('memberUIDs', amp.ListOf(amp.String())),
+    ]
+    response = [
+        ('success', amp.Boolean()),
+    ]
+
+
+
</ins><span class="cx"> class VerifyPlaintextPasswordCommand(amp.Command):
</span><span class="cx">     arguments = [
</span><span class="cx">         ('uid', amp.String()),
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavdpsserverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/dps/server.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/dps/server.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/dps/server.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -18,28 +18,36 @@
</span><span class="cx"> import os
</span><span class="cx"> import uuid
</span><span class="cx"> 
</span><ins>+from calendarserver.tap.util import getDBPool, storeFromConfig
</ins><span class="cx"> from twext.python.log import Logger
</span><ins>+from twext.who.aggregate import DirectoryService as AggregateDirectoryService
</ins><span class="cx"> from twext.who.idirectory import RecordType
</span><ins>+from twext.who.ldap import DirectoryService as LDAPDirectoryService
</ins><span class="cx"> from twisted.application import service
</span><span class="cx"> from twisted.application.strports import service as strPortsService
</span><ins>+from twisted.cred.credentials import UsernamePassword
</ins><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from twisted.internet.protocol import Factory
</span><span class="cx"> from twisted.plugin import IPlugin
</span><span class="cx"> from twisted.protocols import amp
</span><ins>+from twisted.python.constants import Names, NamedConstant
</ins><span class="cx"> from twisted.python.filepath import FilePath
</span><ins>+from twisted.python.reflect import namedClass
</ins><span class="cx"> from twisted.python.usage import Options, UsageError
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
</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,
+    MembersCommand, GroupsCommand, SetMembersCommand,
</ins><span class="cx">     VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand,
</span><span class="cx">     # UpdateRecordsCommand, RemoveRecordsCommand
</span><span class="cx"> )
</span><del>-from twext.who.ldap import DirectoryService as LDAPDirectoryService
</del><ins>+from txdav.who.augment import AugmentedDirectoryService
+from txdav.who.delegates import DirectoryService as DelegateDirectoryService
</ins><span class="cx"> from txdav.who.xml import DirectoryService as XMLDirectoryService
</span><span class="cx"> from zope.interface import implementer
</span><del>-from twisted.cred.credentials import UsernamePassword
</del><span class="cx"> 
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><span class="lines">@@ -63,15 +71,21 @@
</span><span class="cx"> 
</span><span class="cx">     def recordToDict(self, record):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        This to be replaced by something awesome
</del><ins>+        Turn a record in a dictionary of fields which can be reconstituted
+        within the client
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         fields = {}
</span><span class="cx">         if record is not None:
</span><span class="cx">             for field, value in record.fields.iteritems():
</span><del>-                # print(&quot;%s: %s&quot; % (field.name, value))
-                valueType = self._directory.fieldName.valueType(field)
-                if valueType is unicode:
</del><ins>+                valueType = record.service.fieldName.valueType(field)
+                # print(&quot;%s: %s (%s)&quot; % (field.name, value, valueType))
+                if valueType in (unicode, bool):
</ins><span class="cx">                     fields[field.name] = value
</span><ins>+                elif valueType is uuid.UUID:
+                    fields[field.name] = str(value)
+                elif issubclass(valueType, (Names, NamedConstant)):
+                    fields[field.name] = value.name if value else None
+        # print(&quot;Server side fields&quot;, fields)
</ins><span class="cx">         return fields
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -82,7 +96,7 @@
</span><span class="cx">         shortName = shortName.decode(&quot;utf-8&quot;)
</span><span class="cx">         log.debug(&quot;RecordWithShortName: {r} {n}&quot;, r=recordType, n=shortName)
</span><span class="cx">         record = (yield self._directory.recordWithShortName(
</span><del>-            RecordType.lookupByName(recordType), shortName)
</del><ins>+            self._directory.recordType.lookupByName(recordType), shortName)
</ins><span class="cx">         )
</span><span class="cx">         fields = self.recordToDict(record)
</span><span class="cx">         response = {
</span><span class="lines">@@ -158,6 +172,97 @@
</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]
+        log.debug(&quot;RecordsMatchingTokens: {t}&quot;, t=(&quot;, &quot;.join(tokens)))
+        records = yield self._directory.recordsMatchingTokens(
+            tokens, context=context
+        )
+        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)
+
+
+    @MembersCommand.responder
+    @inlineCallbacks
+    def members(self, uid):
+        uid = uid.decode(&quot;utf-8&quot;)
+        log.debug(&quot;Members: {u}&quot;, u=uid)
+        try:
+            record = (yield self._directory.recordWithUID(uid))
+        except Exception as e:
+            log.error(&quot;Failed in members&quot;, error=e)
+            record = None
+
+        fieldsList = []
+        if record is not None:
+            for member in (yield record.members()):
+                fieldsList.append(self.recordToDict(member))
+        response = {
+            &quot;fieldsList&quot;: pickle.dumps(fieldsList),
+        }
+        log.debug(&quot;Responding with: {response}&quot;, response=response)
+        returnValue(response)
+
+
+    @SetMembersCommand.responder
+    @inlineCallbacks
+    def setMembers(self, uid, memberUIDs):
+        uid = uid.decode(&quot;utf-8&quot;)
+        memberUIDs = [m.decode(&quot;utf-8&quot;) for m in memberUIDs]
+        log.debug(&quot;Set Members: {u} -&gt; {m}&quot;, u=uid, m=memberUIDs)
+        try:
+            record = (yield self._directory.recordWithUID(uid))
+        except Exception as e:
+            log.error(&quot;Failed in setMembers&quot;, error=e)
+            record = None
+
+        if record is not None:
+            memberRecords = []
+            for memberUID in memberUIDs:
+                memberRecord = yield self._directory.recordWithUID(memberUID)
+                if memberRecord is not None:
+                    memberRecords.append(memberRecord)
+            yield record.setMembers(memberRecords)
+            success = True
+        else:
+            success = False
+
+        response = {
+            &quot;success&quot;: success,
+        }
+        log.debug(&quot;Responding with: {response}&quot;, response=response)
+        returnValue(response)
+
+
+    @GroupsCommand.responder
+    @inlineCallbacks
+    def groups(self, uid):
+        uid = uid.decode(&quot;utf-8&quot;)
+        log.debug(&quot;Groups: {u}&quot;, u=uid)
+        try:
+            record = (yield self._directory.recordWithUID(uid))
+        except Exception as e:
+            log.error(&quot;Failed in groups&quot;, error=e)
+            record = None
+
+        fieldsList = []
+        for group in (yield record.groups()):
+            fieldsList.append(self.recordToDict(group))
+        response = {
+            &quot;fieldsList&quot;: pickle.dumps(fieldsList),
+        }
+        log.debug(&quot;Responding with: {response}&quot;, response=response)
+        returnValue(response)
+
+
</ins><span class="cx">     @VerifyPlaintextPasswordCommand.responder
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def verifyPlaintextPassword(self, uid, password):
</span><span class="lines">@@ -314,6 +419,89 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def directoryFromConfig(config, store=None):
+    &quot;&quot;&quot;
+    Return a directory service based on the config
+    &quot;&quot;&quot;
+    directoryType = config.DirectoryProxy.DirectoryType
+    args = config.DirectoryProxy.Arguments
+    kwds = config.DirectoryProxy.Keywords
+
+    # FIXME: this needs to talk to its own separate database
+    pool, txnFactory = getDBPool(config)
+    if store is None:
+        store = storeFromConfig(config, txnFactory, None)
+
+    if directoryType == &quot;OD&quot;:
+        from twext.who.opendirectory import DirectoryService as ODDirectoryService
+        primaryDirectory = ODDirectoryService(*args, **kwds)
+
+    elif directoryType == &quot;LDAP&quot;:
+        authDN = kwds.pop(&quot;authDN&quot;, &quot;&quot;)
+        password = kwds.pop(&quot;password&quot;, &quot;&quot;)
+        if authDN and password:
+            creds = UsernamePassword(authDN, password)
+        else:
+            creds = None
+        kwds[&quot;credentials&quot;] = creds
+        debug = kwds.pop(&quot;debug&quot;, &quot;&quot;)
+        primaryDirectory = LDAPDirectoryService(
+            *args, _debug=debug, **kwds
+        )
+
+    elif directoryType == &quot;XML&quot;:
+        path = kwds.pop(&quot;path&quot;, &quot;&quot;)
+        if not path or not os.path.exists(path):
+            log.error(&quot;Path not found for XML directory: {p}&quot;, p=path)
+        fp = FilePath(path)
+        primaryDirectory = XMLDirectoryService(fp, *args, **kwds)
+
+    else:
+        log.error(&quot;Invalid DirectoryType: {dt}&quot;, dt=directoryType)
+
+    #
+    # Setup the Augment Service
+    #
+    if config.AugmentService.type:
+        augmentClass = namedClass(config.AugmentService.type)
+        log.info(
+            &quot;Configuring augment service of type: {augmentClass}&quot;,
+            augmentClass=augmentClass
+        )
+        try:
+            augmentService = augmentClass(**config.AugmentService.params)
+        except IOError:
+            log.error(&quot;Could not start augment service&quot;)
+            raise
+    else:
+        augmentService = None
+
+    delegateDirectory = DelegateDirectoryService(
+        primaryDirectory.realmName,
+        store
+    )
+
+    aggregateDirectory = AggregateDirectoryService(
+        primaryDirectory.realmName,
+        (primaryDirectory, delegateDirectory)
+    )
+    try:
+        augmented = AugmentedDirectoryService(
+            aggregateDirectory, store, augmentService
+        )
+
+        # The delegate directory needs a way to look up user/group records
+        # so hand it a reference to the augmented directory.
+        # FIXME: is there a better pattern to use here?
+        delegateDirectory.setMasterDirectory(augmented)
+
+    except Exception as e:
+        log.error(&quot;Could not create directory service&quot;, error=e)
+        raise
+
+    return augmented
+
+
</ins><span class="cx"> @implementer(IPlugin, service.IServiceMaker)
</span><span class="cx"> class DirectoryProxyServiceMaker(object):
</span><span class="cx"> 
</span><span class="lines">@@ -333,36 +521,20 @@
</span><span class="cx">         else:
</span><span class="cx">             setproctitle(&quot;CalendarServer Directory Proxy Service&quot;)
</span><span class="cx"> 
</span><del>-        directoryType = config.DirectoryProxy.DirectoryType
-        args = config.DirectoryProxy.Arguments
-        kwds = config.DirectoryProxy.Keywords
</del><ins>+        try:
+            print(&quot;XZZZY AAA&quot;)
+            directory = directoryFromConfig(config)
+            print(&quot;XZZZY BBB&quot;)
+        except Exception as e:
+            log.error(&quot;Failed to create directory service&quot;, error=e)
+            raise
</ins><span class="cx"> 
</span><del>-        if directoryType == &quot;OD&quot;:
-            from twext.who.opendirectory import DirectoryService as ODDirectoryService
-            directory = ODDirectoryService(*args, **kwds)
</del><ins>+        log.info(&quot;Created directory service&quot;)
+        print(&quot;XZZZY CCCC&quot;)
</ins><span class="cx"> 
</span><del>-        elif directoryType == &quot;LDAP&quot;:
-            authDN = kwds.pop(&quot;authDN&quot;, &quot;&quot;)
-            password = kwds.pop(&quot;password&quot;, &quot;&quot;)
-            if authDN and password:
-                creds = UsernamePassword(authDN, password)
-            else:
-                creds = None
-            kwds[&quot;credentials&quot;] = creds
-            debug = kwds.pop(&quot;debug&quot;, &quot;&quot;)
-            directory = LDAPDirectoryService(*args, _debug=debug, **kwds)
-
-        elif directoryType == &quot;XML&quot;:
-            path = kwds.pop(&quot;path&quot;, &quot;&quot;)
-            if not path or not os.path.exists(path):
-                log.error(&quot;Path not found for XML directory: {p}&quot;, p=path)
-            fp = FilePath(path)
-            directory = XMLDirectoryService(fp, *args, **kwds)
-
-        else:
-            log.error(&quot;Invalid DirectoryType: {dt}&quot;, dt=directoryType)
-
-        desc = &quot;unix:{path}:mode=660&quot;.format(
-            path=config.DirectoryProxy.SocketPath
</del><ins>+        return strPortsService(
+            &quot;unix:{path}:mode=660&quot;.format(
+                path=config.DirectoryProxy.SocketPath
+            ),
+            DirectoryProxyAMPFactory(directory)
</ins><span class="cx">         )
</span><del>-        return strPortsService(desc, DirectoryProxyAMPFactory(directory))
</del></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavdpstesttestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test.xml (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test.xml        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test.xml        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -23,6 +23,7 @@
</span><span class="cx"> 
</span><span class="cx">   &lt;record type=&quot;user&quot;&gt;
</span><span class="cx">     &lt;uid&gt;__sagen__&lt;/uid&gt;
</span><ins>+    &lt;guid&gt;B3B1158F-0564-4F5B-81E4-A89EA5FF81B0&lt;/guid&gt;
</ins><span class="cx">     &lt;short-name&gt;sagen&lt;/short-name&gt;
</span><span class="cx">     &lt;full-name&gt;Morgen Sagen&lt;/full-name&gt;
</span><span class="cx">     &lt;password&gt;negas&lt;/password&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavdpstesttest_clientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test_client.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test_client.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/dps/test/test_client.py        2014-03-08 02:19:19 UTC (rev 12861)
</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>
<a id="CalendarServerbranchesuserssagenmove2who2txdavwhoaugmentpyfromrev12860CalendarServerbranchesuserssagenmove2whotxdavwhoaugmentpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/sagen/move2who-2/txdav/who/augment.py (from rev 12860, CalendarServer/branches/users/sagen/move2who/txdav/who/augment.py) (0 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/who/augment.py                                (rev 0)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/who/augment.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -0,0 +1,258 @@
</span><ins>+# -*- test-case-name: txdav.who.test.test_augment -*-
+##
+# Copyright (c) 2013 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;
+Augmenting Directory Service
+&quot;&quot;&quot;
+
+from twext.who.idirectory import IDirectoryService, RecordType
+from twext.who.directory import DirectoryRecord
+from twext.who.directory import DirectoryService as BaseDirectoryService
+from twext.who.util import ConstantsContainer
+from txdav.who.directory import (
+    CalendarDirectoryRecordMixin, CalendarDirectoryServiceMixin
+)
+from txdav.who.idirectory import AutoScheduleMode, FieldName
+from txdav.who.idirectory import RecordType as CalRecordType
+from txdav.who.delegates import RecordType as DelegateRecordType
+from twisted.internet.defer import inlineCallbacks, returnValue
+from zope.interface import implementer
+
+from twext.python.log import Logger
+log = Logger()
+
+
+class AugmentedDirectoryRecord(DirectoryRecord, CalendarDirectoryRecordMixin):
+
+    def __init__(self, service, baseRecord, augmentedFields):
+        DirectoryRecord.__init__(self, service, augmentedFields)
+        self._baseRecord = baseRecord
+
+
+    @inlineCallbacks
+    def members(self):
+        augmented = []
+        records = yield self._baseRecord.members()
+        for record in records:
+            augmented.append((yield self.service.augment(record)))
+        returnValue(augmented)
+
+
+    @inlineCallbacks
+    def groups(self):
+        augmented = []
+        txn = self.service._store.newTransaction()
+        groupUIDs = yield txn.groupsFor(self.uid)
+        for groupUID in groupUIDs:
+            groupRecord = yield self.service.recordWithShortName(
+                RecordType.group, groupUID
+            )
+            if groupRecord:
+                augmented.append((yield self.service.augment(groupRecord)))
+        returnValue(augmented)
+
+
+@implementer(IDirectoryService)
+class AugmentedDirectoryService(BaseDirectoryService,
+                                CalendarDirectoryServiceMixin):
+
+    fieldName = ConstantsContainer((
+        BaseDirectoryService.fieldName,
+        FieldName,
+    ))
+
+    recordType = ConstantsContainer((
+        RecordType.user,
+        RecordType.group,
+        CalRecordType.location,
+        CalRecordType.resource,
+        CalRecordType.address,
+        DelegateRecordType.readDelegateGroup,
+        DelegateRecordType.writeDelegateGroup,
+        DelegateRecordType.readDelegatorGroup,
+        DelegateRecordType.writeDelegatorGroup,
+    ))
+
+
+    def __init__(self, directory, store, augmentDB):
+        BaseDirectoryService.__init__(self, directory.realmName)
+        self._directory = directory
+        self._store = store
+        self._augmentDB = augmentDB
+
+
+    def recordTypes(self):
+        return self._directory.recordTypes()
+
+
+    @inlineCallbacks
+    def recordsFromExpression(self, expression):
+        records = yield self._directory.recordsFromExpression(expression)
+        augmented = []
+        for record in records:
+            record = yield self.augment(record)
+            augmented.append(record)
+        returnValue(augmented)
+
+
+    @inlineCallbacks
+    def recordsWithFieldValue(self, fieldName, value):
+        records = yield self._directory.recordsWithFieldValue(
+            fieldName, value
+        )
+        augmented = []
+        for record in records:
+            record = yield self.augment(record)
+            augmented.append(record)
+        returnValue(augmented)
+
+
+    @inlineCallbacks
+    def recordWithUID(self, uid):
+        record = yield self._directory.recordWithUID(uid)
+        record = yield self.augment(record)
+        returnValue(record)
+
+
+    @inlineCallbacks
+    def recordWithGUID(self, guid):
+        record = yield self._directory.recordWithGUID(guid)
+        record = yield self.augment(record)
+        returnValue(record)
+
+
+    @inlineCallbacks
+    def recordsWithRecordType(self, recordType):
+        records = yield self._directory.recordsWithRecordType(recordType)
+        augmented = []
+        for record in records:
+            record = yield self.augment(record)
+            augmented.append(record)
+        returnValue(augmented)
+
+
+    @inlineCallbacks
+    def recordWithShortName(self, recordType, shortName):
+        record = yield self._directory.recordWithShortName(recordType, shortName)
+        record = yield self.augment(record)
+        returnValue(record)
+
+
+    @inlineCallbacks
+    def recordsWithEmailAddress(self, emailAddress):
+        records = yield self._directory.recordsWithEmailAddress(emailAddress)
+        augmented = []
+        for record in records:
+            record = yield self.augment(record)
+            augmented.append(record)
+        returnValue(augmented)
+
+
+    @inlineCallbacks
+    def updateRecords(self, records, create=False):
+        return self._directory.updateRecords(records, create=create)
+
+
+    @inlineCallbacks
+    def removeRecords(self, uids):
+        return self._directory.removeRecords(uids)
+
+
+    def assignToField(self, fields, name, value):
+        field = self.fieldName.lookupByName(name)
+        fields[field] = value
+
+
+    @inlineCallbacks
+    def augment(self, record):
+        if record is None:
+            returnValue(None)
+
+        # MOVE2WHO
+        # FIXME: hacked by appending an &quot;s&quot; -- need a mapping
+        try:
+            augmentRecord = yield self._augmentDB.getAugmentRecord(
+                record.uid,
+                record.recordType.name + &quot;s&quot;
+            )
+        except KeyError:
+            # Augments does not know about this record type, so return
+            # the original record
+            returnValue(record)
+
+        fields = record.fields.copy()
+
+        # print(&quot;Got augment record&quot;, augmentRecord)
+
+        if augmentRecord:
+            # record.enabled = augmentRecord.enabled
+            # record.serverID = augmentRecord.serverID
+            self.assignToField(
+                fields, &quot;hasCalendars&quot;,
+                augmentRecord.enabledForCalendaring
+            )
+            self.assignToField(
+                fields, &quot;hasContacts&quot;,
+                augmentRecord.enabledForAddressBooks
+            )
+            autoScheduleMode = {
+                &quot;none&quot;: AutoScheduleMode.none,
+                &quot;accept-always&quot;: AutoScheduleMode.accept,
+                &quot;decline-always&quot;: AutoScheduleMode.decline,
+                &quot;accept-if-free&quot;: AutoScheduleMode.acceptIfFree,
+                &quot;decline-if-busy&quot;: AutoScheduleMode.declineIfBusy,
+                &quot;automatic&quot;: AutoScheduleMode.acceptIfFreeDeclineIfBusy,
+            }.get(augmentRecord.autoScheduleMode, None)
+            self.assignToField(
+                fields, &quot;autoScheduleMode&quot;,
+                autoScheduleMode
+            )
+            self.assignToField(
+                fields, &quot;autoAcceptGroup&quot;,
+                unicode(augmentRecord.autoAcceptGroup)
+            )
+            self.assignToField(
+                fields, &quot;loginAllowed&quot;,
+                augmentRecord.enabledForLogin
+            )
+
+            if (
+                (
+                    fields.get(self.fieldName.lookupByName(&quot;hasCalendars&quot;), False) or
+                    fields.get(self.fieldName.lookupByName(&quot;hasContacts&quot;), False)
+                ) and record.recordType == RecordType.group
+            ):
+                self.assignToField(fields, &quot;hasCalendars&quot;, False)
+                self.assignToField(fields, &quot;hasContacts&quot;, False)
+
+                # For augment records cloned from the Default augment record,
+                # don't emit this message:
+                if not augmentRecord.clonedFromDefault:
+                    log.error(&quot;Group '%s(%s)' cannot be enabled for calendaring or address books&quot; % (record.guid, record.shortNames[0],))
+
+        else:
+            # Groups are by default always enabled
+            # record.enabled = (record.recordType == record.service.recordType_groups)
+            # record.serverID = &quot;&quot;
+            self.assignToField(fields, &quot;hasCalendars&quot;, False)
+            self.assignToField(fields, &quot;hasContacts&quot;, False)
+            self.assignToField(fields, &quot;loginAllowed&quot;, False)
+
+        # print(&quot;Augmented fields&quot;, fields)
+
+        # Clone to a new record with the augmented fields
+        returnValue(AugmentedDirectoryRecord(self, record, fields))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/who/delegates.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/who/delegates.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/who/delegates.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-# -*- test-case-name: twext.who.test.test_delegates -*-
</del><ins>+# -*- test-case-name: txdav.who.test.test_delegates -*-
</ins><span class="cx"> ##
</span><span class="cx"> # Copyright (c) 2013 Apple Inc. All rights reserved.
</span><span class="cx"> #
</span><span class="lines">@@ -19,13 +19,201 @@
</span><span class="cx"> Delegate assignments
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.who.idirectory import RecordType
</del><ins>+from twext.python.log import Logger
+from twext.who.directory import (
+    DirectoryService as BaseDirectoryService,
+    DirectoryRecord as BaseDirectoryRecord
+)
+from twext.who.expression import MatchExpression, MatchType
+from twext.who.idirectory import (
+    RecordType as BaseRecordType, FieldName, NotAllowedError
+)
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twisted.python.constants import Names, NamedConstant
</ins><span class="cx"> 
</span><del>-from twext.python.log import Logger
</del><ins>+
</ins><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
+class RecordType(Names):
+    &quot;&quot;&quot;
+    Constants for read-only delegates and read-write delegate groups
+    &quot;&quot;&quot;
+
+    readDelegateGroup = NamedConstant()
+    readDelegateGroup.description = u&quot;read-delegate-group&quot;
+
+    writeDelegateGroup = NamedConstant()
+    writeDelegateGroup.description = u&quot;write-delegate-group&quot;
+
+    readDelegatorGroup = NamedConstant()
+    readDelegatorGroup.description = u&quot;read-delegator-group&quot;
+
+    writeDelegatorGroup = NamedConstant()
+    writeDelegatorGroup.description = u&quot;write-delegator-group&quot;
+
+
+class DirectoryRecord(BaseDirectoryRecord):
+
+
+    @inlineCallbacks
+    def members(self, expanded=False):
+        &quot;&quot;&quot;
+        If this is a readDelegateGroup or writeDelegateGroup, the members
+        will consist of the records who are delegates *of* this record.
+        If this is a readDelegatorGroup or writeDelegatorGroup,
+        the members will consist of the records who have delegated *to*
+        this record.
+        &quot;&quot;&quot;
+        parentUID, proxyType = self.uid.split(&quot;#&quot;)
+
+        txn = self.service._store.newTransaction()
+
+        if self.recordType in (
+            RecordType.readDelegateGroup, RecordType.writeDelegateGroup
+        ):  # Members are delegates of this record
+            readWrite = (self.recordType is RecordType.writeDelegateGroup)
+            delegateUIDs = (
+                yield txn.delegates(parentUID, readWrite, expanded=expanded)
+            )
+
+        else:  # Members have delegated to this record
+            readWrite = (self.recordType is RecordType.writeDelegatorGroup)
+            delegateUIDs = (
+                yield txn.delegators(parentUID, readWrite)
+            )
+
+        records = []
+        for uid in delegateUIDs:
+            if uid != parentUID:
+                record = (yield self.service._masterDirectory.recordWithUID(uid))
+                if record is not None:
+                    records.append(record)
+        yield txn.commit()
+
+        returnValue(records)
+
+
+
+    @inlineCallbacks
+    def setMembers(self, memberRecords):
+        &quot;&quot;&quot;
+        Replace the members of this group with the new members.
+
+        @param memberRecords: The new members of the group
+        @type memberRecords: iterable of L{iDirectoryRecord}s
+        &quot;&quot;&quot;
+        if self.recordType not in (
+            RecordType.readDelegateGroup, RecordType.writeDelegateGroup
+        ):
+            raise NotAllowedError(&quot;Setting members not supported&quot;)
+
+        parentUID, proxyType = self.uid.split(&quot;#&quot;)
+        readWrite = (self.recordType is RecordType.writeDelegateGroup)
+
+        log.debug(
+            &quot;Setting delegate assignments for {u} ({rw}) to {m}&quot;.format(
+                u=parentUID, rw=(&quot;write&quot; if readWrite else &quot;read&quot;),
+                m=[r.uid for r in memberRecords]
+            )
+        )
+
+        txn = self.service._store.newTransaction()
+
+        yield txn.removeDelegates(parentUID, readWrite)
+        yield txn.removeDelegateGroups(parentUID, readWrite)
+
+        delegator = yield self.service._masterDirectory.recordWithUID(parentUID)
+
+        for delegate in memberRecords:
+            yield addDelegate(txn, delegator, delegate, readWrite)
+
+        yield txn.commit()
+
+
+
+def recordTypeToProxyType(recordType):
+    return {
+        RecordType.readDelegateGroup: &quot;calendar-proxy-read&quot;,
+        RecordType.writeDelegateGroup: &quot;calendar-proxy-write&quot;,
+        RecordType.readDelegatorGroup: &quot;calendar-proxy-read-for&quot;,
+        RecordType.writeDelegatorGroup: &quot;calendar-proxy-write-for&quot;,
+    }.get(recordType, None)
+
+
+def proxyTypeToRecordType(proxyType):
+    return {
+        &quot;calendar-proxy-read&quot;: RecordType.readDelegateGroup,
+        &quot;calendar-proxy-write&quot;: RecordType.writeDelegateGroup,
+        &quot;calendar-proxy-read-for&quot;: RecordType.readDelegatorGroup,
+        &quot;calendar-proxy-write-for&quot;: RecordType.writeDelegatorGroup,
+    }.get(proxyType, None)
+
+
+
+class DirectoryService(BaseDirectoryService):
+    &quot;&quot;&quot;
+    Delegate directory service
+    &quot;&quot;&quot;
+
+    recordType = RecordType
+
+
+    def __init__(self, realmName, store):
+        BaseDirectoryService.__init__(self, realmName)
+        self._store = store
+        self._masterDirectory = None
+
+
+    def setMasterDirectory(self, masterDirectory):
+        self._masterDirectory = masterDirectory
+
+
+    def recordWithShortName(self, recordType, shortName):
+        uid = shortName + &quot;#&quot; + recordTypeToProxyType(recordType)
+
+        record = DirectoryRecord(self, {
+            FieldName.uid: uid,
+            FieldName.recordType: recordType,
+            FieldName.shortNames: (uid,),
+        })
+        return succeed(record)
+
+
+    def recordWithUID(self, uid):
+        if &quot;#&quot; not in uid:  # Not a delegate group uid
+            return succeed(None)
+        uid, proxyType = uid.split(&quot;#&quot;)
+        recordType = proxyTypeToRecordType(proxyType)
+        if recordType is None:
+            return succeed(None)
+        return self.recordWithShortName(recordType, uid)
+
+
+    @inlineCallbacks
+    def recordsFromExpression(self, expression, records=None):
+        &quot;&quot;&quot;
+        It's only ever appropriate to look up delegate group record by
+        shortName or uid.  When wrapped by an aggregate directory, looking up
+        by shortName will already go directly to recordWithShortName.  However
+        when looking up by UID, it won't.  Inspect the expression to see if
+        it's one we can handle.
+        &quot;&quot;&quot;
+        if isinstance(expression, MatchExpression):
+            if(
+                (expression.fieldName is FieldName.uid) and
+                (expression.matchType is MatchType.equals) and
+                (&quot;#&quot; in expression.fieldValue)
+            ):
+                record = yield self.recordWithUID(expression.fieldValue)
+                if record is not None:
+                    returnValue((record,))
+
+        returnValue(())
+
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def addDelegate(txn, delegator, delegate, readWrite):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -39,12 +227,12 @@
</span><span class="cx">     @param readWrite: if True, read and write access is granted; read-only
</span><span class="cx">         access otherwise
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    if delegate.recordType == RecordType.group:
</del><ins>+    if delegate.recordType == BaseRecordType.group:
</ins><span class="cx">         # find the groupID
</span><del>-        groupID, name, membershipHash = (yield txn.groupByGUID(delegate.guid))
-        yield txn.addDelegateGroup(delegator.guid, groupID, readWrite)
</del><ins>+        groupID, name, membershipHash = (yield txn.groupByUID(delegate.uid))
+        yield txn.addDelegateGroup(delegator.uid, groupID, readWrite)
</ins><span class="cx">     else:
</span><del>-        yield txn.addDelegate(delegator.guid, delegate.guid, readWrite)
</del><ins>+        yield txn.addDelegate(delegator.uid, delegate.uid, readWrite)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -60,16 +248,16 @@
</span><span class="cx">     @param readWrite: if True, read and write access is revoked; read-only
</span><span class="cx">         access otherwise
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    if delegate.recordType == RecordType.group:
</del><ins>+    if delegate.recordType == BaseRecordType.group:
</ins><span class="cx">         # find the groupID
</span><del>-        groupID, name, membershipHash = (yield txn.groupByGUID(delegate.guid))
-        yield txn.removeDelegateGroup(delegator.guid, groupID, readWrite)
</del><ins>+        groupID, name, membershipHash = (yield txn.groupByUID(delegate.uid))
+        yield txn.removeDelegateGroup(delegator.uid, groupID, readWrite)
</ins><span class="cx">     else:
</span><del>-        yield txn.removeDelegate(delegator.guid, delegate.guid, readWrite)
</del><ins>+        yield txn.removeDelegate(delegator.uid, delegate.uid, readWrite)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def delegatesOf(txn, delegator, readWrite):
</del><ins>+def delegatesOf(txn, delegator, readWrite, expanded=False):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Return the records of the delegates of &quot;delegator&quot;.  The type of access
</span><span class="cx">     is specified by the &quot;readWrite&quot; parameter.
</span><span class="lines">@@ -83,10 +271,12 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     records = []
</span><span class="cx">     directory = delegator.service
</span><del>-    delegateGUIDs = (yield txn.delegates(delegator.guid, readWrite))
-    for guid in delegateGUIDs:
-        if guid != delegator.guid:
-            record = (yield directory.recordWithGUID(guid))
</del><ins>+    delegateUIDs = (
+        yield txn.delegates(delegator.uid, readWrite, expanded=expanded)
+    )
+    for uid in delegateUIDs:
+        if uid != delegator.uid:
+            record = (yield directory.recordWithUID(uid))
</ins><span class="cx">             if record is not None:
</span><span class="cx">                 records.append(record)
</span><span class="cx">     returnValue(records)
</span><span class="lines">@@ -107,18 +297,10 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     records = []
</span><span class="cx">     directory = delegate.service
</span><del>-    delegatorGUIDs = (yield txn.delegators(delegate.guid, readWrite))
-    for guid in delegatorGUIDs:
-        if guid != delegate.guid:
-            record = (yield directory.recordWithGUID(guid))
</del><ins>+    delegatorUIDs = (yield txn.delegators(delegate.uid, readWrite))
+    for uid in delegatorUIDs:
+        if uid != delegate.uid:
+            record = (yield directory.recordWithUID(uid))
</ins><span class="cx">             if record is not None:
</span><span class="cx">                 records.append(record)
</span><span class="cx">     returnValue(records)
</span><del>-
-
-def allGroupDelegates(txn):
-    &quot;&quot;&quot;
-    @return: the GUIDs of all groups which are currently delegated to
-    @rtype: a Deferred which fires with a set() of GUID strings
-    &quot;&quot;&quot;
-    return txn.allGroupDelegates()
</del></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavwhodirectorypyfromrev12860CalendarServerbranchesuserssagenmove2whotxdavwhodirectorypy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/sagen/move2who-2/txdav/who/directory.py (from rev 12860, CalendarServer/branches/users/sagen/move2who/txdav/who/directory.py) (0 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/who/directory.py                                (rev 0)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/who/directory.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -0,0 +1,241 @@
</span><ins>+##
+# Copyright (c) 2006-2014 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;
+Calendar/Contacts specific methods for DirectoryRecord
+&quot;&quot;&quot;
+
+
+import uuid
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twext.who.expression import (
+    MatchType, Operand, MatchExpression, CompoundExpression, MatchFlags
+)
+
+
+__all__ = [
+    &quot;CalendarDirectoryRecordMixin&quot;,
+    &quot;CalendarDirectoryServiceMixin&quot;,
+]
+
+
+class CalendarDirectoryServiceMixin(object):
+
+    # Must maintain the hack for a bit longer:
+    def setPrincipalCollection(self, principalCollection):
+        &quot;&quot;&quot;
+        Set the principal service that the directory relies on for doing proxy tests.
+
+        @param principalService: the principal service.
+        @type principalService: L{DirectoryProvisioningResource}
+        &quot;&quot;&quot;
+        self.principalCollection = principalCollection
+
+
+    @inlineCallbacks
+    def recordWithCalendarUserAddress(self, address):
+        # FIXME: Circular
+        from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
+        address = normalizeCUAddr(address)
+        record = None
+        if address.startswith(&quot;urn:uuid:&quot;):
+            guid = address[9:]
+            record = yield self.recordWithGUID(uuid.UUID(guid))
+        elif address.startswith(&quot;mailto:&quot;):
+            records = yield self.recordsWithEmailAddress(address[7:])
+            if records:
+                returnValue(records[0])
+            else:
+                returnValue(None)
+        elif address.startswith(&quot;/principals/&quot;):
+            parts = address.split(&quot;/&quot;)
+            if len(parts) == 4:
+                if parts[2] == &quot;__uids__&quot;:
+                    uid = parts[3]
+                    record = yield self.recordWithUID(uid)
+                else:
+                    recordType = self.fieldName.lookupByName(parts[2])
+                    record = yield self.recordWithShortName(recordType, parts[3])
+
+        returnValue(record if record and record.hasCalendars else None)
+
+
+    def recordsMatchingTokens(self, tokens, context=None, limitResults=50,
+                              timeoutSeconds=10):
+        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.fieldName.lookupByName(name),
+                        token,
+                        matchType,
+                        MatchFlags.caseInsensitive
+                    )
+                )
+            outer.append(
+                CompoundExpression(
+                    inner,
+                    Operand.OR
+                )
+            )
+        expression = CompoundExpression(outer, Operand.AND)
+        return self.recordsFromExpression(expression)
+
+
+    # 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;
+
+
+    def oldNameToRecordType(self, oldName):
+        return self.recordType.lookupByName(oldName[:-1])
+
+
+
+class CalendarDirectoryRecordMixin(object):
+
+    @property
+    def calendarUserAddresses(self):
+        if not self.hasCalendars:
+            return frozenset()
+
+        try:
+            cuas = set(
+                [&quot;mailto:%s&quot; % (emailAddress,)
+                 for emailAddress in self.emailAddresses]
+            )
+        except AttributeError:
+            cuas = set()
+
+        try:
+            if self.guid:
+                if isinstance(self.guid, uuid.UUID):
+                    guid = unicode(self.guid).upper()
+                else:
+                    guid = self.guid
+                cuas.add(&quot;urn:uuid:{guid}&quot;.format(guid=guid))
+        except AttributeError:
+            # No guid
+            pass
+        cuas.add(&quot;/principals/__uids__/{uid}/&quot;.format(uid=self.uid))
+        for shortName in self.shortNames:
+            cuas.add(&quot;/principals/{rt}/{sn}/&quot;.format(
+                rt=self.recordType.name + &quot;s&quot;, sn=shortName)
+            )
+        return frozenset(cuas)
+
+
+    def getCUType(self):
+        # Mapping from directory record.recordType to RFC2445 CUTYPE values
+        self._cuTypes = {
+            self.service.recordType.user: 'INDIVIDUAL',
+            self.service.recordType.group: 'GROUP',
+            self.service.recordType.resource: 'RESOURCE',
+            self.service.recordType.location: 'ROOM',
+        }
+
+        return self._cuTypes.get(self.recordType, &quot;UNKNOWN&quot;)
+
+
+    @property
+    def displayName(self):
+        return self.fullNames[0]
+
+
+    def cacheToken(self):
+        &quot;&quot;&quot;
+        Generate a token that can be uniquely used to identify the state of this record for use
+        in a cache.
+        &quot;&quot;&quot;
+        return hash((
+            self.__class__.__name__,
+            self.service.realmName,
+            self.recordType.name,
+            self.shortNames,
+            self.guid,
+            self.hasCalendars,
+        ))
+
+
+    def canonicalCalendarUserAddress(self):
+        &quot;&quot;&quot;
+            Return a CUA for this record, preferring in this order:
+            urn:uuid: form
+            mailto: form
+            first in calendarUserAddresses list
+        &quot;&quot;&quot;
+
+        cua = &quot;&quot;
+        for candidate in self.calendarUserAddresses:
+            # Pick the first one, but urn:uuid: and mailto: can override
+            if not cua:
+                cua = candidate
+            # But always immediately choose the urn:uuid: form
+            if candidate.startswith(&quot;urn:uuid:&quot;):
+                cua = candidate
+                break
+            # Prefer mailto: if no urn:uuid:
+            elif candidate.startswith(&quot;mailto:&quot;):
+                cua = candidate
+        return cua
+
+
+    def enabledAsOrganizer(self):
+        # MOVE2WHO FIXME TO LOOK AT CONFIG
+        if self.recordType == self.service.recordType.user:
+            return True
+        elif self.recordType == self.service.recordType.group:
+            return False  # config.Scheduling.Options.AllowGroupAsOrganizer
+        elif self.recordType == self.service.recordType.location:
+            return False  # config.Scheduling.Options.AllowLocationAsOrganizer
+        elif self.recordType == self.service.recordType.resource:
+            return False  # config.Scheduling.Options.AllowResourceAsOrganizer
+        else:
+            return False
+
+
+    #MOVE2WHO
+    def thisServer(self):
+        return True
+
+
+    def isLoginEnabled(self):
+        return self.loginAllowed
+
+
+    #MOVE2WHO
+    def calendarsEnabled(self):
+        # In the old world, this *also* looked at config:
+        # return config.EnableCalDAV and self.enabledForCalendaring
+        return self.hasCalendars
+
+
+    def getAutoScheduleMode(self, organizer):
+        # MOVE2WHO Fix this to take organizer into account:
+        return self.autoScheduleMode
+
+
+    def canAutoSchedule(self, organizer=None):
+        # MOVE2WHO Fix this:
+        return True
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/who/groups.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/who/groups.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/who/groups.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-# -*- test-case-name: twext.who.test.test_groups -*-
</del><ins>+# -*- test-case-name: txdav.who.test.test_groups -*-
</ins><span class="cx"> ##
</span><span class="cx"> # Copyright (c) 2013 Apple Inc. All rights reserved.
</span><span class="cx"> #
</span><span class="lines">@@ -22,7 +22,6 @@
</span><span class="cx"> from twext.enterprise.dal.record import fromTable
</span><span class="cx"> from twext.enterprise.dal.syntax import Delete, Select
</span><span class="cx"> from twext.enterprise.jobqueue import WorkItem, PeerConnectionPool
</span><del>-from txdav.who.delegates import allGroupDelegates
</del><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="lines">@@ -68,7 +67,7 @@
</span><span class="cx"> 
</span><span class="cx">             # New implmementation
</span><span class="cx">             try:
</span><del>-                newGroupCacher.update(self.transaction)
</del><ins>+                yield newGroupCacher.update(self.transaction)
</ins><span class="cx">             except Exception, e:
</span><span class="cx">                 log.error(
</span><span class="cx">                     &quot;Failed to update new group membership cache ({error})&quot;,
</span><span class="lines">@@ -76,13 +75,13 @@
</span><span class="cx">                 )
</span><span class="cx"> 
</span><span class="cx">             # Old implmementation
</span><del>-            try:
-                oldGroupCacher.updateCache()
-            except Exception, e:
-                log.error(
-                    &quot;Failed to update old group membership cache ({error})&quot;,
-                    error=e
-                )
</del><ins>+            # try:
+            #     oldGroupCacher.updateCache()
+            # except Exception, e:
+            #     log.error(
+            #         &quot;Failed to update old group membership cache ({error})&quot;,
+            #         error=e
+            #     )
</ins><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             notBefore = (
</span><span class="lines">@@ -126,25 +125,28 @@
</span><span class="cx"> 
</span><span class="cx"> class GroupRefreshWork(WorkItem, fromTable(schema.GROUP_REFRESH_WORK)):
</span><span class="cx"> 
</span><del>-    group = property(lambda self: self.groupGUID)
</del><ins>+    # Note, the schema has &quot;groupGuid&quot;, but really it's a UID.  At some point
+    # we should change the column name.
+    group = property(lambda self: self.groupGuid)
</ins><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def doWork(self):
</span><del>-
</del><span class="cx">         # Delete all other work items for this group
</span><span class="cx">         yield Delete(
</span><del>-            From=self.table, Where=(self.table.GROUP_GUID == self.groupGUID)
</del><ins>+            From=self.table, Where=(self.table.GROUP_GUID == self.groupGuid)
</ins><span class="cx">         ).on(self.transaction)
</span><span class="cx"> 
</span><del>-        groupCacher = getattr(self.transaction, &quot;_groupCacher&quot;, None)
-        if groupCacher is not None:
</del><ins>+        newGroupCacher = getattr(self.transaction, &quot;_newGroupCacher&quot;, None)
+        if newGroupCacher is not None:
</ins><span class="cx"> 
</span><span class="cx">             try:
</span><del>-                groupCacher.refreshGroup(self.transaction, self.groupGUID)
</del><ins>+                yield newGroupCacher.refreshGroup(
+                    self.transaction, self.groupGuid.decode(&quot;utf-8&quot;)
+                )
</ins><span class="cx">             except Exception, e:
</span><span class="cx">                 log.error(
</span><span class="cx">                     &quot;Failed to refresh group {group} {err}&quot;,
</span><del>-                    group=self.groupGUID, err=e
</del><ins>+                    group=self.groupGuid, err=e
</ins><span class="cx">                 )
</span><span class="cx"> 
</span><span class="cx">         else:
</span><span class="lines">@@ -154,11 +156,11 @@
</span><span class="cx">             )
</span><span class="cx">             log.debug(
</span><span class="cx">                 &quot;Rescheduling group refresh for {group}: {when}&quot;,
</span><del>-                group=self.groupGUID, when=notBefore
</del><ins>+                group=self.groupGuid, when=notBefore
</ins><span class="cx">             )
</span><span class="cx">             yield self.transaction.enqueue(
</span><span class="cx">                 GroupRefreshWork,
</span><del>-                groupGUID=self.groupGUID, notBefore=notBefore
</del><ins>+                groupGuid=self.groupGuid, notBefore=notBefore
</ins><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -182,6 +184,7 @@
</span><span class="cx">             )
</span><span class="cx">         ).on(self.transaction)
</span><span class="cx"> 
</span><ins>+    # MOVE2WHO
</ins><span class="cx">     # TODO: Pull this over from groupcacher branch
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -201,6 +204,7 @@
</span><span class="cx">         records.add(record)
</span><span class="cx">         for member in (yield record.members()):
</span><span class="cx">             if member not in records:
</span><ins>+                #MOVE2WHO
</ins><span class="cx">                 #TODO:  HACK for old-style XML. FIX
</span><span class="cx">                 if (
</span><span class="cx">                     member.recordType != RecordType.group and
</span><span class="lines">@@ -219,14 +223,14 @@
</span><span class="cx">     of two lists -- one for added/updated assignments, and one for removed
</span><span class="cx">     assignments.
</span><span class="cx"> 
</span><del>-    @param old: dictionary of delegator: (readGroupGUID, writeGroupGUID)
</del><ins>+    @param old: dictionary of delegator: (readGroupUID, writeGroupUID)
</ins><span class="cx">     @type old: C{dict}
</span><span class="cx"> 
</span><del>-    @param new: dictionary of delegator: (readGroupGUID, writeGroupGUID)
</del><ins>+    @param new: dictionary of delegator: (readGroupUID, writeGroupUID)
</ins><span class="cx">     @type new: C{dict}
</span><span class="cx"> 
</span><span class="cx">     @return: Tuple of two lists; the first list contains tuples of (delegator,
</span><del>-        (readGroupGUID, writeGroupGUID)), and represents all the new or updated
</del><ins>+        (readGroupUID, writeGroupUID)), and represents all the new or updated
</ins><span class="cx">         assignments.  The second list contains all the delegators which used to
</span><span class="cx">         have a delegate but don't anymore.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -269,19 +273,21 @@
</span><span class="cx">         # yield self.applyExternalAssignments(txn, externalAssignments)
</span><span class="cx"> 
</span><span class="cx">         # Figure out which groups matter
</span><del>-        groupGUIDs = yield self.groupsToRefresh(txn)
</del><ins>+        groupUIDs = yield self.groupsToRefresh(txn)
</ins><span class="cx">         self.log.debug(
</span><del>-            &quot;Number of groups to refresh: {num}&quot;, num=len(groupGUIDs)
</del><ins>+            &quot;Number of groups to refresh: {num}&quot;, num=len(groupUIDs)
</ins><span class="cx">         )
</span><span class="cx">         # For each of those groups, create a per-group refresh work item
</span><del>-        for groupGUID in groupGUIDs:
</del><ins>+        for groupUID in groupUIDs:
</ins><span class="cx">             notBefore = (
</span><span class="cx">                 datetime.datetime.utcnow() +
</span><span class="cx">                 datetime.timedelta(seconds=1)
</span><span class="cx">             )
</span><ins>+            self.log.debug(&quot;Enqueuing group refresh for {u}&quot;, u=groupUID)
</ins><span class="cx">             yield txn.enqueue(
</span><del>-                GroupRefreshWork, groupGUID=groupGUID, notBefore=notBefore
</del><ins>+                GroupRefreshWork, groupGuid=groupUID, notBefore=notBefore
</ins><span class="cx">             )
</span><ins>+            self.log.debug(&quot;Enqueued group refresh for {u}&quot;, u=groupUID)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -290,84 +296,91 @@
</span><span class="cx">         oldAssignments = (yield txn.externalDelegates())
</span><span class="cx"> 
</span><span class="cx">         # external assignments is of the form:
</span><del>-        # { delegatorGUID: (readDelegateGroupGUID, writeDelegateGroupGUID),
</del><ins>+        # { delegatorUID: (readDelegateGroupUID, writeDelegateGroupUID),
</ins><span class="cx">         # }
</span><span class="cx"> 
</span><span class="cx">         changed, removed = diffAssignments(oldAssignments, newAssignments)
</span><span class="cx">         if changed:
</span><span class="cx">             for (
</span><del>-                delegatorGUID, (readDelegateGUID, writeDelegateGUID)
</del><ins>+                delegatorUID, (readDelegateUID, writeDelegateUID)
</ins><span class="cx">             ) in changed:
</span><span class="cx">                 readDelegateGroupID = writeDelegateGroupID = None
</span><del>-                if readDelegateGUID:
</del><ins>+                if readDelegateUID:
</ins><span class="cx">                     readDelegateGroupID, _ignore_name, hash = (
</span><del>-                        yield txn.groupByGUID(readDelegateGUID)
</del><ins>+                        yield txn.groupByUID(readDelegateUID)
</ins><span class="cx">                     )
</span><del>-                if writeDelegateGUID:
</del><ins>+                if writeDelegateUID:
</ins><span class="cx">                     writeDelegateGroupID, _ignore_name, hash = (
</span><del>-                        yield txn.groupByGUID(writeDelegateGUID)
</del><ins>+                        yield txn.groupByUID(writeDelegateUID)
</ins><span class="cx">                     )
</span><span class="cx">                 yield txn.assignExternalDelegates(
</span><del>-                    delegatorGUID, readDelegateGroupID, writeDelegateGroupID,
-                    readDelegateGUID, writeDelegateGUID
</del><ins>+                    delegatorUID, readDelegateGroupID, writeDelegateGroupID,
+                    readDelegateUID, writeDelegateUID
</ins><span class="cx">                 )
</span><span class="cx">         if removed:
</span><del>-            for delegatorGUID in removed:
</del><ins>+            for delegatorUID in removed:
</ins><span class="cx">                 yield txn.assignExternalDelegates(
</span><del>-                    delegatorGUID, None, None, None, None
</del><ins>+                    delegatorUID, None, None, None, None
</ins><span class="cx">                 )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def refreshGroup(self, txn, groupGUID):
</del><ins>+    def refreshGroup(self, txn, groupUID):
</ins><span class="cx">         # Does the work of a per-group refresh work item
</span><del>-        # Faults in the flattened membership of a group, as GUIDs
</del><ins>+        # Faults in the flattened membership of a group, as UIDs
</ins><span class="cx">         # and updates the GROUP_MEMBERSHIP table
</span><del>-        record = (yield self.directory.recordWithGUID(groupGUID))
-        membershipHashContent = hashlib.md5()
-        members = (yield expandedMembers(record))
-        members = list(members)
-        members.sort(cmp=lambda x, y: cmp(x.guid, y.guid))
-        for member in members:
-            membershipHashContent.update(str(member.guid))
-        membershipHash = membershipHashContent.hexdigest()
-        groupID, _ignore_cachedName, cachedMembershipHash = (
-            yield txn.groupByGUID(groupGUID)
-        )
-
-        if cachedMembershipHash != membershipHash:
-            membershipChanged = True
-            self.log.debug(
-                &quot;Group '{group}' changed&quot;, group=record.fullNames[0]
</del><ins>+        self.log.debug(&quot;Faulting in group: {g}&quot;, g=groupUID)
+        record = (yield self.directory.recordWithUID(groupUID))
+        if record is None:
+            # FIXME: the group has disappeared from the directory.
+            # How do we want to handle this?
+            self.log.info(&quot;Group has disappeared: {g}&quot;, g=groupUID)
+        else:
+            self.log.debug(&quot;Got group record: {u}&quot;, u=record.uid)
+            membershipHashContent = hashlib.md5()
+            members = (yield expandedMembers(record))
+            members = list(members)
+            members.sort(cmp=lambda x, y: cmp(x.uid, y.uid))
+            for member in members:
+                membershipHashContent.update(str(member.uid))
+            membershipHash = membershipHashContent.hexdigest()
+            groupID, _ignore_cachedName, cachedMembershipHash = (
+                yield txn.groupByUID(groupUID)
</ins><span class="cx">             )
</span><del>-        else:
-            membershipChanged = False
</del><span class="cx"> 
</span><del>-        yield txn.updateGroup(groupGUID, record.fullNames[0], membershipHash)
</del><ins>+            if cachedMembershipHash != membershipHash:
+                membershipChanged = True
+                self.log.debug(
+                    &quot;Group '{group}' changed&quot;, group=record.fullNames[0]
+                )
+            else:
+                membershipChanged = False
</ins><span class="cx"> 
</span><del>-        if membershipChanged:
-            newMemberGUIDs = set()
-            for member in members:
-                newMemberGUIDs.add(member.guid)
-            yield self.synchronizeMembers(txn, groupID, newMemberGUIDs)
</del><ins>+            yield txn.updateGroup(groupUID, record.fullNames[0], membershipHash)
</ins><span class="cx"> 
</span><del>-        yield self.scheduleEventReconciliations(txn, groupID, groupGUID)
</del><ins>+            if membershipChanged:
+                newMemberUIDs = set()
+                for member in members:
+                    newMemberUIDs.add(member.uid)
+                yield self.synchronizeMembers(txn, groupID, newMemberUIDs)
</ins><span class="cx"> 
</span><ins>+            yield self.scheduleEventReconciliations(txn, groupID, groupUID)
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><del>-    def synchronizeMembers(self, txn, groupID, newMemberGUIDs):
</del><ins>+    def synchronizeMembers(self, txn, groupID, newMemberUIDs):
</ins><span class="cx">         numRemoved = numAdded = 0
</span><del>-        cachedMemberGUIDs = (yield txn.membersOfGroup(groupID))
</del><ins>+        cachedMemberUIDs = (yield txn.membersOfGroup(groupID))
</ins><span class="cx"> 
</span><del>-        for memberGUID in cachedMemberGUIDs:
-            if memberGUID not in newMemberGUIDs:
</del><ins>+        for memberUID in cachedMemberUIDs:
+            if memberUID not in newMemberUIDs:
</ins><span class="cx">                 numRemoved += 1
</span><del>-                yield txn.removeMemberFromGroup(memberGUID, groupID)
</del><ins>+                yield txn.removeMemberFromGroup(memberUID, groupID)
</ins><span class="cx"> 
</span><del>-        for memberGUID in newMemberGUIDs:
-            if memberGUID not in cachedMemberGUIDs:
</del><ins>+        for memberUID in newMemberUIDs:
+            if memberUID not in cachedMemberUIDs:
</ins><span class="cx">                 numAdded += 1
</span><del>-                yield txn.addMemberToGroup(memberGUID, groupID)
</del><ins>+                yield txn.addMemberToGroup(memberUID, groupID)
</ins><span class="cx"> 
</span><span class="cx">         returnValue((numAdded, numRemoved))
</span><span class="cx"> 
</span><span class="lines">@@ -378,23 +391,23 @@
</span><span class="cx">         The members of the given group as recorded in the db
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         members = set()
</span><del>-        memberGUIDs = (yield txn.membersOfGroup(groupID))
-        for guid in memberGUIDs:
-            record = (yield self.directory.recordWithGUID(guid))
</del><ins>+        memberUIDs = (yield txn.membersOfGroup(groupID))
+        for uid in memberUIDs:
+            record = (yield self.directory.recordWithUID(uid))
</ins><span class="cx">             if record is not None:
</span><span class="cx">                 members.add(record)
</span><span class="cx">         returnValue(members)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def cachedGroupsFor(self, txn, guid):
</del><ins>+    def cachedGroupsFor(self, txn, uid):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        The IDs of the groups the guid is a member of
</del><ins>+        The UIDs of the groups the uid is a member of
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        return txn.groupsFor(guid)
</del><ins>+        return txn.groupsFor(uid)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def scheduleEventReconciliations(self, txn, groupID, groupGUID):
</del><ins>+    def scheduleEventReconciliations(self, txn, groupID, groupUID):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find all events who have this groupID as an attendee and create
</span><span class="cx">         work items for them.
</span><span class="lines">@@ -415,29 +428,29 @@
</span><span class="cx">             )
</span><span class="cx">             log.debug(
</span><span class="cx">                 &quot;scheduling group reconciliation for &quot;
</span><del>-                &quot;({eventID}, {groupID}, {groupGUID}): {when}&quot;,
</del><ins>+                &quot;({eventID}, {groupID}, {groupUID}): {when}&quot;,
</ins><span class="cx">                 eventID=eventID,
</span><span class="cx">                 groupID=groupID,
</span><del>-                groupGUID=groupGUID,
</del><ins>+                groupUID=groupUID,
</ins><span class="cx">                 when=notBefore)
</span><span class="cx"> 
</span><span class="cx">             yield txn.enqueue(
</span><span class="cx">                 GroupAttendeeReconciliationWork,
</span><span class="cx">                 eventID=eventID,
</span><span class="cx">                 groupID=groupID,
</span><del>-                groupGUID=groupGUID,
</del><ins>+                groupGuid=groupUID,
</ins><span class="cx">                 notBefore=notBefore
</span><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def groupsToRefresh(self, txn):
</span><del>-        delegatedGUIDs = set((yield allGroupDelegates(txn)))
</del><ins>+        delegatedUIDs = set((yield txn.allGroupDelegates()))
</ins><span class="cx">         self.log.info(
</span><del>-            &quot;There are {count} group delegates&quot;, count=len(delegatedGUIDs)
</del><ins>+            &quot;There are {count} group delegates&quot;, count=len(delegatedUIDs)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><del>-        attendeeGroupGUIDs = set()
</del><ins>+        attendeeGroupUIDs = set()
</ins><span class="cx"> 
</span><span class="cx">         # get all groups from events
</span><span class="cx">         groupAttendee = schema.GROUP_ATTENDEE
</span><span class="lines">@@ -447,7 +460,7 @@
</span><span class="cx">         ).on(txn)
</span><span class="cx">         groupIDs = set([row[0] for row in rows])
</span><span class="cx"> 
</span><del>-        # get groupGUIDs
</del><ins>+        # get groupUIDs
</ins><span class="cx">         if groupIDs:
</span><span class="cx">             gr = schema.GROUPS
</span><span class="cx">             rows = yield Select(
</span><span class="lines">@@ -455,6 +468,8 @@
</span><span class="cx">                 From=gr,
</span><span class="cx">                 Where=gr.GROUP_ID.In(groupIDs)
</span><span class="cx">             ).on(txn)
</span><del>-            attendeeGroupGUIDs = set([row[0] for row in rows])
</del><ins>+            attendeeGroupUIDs = set([row[0] for row in rows])
</ins><span class="cx"> 
</span><del>-        returnValue(delegatedGUIDs.union(attendeeGroupGUIDs))
</del><ins>+        # FIXME: is this a good place to clear out unreferenced groups?
+
+        returnValue(delegatedUIDs.union(attendeeGroupUIDs))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavwhotestaccountsaccountsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/accounts/accounts.xml (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/accounts/accounts.xml        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/accounts/accounts.xml        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -43,6 +43,7 @@
</span><span class="cx">     &lt;name&gt;Wilfredo Sanchez Vega&lt;/name&gt;
</span><span class="cx">     &lt;first-name&gt;Wilfredo&lt;/first-name&gt;
</span><span class="cx">     &lt;last-name&gt;Sanchez Vega&lt;/last-name&gt;
</span><ins>+    &lt;auto-schedule-mode&gt;&lt;accept-if-free-decline-if-busy /&gt;&lt;/auto-schedule-mode&gt;
</ins><span class="cx">   &lt;/user&gt;
</span><span class="cx">   &lt;user&gt;
</span><span class="cx">     &lt;uid&gt;cdaboo&lt;/uid&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who2txdavwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/test_delegates.py (12860 => 12861)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/test_delegates.py        2014-03-08 01:57:17 UTC (rev 12860)
+++ CalendarServer/branches/users/sagen/move2who-2/txdav/who/test/test_delegates.py        2014-03-08 02:19:19 UTC (rev 12861)
</span><span class="lines">@@ -19,14 +19,14 @@
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> from txdav.who.delegates import (
</span><del>-    addDelegate, removeDelegate, delegatesOf, delegatedTo, allGroupDelegates
</del><ins>+    addDelegate, removeDelegate, delegatesOf, delegatedTo,
+    DirectoryService, RecordType as DelegateRecordType
</ins><span class="cx"> )
</span><span class="cx"> from txdav.who.groups import GroupCacher
</span><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="cx"> from twext.who.test.test_xml import xmlService
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><del>-from uuid import UUID
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class DelegationTest(StoreTestCase):
</span><span class="lines">@@ -34,14 +34,19 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def setUp(self):
</span><span class="cx">         yield super(DelegationTest, self).setUp()
</span><ins>+        self.store = self.storeUnderTest()
</ins><span class="cx">         self.xmlService = xmlService(self.mktemp(), xmlData=testXMLConfig)
</span><span class="cx">         self.groupCacher = GroupCacher(self.xmlService)
</span><ins>+        self.delegateService = DirectoryService(
+            self.xmlService.realmName,
+            self.store
+        )
+        self.delegateService.setMasterDirectory(self.xmlService)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_directDelegation(self):
</span><del>-        store = self.storeUnderTest()
-        txn = store.newTransaction()
</del><ins>+        txn = self.store.newTransaction()
</ins><span class="cx"> 
</span><span class="cx">         delegator = yield self.xmlService.recordWithUID(u&quot;__wsanchez__&quot;)
</span><span class="cx">         delegate1 = yield self.xmlService.recordWithUID(u&quot;__sagen__&quot;)
</span><span class="lines">@@ -50,24 +55,68 @@
</span><span class="cx">         # Add 1 delegate
</span><span class="cx">         yield addDelegate(txn, delegator, delegate1, True)
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><del>-        self.assertEquals([&quot;sagen&quot;], [d.shortNames[0] for d in delegates])
</del><ins>+        self.assertEquals([&quot;__sagen__&quot;], [d.uid for d in delegates])
</ins><span class="cx">         delegators = (yield delegatedTo(txn, delegate1, True))
</span><del>-        self.assertEquals([&quot;wsanchez&quot;], [d.shortNames[0] for d in delegators])
</del><ins>+        self.assertEquals([&quot;__wsanchez__&quot;], [d.uid for d in delegators])
</ins><span class="cx"> 
</span><ins>+        yield txn.commit()  # So delegateService will see the changes
+        txn = self.store.newTransaction()
+
+        # The &quot;proxy-write&quot; pseudoGroup will have one member
+        pseudoGroup = yield self.delegateService.recordWithShortName(
+            DelegateRecordType.writeDelegateGroup,
+            u&quot;__wsanchez__&quot;
+        )
+        self.assertEquals(pseudoGroup.uid, u&quot;__wsanchez__#calendar-proxy-write&quot;)
+        self.assertEquals(
+            [r.uid for r in (yield pseudoGroup.members())],
+            [u&quot;__sagen__&quot;]
+        )
+        # The &quot;proxy-read&quot; pseudoGroup will have no members
+        pseudoGroup = yield self.delegateService.recordWithShortName(
+            DelegateRecordType.readDelegateGroup,
+            u&quot;__wsanchez__&quot;
+        )
+        self.assertEquals(pseudoGroup.uid, u&quot;__wsanchez__#calendar-proxy-read&quot;)
+        self.assertEquals(
+            [r.uid for r in (yield pseudoGroup.members())],
+            []
+        )
+        # The &quot;proxy-write-for&quot; pseudoGroup will have one member
+        pseudoGroup = yield self.delegateService.recordWithShortName(
+            DelegateRecordType.writeDelegatorGroup,
+            u&quot;__sagen__&quot;
+        )
+        self.assertEquals(pseudoGroup.uid, u&quot;__sagen__#calendar-proxy-write-for&quot;)
+        self.assertEquals(
+            [r.uid for r in (yield pseudoGroup.members())],
+            [u&quot;__wsanchez__&quot;]
+        )
+        # The &quot;proxy-read-for&quot; pseudoGroup will have no members
+        pseudoGroup = yield self.delegateService.recordWithShortName(
+            DelegateRecordType.readDelegatorGroup,
+            u&quot;__sagen__&quot;
+        )
+        self.assertEquals(pseudoGroup.uid, u&quot;__sagen__#calendar-proxy-read-for&quot;)
+        self.assertEquals(
+            [r.uid for r in (yield pseudoGroup.members())],
+            []
+        )
+
</ins><span class="cx">         # Add another delegate
</span><span class="cx">         yield addDelegate(txn, delegator, delegate2, True)
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx">         self.assertEquals(
</span><del>-            set([&quot;sagen&quot;, &quot;cdaboo&quot;]),
-            set([d.shortNames[0] for d in delegates])
</del><ins>+            set([&quot;__sagen__&quot;, &quot;__cdaboo__&quot;]),
+            set([d.uid for d in delegates])
</ins><span class="cx">         )
</span><span class="cx">         delegators = (yield delegatedTo(txn, delegate2, True))
</span><del>-        self.assertEquals([&quot;wsanchez&quot;], [d.shortNames[0] for d in delegators])
</del><ins>+        self.assertEquals([&quot;__wsanchez__&quot;], [d.uid for d in delegators])
</ins><span class="cx"> 
</span><span class="cx">         # Remove 1 delegate
</span><span class="cx">         yield removeDelegate(txn, delegator, delegate1, True)
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><del>-        self.assertEquals([&quot;cdaboo&quot;], [d.shortNames[0] for d in delegates])
</del><ins>+        self.assertEquals([&quot;__cdaboo__&quot;], [d.uid for d in delegates])
</ins><span class="cx">         delegators = (yield delegatedTo(txn, delegate1, True))
</span><span class="cx">         self.assertEquals(0, len(delegators))
</span><span class="cx"> 
</span><span class="lines">@@ -78,11 +127,40 @@
</span><span class="cx">         delegators = (yield delegatedTo(txn, delegate2, True))
</span><span class="cx">         self.assertEquals(0, len(delegators))
</span><span class="cx"> 
</span><ins>+        yield txn.commit()  # So delegateService will see the changes
</ins><span class="cx"> 
</span><ins>+        # Now set delegate assignments by using pseudoGroup.setMembers()
+        pseudoGroup = yield self.delegateService.recordWithShortName(
+            DelegateRecordType.writeDelegateGroup,
+            u&quot;__wsanchez__&quot;
+        )
+        yield pseudoGroup.setMembers([delegate1, delegate2])
+
+        # Verify the assignments were made
+        txn = self.store.newTransaction()
+        delegates = (yield delegatesOf(txn, delegator, True))
+        self.assertEquals(
+            set([&quot;__sagen__&quot;, &quot;__cdaboo__&quot;]),
+            set([d.uid for d in delegates])
+        )
+        yield txn.commit()
+
+        # Set a different group of assignments:
+        yield pseudoGroup.setMembers([delegate2])
+
+        # Verify the assignments were made
+        txn = self.store.newTransaction()
+        delegates = (yield delegatesOf(txn, delegator, True))
+        self.assertEquals(
+            set([&quot;__cdaboo__&quot;]),
+            set([d.uid for d in delegates])
+        )
+        yield txn.commit()
+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_indirectDelegation(self):
</span><del>-        store = self.storeUnderTest()
-        txn = store.newTransaction()
</del><ins>+        txn = self.store.newTransaction()
</ins><span class="cx"> 
</span><span class="cx">         delegator = yield self.xmlService.recordWithUID(u&quot;__wsanchez__&quot;)
</span><span class="cx">         delegate1 = yield self.xmlService.recordWithUID(u&quot;__sagen__&quot;)
</span><span class="lines">@@ -92,37 +170,41 @@
</span><span class="cx">         # Add group delegate, but before the group membership has been
</span><span class="cx">         # pulled in
</span><span class="cx">         yield addDelegate(txn, delegator, group1, True)
</span><del>-        delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+        # Passing expanded=False will return the group
+        delegates = (yield delegatesOf(txn, delegator, True, expanded=False))
+        self.assertEquals(1, len(delegates))
+        self.assertEquals(delegates[0].uid, u&quot;__top_group_1__&quot;)
+        # Passing expanded=True will return not the group -- it only returns
+        # non-groups
+        delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx">         self.assertEquals(0, len(delegates))
</span><span class="cx"> 
</span><span class="cx">         # Now refresh the group and there will be 3 delegates (contained
</span><span class="cx">         # within 2 nested groups)
</span><span class="cx">         # guid = &quot;49b350c69611477b94d95516b13856ab&quot;
</span><del>-        yield self.groupCacher.refreshGroup(txn, group1.guid)
-        yield self.groupCacher.refreshGroup(txn, group2.guid)
-        delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+        yield self.groupCacher.refreshGroup(txn, group1.uid)
+        yield self.groupCacher.refreshGroup(txn, group2.uid)
+        delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx">         self.assertEquals(
</span><del>-            set([&quot;sagen&quot;, &quot;cdaboo&quot;, &quot;glyph&quot;]),
-            set([d.shortNames[0] for d in delegates])
</del><ins>+            set([&quot;__sagen__&quot;, &quot;__cdaboo__&quot;, &quot;__glyph__&quot;]),
+            set([d.uid for d in delegates])
</ins><span class="cx">         )
</span><span class="cx">         delegators = (yield delegatedTo(txn, delegate1, True))
</span><del>-        self.assertEquals([&quot;wsanchez&quot;], [d.shortNames[0] for d in delegators])
</del><ins>+        self.assertEquals([&quot;__wsanchez__&quot;], [d.uid for d in delegators])
</ins><span class="cx"> 
</span><span class="cx">         # Verify we can ask for all delegated-to groups
</span><span class="cx">         yield addDelegate(txn, delegator, group2, True)
</span><del>-        groups = (yield allGroupDelegates(txn))
</del><ins>+        groups = (yield txn.allGroupDelegates())
</ins><span class="cx">         self.assertEquals(
</span><del>-            set([
-                UUID(&quot;49b350c69611477b94d95516b13856ab&quot;),
-                UUID(&quot;86144f73345a409782f1b782672087c7&quot;)
-                ]), set(groups))
</del><ins>+            set([u'__sub_group_1__', u'__top_group_1__']), set(groups)
+        )
</ins><span class="cx"> 
</span><span class="cx">         # Delegate to a user who is already indirectly delegated-to
</span><span class="cx">         yield addDelegate(txn, delegator, delegate1, True)
</span><del>-        delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+        delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx">         self.assertEquals(
</span><del>-            set([&quot;sagen&quot;, &quot;cdaboo&quot;, &quot;glyph&quot;]),
-            set([d.shortNames[0] for d in delegates])
</del><ins>+            set([&quot;__sagen__&quot;, &quot;__cdaboo__&quot;, &quot;__glyph__&quot;]),
+            set([d.uid for d in delegates])
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         # Add a member to the group; they become a delegate
</span><span class="lines">@@ -131,35 +213,38 @@
</span><span class="cx">             record = (
</span><span class="cx">                 yield self.xmlService.recordWithShortName(RecordType.user, name)
</span><span class="cx">             )
</span><del>-            newSet.add(record.guid)
-        groupID, name, membershipHash = (yield txn.groupByGUID(group1.guid))
</del><ins>+            newSet.add(record.uid)
+        groupID, name, membershipHash = (yield txn.groupByUID(group1.uid))
</ins><span class="cx">         numAdded, numRemoved = (
</span><span class="cx">             yield self.groupCacher.synchronizeMembers(txn, groupID, newSet)
</span><span class="cx">         )
</span><del>-        delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+   &nb