<!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>[13651] CalendarServer/branches/release/CalendarServer-5.3-dev</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/13651">13651</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2014-06-18 11:06:15 -0700 (Wed, 18 Jun 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Hide missing or invalid proxyFors. Fix some sharing issues related to missing or invalid users.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectoryprincipalpy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaccountsxml">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaugmentsxml">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytesttest_proxyprincipalmemberspy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavsharingpy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavtesttest_sharingpy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtxdavcommondatastoresqlpy">CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -923,7 +923,7 @@
</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, read_write, resolve_memberships=True, ignoreDisabled=True):
</ins><span class="cx"> 
</span><span class="cx">         proxyFors = set()
</span><span class="cx"> 
</span><span class="lines">@@ -960,7 +960,7 @@
</span><span class="cx"> 
</span><span class="cx">         uids = set()
</span><span class="cx">         for principal in tuple(proxyFors):
</span><del>-            if principal.principalUID() in uids:
</del><ins>+            if principal.principalUID() in uids or (ignoreDisabled and not principal.record.enabledForCalendaring):
</ins><span class="cx">                 proxyFors.remove(principal)
</span><span class="cx">             else:
</span><span class="cx">                 uids.add(principal.principalUID())
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaccountsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -139,7 +139,7 @@
</span><span class="cx">       &lt;member type=&quot;users&quot;&gt;delegateviagroup&lt;/member&gt;
</span><span class="cx">     &lt;/members&gt;
</span><span class="cx">   &lt;/group&gt;
</span><del>-  &lt;user repeat=&quot;2&quot;&gt;
</del><ins>+  &lt;user repeat=&quot;3&quot;&gt;
</ins><span class="cx">     &lt;uid&gt;user%02d&lt;/uid&gt;
</span><span class="cx">     &lt;guid&gt;user%02d&lt;/guid&gt;
</span><span class="cx">     &lt;password&gt;%02duser&lt;/password&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaugmentsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -57,7 +57,7 @@
</span><span class="cx">     &lt;enable-calendar&gt;false&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;false&lt;/enable-addressbook&gt;
</span><span class="cx">   &lt;/record&gt;
</span><del>-  &lt;record repeat=&quot;2&quot;&gt;
</del><ins>+  &lt;record repeat=&quot;3&quot;&gt;
</ins><span class="cx">     &lt;uid&gt;user%02d&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytesttest_proxyprincipalmemberspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -504,3 +504,49 @@
</span><span class="cx">             DirectoryService.recordType_users, &quot;delegator&quot;, &quot;calendar-proxy-write&quot;,
</span><span class="cx">             (&quot;Occasional Delegate&quot;, &quot;Delegate Via Group&quot;, &quot;Delegate Group&quot;),
</span><span class="cx">         )
</span><ins>+
+
+    @inlineCallbacks
+    def test_hideDisabledProxies(self):
+        &quot;&quot;&quot;
+        Make sure users that are missing or not enabled for calendaring are removed
+        from the proxyFor list.
+        &quot;&quot;&quot;
+
+        # Check proxies empty right now
+        principal01 = self._getPrincipalByShortName(self.directoryService.recordType_users, &quot;user01&quot;)
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        principal02 = self._getPrincipalByShortName(self.directoryService.recordType_users, &quot;user02&quot;)
+        self.assertTrue(len((yield principal02.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal02.proxyFor(True))) == 0)
+
+        principal03 = self._getPrincipalByShortName(self.directoryService.recordType_users, &quot;user03&quot;)
+        self.assertTrue(len((yield principal03.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal03.proxyFor(True))) == 0)
+
+        # Make user01 a read-only proxy for user02 and user03
+        yield self._addProxy(principal02, &quot;calendar-proxy-read&quot;, principal01)
+        yield self._addProxy(principal03, &quot;calendar-proxy-read&quot;, principal01)
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now disable user02
+        principal02.record.enabledForCalendaring = False
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now enable user02
+        principal02.record.enabledForCalendaring = True
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now remove user02
+        self.directoryService._removeFromIndex(principal02.record)
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
</ins></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavsharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -103,12 +103,16 @@
</span><span class="cx">                     invitations = yield original.validateInvites(request)
</span><span class="cx"> 
</span><span class="cx">                     ownerPrincipal = (yield original.ownerPrincipal(request))
</span><del>-                    # FIXME:  use urn:uuid in all cases
-                    if self.isCalendarCollection():
-                        owner = ownerPrincipal.principalURL()
</del><ins>+                    if ownerPrincipal is None:
+                        owner = &quot;invalid&quot;
+                        ownerCN = &quot;Invalid&quot;
</ins><span class="cx">                     else:
</span><del>-                        owner = &quot;urn:uuid:&quot; + ownerPrincipal.principalUID()
-                    ownerCN = ownerPrincipal.displayName()
</del><ins>+                        # FIXME:  use urn:uuid in all cases
+                        if self.isCalendarCollection():
+                            owner = ownerPrincipal.principalURL()
+                        else:
+                            owner = &quot;urn:uuid:&quot; + ownerPrincipal.principalUID()
+                        ownerCN = ownerPrincipal.displayName()
</ins><span class="cx"> 
</span><span class="cx">                     returnValue(customxml.Invite(
</span><span class="cx">                         customxml.Organizer(
</span><span class="lines">@@ -315,7 +319,7 @@
</span><span class="cx">             access control mechanism has dictate the home should no longer have
</span><span class="cx">             any access at all.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        if self._share.direct():
</del><ins>+        if self._share is not None and self._share.direct():
</ins><span class="cx">             ownerUID = self._share.ownerUID()
</span><span class="cx">             owner = self.principalForUID(ownerUID)
</span><span class="cx">             if owner.record.recordType == WikiDirectoryService.recordType_wikis:
</span><span class="lines">@@ -482,14 +486,17 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         # assert request
</span><span class="cx">         invitations = yield self._allInvitations()
</span><ins>+        adjusted_invitations = []
</ins><span class="cx">         for invitation in invitations:
</span><span class="cx">             if invitation.status() != _BIND_STATUS_INVALID:
</span><span class="cx">                 if not (yield self.validUserIDForShare(&quot;urn:uuid:&quot; + invitation.shareeUID(), request)):
</span><span class="cx">                     # FIXME: temporarily disable this to deal with flaky directory
</span><span class="cx">                     #yield self._updateInvitation(invitation, status=_BIND_STATUS_INVALID)
</span><span class="cx">                     self.log.error(&quot;Invalid sharee detected: {uid}&quot;, uid=invitation.shareeUID())
</span><ins>+                    invitation = invitation._replace(status=_BIND_STATUS_INVALID)
+            adjusted_invitations.append(invitation)
</ins><span class="cx"> 
</span><del>-        returnValue(invitations)
</del><ins>+        returnValue(adjusted_invitations)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def inviteUserToShare(self, userid, cn, ace, summary, request):
</span><span class="lines">@@ -648,15 +655,19 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def uninviteSingleUserFromShare(self, userid, aces, request): #@UnusedVariable
</span><del>-        # Cancel invites - we'll just use whatever userid we are given
</del><span class="cx"> 
</span><ins>+        # Cancel invites - we'll just use whatever userid we are given. However, if we
+        # cannot find a matching principal, try to extract the uid from the userid
+        # and use that (to allow invalid principals to be removed).
</ins><span class="cx">         sharee = self.principalForCalendarUserAddress(userid)
</span><del>-        if not sharee:
</del><ins>+        if sharee is not None:
+            uid = sharee.principalUID()
+        elif userid.startswith(&quot;urn:uuid:&quot;):
+            uid = userid[9:]
+        else:
</ins><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><del>-        shareeUID = sharee.principalUID()
-
-        invitation = yield self._invitationForShareeUID(shareeUID)
</del><ins>+        invitation = yield self._invitationForShareeUID(uid)
</ins><span class="cx">         if invitation:
</span><span class="cx">             result = (yield self.uninviteFromShare(invitation, request))
</span><span class="cx">         else:
</span><span class="lines">@@ -1026,8 +1037,8 @@
</span><span class="cx">         L{SharedHomeMixin} is a I{sharee}'s view of a shared calendar object,
</span><span class="cx">         associate it with a L{Share}.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        share = yield self._shareForStoreObject(child._newStoreObject, request)
-        if share:
</del><ins>+        if child._newStoreObject is not None and not child._newStoreObject.owned():
+            share = yield self._shareForStoreObject(child._newStoreObject, request)
</ins><span class="cx">             child.setShare(share)
</span><span class="cx">             access = yield child._checkAccessControl()
</span><span class="cx">             if access is None:
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavtesttest_sharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -51,6 +51,12 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class FakeRequest(object):
+    def locateResource(self, url):
+        return succeed(None)
+
+
+
</ins><span class="cx"> class FakeHome(object):
</span><span class="cx">     def removeShareByUID(self, request, uid):
</span><span class="cx">         pass
</span><span class="lines">@@ -171,8 +177,8 @@
</span><span class="cx">             that marks any principal without 'bogus' in its name.
</span><span class="cx">             &quot;&quot;&quot;
</span><span class="cx">             result = principalForCalendarUserAddress(resourceSelf, userid)
</span><del>-            if result is None:
-                return result
</del><ins>+            if result is None or userid[9:] in result.invalid_names:
+                return None
</ins><span class="cx">             return result.principalURL()
</span><span class="cx"> 
</span><span class="cx">         @patched
</span><span class="lines">@@ -221,8 +227,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK):
-        request = SimpleStoreRequest(self, &quot;POST&quot;, &quot;/calendars/__uids__/user02/&quot;, content=body, authid=&quot;user02&quot;)
</del><ins>+    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK, sharer=&quot;user02&quot;):
+        request = SimpleStoreRequest(self, &quot;POST&quot;, &quot;/calendars/__uids__/{}/&quot;.format(sharer), content=body, authid=sharer)
</ins><span class="cx">         request.headers.setHeader(&quot;content-type&quot;, MimeType(&quot;text&quot;, &quot;xml&quot;))
</span><span class="cx">         response = yield self.send(request)
</span><span class="cx">         response = IResponse(response)
</span><span class="lines">@@ -252,12 +258,22 @@
</span><span class="cx">         return None
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _getUIDElementValues(self, xml):
+
+        results = {}
+        for user in xml.children:
+            href = str(user.childOfType(davxml.HRef))
+            uid = str(user.childOfType(customxml.UID))
+            results[href] = uid
+        return results
+
+
</ins><span class="cx">     def _clearUIDElementValue(self, xml):
</span><span class="cx"> 
</span><span class="cx">         for user in xml.children:
</span><del>-            for element in user.children:
-                if type(element) == customxml.UID:
-                    element.children[0].data = &quot;&quot;
</del><ins>+            uid = user.childOfType(customxml.UID)
+            if uid is not None:
+                uid.children[0].data = &quot;&quot;
</ins><span class="cx">         return xml
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -717,25 +733,23 @@
</span><span class="cx">             )
</span><span class="cx">         ))
</span><span class="cx"> 
</span><del>-        self.resource.validUserIDForShare = lambda userid, request: None
-        self.resource.principalForCalendarUserAddress = lambda cuaddr: None
-        self.resource.principalForUID = lambda principalUID: None
</del><ins>+        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user02&quot;,)))
</ins><span class="cx"> 
</span><span class="cx">         propInvite = (yield self.resource.readProperty(customxml.Invite, None))
</span><span class="cx">         self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
</span><span class="cx">             customxml.InviteUser(
</span><span class="cx">                 customxml.UID.fromString(&quot;&quot;),
</span><span class="cx">                 davxml.HRef.fromString(&quot;urn:uuid:user02&quot;),
</span><del>-                customxml.CommonName.fromString(&quot;user02&quot;),
</del><ins>+                customxml.CommonName.fromString(&quot;USER02&quot;),
</ins><span class="cx">                 customxml.InviteAccess(customxml.ReadWriteAccess()),
</span><del>-                customxml.InviteStatusNoResponse(),
</del><ins>+                customxml.InviteStatusInvalid(),
</ins><span class="cx">             )
</span><span class="cx">         ))
</span><span class="cx"> 
</span><span class="cx">         yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
</span><span class="cx">             &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
</span><span class="cx">                 &lt;CS:remove&gt;
</span><del>-                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
</del><ins>+                    &lt;D:href&gt;urn:uuid:user02&lt;/D:href&gt;
</ins><span class="cx">                 &lt;/CS:remove&gt;
</span><span class="cx">             &lt;/CS:share&gt;
</span><span class="cx">             &quot;&quot;&quot;)
</span><span class="lines">@@ -828,7 +842,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_POSTDowngradeWithDisabledInvitee(self):
</del><ins>+    def test_POSTDowngradeWithMissingInvitee(self):
</ins><span class="cx"> 
</span><span class="cx">         yield self.resource.upgradeToShare()
</span><span class="cx"> 
</span><span class="lines">@@ -858,7 +872,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_POSTRemoveWithDisabledInvitee(self):
</del><ins>+    def test_POSTRemoveWithMissingInvitee(self):
</ins><span class="cx"> 
</span><span class="cx">         yield self.resource.upgradeToShare()
</span><span class="cx"> 
</span><span class="lines">@@ -888,7 +902,7 @@
</span><span class="cx">         yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
</span><span class="cx">             &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
</span><span class="cx">                 &lt;CS:remove&gt;
</span><del>-                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
</del><ins>+                    &lt;D:href&gt;urn:uuid:user02&lt;/D:href&gt;
</ins><span class="cx">                 &lt;/CS:remove&gt;
</span><span class="cx">             &lt;/CS:share&gt;
</span><span class="cx">             &quot;&quot;&quot;)
</span><span class="lines">@@ -951,3 +965,252 @@
</span><span class="cx"> 
</span><span class="cx">         resource = (yield self._getResourceSharer(href))
</span><span class="cx">         self.assertFalse(resource.exists())
</span><ins>+
+
+    @inlineCallbacks
+    def test_POSTShareeRemoveWithMissingSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user02&quot;),
+                customxml.CommonName.fromString(&quot;USER02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;USER02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uid,))
+        )
+        href = self._getHRefElementValue(result) + &quot;/&quot;
+
+        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user01&quot;,)))
+
+        resource = (yield self._getResourceSharer(href))
+        yield resource.removeShareeResource(SimpleStoreRequest(self, &quot;DELETE&quot;, href))
+
+        resource = (yield self._getResourceSharer(href))
+        self.assertFalse(resource.exists())
+
+
+    @inlineCallbacks
+    def test_shareeInviteWithDisabledSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user02&quot;),
+                customxml.CommonName.fromString(&quot;USER02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;USER02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uid,))
+        )
+        href = self._getHRefElementValue(result) + &quot;/&quot;
+
+        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user01&quot;,)))
+
+        resource = (yield self._getResourceSharer(href))
+        propInvite = yield resource.inviteProperty(FakeRequest())
+        self.assertEquals(propInvite, None)
+
+
+    @inlineCallbacks
+    def test_shareeInviteWithMissingSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user02&quot;),
+                customxml.CommonName.fromString(&quot;USER02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;USER02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uid,))
+        )
+        href = self._getHRefElementValue(result) + &quot;/&quot;
+
+        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user01&quot;,)))
+
+        resource = (yield self._getResourceSharer(href))
+        propInvite = yield resource.inviteProperty(FakeRequest())
+        self.assertEquals(propInvite, None)
+
+
+    @inlineCallbacks
+    def test_hideInvalidSharers(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user03@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uids = self._getUIDElementValues(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user02&quot;),
+                customxml.CommonName.fromString(&quot;USER02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user03&quot;),
+                customxml.CommonName.fromString(&quot;USER03&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;USER02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uids[&quot;urn:uuid:user02&quot;],))
+
+        yield self._doPOSTSharerAccept(
+            &quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+                &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+                  &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+                  &lt;invite-accepted/&gt;
+                  &lt;hosturl&gt;
+                    &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+                  &lt;/hosturl&gt;
+                  &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+                  &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+                  &lt;common-name&gt;USER03&lt;/common-name&gt;
+                  &lt;first-name&gt;user&lt;/first-name&gt;
+                  &lt;last-name&gt;03&lt;/last-name&gt;
+                &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uids[&quot;urn:uuid:user03&quot;],),
+            sharer=&quot;user03&quot;
+        )
+
+        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user02&quot;,)))
+
+        resource = yield self._getResource()
+        propInvite = yield resource.inviteProperty(None)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user02&quot;),
+                customxml.CommonName.fromString(&quot;USER02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusInvalid(),
+            ),
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:uuid:user03&quot;),
+                customxml.CommonName.fromString(&quot;USER03&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusAccepted(),
+            ),
+        ))
</ins></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -2740,6 +2740,27 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _replace(self, status):
+        &quot;&quot;&quot;
+        Create a copy of this invitation with the status changed to the specified value.
+
+        @param status: the new status value
+        @type status: L{int}
+        &quot;&quot;&quot;
+        return SharingInvitation(
+            self._uid,
+            self._owner_uid,
+            self._owner_rid,
+            self._sharee_uid,
+            self._sharee_rid,
+            self._resource_id,
+            self._resource_name,
+            self._mode,
+            status,
+            self._summary,
+        )
+
+
</ins><span class="cx">     def uid(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         This maps to the resource name in the bind table, the &quot;bind name&quot;. This is used as the &quot;uid&quot;
</span></span></pre>
</div>
</div>

</body>
</html>