<!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>[15328] CalendarServer/trunk</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/15328">15328</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2015-11-17 16:56:59 -0800 (Tue, 17 Nov 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Support cross-pod scheduling between v5 and v8 servers.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkconfcaldavdtestpodAplist">CalendarServer/trunk/conf/caldavd-test-podA.plist</a></li>
<li><a href="#CalendarServertrunkconfcaldavdtestpodBplist">CalendarServer/trunk/conf/caldavd-test-podB.plist</a></li>
<li><a href="#CalendarServertrunkconflocalserversdtd">CalendarServer/trunk/conf/localservers.dtd</a></li>
<li><a href="#CalendarServertrunktwistedcaldavicalpy">CalendarServer/trunk/twistedcaldav/ical.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavtesttest_icalendarpy">CalendarServer/trunk/twistedcaldav/test/test_icalendar.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoreschedulingischeduledeliverypy">CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/delivery.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoreschedulingischedulelocalserverspy">CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoreschedulingischeduleremoteserverspy">CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoreschedulingischeduletesttest_deliverypy">CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoreschedulingischeduletesttest_localserverspy">CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkconfcaldavdtestpodAplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/conf/caldavd-test-podA.plist (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/conf/caldavd-test-podA.plist        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/conf/caldavd-test-podA.plist        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -106,8 +106,6 @@
</span><span class="cx">             &lt;string&gt;./conf/localservers-test.xml&lt;/string&gt;
</span><span class="cx">             &lt;key&gt;MaxClients&lt;/key&gt;
</span><span class="cx">             &lt;integer&gt;5&lt;/integer&gt;
</span><del>-            &lt;key&gt;InboxName&lt;/key&gt;
-            &lt;string&gt;podding&lt;/string&gt;
</del><span class="cx">         &lt;/dict&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;key&gt;Notifications&lt;/key&gt;
</span></span></pre></div>
<a id="CalendarServertrunkconfcaldavdtestpodBplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/conf/caldavd-test-podB.plist (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/conf/caldavd-test-podB.plist        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/conf/caldavd-test-podB.plist        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -106,8 +106,6 @@
</span><span class="cx">             &lt;string&gt;./conf/localservers-test.xml&lt;/string&gt;
</span><span class="cx">             &lt;key&gt;MaxClients&lt;/key&gt;
</span><span class="cx">             &lt;integer&gt;5&lt;/integer&gt;
</span><del>-            &lt;key&gt;InboxName&lt;/key&gt;
-            &lt;string&gt;podding&lt;/string&gt;
</del><span class="cx">         &lt;/dict&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;key&gt;Notifications&lt;/key&gt;
</span></span></pre></div>
<a id="CalendarServertrunkconflocalserversdtd"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/conf/localservers.dtd (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/conf/localservers.dtd        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/conf/localservers.dtd        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -17,7 +17,7 @@
</span><span class="cx"> &lt;!ELEMENT servers (server*) &gt;
</span><span class="cx"> 
</span><span class="cx">         &lt;!ELEMENT server (id, uri, allowed-from*, shared-secret?) &gt;
</span><del>-                &lt;!ATTLIST server implicit (yes|no) &quot;yes&quot;&gt;
</del><ins>+                &lt;!ATTLIST server v5 (yes|no) &quot;no&quot;&gt;
</ins><span class="cx"> 
</span><span class="cx">                 &lt;!ELEMENT id  (#PCDATA) &gt;
</span><span class="cx">                 &lt;!ELEMENT uri (#PCDATA) &gt;
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavicalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/ical.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/ical.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/twistedcaldav/ical.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -3463,7 +3463,7 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def normalizeCalendarUserAddresses(
</span><del>-        self, lookupFunction, recordFunction, toCanonical=True
</del><ins>+        self, lookupFunction, recordFunction, toCanonical=True, toURN_UUID=False,
</ins><span class="cx">     ):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Do the ORGANIZER/ATTENDEE property normalization.
</span><span class="lines">@@ -3477,10 +3477,14 @@
</span><span class="cx">         @param toCanonical: whether to convert to the canonical CUA form (True)
</span><span class="cx">             or to the mailto: form (False)
</span><span class="cx">         @type toCanonical: L{bool}
</span><ins>+
+        @param toURN_UUID: whether to convert to urn:x-uid to urn:uuid: for
+            compatibility with older servers
+        @type toURN_UUID: L{bool}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # Keep a cache of records because events with lots of recurrence overrides can contain
</span><del>-        # the same attendee cu-address multuiple times
</del><ins>+        # the same attendee cu-address multiple times
</ins><span class="cx">         cache = {}
</span><span class="cx"> 
</span><span class="cx">         for component in self.subcomponents(ignore=True):
</span><span class="lines">@@ -3513,6 +3517,10 @@
</span><span class="cx">                     # Always re-write value to urn:x-uid
</span><span class="cx">                     prop.setValue(&quot;urn:x-uid:{uid}&quot;.format(uid=uid))
</span><span class="cx"> 
</span><ins>+                # Look for urn:x-uid: -&gt; urn:uuid: conversion
+                elif toURN_UUID and cuaddr.startswith(&quot;urn:x-uid:&quot;):
+                    prop.setValue(cuaddr.replace(&quot;urn:x-uid:&quot;, &quot;urn:uuid:&quot;))
+
</ins><span class="cx">                 # If it is already a non-x-uid address leave it be
</span><span class="cx">                 elif (cuaddr.startswith(&quot;urn:x-uid:&quot;) or cuaddr.startswith(&quot;urn:uuid:&quot;)):
</span><span class="cx"> 
</span><span class="lines">@@ -3910,7 +3918,7 @@
</span><span class="cx"> # #
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def normalizeCUAddress(cuaddr, lookupFunction, recordFunction, toCanonical=True):
</del><ins>+def normalizeCUAddress(cuaddr, lookupFunction, recordFunction, toCanonical=True, toURN_UUID=False):
</ins><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">     _ignore_name, uid, _ignore_cuType, cuaddrs = (yield lookupFunction(normalizeCUAddr(cuaddr), recordFunction, config))
</span><span class="lines">@@ -3920,6 +3928,10 @@
</span><span class="cx">         if uid:
</span><span class="cx">             returnValue(&quot;urn:x-uid:{0}&quot;.format(uid,))
</span><span class="cx"> 
</span><ins>+    # Look for urn:x-uid: -&gt; urn:uuid: conversion
+    elif toURN_UUID and cuaddr.startswith(&quot;urn:x-uid:&quot;):
+        returnValue(cuaddr.replace(&quot;urn:x-uid:&quot;, &quot;urn:uuid:&quot;))
+
</ins><span class="cx">     # If it is already a non-x-uid address leave it be
</span><span class="cx">     elif (cuaddr.startswith(&quot;urn:x-uid:&quot;) or cuaddr.startswith(&quot;urn:uuid:&quot;)):
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavtesttest_icalendarpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/test/test_icalendar.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/test/test_icalendar.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/twistedcaldav/test/test_icalendar.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -8191,6 +8191,76 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def test_normalizeCalendarUserAddressesFromCanonical_URN_UUID(self):
+        &quot;&quot;&quot;
+        Ensure mailto is preferred, followed by path form, then http form.
+        &quot;&quot;&quot;
+
+        data = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+DTSTART:20071114T000000Z
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ATTENDEE:urn:x-uid:foo
+ATTENDEE:urn:x-uid:bar
+ATTENDEE:urn:x-uid:baz
+ATTENDEE:urn:x-uid:buz
+DTSTAMP:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        component = Component.fromString(data)
+
+
+        def lookupFunction(cuaddr, ignored1, ignored2):
+            return succeed(
+                {
+                    &quot;urn:x-uid:foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:foo&quot;, &quot;urn:uuid:foo&quot;, &quot;http://example.com/foo&quot;, &quot;/foo&quot;)
+                    ),
+                    &quot;urn:x-uid:bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:bar&quot;, &quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;, &quot;http://example.com/bar&quot;, &quot;/bar&quot;)
+                    ),
+                    &quot;urn:x-uid:baz&quot; : (
+                        &quot;Baz&quot;,
+                        &quot;baz&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:baz&quot;, &quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;)
+                    ),
+                    &quot;urn:x-uid:buz&quot; : (
+                        &quot;Buz&quot;,
+                        &quot;buz&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:buz&quot;, &quot;urn:uuid:buz&quot;, &quot;http://example.com/buz&quot;)
+                    ),
+                }[cuaddr]
+            )
+
+        yield component.normalizeCalendarUserAddresses(lookupFunction, None, toCanonical=False, toURN_UUID=True)
+
+        self.assertEquals(
+            &quot;urn:uuid:foo&quot;,
+            component.getAttendeeProperty((&quot;urn:uuid:foo&quot;,)).value())
+        self.assertEquals(
+            &quot;urn:uuid:bar&quot;,
+            component.getAttendeeProperty((&quot;urn:uuid:bar&quot;,)).value())
+        self.assertEquals(
+            &quot;urn:uuid:baz&quot;,
+            component.getAttendeeProperty((&quot;urn:uuid:baz&quot;,)).value())
+        self.assertEquals(
+            &quot;urn:uuid:buz&quot;,
+            component.getAttendeeProperty((&quot;urn:uuid:buz&quot;,)).value())
+
+
+    @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">@@ -9174,6 +9244,82 @@
</span><span class="cx">             self.assertEquals(new_cuaddr, result)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def test_normalizeCUAddressFromCanonical_URN_UUID(self):
+        &quot;&quot;&quot;
+        Ensure mailto is preferred, followed by path form, then http form.
+        &quot;&quot;&quot;
+
+        data = (
+            (&quot;urn:uuid:foo&quot;, &quot;/foo&quot;),
+            (&quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;,),
+            (&quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;,),
+            (&quot;urn:uuid:buz&quot;, &quot;urn:x-uid:buz&quot;,),
+            (&quot;urn:x-uid:foo&quot;, &quot;urn:uuid:foo&quot;),
+            (&quot;urn:x-uid:bar&quot;, &quot;urn:uuid:bar&quot;,),
+            (&quot;urn:x-uid:baz&quot;, &quot;urn:uuid:baz&quot;,),
+            (&quot;urn:x-uid:buz&quot;, &quot;urn:uuid:buz&quot;,),
+        )
+
+        def lookupFunction(cuaddr, ignored1, ignored2):
+            return succeed(
+                {
+                    &quot;urn:uuid:foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid: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;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid: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;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid: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;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:buz&quot;, &quot;urn:uuid:buz&quot;,)
+                    ),
+                    &quot;urn:x-uid:foo&quot; : (
+                        &quot;Foo&quot;,
+                        &quot;foo&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:foo&quot;, &quot;urn:uuid:foo&quot;, &quot;http://example.com/foo&quot;, &quot;/foo&quot;)
+                    ),
+                    &quot;urn:x-uid:bar&quot; : (
+                        &quot;Bar&quot;,
+                        &quot;bar&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:bar&quot;, &quot;urn:uuid:bar&quot;, &quot;mailto:bar@example.com&quot;, &quot;http://example.com/bar&quot;, &quot;/bar&quot;)
+                    ),
+                    &quot;urn:x-uid:baz&quot; : (
+                        &quot;Baz&quot;,
+                        &quot;baz&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:baz&quot;, &quot;urn:uuid:baz&quot;, &quot;http://example.com/baz&quot;)
+                    ),
+                    &quot;urn:x-uid:buz&quot; : (
+                        &quot;Buz&quot;,
+                        &quot;buz&quot;,
+                        &quot;INDIVIDUAL&quot;,
+                        (&quot;urn:x-uid:buz&quot;, &quot;urn:uuid:buz&quot;,)
+                    ),
+                }[cuaddr]
+            )
+
+        for cuaddr, result in data:
+            new_cuaddr = yield normalizeCUAddress(cuaddr, lookupFunction, None, toCanonical=False, toURN_UUID=True)
+            self.assertEquals(new_cuaddr, result)
+
+
</ins><span class="cx">     def test_hasPropertyWithParameterMatch(self):
</span><span class="cx"> 
</span><span class="cx">         data = (
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoreschedulingischeduledeliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/delivery.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -193,7 +193,7 @@
</span><span class="cx">         if serverURI not in self.otherServers:
</span><span class="cx">             self.otherServers[serverURI] = IScheduleServerRecord(
</span><span class="cx">                 uri=joinURL(serverURI, config.Servers.InboxName),
</span><del>-                unNormalizeAddresses=not recipient.record.server().isImplicit,
</del><ins>+                rewriteCUAddresses=recipient.record.server().v5,
</ins><span class="cx">                 moreHeaders=[recipient.record.server().secretHeader(), ],
</span><span class="cx">                 podding=True,
</span><span class="cx">             )
</span><span class="lines">@@ -379,13 +379,28 @@
</span><span class="cx"> 
</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><del>-        if self.server.unNormalizeAddresses:
-            originator = yield normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toCanonical=False)
</del><ins>+        if self.server.rewriteCUAddresses:
+            originator = yield normalizeCUAddress(
+                originator,
+                normalizationLookup,
+                self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
+                toCanonical=False,
+                toURN_UUID=self.server.podding(),
+            )
</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="cx">         for recipient in self.recipients:
</span><del>-            self.headers.addRawHeader(&quot;Recipient&quot;, utf8String(recipient.cuaddr))
</del><ins>+            cuaddr = recipient.cuaddr
+            if self.server.rewriteCUAddresses and self.server.podding():
+                cuaddr = yield normalizeCUAddress(
+                    cuaddr,
+                    normalizationLookup,
+                    self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
+                    toCanonical=False,
+                    toURN_UUID=True,
+                )
+            self.headers.addRawHeader(&quot;Recipient&quot;, utf8String(cuaddr))
</ins><span class="cx"> 
</span><span class="cx">         # Only one Recipient header as they get concatenated in ischedule-relaxed canonicalization
</span><span class="cx">         self.sign_headers.append(&quot;Recipient&quot;)
</span><span class="lines">@@ -434,11 +449,13 @@
</span><span class="cx">             # Need to remap cuaddrs from urn:uuid
</span><span class="cx">             normalizedCalendar = self.scheduler.calendar.duplicate()
</span><span class="cx">             self.original_organizer = normalizedCalendar.getOrganizer()
</span><del>-            if self.server.unNormalizeAddresses:
</del><ins>+            if self.server.rewriteCUAddresses:
</ins><span class="cx">                 yield normalizedCalendar.normalizeCalendarUserAddresses(
</span><span class="cx">                     normalizationLookup,
</span><span class="cx">                     self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
</span><del>-                    toCanonical=False)
</del><ins>+                    toCanonical=False,
+                    toURN_UUID=self.server.podding(),
+                )
</ins><span class="cx"> 
</span><span class="cx">             # For VFREEBUSY we need to strip out ATTENDEEs that do not match the recipient list
</span><span class="cx">             if self.scheduler.isfreebusy:
</span><span class="lines">@@ -524,7 +541,7 @@
</span><span class="cx">             calendar_data = response.childOfType(CalendarData)
</span><span class="cx">             if calendar_data:
</span><span class="cx">                 calendar_data = str(calendar_data)
</span><del>-                if self.server.unNormalizeAddresses and self.original_organizer is not None:
</del><ins>+                if self.server.rewriteCUAddresses and self.original_organizer is not None:
</ins><span class="cx">                     # Need to restore original ORGANIZER value if it got unnormalized
</span><span class="cx">                     calendar = Component.fromString(calendar_data)
</span><span class="cx">                     organizers = calendar.getAllPropertiesInAnyComponent(&quot;ORGANIZER&quot;, depth=1)
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoreschedulingischedulelocalserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -148,7 +148,7 @@
</span><span class="cx">         self.ips = set()
</span><span class="cx">         self.allowed_from_ips = set()
</span><span class="cx">         self.shared_secret = sharedSecret
</span><del>-        self.isImplicit = True
</del><ins>+        self.v5 = False   # Needs old style urn:uuid cu-address
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def details(self):
</span><span class="lines">@@ -274,7 +274,7 @@
</span><span class="cx"> ELEMENT_URI = &quot;uri&quot;
</span><span class="cx"> ELEMENT_ALLOWED_FROM = &quot;allowed-from&quot;
</span><span class="cx"> ELEMENT_SHARED_SECRET = &quot;shared-secret&quot;
</span><del>-ATTR_IMPLICIT = &quot;implicit&quot;
</del><ins>+ATTR_V5 = &quot;v5&quot;
</ins><span class="cx"> ATTR_VALUE_YES = &quot;yes&quot;
</span><span class="cx"> ATTR_VALUE_NO = &quot;no&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -299,7 +299,7 @@
</span><span class="cx">                 raise RuntimeError(&quot;Unknown server type: '{}' in servers file: '{}'&quot;.format(child.tag, xmlFile,))
</span><span class="cx"> 
</span><span class="cx">             server = Server()
</span><del>-            server.isImplicit = child.get(ATTR_IMPLICIT, ATTR_VALUE_YES) == ATTR_VALUE_YES
</del><ins>+            server.v5 = child.get(ATTR_V5, ATTR_VALUE_NO) == ATTR_VALUE_YES
</ins><span class="cx"> 
</span><span class="cx">             for node in child:
</span><span class="cx">                 if node.tag == ELEMENT_ID:
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoreschedulingischeduleremoteserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -138,7 +138,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Contains server-to-server details.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    def __init__(self, uri=None, unNormalizeAddresses=True, moreHeaders=[], podding=False):
</del><ins>+    def __init__(self, uri=None, rewriteCUAddresses=True, moreHeaders=[], podding=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param recordType: record type for directory entry.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -148,7 +148,7 @@
</span><span class="cx">         self.allow_to = True
</span><span class="cx">         self.domains = []
</span><span class="cx">         self.client_hosts = []
</span><del>-        self.unNormalizeAddresses = unNormalizeAddresses
</del><ins>+        self.rewriteCUAddresses = rewriteCUAddresses
</ins><span class="cx">         self.moreHeaders = moreHeaders
</span><span class="cx">         self._podding = podding
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoreschedulingischeduletesttest_deliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -20,11 +20,17 @@
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx"> from twistedcaldav.stdconfig import config
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule import utils
</span><del>-from txdav.caldav.datastore.scheduling.ischedule.delivery import ScheduleViaISchedule
</del><ins>+from txdav.caldav.datastore.scheduling.ischedule.delivery import ScheduleViaISchedule, \
+    IScheduleRequest
+from txdav.caldav.datastore.scheduling.ischedule.remoteservers import IScheduleServerRecord
+from txdav.caldav.datastore.scheduling.cuaddress import CalendarUser
+from txdav.common.datastore.test.util import CommonCommonTests
+import txweb2.dav.test.util
+from twistedcaldav.ical import Component
</ins><span class="cx"> 
</span><del>-class CalDAV (unittest.TestCase):
</del><ins>+class TestiSchedule (unittest.TestCase):
</ins><span class="cx">     &quot;&quot;&quot;
</span><del>-    txdav.caldav.datastore.scheduling.caldav tests
</del><ins>+    txdav.caldav.datastore.scheduling.ischedule tests
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def tearDown(self):
</span><span class="lines">@@ -71,3 +77,219 @@
</span><span class="cx">         self.patch(config.Scheduling.iSchedule, &quot;Enabled&quot;, False)
</span><span class="cx">         result = yield ScheduleViaISchedule.matchCalendarUserAddress(&quot;mailto:user@example.com&quot;)
</span><span class="cx">         self.assertFalse(result)
</span><ins>+
+
+
+class TestIScheduleRequest (CommonCommonTests, txweb2.dav.test.util.TestCase):
+    &quot;&quot;&quot;
+    txdav.caldav.datastore.scheduling.ischedule tests
+    &quot;&quot;&quot;
+
+    class FakeScheduler(object):
+        def __init__(self, txn, organizer, caldata=None):
+            self.txn = txn
+            self.organizer = CalendarUser(organizer)
+            self.calendar = Component.fromString(caldata) if caldata else None
+            self.isiTIPRequest = True
+            self.isfreebusy = False
+
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(TestIScheduleRequest, self).setUp()
+        yield self.buildStoreAndDirectory()
+
+
+    @inlineCallbacks
+    def test_prepareHeaders_podding(self):
+        &quot;&quot;&quot;
+        Make sure Originator header is properly re-written.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        server = IScheduleServerRecord(&quot;https://calendar.example.com&quot;, rewriteCUAddresses=False, podding=True)
+        request = IScheduleRequest(
+            self.FakeScheduler(txn, &quot;urn:x-uid:user01&quot;),
+            server,
+            (CalendarUser(&quot;urn:x-uid:user02&quot;),),
+            None, False,
+        )
+        _ignore_ssl, host, port, _ignore_path = server.details()
+        yield request._prepareHeaders(host, port, &quot;VEVENT&quot;, &quot;REQUEST&quot;)
+        yield txn.commit()
+
+        self.assertEqual(request.headers.getRawHeaders(&quot;Originator&quot;)[0], &quot;urn:x-uid:user01&quot;)
+        self.assertEqual(request.headers.getRawHeaders(&quot;Recipient&quot;)[0], &quot;urn:x-uid:user02&quot;)
+
+
+    @inlineCallbacks
+    def test_prepareHeaders_podding_with_rewrite(self):
+        &quot;&quot;&quot;
+        Make sure Originator header is properly re-written.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        server = IScheduleServerRecord(&quot;https://calendar.example.com&quot;, rewriteCUAddresses=True, podding=True)
+        request = IScheduleRequest(
+            self.FakeScheduler(txn, &quot;urn:x-uid:user01&quot;),
+            server,
+            (CalendarUser(&quot;urn:x-uid:user02&quot;),),
+            None, False,
+        )
+        _ignore_ssl, host, port, _ignore_path = server.details()
+        yield request._prepareHeaders(host, port, &quot;VEVENT&quot;, &quot;REQUEST&quot;)
+        yield txn.commit()
+
+        self.assertEqual(request.headers.getRawHeaders(&quot;Originator&quot;)[0], &quot;urn:uuid:user01&quot;)
+        self.assertEqual(request.headers.getRawHeaders(&quot;Recipient&quot;)[0], &quot;urn:uuid:user02&quot;)
+
+
+    @inlineCallbacks
+    def test_prepareHeaders_nopodding_with_rewrite(self):
+        &quot;&quot;&quot;
+        Make sure Originator header is properly re-written.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        server = IScheduleServerRecord(&quot;https://calendar.example.com&quot;, rewriteCUAddresses=True, podding=False)
+        request = IScheduleRequest(
+            self.FakeScheduler(txn, &quot;urn:x-uid:user01&quot;),
+            server,
+            (CalendarUser(&quot;mailto:user02@example.com&quot;),),
+            None, False,
+        )
+        _ignore_ssl, host, port, _ignore_path = server.details()
+        yield request._prepareHeaders(host, port, &quot;VEVENT&quot;, &quot;REQUEST&quot;)
+        yield txn.commit()
+
+        self.assertEqual(request.headers.getRawHeaders(&quot;Originator&quot;)[0], &quot;mailto:user01@example.com&quot;)
+        self.assertEqual(request.headers.getRawHeaders(&quot;Recipient&quot;)[0], &quot;mailto:user02@example.com&quot;)
+
+
+    @inlineCallbacks
+    def test_prepareData_podding(self):
+        &quot;&quot;&quot;
+        Make sure Originator header is properly re-written.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        server = IScheduleServerRecord(&quot;https://calendar.example.com&quot;, rewriteCUAddresses=False, podding=True)
+        request = IScheduleRequest(
+            self.FakeScheduler(
+                txn, &quot;urn:x-uid:user01&quot;,
+                &quot;&quot;&quot;BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART:20060101T100000Z
+DURATION:PT1H
+SUMMARY:event 1
+UID:deadlocked
+ORGANIZER:urn:x-uid:user01
+ATTENDEE;PARTSTAT=ACCEPTED:urn:x-uid:user01
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:urn:x-uid:user02
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;,
+            ),
+            server,
+            (), None, False,
+        )
+        yield request._prepareData()
+        yield txn.commit()
+
+        ical = Component.fromString(request.data)
+        self.assertEqual(ical.masterComponent().getOrganizer(), &quot;urn:x-uid:user01&quot;)
+        self.assertEqual(
+            set([attendee.value() for attendee in ical.getAllAttendeeProperties()]),
+            set((&quot;urn:x-uid:user01&quot;, &quot;urn:x-uid:user02&quot;)),
+        )
+
+
+    @inlineCallbacks
+    def test_prepareData_podding_with_rewrite(self):
+        &quot;&quot;&quot;
+        Make sure Originator header is properly re-written.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        server = IScheduleServerRecord(&quot;https://calendar.example.com&quot;, rewriteCUAddresses=True, podding=True)
+        request = IScheduleRequest(
+            self.FakeScheduler(
+                txn, &quot;urn:x-uid:user01&quot;,
+                &quot;&quot;&quot;BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART:20060101T100000Z
+DURATION:PT1H
+SUMMARY:event 1
+UID:deadlocked
+ORGANIZER:urn:x-uid:user01
+ATTENDEE;PARTSTAT=ACCEPTED:urn:x-uid:user01
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:urn:x-uid:user02
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;,
+            ),
+            server,
+            (), None, False,
+        )
+        yield request._prepareData()
+        yield txn.commit()
+
+        ical = Component.fromString(request.data)
+        self.assertEqual(ical.masterComponent().getOrganizer(), &quot;urn:uuid:user01&quot;)
+        self.assertEqual(
+            set([attendee.value() for attendee in ical.getAllAttendeeProperties()]),
+            set((&quot;urn:uuid:user01&quot;, &quot;urn:uuid:user02&quot;)),
+        )
+
+
+    @inlineCallbacks
+    def test_prepareData_nopodding_with_rewrite(self):
+        &quot;&quot;&quot;
+        Make sure Originator header is properly re-written.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        server = IScheduleServerRecord(&quot;https://calendar.example.com&quot;, rewriteCUAddresses=True, podding=False)
+        request = IScheduleRequest(
+            self.FakeScheduler(
+                txn, &quot;urn:x-uid:user01&quot;,
+                &quot;&quot;&quot;BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART:20060101T100000Z
+DURATION:PT1H
+SUMMARY:event 1
+UID:deadlocked
+ORGANIZER:urn:x-uid:user01
+ATTENDEE;PARTSTAT=ACCEPTED:urn:x-uid:user01
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:urn:x-uid:user02
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;,
+            ),
+            server,
+            (), None, False,
+        )
+        yield request._prepareData()
+        yield txn.commit()
+
+        ical = Component.fromString(request.data)
+        self.assertEqual(ical.masterComponent().getOrganizer(), &quot;mailto:user01@example.com&quot;)
+        self.assertEqual(
+            set([attendee.value() for attendee in ical.getAllAttendeeProperties()]),
+            set((&quot;mailto:user01@example.com&quot;, &quot;mailto:user02@example.com&quot;)),
+        )
</ins></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoreschedulingischeduletesttest_localserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py (15327 => 15328)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py        2015-11-18 00:27:31 UTC (rev 15327)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py        2015-11-18 00:56:59 UTC (rev 15328)
</span><span class="lines">@@ -59,6 +59,26 @@
</span><span class="cx"> &lt;/servers&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    data_v5 = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+&lt;servers&gt;
+  &lt;server&gt;
+    &lt;id&gt;00001&lt;/id&gt;
+    &lt;uri&gt;http://caldav1.example.com:8008&lt;/uri&gt;
+    &lt;allowed-from&gt;127.0.0.1&lt;/allowed-from&gt;
+    &lt;shared-secret&gt;foobar&lt;/shared-secret&gt;
+  &lt;/server&gt;
+  &lt;server v5='yes'&gt;
+    &lt;id&gt;00002&lt;/id&gt;
+    &lt;uri&gt;https://caldav2.example.com:8843&lt;/uri&gt;
+  &lt;/server&gt;
+  &lt;server v5='no'&gt;
+    &lt;id&gt;00003&lt;/id&gt;
+    &lt;uri&gt;https://caldav3.example.com:8943&lt;/uri&gt;
+  &lt;/server&gt;
+&lt;/servers&gt;
+&quot;&quot;&quot;
+
+
</ins><span class="cx">     def _setupServers(self, data=data1):
</span><span class="cx">         self.patch(config, &quot;ServerHostName&quot;, &quot;caldav1.example.com&quot;)
</span><span class="cx">         self.patch(config, &quot;HTTPPort&quot;, 8008)
</span><span class="lines">@@ -178,3 +198,12 @@
</span><span class="cx"> 
</span><span class="cx">         request = SimpleRequest(None, &quot;POST&quot;, &quot;/ischedule&quot;)
</span><span class="cx">         self.assertTrue(servers.getServerById(&quot;00002&quot;).checkSharedSecret(request.headers))
</span><ins>+
+
+    def test_urn_uuid(self):
+
+        servers = self._setupServers(self.data_v5)
+
+        self.assertFalse(servers.getServerById(&quot;00001&quot;).v5)
+        self.assertTrue(servers.getServerById(&quot;00002&quot;).v5)
+        self.assertFalse(servers.getServerById(&quot;00003&quot;).v5)
</ins></span></pre>
</div>
</div>

</body>
</html>