<!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>[12243] CalendarServer/branches/release/CalendarServer-5.2-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/12243">12243</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2014-01-06 14:03:14 -0800 (Mon, 06 Jan 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Port structured location feature to branch</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolsgatewaypy">CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/gateway.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolsprincipalspy">CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolstestgatewaycaldavdplist">CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/gateway/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolstesttest_gatewaypy">CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/test_gateway.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devconfauthaugmentstestxml">CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/augments-test.xml</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devconfauthresourcestestxml">CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/resources-test.xml</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devconfcaldavdtestplist">CalendarServer/branches/release/CalendarServer-5.2-dev/conf/caldavd-test.plist</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdatafiltersperuserdatapy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/datafilters/peruserdata.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryaugmentpy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/augment.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorydirectorypy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/directory.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryldapdirectorypy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytesttest_aggregatepy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_aggregate.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytesttest_ldapdirectorypy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytesttest_xmlfilepy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_xmlfile.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytestutilpy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/util.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryxmlaccountsparserpy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlaccountsparser.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryxmlfilepy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlfile.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavstdconfigpy">CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtxdavcaldavdatastoresqlpy">CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtxdavcaldavdatastoretesttest_sqlpy">CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/test_sql.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtxdavcaldavdatastoretestutilpy">CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtxdavcommondatastoretestutilpy">CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer52devtxdavcommonidirectoryservicepy">CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/idirectoryservice.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolsgatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/gateway.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/gateway.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/gateway.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx"> import sys
</span><span class="cx"> import xml
</span><span class="cx"> 
</span><del>-from twext.python.plistlib import readPlistFromString, writePlistToString
</del><ins>+from plistlib import readPlistFromString, writePlistToString
</ins><span class="cx"> 
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed
</span><span class="cx"> from twistedcaldav.directory.directory import DirectoryError
</span><span class="lines">@@ -32,13 +32,15 @@
</span><span class="cx">     principalForPrincipalID, proxySubprincipal, addProxy, removeProxy,
</span><span class="cx">     ProxyError, ProxyWarning, autoDisableMemcached
</span><span class="cx"> )
</span><del>-from calendarserver.tools.principals import getProxies, setProxies, updateRecord
</del><ins>+from calendarserver.tools.principals import (
+    getProxies, setProxies, updateRecord, attrMap
+)
</ins><span class="cx"> from calendarserver.tools.purge import WorkerService, PurgeOldEventsService, DEFAULT_BATCH_SIZE, DEFAULT_RETAIN_DAYS
</span><span class="cx"> from calendarserver.tools.cmdline import utilityMain
</span><span class="cx"> 
</span><span class="cx"> from pycalendar.datetime import PyCalendarDateTime
</span><span class="cx"> 
</span><del>-from twistedcaldav.config import config, ConfigDict 
</del><ins>+from twistedcaldav.config import config, ConfigDict
</ins><span class="cx"> 
</span><span class="cx"> from calendarserver.tools.config import WRITABLE_CONFIG_KEYS, setKeyPath, getKeyPath, flattenDictionary, WritableConfig
</span><span class="cx"> 
</span><span class="lines">@@ -140,25 +142,6 @@
</span><span class="cx">     utilityMain(configFileName, RunnerService, verbose=debug)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-attrMap = {
-    'GeneratedUID' : { 'attr' : 'guid', },
-    'RealName' : { 'attr' : 'fullName', },
-    'RecordName' : { 'attr' : 'shortNames', },
-    'Comment' : { 'extras' : True, 'attr' : 'comment', },
-    'Description' : { 'extras' : True, 'attr' : 'description', },
-    'Type' : { 'extras' : True, 'attr' : 'type', },
-    'Capacity' : { 'extras' : True, 'attr' : 'capacity', },
-    'Building' : { 'extras' : True, 'attr' : 'building', },
-    'Floor' : { 'extras' : True, 'attr' : 'floor', },
-    'Street' : { 'extras' : True, 'attr' : 'street', },
-    'City' : { 'extras' : True, 'attr' : 'city', },
-    'State' : { 'extras' : True, 'attr' : 'state', },
-    'ZIP' : { 'extras' : True, 'attr' : 'zip', },
-    'Country' : { 'extras' : True, 'attr' : 'country', },
-    'Phone' : { 'extras' : True, 'attr' : 'phone', },
-    'AutoSchedule' : { 'attr' : 'autoSchedule', },
-    'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
-}
</del><span class="cx"> 
</span><span class="cx"> class Runner(object):
</span><span class="cx"> 
</span><span class="lines">@@ -217,9 +200,9 @@
</span><span class="cx">             self.respondWithError(&quot;Command failed: '%s'&quot; % (str(e),))
</span><span class="cx">             raise
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     # Locations
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def command_getLocationList(self, command):
</span><span class="cx">         self.respondWithRecordsOfTypes(self.dir, command, [&quot;locations&quot;])
</span><span class="cx"> 
</span><span class="lines">@@ -265,6 +248,7 @@
</span><span class="cx"> 
</span><span class="cx">     command_getResourceAttributes = command_getLocationAttributes
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def command_setLocationAttributes(self, command):
</span><span class="cx"> 
</span><span class="lines">@@ -305,9 +289,9 @@
</span><span class="cx">             return
</span><span class="cx">         self.respondWithRecordsOfTypes(self.dir, command, [&quot;locations&quot;])
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     # Resources
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def command_getResourceList(self, command):
</span><span class="cx">         self.respondWithRecordsOfTypes(self.dir, command, [&quot;resources&quot;])
</span><span class="cx"> 
</span><span class="lines">@@ -373,11 +357,72 @@
</span><span class="cx">             return
</span><span class="cx">         self.respondWithRecordsOfTypes(self.dir, command, [&quot;resources&quot;])
</span><span class="cx"> 
</span><del>-        
</del><ins>+
</ins><span class="cx">     def command_getLocationAndResourceList(self, command):
</span><span class="cx">         self.respondWithRecordsOfTypes(self.dir, command, [&quot;locations&quot;, &quot;resources&quot;])
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    # Addresses
+
+    def command_getAddressList(self, command):
+        self.respondWithRecordsOfTypes(self.dir, command, [&quot;addresses&quot;])
+
+
+    @inlineCallbacks
+    def command_createAddress(self, command):
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if key in command:
+                kwargs[info['attr']] = command[key]
+
+        try:
+            yield updateRecord(True, self.dir, &quot;addresses&quot;, **kwargs)
+        except DirectoryError, e:
+            self.respondWithError(str(e))
+            return
+
+        self.respondWithRecordsOfTypes(self.dir, command, [&quot;addresses&quot;])
+
+
+    def command_getAddressAttributes(self, command):
+        guid = command['GeneratedUID']
+        record = self.dir.recordWithGUID(guid)
+        if record is None:
+            self.respondWithError(&quot;Principal not found: %s&quot; % (guid,))
+            return
+        recordDict = recordToDict(record)
+        self.respond(command, recordDict)
+        return succeed(None)
+
+
+    @inlineCallbacks
+    def command_setAddressAttributes(self, command):
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if key in command:
+                kwargs[info['attr']] = command[key]
+        try:
+            yield updateRecord(False, self.dir, &quot;addresses&quot;, **kwargs)
+        except DirectoryError, e:
+            self.respondWithError(str(e))
+            return
+
+        yield self.command_getAddressAttributes(command)
+
+
+    def command_deleteAddress(self, command):
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if key in command:
+                kwargs[info['attr']] = command[key]
+        try:
+            self.dir.destroyRecord(&quot;addresses&quot;, **kwargs)
+        except DirectoryError, e:
+            self.respondWithError(str(e))
+            return
+        self.respondWithRecordsOfTypes(self.dir, command, [&quot;addresses&quot;])
+
+
</ins><span class="cx">     # Config
</span><span class="cx"> 
</span><span class="cx">     def command_readConfig(self, command):
</span><span class="lines">@@ -424,10 +469,8 @@
</span><span class="cx">             self.command_readConfig(command)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     # Proxies
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     @inlineCallbacks
</span><span class="cx">     def command_listWriteProxies(self, command):
</span><span class="cx">         principal = principalForPrincipalID(command['Principal'], directory=self.dir)
</span><span class="lines">@@ -545,7 +588,6 @@
</span><span class="cx">         self.respond(command, {'EventsRemoved' : eventCount, &quot;RetainDays&quot; : retainDays})
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     @inlineCallbacks
</span><span class="cx">     def respondWithProxies(self, directory, command, principal, proxyType):
</span><span class="cx">         proxies = []
</span><span class="lines">@@ -562,7 +604,6 @@
</span><span class="cx">         })
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def respondWithRecordsOfTypes(self, directory, command, recordTypes):
</span><span class="cx">         result = []
</span><span class="cx">         for recordType in recordTypes:
</span><span class="lines">@@ -572,7 +613,6 @@
</span><span class="cx">         self.respond(command, result)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def respond(self, command, result):
</span><span class="cx">         self.output.write(writePlistToString({'command' : command['command'], 'result' : result}))
</span><span class="cx"> 
</span><span class="lines">@@ -581,6 +621,7 @@
</span><span class="cx">         self.output.write(writePlistToString({'error' : msg, }))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def recordToDict(record):
</span><span class="cx">     recordDict = {}
</span><span class="cx">     for key, info in attrMap.iteritems():
</span><span class="lines">@@ -596,6 +637,8 @@
</span><span class="cx">             pass
</span><span class="cx">     return recordDict
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> def respondWithError(msg, status=1):
</span><span class="cx">     sys.stdout.write(writePlistToString({'error' : msg, }))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/principals.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/principals.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/principals.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -87,8 +87,14 @@
</span><span class="cx">     print(&quot;  --get-auto-schedule-mode: read auto-schedule mode&quot;)
</span><span class="cx">     print(&quot;  --set-auto-accept-group=principal: set auto-accept-group&quot;)
</span><span class="cx">     print(&quot;  --get-auto-accept-group: read auto-accept-group&quot;)
</span><del>-    print(&quot;  --add {locations|resources} 'full name' [record name] [GUID]: add a principal&quot;)
</del><ins>+    print(&quot;  --add {locations|resources|addresses} 'full name' [record name] [GUID]: add a principal&quot;)
</ins><span class="cx">     print(&quot;  --remove: remove a principal&quot;)
</span><ins>+    print(&quot;  --set-geo=url: set the geo: url for an address (e.g. geo:37.331741,-122.030333)&quot;)
+    print(&quot;  --get-geo: get the geo: url for an address&quot;)
+    print(&quot;  --set-street-address=streetaddress: set the street address string for an address&quot;)
+    print(&quot;  --get-street-address: get the street address string for an address&quot;)
+    print(&quot;  --set-address=guid: associate principal with an address (by guid)&quot;)
+    print(&quot;  --get-address: get the associated address's guid&quot;)
</ins><span class="cx"> 
</span><span class="cx">     if e:
</span><span class="cx">         sys.exit(64)
</span><span class="lines">@@ -116,8 +122,29 @@
</span><span class="cx">             directory = rootResource.getDirectory()
</span><span class="cx">             yield self.function(rootResource, directory, self.store, *self.params)
</span><span class="cx"> 
</span><ins>+attrMap = {
+    'GeneratedUID' : { 'attr' : 'guid', },
+    'RealName' : { 'attr' : 'fullName', },
+    'RecordName' : { 'attr' : 'shortNames', },
+    'AutoSchedule' : { 'attr' : 'autoSchedule', },
+    'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
</ins><span class="cx"> 
</span><ins>+    'Comment' : { 'extras' : True, 'attr' : 'comment', },
+    'Description' : { 'extras' : True, 'attr' : 'description', },
+    'Type' : { 'extras' : True, 'attr' : 'type', },
</ins><span class="cx"> 
</span><ins>+    # For &quot;Locations&quot;, i.e. scheduled spaces
+    'Capacity' : { 'extras' : True, 'attr' : 'capacity', },
+    'Floor' : { 'extras' : True, 'attr' : 'floor', },
+    'AssociatedAddress' : { 'extras' : True, 'attr' : 'associatedAddress', },
+
+    # For &quot;Addresses&quot;, i.e. nonscheduled areas containing Locations
+    'AbbreviatedName' : { 'extras' : True, 'attr' : 'abbreviatedName', },
+    'StreetAddress' : { 'extras' : True, 'attr' : 'streetAddress', },
+    'Geo' : { 'extras' : True, 'attr' : 'geo', },
+}
+
+
</ins><span class="cx"> def main():
</span><span class="cx">     try:
</span><span class="cx">         (optargs, args) = getopt(
</span><span class="lines">@@ -142,6 +169,12 @@
</span><span class="cx">                 &quot;get-auto-schedule-mode&quot;,
</span><span class="cx">                 &quot;set-auto-accept-group=&quot;,
</span><span class="cx">                 &quot;get-auto-accept-group&quot;,
</span><ins>+                &quot;set-geo=&quot;,
+                &quot;get-geo&quot;,
+                &quot;set-address=&quot;,
+                &quot;get-address&quot;,
+                &quot;set-street-address=&quot;,
+                &quot;get-street-address&quot;,
</ins><span class="cx">                 &quot;verbose&quot;,
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="lines">@@ -258,6 +291,24 @@
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--get-auto-accept-group&quot;):
</span><span class="cx">             principalActions.append((action_getAutoAcceptGroup,))
</span><span class="cx"> 
</span><ins>+        elif opt in (&quot;&quot;, &quot;--set-geo&quot;):
+            principalActions.append((action_setValue, &quot;Geo&quot;, arg))
+
+        elif opt in (&quot;&quot;, &quot;--get-geo&quot;):
+            principalActions.append((action_getValue, &quot;Geo&quot;))
+
+        elif opt in (&quot;&quot;, &quot;--set-street-address&quot;):
+            principalActions.append((action_setValue, &quot;StreetAddress&quot;, arg))
+
+        elif opt in (&quot;&quot;, &quot;--get-street-address&quot;):
+            principalActions.append((action_getValue, &quot;StreetAddress&quot;))
+
+        elif opt in (&quot;&quot;, &quot;--set-address&quot;):
+            principalActions.append((action_setValue, &quot;AssociatedAddress&quot;, arg))
+
+        elif opt in (&quot;&quot;, &quot;--get-address&quot;):
+            principalActions.append((action_getValue, &quot;AssociatedAddress&quot;))
+
</ins><span class="cx">         else:
</span><span class="cx">             raise NotImplementedError(opt)
</span><span class="cx"> 
</span><span class="lines">@@ -274,7 +325,7 @@
</span><span class="cx">     elif addType:
</span><span class="cx"> 
</span><span class="cx">         try:
</span><del>-            addType = matchStrings(addType, [&quot;locations&quot;, &quot;resources&quot;])
</del><ins>+            addType = matchStrings(addType, [&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">@@ -296,7 +347,7 @@
</span><span class="cx">     elif listPrincipals:
</span><span class="cx">         try:
</span><span class="cx">             listPrincipals = matchStrings(listPrincipals, [&quot;users&quot;, &quot;groups&quot;,
</span><del>-                &quot;locations&quot;, &quot;resources&quot;])
</del><ins>+                &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">@@ -393,6 +444,7 @@
</span><span class="cx">                  &quot;groups&quot; : &quot;Group&quot;,
</span><span class="cx">                  &quot;locations&quot; : &quot;Place&quot;,
</span><span class="cx">                  &quot;resources&quot; : &quot;Resource&quot;,
</span><ins>+                 &quot;addresses&quot; : &quot;Address&quot;,
</ins><span class="cx">                 }.get(record.recordType),
</span><span class="cx">             ))
</span><span class="cx">             print(&quot;   GUID: %s&quot; % (record.guid,))
</span><span class="lines">@@ -667,7 +719,30 @@
</span><span class="cx">         print(&quot;No auto-accept-group assigned to %s&quot; % (prettyPrincipal(principal),))
</span><span class="cx"> 
</span><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
+    ))
+
+
+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"> def abort(msg, status=1):
</span><span class="cx">     sys.stdout.write(&quot;%s\n&quot; % (msg,))
</span><span class="cx">     try:
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolstestgatewaycaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/gateway/caldavd.plist (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/gateway/caldavd.plist        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/gateway/caldavd.plist        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -176,6 +176,7 @@
</span><span class="cx">         &lt;array&gt;
</span><span class="cx">             &lt;string&gt;resources&lt;/string&gt;
</span><span class="cx">             &lt;string&gt;locations&lt;/string&gt;
</span><ins>+            &lt;string&gt;addresses&lt;/string&gt;
</ins><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devcalendarservertoolstesttest_gatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/test_gateway.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/test_gateway.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/calendarserver/tools/test/test_gateway.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -17,7 +17,7 @@
</span><span class="cx"> 
</span><span class="cx"> import os
</span><span class="cx"> import sys
</span><del>-from twext.python.plistlib import readPlistFromString
</del><ins>+from plistlib import readPlistFromString
</ins><span class="cx"> import xml
</span><span class="cx"> 
</span><span class="cx"> from twext.python.filepath import CachingFilePath as FilePath
</span><span class="lines">@@ -127,15 +127,9 @@
</span><span class="cx">     def test_getLocationAttributes(self):
</span><span class="cx">         yield self.runCommand(command_createLocation)
</span><span class="cx">         results = yield self.runCommand(command_getLocationAttributes)
</span><del>-        self.assertEquals(results[&quot;result&quot;][&quot;Building&quot;], &quot;Test Building&quot;)
-        self.assertEquals(results[&quot;result&quot;][&quot;City&quot;], &quot;Cupertino&quot;)
</del><span class="cx">         self.assertEquals(results[&quot;result&quot;][&quot;Capacity&quot;], &quot;40&quot;)
</span><span class="cx">         self.assertEquals(results[&quot;result&quot;][&quot;Description&quot;], &quot;Test Description&quot;)
</span><del>-        self.assertEquals(results[&quot;result&quot;][&quot;ZIP&quot;], &quot;95014&quot;)
-        self.assertEquals(results[&quot;result&quot;][&quot;Floor&quot;], &quot;First&quot;)
</del><span class="cx">         self.assertEquals(results[&quot;result&quot;][&quot;RecordName&quot;], [&quot;createdlocation01&quot;])
</span><del>-        self.assertEquals(results[&quot;result&quot;][&quot;State&quot;], &quot;CA&quot;)
-        self.assertEquals(results[&quot;result&quot;][&quot;Street&quot;], &quot;1 Infinite Loop&quot;)
</del><span class="cx">         self.assertEquals(results[&quot;result&quot;][&quot;RealName&quot;],
</span><span class="cx">             &quot;Created Location 01 %s %s&quot; % (unichr(208), u&quot;\ud83d\udca3&quot;))
</span><span class="cx">         self.assertEquals(results[&quot;result&quot;][&quot;Comment&quot;], &quot;Test Comment&quot;)
</span><span class="lines">@@ -162,6 +156,45 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def test_createAddress(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID(&quot;C701069D-9CA1-4925-A1A9-5CD94767B74B&quot;)
+        self.assertEquals(record, None)
+        yield self.runCommand(command_createAddress)
+
+        directory.flushCaches()
+
+        record = directory.recordWithUID(&quot;C701069D-9CA1-4925-A1A9-5CD94767B74B&quot;)
+        self.assertEquals(record.fullName.decode(&quot;utf-8&quot;),
+            &quot;Created Address 01 %s %s&quot; % (unichr(208), u&quot;\ud83d\udca3&quot;))
+
+        self.assertNotEquals(record, None)
+
+        self.assertEquals(record.extras[&quot;abbreviatedName&quot;], &quot;Addr1&quot;)
+        self.assertEquals(record.extras[&quot;streetAddress&quot;], &quot;1 Infinite Loop\nCupertino, 95014\nCA&quot;)
+        self.assertEquals(record.extras[&quot;geo&quot;], &quot;geo:37.331,-122.030&quot;)
+
+        results = yield self.runCommand(command_getAddressList)
+        self.assertEquals(len(results[&quot;result&quot;]), 1)
+
+        results = yield self.runCommand(command_getAddressAttributes)
+        self.assertEquals(results[&quot;result&quot;][&quot;RealName&quot;], u'Created Address 01 \xd0 \U0001f4a3')
+
+        results = yield self.runCommand(command_setAddressAttributes)
+
+        results = yield self.runCommand(command_getAddressAttributes)
+        self.assertEquals(results[&quot;result&quot;][&quot;RealName&quot;], u'Updated Address')
+        self.assertEquals(results[&quot;result&quot;][&quot;StreetAddress&quot;], u'Updated Street Address')
+        self.assertEquals(results[&quot;result&quot;][&quot;Geo&quot;], u'Updated Geo')
+        
+        results = yield self.runCommand(command_deleteAddress)
+
+        results = yield self.runCommand(command_getAddressList)
+        self.assertEquals(len(results[&quot;result&quot;]), 0)
+
+
+    @inlineCallbacks
</ins><span class="cx">     def test_createLocation(self):
</span><span class="cx">         directory = getDirectory()
</span><span class="cx"> 
</span><span class="lines">@@ -184,15 +217,8 @@
</span><span class="cx">         self.assertEquals(record.autoSchedule, True)
</span><span class="cx"> 
</span><span class="cx">         self.assertEquals(record.extras[&quot;comment&quot;], &quot;Test Comment&quot;)
</span><del>-        self.assertEquals(record.extras[&quot;building&quot;], &quot;Test Building&quot;)
</del><span class="cx">         self.assertEquals(record.extras[&quot;floor&quot;], &quot;First&quot;)
</span><span class="cx">         self.assertEquals(record.extras[&quot;capacity&quot;], &quot;40&quot;)
</span><del>-        self.assertEquals(record.extras[&quot;street&quot;], &quot;1 Infinite Loop&quot;)
-        self.assertEquals(record.extras[&quot;city&quot;], &quot;Cupertino&quot;)
-        self.assertEquals(record.extras[&quot;state&quot;], &quot;CA&quot;)
-        self.assertEquals(record.extras[&quot;zip&quot;], &quot;95014&quot;)
-        self.assertEquals(record.extras[&quot;country&quot;], &quot;USA&quot;)
-        self.assertEquals(record.extras[&quot;phone&quot;], &quot;(408) 555-1212&quot;)
</del><span class="cx"> 
</span><span class="cx">         results = yield self.runCommand(command_getLocationAttributes)
</span><span class="cx">         self.assertEquals(set(results[&quot;result&quot;][&quot;ReadProxies&quot;]), set(['user03', 'user04']))
</span><span class="lines">@@ -215,15 +241,9 @@
</span><span class="cx">         record = directory.recordWithUID(&quot;836B1B66-2E9A-4F46-8B1C-3DD6772C20B2&quot;)
</span><span class="cx"> 
</span><span class="cx">         self.assertEquals(record.extras[&quot;comment&quot;], &quot;Updated Test Comment&quot;)
</span><del>-        self.assertEquals(record.extras[&quot;building&quot;], &quot;Updated Test Building&quot;)
</del><span class="cx">         self.assertEquals(record.extras[&quot;floor&quot;], &quot;Second&quot;)
</span><span class="cx">         self.assertEquals(record.extras[&quot;capacity&quot;], &quot;41&quot;)
</span><del>-        self.assertEquals(record.extras[&quot;street&quot;], &quot;2 Infinite Loop&quot;)
-        self.assertEquals(record.extras[&quot;city&quot;], &quot;Updated Cupertino&quot;)
-        self.assertEquals(record.extras[&quot;state&quot;], &quot;Updated CA&quot;)
-        self.assertEquals(record.extras[&quot;zip&quot;], &quot;95015&quot;)
-        self.assertEquals(record.extras[&quot;country&quot;], &quot;Updated USA&quot;)
-        self.assertEquals(record.extras[&quot;phone&quot;], &quot;(408) 555-1213&quot;)
</del><ins>+        self.assertEquals(record.extras[&quot;streetAddress&quot;], &quot;2 Infinite Loop\nCupertino, 95014\nCA&quot;)
</ins><span class="cx">         self.assertEquals(record.autoSchedule, True)
</span><span class="cx">         self.assertEquals(record.autoAcceptGroup, &quot;F5A6142C-4189-4E9E-90B0-9CD0268B314B&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -384,6 +404,31 @@
</span><span class="cx"> &lt;/plist&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+command_createAddress = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;command&lt;/key&gt;
+        &lt;string&gt;createAddress&lt;/string&gt;
+        &lt;key&gt;GeneratedUID&lt;/key&gt;
+        &lt;string&gt;C701069D-9CA1-4925-A1A9-5CD94767B74B&lt;/string&gt;
+        &lt;key&gt;RealName&lt;/key&gt;
+        &lt;string&gt;Created Address 01 %s %s&lt;/string&gt;
+        &lt;key&gt;AbbreviatedName&lt;/key&gt;
+        &lt;string&gt;Addr1&lt;/string&gt;
+        &lt;key&gt;RecordName&lt;/key&gt;
+        &lt;array&gt;
+                &lt;string&gt;createdaddress01&lt;/string&gt;
+        &lt;/array&gt;
+        &lt;key&gt;StreetAddress&lt;/key&gt;
+        &lt;string&gt;1 Infinite Loop\nCupertino, 95014\nCA&lt;/string&gt;
+        &lt;key&gt;Geo&lt;/key&gt;
+        &lt;string&gt;geo:37.331,-122.030&lt;/string&gt;
+&lt;/dict&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot; % (unichr(208), u&quot;\ud83d\udca3&quot;)
+
+
</ins><span class="cx"> command_createLocation = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
</span><span class="cx"> &lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
</span><span class="cx"> &lt;plist version=&quot;1.0&quot;&gt;
</span><span class="lines">@@ -406,24 +451,12 @@
</span><span class="cx">         &lt;string&gt;Test Comment&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;Description&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;Test Description&lt;/string&gt;
</span><del>-        &lt;key&gt;Building&lt;/key&gt;
-        &lt;string&gt;Test Building&lt;/string&gt;
</del><span class="cx">         &lt;key&gt;Floor&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;First&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;Capacity&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;40&lt;/string&gt;
</span><del>-        &lt;key&gt;Street&lt;/key&gt;
-        &lt;string&gt;1 Infinite Loop&lt;/string&gt;
-        &lt;key&gt;City&lt;/key&gt;
-        &lt;string&gt;Cupertino&lt;/string&gt;
-        &lt;key&gt;State&lt;/key&gt;
-        &lt;string&gt;CA&lt;/string&gt;
-        &lt;key&gt;ZIP&lt;/key&gt;
-        &lt;string&gt;95014&lt;/string&gt;
-        &lt;key&gt;Country&lt;/key&gt;
-        &lt;string&gt;USA&lt;/string&gt;
-        &lt;key&gt;Phone&lt;/key&gt;
-        &lt;string&gt;(408) 555-1212&lt;/string&gt;
</del><ins>+        &lt;key&gt;AssociatedAddress&lt;/key&gt;
+        &lt;string&gt;C701069D-9CA1-4925-A1A9-5CD94767B74B&lt;/string&gt;
</ins><span class="cx">         &lt;key&gt;ReadProxies&lt;/key&gt;
</span><span class="cx">         &lt;array&gt;
</span><span class="cx">             &lt;string&gt;users:user03&lt;/string&gt;
</span><span class="lines">@@ -451,14 +484,16 @@
</span><span class="cx">         &lt;string&gt;AF575A61-CFA6-49E1-A0F6-B5662C9D9801&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;RealName&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;Laptop 1&lt;/string&gt;
</span><ins>+        &lt;key&gt;Comment&lt;/key&gt;
+        &lt;string&gt;Test Comment&lt;/string&gt;
+        &lt;key&gt;Description&lt;/key&gt;
+        &lt;string&gt;Test Description&lt;/string&gt;
</ins><span class="cx">         &lt;key&gt;Type&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;Computer&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;RecordName&lt;/key&gt;
</span><span class="cx">         &lt;array&gt;
</span><span class="cx">                 &lt;string&gt;laptop1&lt;/string&gt;
</span><span class="cx">         &lt;/array&gt;
</span><del>-        &lt;key&gt;Comment&lt;/key&gt;
-        &lt;string&gt;Test Comment&lt;/string&gt;
</del><span class="cx">         &lt;key&gt;ReadProxies&lt;/key&gt;
</span><span class="cx">         &lt;array&gt;
</span><span class="cx">             &lt;string&gt;users:user03&lt;/string&gt;
</span><span class="lines">@@ -497,6 +532,19 @@
</span><span class="cx"> &lt;/plist&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+command_deleteAddress = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;command&lt;/key&gt;
+        &lt;string&gt;deleteAddress&lt;/string&gt;
+        &lt;key&gt;GeneratedUID&lt;/key&gt;
+        &lt;string&gt;C701069D-9CA1-4925-A1A9-5CD94767B74B&lt;/string&gt;
+&lt;/dict&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+
</ins><span class="cx"> command_getLocationAndResourceList = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
</span><span class="cx"> &lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
</span><span class="cx"> &lt;plist version=&quot;1.0&quot;&gt;
</span><span class="lines">@@ -527,6 +575,17 @@
</span><span class="cx"> &lt;/plist&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+command_getAddressList = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;command&lt;/key&gt;
+        &lt;string&gt;getAddressList&lt;/string&gt;
+&lt;/dict&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+
+
</ins><span class="cx"> command_listReadProxies = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
</span><span class="cx"> &lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
</span><span class="cx"> &lt;plist version=&quot;1.0&quot;&gt;
</span><span class="lines">@@ -601,24 +660,12 @@
</span><span class="cx">         &lt;string&gt;Updated Test Comment&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;Description&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;Updated Test Description&lt;/string&gt;
</span><del>-        &lt;key&gt;Building&lt;/key&gt;
-        &lt;string&gt;Updated Test Building&lt;/string&gt;
</del><span class="cx">         &lt;key&gt;Floor&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;Second&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;Capacity&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;41&lt;/string&gt;
</span><del>-        &lt;key&gt;Street&lt;/key&gt;
-        &lt;string&gt;2 Infinite Loop&lt;/string&gt;
-        &lt;key&gt;City&lt;/key&gt;
-        &lt;string&gt;Updated Cupertino&lt;/string&gt;
-        &lt;key&gt;State&lt;/key&gt;
-        &lt;string&gt;Updated CA&lt;/string&gt;
-        &lt;key&gt;ZIP&lt;/key&gt;
-        &lt;string&gt;95015&lt;/string&gt;
-        &lt;key&gt;Country&lt;/key&gt;
-        &lt;string&gt;Updated USA&lt;/string&gt;
-        &lt;key&gt;Phone&lt;/key&gt;
-        &lt;string&gt;(408) 555-1213&lt;/string&gt;
</del><ins>+        &lt;key&gt;StreetAddress&lt;/key&gt;
+        &lt;string&gt;2 Infinite Loop\nCupertino, 95014\nCA&lt;/string&gt;
</ins><span class="cx">         &lt;key&gt;ReadProxies&lt;/key&gt;
</span><span class="cx">         &lt;array&gt;
</span><span class="cx">             &lt;string&gt;users:user03&lt;/string&gt;
</span><span class="lines">@@ -645,6 +692,38 @@
</span><span class="cx"> &lt;/plist&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+command_getAddressAttributes = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;command&lt;/key&gt;
+        &lt;string&gt;getAddressAttributes&lt;/string&gt;
+        &lt;key&gt;GeneratedUID&lt;/key&gt;
+        &lt;string&gt;C701069D-9CA1-4925-A1A9-5CD94767B74B&lt;/string&gt;
+&lt;/dict&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+
+command_setAddressAttributes = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;command&lt;/key&gt;
+        &lt;string&gt;setAddressAttributes&lt;/string&gt;
+        &lt;key&gt;GeneratedUID&lt;/key&gt;
+        &lt;string&gt;C701069D-9CA1-4925-A1A9-5CD94767B74B&lt;/string&gt;
+        &lt;key&gt;RealName&lt;/key&gt;
+        &lt;string&gt;Updated Address&lt;/string&gt;
+        &lt;key&gt;StreetAddress&lt;/key&gt;
+        &lt;string&gt;Updated Street Address&lt;/string&gt;
+        &lt;key&gt;Geo&lt;/key&gt;
+        &lt;string&gt;Updated Geo&lt;/string&gt;
+
+&lt;/dict&gt;
+&lt;/plist&gt;
+&quot;&quot;&quot;
+
+
</ins><span class="cx"> command_setResourceAttributes = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
</span><span class="cx"> &lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
</span><span class="cx"> &lt;plist version=&quot;1.0&quot;&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devconfauthaugmentstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/augments-test.xml (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/augments-test.xml        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/augments-test.xml        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -109,6 +109,7 @@
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span><span class="cx">     &lt;enable-login&gt;true&lt;/enable-login&gt;
</span><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><ins>+    &lt;auto-schedule-mode&gt;default&lt;/auto-schedule-mode&gt;
</ins><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;C38BEE7A-36EE-478C-9DCB-CBF4612AFE65&lt;/uid&gt;
</span><span class="lines">@@ -136,4 +137,49 @@
</span><span class="cx">     &lt;enable-login&gt;true&lt;/enable-login&gt;
</span><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><span class="cx">   &lt;/record&gt;
</span><ins>+  &lt;record&gt;
+    &lt;uid&gt;6F9EE33B-78F6-481B-9289-3D0812FF0D64&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+    &lt;enable-login&gt;true&lt;/enable-login&gt;
+    &lt;auto-schedule&gt;false&lt;/auto-schedule&gt;
+    &lt;auto-schedule-mode&gt;default&lt;/auto-schedule-mode&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;76E7ECA6-08BC-4AE7-930D-F2E7453993A5&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+    &lt;enable-login&gt;true&lt;/enable-login&gt;
+    &lt;auto-schedule&gt;false&lt;/auto-schedule&gt;
+    &lt;auto-schedule-mode&gt;default&lt;/auto-schedule-mode&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;63A2F949-2D8D-4C8D-B8A5-DCF2A94610F3&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+    &lt;enable-login&gt;true&lt;/enable-login&gt;
+    &lt;auto-schedule&gt;false&lt;/auto-schedule&gt;
+    &lt;auto-schedule-mode&gt;default&lt;/auto-schedule-mode&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;06E3BDCB-9C19-485A-B14E-F146A80ADDC6&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+    &lt;enable-login&gt;true&lt;/enable-login&gt;
+    &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
+    &lt;auto-schedule-mode&gt;default&lt;/auto-schedule-mode&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;4D66A20A-1437-437D-8069-2F14E8322234&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+    &lt;enable-login&gt;true&lt;/enable-login&gt;
+    &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
+    &lt;auto-schedule-mode&gt;default&lt;/auto-schedule-mode&gt;
+  &lt;/record&gt;
</ins><span class="cx"> &lt;/augments&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devconfauthresourcestestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/resources-test.xml (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/resources-test.xml        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/conf/auth/resources-test.xml        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -1,5 +1,13 @@
</span><span class="cx"> &lt;accounts realm=&quot;Test Realm&quot;&gt;
</span><span class="cx">   &lt;location&gt;
</span><ins>+    &lt;uid&gt;fantastic&lt;/uid&gt;
+    &lt;guid&gt;4D66A20A-1437-437D-8069-2F14E8322234&lt;/guid&gt;
+    &lt;name&gt;Fantastic Conference Room&lt;/name&gt;
+    &lt;extras&gt;
+      &lt;associatedAddress&gt;63A2F949-2D8D-4C8D-B8A5-DCF2A94610F3&lt;/associatedAddress&gt;
+    &lt;/extras&gt;
+  &lt;/location&gt;
+  &lt;location&gt;
</ins><span class="cx">     &lt;uid&gt;jupiter&lt;/uid&gt;
</span><span class="cx">     &lt;guid&gt;jupiter&lt;/guid&gt;
</span><span class="cx">     &lt;name&gt;Jupiter Conference Room, Building 2, 1st Floor&lt;/name&gt;
</span><span class="lines">@@ -78,6 +86,9 @@
</span><span class="cx">     &lt;uid&gt;sharissroom&lt;/uid&gt;
</span><span class="cx">     &lt;guid&gt;80689D41-DAF8-4189-909C-DB017B271892&lt;/guid&gt;
</span><span class="cx">     &lt;name&gt;Shari's Room&lt;/name&gt;
</span><ins>+    &lt;extras&gt;
+      &lt;associatedAddress&gt;6F9EE33B-78F6-481B-9289-3D0812FF0D64&lt;/associatedAddress&gt;
+    &lt;/extras&gt;
</ins><span class="cx">   &lt;/location&gt;
</span><span class="cx">   &lt;location&gt;
</span><span class="cx">     &lt;uid&gt;pluto&lt;/uid&gt;
</span><span class="lines">@@ -95,6 +106,14 @@
</span><span class="cx">     &lt;name&gt;Room 10&lt;/name&gt;
</span><span class="cx">   &lt;/location&gt;
</span><span class="cx">   &lt;location&gt;
</span><ins>+    &lt;uid&gt;pretend&lt;/uid&gt;
+    &lt;guid&gt;06E3BDCB-9C19-485A-B14E-F146A80ADDC6&lt;/guid&gt;
+    &lt;name&gt;Pretend Conference Room&lt;/name&gt;
+    &lt;extras&gt;
+      &lt;associatedAddress&gt;76E7ECA6-08BC-4AE7-930D-F2E7453993A5&lt;/associatedAddress&gt;
+    &lt;/extras&gt;
+  &lt;/location&gt;
+  &lt;location&gt;
</ins><span class="cx">     &lt;uid&gt;neptune&lt;/uid&gt;
</span><span class="cx">     &lt;guid&gt;neptune&lt;/guid&gt;
</span><span class="cx">     &lt;name&gt;Neptune Conference Room, Building 2, 1st Floor&lt;/name&gt;
</span><span class="lines">@@ -224,4 +243,31 @@
</span><span class="cx">     &lt;guid&gt;resource09&lt;/guid&gt;
</span><span class="cx">     &lt;name&gt;Resource 09&lt;/name&gt;
</span><span class="cx">   &lt;/resource&gt;
</span><ins>+  &lt;address&gt;
+    &lt;uid&gt;testaddress1&lt;/uid&gt;
+    &lt;guid&gt;6F9EE33B-78F6-481B-9289-3D0812FF0D64&lt;/guid&gt;
+    &lt;name&gt;Test Address One&lt;/name&gt;
+    &lt;extras&gt;
+      &lt;streetAddress&gt;20300 Stevens Creek Blvd, Cupertino, CA 95014&lt;/streetAddress&gt;
+      &lt;geo&gt;37.322281,-122.028345&lt;/geo&gt;
+    &lt;/extras&gt;
+  &lt;/address&gt;
+  &lt;address&gt;
+    &lt;uid&gt;il2&lt;/uid&gt;
+    &lt;guid&gt;63A2F949-2D8D-4C8D-B8A5-DCF2A94610F3&lt;/guid&gt;
+    &lt;name&gt;IL2&lt;/name&gt;
+    &lt;extras&gt;
+      &lt;streetAddress&gt;2 Infinite Loop, Cupertino, CA 95014&lt;/streetAddress&gt;
+      &lt;geo&gt;37.332633,-122.030502&lt;/geo&gt;
+    &lt;/extras&gt;
+  &lt;/address&gt;
+  &lt;address&gt;
+    &lt;uid&gt;il1&lt;/uid&gt;
+    &lt;guid&gt;76E7ECA6-08BC-4AE7-930D-F2E7453993A5&lt;/guid&gt;
+    &lt;name&gt;IL1&lt;/name&gt;
+    &lt;extras&gt;
+      &lt;streetAddress&gt;1 Infinite Loop, Cupertino, CA 95014&lt;/streetAddress&gt;
+      &lt;geo&gt;37.331741,-122.030333&lt;/geo&gt;
+    &lt;/extras&gt;
+  &lt;/address&gt;
</ins><span class="cx"> &lt;/accounts&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devconfcaldavdtestplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/conf/caldavd-test.plist (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/conf/caldavd-test.plist        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/conf/caldavd-test.plist        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -284,29 +284,20 @@
</span><span class="cx">                     &lt;string&gt;mail&lt;/string&gt;
</span><span class="cx">                     &lt;string&gt;mailAlias&lt;/string&gt;
</span><span class="cx">                 &lt;/array&gt;
</span><del>-                &lt;key&gt;firstName&lt;/key&gt;
-                &lt;string&gt;&lt;/string&gt;
-                &lt;key&gt;lastName&lt;/key&gt;
-                &lt;string&gt;&lt;/string&gt;
</del><span class="cx">             &lt;/dict&gt;
</span><span class="cx">           &lt;/dict&gt;
</span><span class="cx">           &lt;key&gt;locations&lt;/key&gt;
</span><span class="cx">           &lt;dict&gt;
</span><span class="cx">             &lt;key&gt;rdn&lt;/key&gt;
</span><span class="cx">             &lt;string&gt;ou=locations&lt;/string&gt;
</span><ins>+            &lt;key&gt;associatedAddressAttr&lt;/key&gt;
+            &lt;string&gt;&lt;/string&gt;
</ins><span class="cx">             &lt;key&gt;mapping&lt;/key&gt;
</span><span class="cx">             &lt;dict&gt;
</span><span class="cx">                 &lt;key&gt;recordName&lt;/key&gt;
</span><span class="cx">                 &lt;string&gt;cn&lt;/string&gt;
</span><span class="cx">                 &lt;key&gt;fullName&lt;/key&gt;
</span><span class="cx">                 &lt;string&gt;cn&lt;/string&gt;
</span><del>-                &lt;key&gt;emailAddresses&lt;/key&gt;
-                &lt;array&gt;
-                &lt;/array&gt;
-                &lt;key&gt;firstName&lt;/key&gt;
-                &lt;string&gt;&lt;/string&gt;
-                &lt;key&gt;lastName&lt;/key&gt;
-                &lt;string&gt;&lt;/string&gt;
</del><span class="cx">             &lt;/dict&gt;
</span><span class="cx">           &lt;/dict&gt;
</span><span class="cx">           &lt;key&gt;resources&lt;/key&gt;
</span><span class="lines">@@ -319,15 +310,24 @@
</span><span class="cx">                 &lt;string&gt;cn&lt;/string&gt;
</span><span class="cx">                 &lt;key&gt;fullName&lt;/key&gt;
</span><span class="cx">                 &lt;string&gt;cn&lt;/string&gt;
</span><del>-                &lt;key&gt;emailAddresses&lt;/key&gt;
-                &lt;array&gt;
-                &lt;/array&gt;
-                &lt;key&gt;firstName&lt;/key&gt;
-                &lt;string&gt;&lt;/string&gt;
-                &lt;key&gt;lastName&lt;/key&gt;
-                &lt;string&gt;&lt;/string&gt;
</del><span class="cx">             &lt;/dict&gt;
</span><span class="cx">           &lt;/dict&gt;
</span><ins>+          &lt;key&gt;addresses&lt;/key&gt;
+          &lt;dict&gt;
+            &lt;key&gt;rdn&lt;/key&gt;
+            &lt;string&gt;ou=buildings&lt;/string&gt;
+            &lt;key&gt;geoAttr&lt;/key&gt;
+            &lt;string&gt;&lt;/string&gt;
+            &lt;key&gt;streetAddressAttr&lt;/key&gt;
+            &lt;string&gt;&lt;/string&gt;
+            &lt;key&gt;mapping&lt;/key&gt;
+            &lt;dict&gt;
+                &lt;key&gt;recordName&lt;/key&gt;
+                &lt;string&gt;cn&lt;/string&gt;
+                &lt;key&gt;fullName&lt;/key&gt;
+                &lt;string&gt;cn&lt;/string&gt;
+            &lt;/dict&gt;
+          &lt;/dict&gt;
</ins><span class="cx">         &lt;/dict&gt;
</span><span class="cx">         &lt;key&gt;groupSchema&lt;/key&gt;
</span><span class="cx">         &lt;dict&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdatafiltersperuserdatapy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/datafilters/peruserdata.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/datafilters/peruserdata.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/datafilters/peruserdata.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -67,7 +67,7 @@
</span><span class="cx"> 
</span><span class="cx">     # X- properties that are ignored - by default all X- properties are treated as per-user except for the
</span><span class="cx">     # ones listed here
</span><del>-    IGNORE_X_PROPERTIES = (Component.HIDDEN_INSTANCE_PROPERTY,)
</del><ins>+    IGNORE_X_PROPERTIES = [Component.HIDDEN_INSTANCE_PROPERTY]
</ins><span class="cx"> 
</span><span class="cx">     def __init__(self, uid):
</span><span class="cx">         &quot;&quot;&quot;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/augment.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/augment.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/augment.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -81,6 +81,7 @@
</span><span class="cx">     &quot;groups&quot; : &quot;Group&quot;,
</span><span class="cx">     &quot;locations&quot; : &quot;Location&quot;,
</span><span class="cx">     &quot;resources&quot; : &quot;Resource&quot;,
</span><ins>+    &quot;addresses&quot; : &quot;Address&quot;,
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> class AugmentDB(object):
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorydirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/directory.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/directory.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/directory.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -86,6 +86,7 @@
</span><span class="cx">     recordType_groups = &quot;groups&quot;
</span><span class="cx">     recordType_locations = &quot;locations&quot;
</span><span class="cx">     recordType_resources = &quot;resources&quot;
</span><ins>+    recordType_addresses = &quot;addresses&quot;
</ins><span class="cx"> 
</span><span class="cx">     searchContext_location = &quot;location&quot;
</span><span class="cx">     searchContext_resource = &quot;resource&quot;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/ldapdirectory.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/ldapdirectory.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/ldapdirectory.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -1,6 +1,6 @@
</span><span class="cx"> ##
</span><span class="cx"> # Copyright (c) 2008-2009 Aymeric Augustin. All rights reserved.
</span><del>-# Copyright (c) 2006-2013 Apple Inc. All rights reserved.
</del><ins>+# Copyright (c) 2006-2014 Apple Inc. All rights reserved.
</ins><span class="cx"> #
</span><span class="cx"> # Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
</span><span class="cx"> # you may not use this file except in compliance with the License.
</span><span class="lines">@@ -112,8 +112,6 @@
</span><span class="cx">                 &quot;guidAttr&quot;: &quot;entryUUID&quot;,
</span><span class="cx">                 &quot;users&quot;: {
</span><span class="cx">                     &quot;rdn&quot;: &quot;ou=People&quot;,
</span><del>-                    &quot;attr&quot;: &quot;uid&quot;, # used only to synthesize email address
-                    &quot;emailSuffix&quot;: None, # used only to synthesize email address
</del><span class="cx">                     &quot;filter&quot;: None, # additional filter for this type
</span><span class="cx">                     &quot;loginEnabledAttr&quot; : &quot;&quot;, # attribute controlling login
</span><span class="cx">                     &quot;loginEnabledValue&quot; : &quot;yes&quot;, # &quot;True&quot; value of above attribute
</span><span class="lines">@@ -129,8 +127,6 @@
</span><span class="cx">                 },
</span><span class="cx">                 &quot;groups&quot;: {
</span><span class="cx">                     &quot;rdn&quot;: &quot;ou=Group&quot;,
</span><del>-                    &quot;attr&quot;: &quot;cn&quot;, # used only to synthesize email address
-                    &quot;emailSuffix&quot;: None, # used only to synthesize email address
</del><span class="cx">                     &quot;filter&quot;: None, # additional filter for this type
</span><span class="cx">                     &quot;mapping&quot; : { # maps internal record names to LDAP
</span><span class="cx">                         &quot;recordName&quot;: &quot;cn&quot;,
</span><span class="lines">@@ -142,23 +138,18 @@
</span><span class="cx">                 },
</span><span class="cx">                 &quot;locations&quot;: {
</span><span class="cx">                     &quot;rdn&quot;: &quot;ou=Places&quot;,
</span><del>-                    &quot;attr&quot;: &quot;cn&quot;, # used only to synthesize email address
-                    &quot;emailSuffix&quot;: None, # used only to synthesize email address
</del><span class="cx">                     &quot;filter&quot;: None, # additional filter for this type
</span><span class="cx">                     &quot;calendarEnabledAttr&quot; : &quot;&quot;, # attribute controlling enabledForCalendaring
</span><span class="cx">                     &quot;calendarEnabledValue&quot; : &quot;yes&quot;, # &quot;True&quot; value of above attribute
</span><ins>+                    &quot;associatedAddressAttr&quot; : &quot;&quot;,
</ins><span class="cx">                     &quot;mapping&quot; : { # maps internal record names to LDAP
</span><span class="cx">                         &quot;recordName&quot;: &quot;cn&quot;,
</span><span class="cx">                         &quot;fullName&quot; : &quot;cn&quot;,
</span><span class="cx">                         &quot;emailAddresses&quot; : [&quot;mail&quot;], # multiple LDAP fields supported
</span><del>-                        &quot;firstName&quot; : &quot;givenName&quot;,
-                        &quot;lastName&quot; : &quot;sn&quot;,
</del><span class="cx">                     },
</span><span class="cx">                 },
</span><span class="cx">                 &quot;resources&quot;: {
</span><span class="cx">                     &quot;rdn&quot;: &quot;ou=Resources&quot;,
</span><del>-                    &quot;attr&quot;: &quot;cn&quot;, # used only to synthesize email address
-                    &quot;emailSuffix&quot;: None, # used only to synthesize email address
</del><span class="cx">                     &quot;filter&quot;: None, # additional filter for this type
</span><span class="cx">                     &quot;calendarEnabledAttr&quot; : &quot;&quot;, # attribute controlling enabledForCalendaring
</span><span class="cx">                     &quot;calendarEnabledValue&quot; : &quot;yes&quot;, # &quot;True&quot; value of above attribute
</span><span class="lines">@@ -166,10 +157,18 @@
</span><span class="cx">                         &quot;recordName&quot;: &quot;cn&quot;,
</span><span class="cx">                         &quot;fullName&quot; : &quot;cn&quot;,
</span><span class="cx">                         &quot;emailAddresses&quot; : [&quot;mail&quot;], # multiple LDAP fields supported
</span><del>-                        &quot;firstName&quot; : &quot;givenName&quot;,
-                        &quot;lastName&quot; : &quot;sn&quot;,
</del><span class="cx">                     },
</span><span class="cx">                 },
</span><ins>+                &quot;addresses&quot;: {
+                    &quot;rdn&quot;: &quot;ou=Buildings&quot;,
+                    &quot;filter&quot;: None, # additional filter for this type
+                    &quot;streetAddressAttr&quot; : &quot;&quot;,
+                    &quot;geoAttr&quot; : &quot;&quot;,
+                    &quot;mapping&quot; : { # maps internal record names to LDAP
+                        &quot;recordName&quot;: &quot;cn&quot;,
+                        &quot;fullName&quot; : &quot;cn&quot;,
+                    },
+                },
</ins><span class="cx">             },
</span><span class="cx">             &quot;groupSchema&quot;: {
</span><span class="cx">                 &quot;membersAttr&quot;: &quot;member&quot;, # how members are specified
</span><span class="lines">@@ -238,8 +237,10 @@
</span><span class="cx">         for recordType in self.recordTypes():
</span><span class="cx">             if self.rdnSchema[recordType][&quot;attr&quot;]:
</span><span class="cx">                 attrSet.add(self.rdnSchema[recordType][&quot;attr&quot;])
</span><del>-            if self.rdnSchema[recordType].get(&quot;calendarEnabledAttr&quot;, False):
-                attrSet.add(self.rdnSchema[recordType][&quot;calendarEnabledAttr&quot;])
</del><ins>+            for n in (&quot;calendarEnabledAttr&quot;, &quot;associatedAddressAttr&quot;,
+                &quot;streetAddressAttr&quot;, &quot;geoAttr&quot;):
+                if self.rdnSchema[recordType].get(n, False):
+                    attrSet.add(self.rdnSchema[recordType][n])
</ins><span class="cx">             for attrList in self.rdnSchema[recordType][&quot;mapping&quot;].values():
</span><span class="cx">                 if attrList:
</span><span class="cx">                     # Since emailAddresses can map to multiple LDAP fields,
</span><span class="lines">@@ -308,7 +309,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Build filter
</span><span class="cx">         filterstr = &quot;(!(objectClass=organizationalUnit))&quot;
</span><del>-        typeFilter = self.rdnSchema[recordType][&quot;filter&quot;]
</del><ins>+        typeFilter = self.rdnSchema[recordType].get(&quot;filter&quot;, &quot;&quot;)
</ins><span class="cx">         if typeFilter:
</span><span class="cx">             filterstr = &quot;(&amp;%s%s)&quot; % (filterstr, typeFilter)
</span><span class="cx"> 
</span><span class="lines">@@ -779,6 +780,7 @@
</span><span class="cx">         enabledForAddressBooks = None
</span><span class="cx">         uid = None
</span><span class="cx">         enabledForLogin = True
</span><ins>+        extras = {}
</ins><span class="cx"> 
</span><span class="cx">         shortNames = tuple(self._getMultipleLdapAttributes(attrs, self.rdnSchema[recordType][&quot;mapping&quot;][&quot;recordName&quot;]))
</span><span class="cx">         if not shortNames:
</span><span class="lines">@@ -795,17 +797,17 @@
</span><span class="cx"> 
</span><span class="cx">         # Find or build email
</span><span class="cx">         # (The emailAddresses mapping is a list of ldap fields)
</span><del>-        emailAddressesMappedTo = self.rdnSchema[recordType][&quot;mapping&quot;][&quot;emailAddresses&quot;]
</del><ins>+        emailAddressesMappedTo = self.rdnSchema[recordType][&quot;mapping&quot;].get(&quot;emailAddresses&quot;, &quot;&quot;)
</ins><span class="cx">         # Supporting either string or list for emailAddresses:
</span><span class="cx">         if isinstance(emailAddressesMappedTo, str):
</span><del>-            emailAddresses = set(self._getMultipleLdapAttributes(attrs, self.rdnSchema[recordType][&quot;mapping&quot;][&quot;emailAddresses&quot;]))
</del><ins>+            emailAddresses = set(self._getMultipleLdapAttributes(attrs, self.rdnSchema[recordType][&quot;mapping&quot;].get(&quot;emailAddresses&quot;, &quot;&quot;)))
</ins><span class="cx">         else:
</span><span class="cx">             emailAddresses = set(self._getMultipleLdapAttributes(attrs, *self.rdnSchema[recordType][&quot;mapping&quot;][&quot;emailAddresses&quot;]))
</span><del>-        emailSuffix = self.rdnSchema[recordType][&quot;emailSuffix&quot;]
</del><ins>+        emailSuffix = self.rdnSchema[recordType].get(&quot;emailSuffix&quot;, None)
</ins><span class="cx"> 
</span><span class="cx">         if len(emailAddresses) == 0 and emailSuffix:
</span><span class="cx">             emailPrefix = self._getUniqueLdapAttribute(attrs,
</span><del>-                self.rdnSchema[recordType][&quot;attr&quot;])
</del><ins>+                self.rdnSchema[recordType].get(&quot;attr&quot;, &quot;cn&quot;))
</ins><span class="cx">             emailAddresses.add(emailPrefix + emailSuffix)
</span><span class="cx"> 
</span><span class="cx">         proxyGUIDs = ()
</span><span class="lines">@@ -891,6 +893,25 @@
</span><span class="cx">                     autoAcceptGroup = self._getUniqueLdapAttribute(attrs,
</span><span class="cx">                         self.resourceSchema[&quot;autoAcceptGroupAttr&quot;])
</span><span class="cx"> 
</span><ins>+            if recordType == self.recordType_locations:
+                if self.rdnSchema[recordType][&quot;associatedAddressAttr&quot;]:
+                    associatedAddress = self._getUniqueLdapAttribute(attrs,
+                        self.rdnSchema[recordType][&quot;associatedAddressAttr&quot;])
+                    if associatedAddress:
+                        extras[&quot;associatedAddress&quot;] = associatedAddress
+
+        elif recordType == self.recordType_addresses:
+            if self.rdnSchema[recordType].get(&quot;geoAttr&quot;, &quot;&quot;):
+                geo = self._getUniqueLdapAttribute(attrs,
+                    self.rdnSchema[recordType][&quot;geoAttr&quot;])
+                if geo:
+                    extras[&quot;geo&quot;] = geo
+            if self.rdnSchema[recordType].get(&quot;streetAddressAttr&quot;, &quot;&quot;):
+                street = self._getUniqueLdapAttribute(attrs,
+                    self.rdnSchema[recordType][&quot;streetAddressAttr&quot;])
+                if street:
+                    extras[&quot;streetAddress&quot;] = street
+
</ins><span class="cx">         serverID = partitionID = None
</span><span class="cx">         if self.partitionSchema[&quot;serverIdAttr&quot;]:
</span><span class="cx">             serverID = self._getUniqueLdapAttribute(attrs,
</span><span class="lines">@@ -915,6 +936,7 @@
</span><span class="cx">             extProxies              = proxyGUIDs,
</span><span class="cx">             extReadOnlyProxies      = readOnlyProxyGUIDs,
</span><span class="cx">             attrs                   = attrs,
</span><ins>+            **extras
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         if self.augmentService is not None:
</span><span class="lines">@@ -986,7 +1008,7 @@
</span><span class="cx"> 
</span><span class="cx">             # Build filter
</span><span class="cx">             filterstr = &quot;(!(objectClass=organizationalUnit))&quot;
</span><del>-            typeFilter = self.rdnSchema[recordType][&quot;filter&quot;]
</del><ins>+            typeFilter = self.rdnSchema[recordType].get(&quot;filter&quot;, &quot;&quot;)
</ins><span class="cx">             if typeFilter:
</span><span class="cx">                 filterstr = &quot;(&amp;%s%s)&quot; % (filterstr, typeFilter)
</span><span class="cx"> 
</span><span class="lines">@@ -1007,17 +1029,17 @@
</span><span class="cx">             elif indexType == self.INDEX_TYPE_CUA:
</span><span class="cx">                 # indexKey is of the form &quot;mailto:test@example.net&quot;
</span><span class="cx">                 email = indexKey[7:] # strip &quot;mailto:&quot;
</span><del>-                emailSuffix = self.rdnSchema[recordType][&quot;emailSuffix&quot;]
</del><ins>+                emailSuffix = self.rdnSchema[recordType].get(&quot;emailSuffix&quot;, None)
</ins><span class="cx">                 if emailSuffix is not None and email.partition(&quot;@&quot;)[2] == emailSuffix:
</span><span class="cx">                     filterstr = &quot;(&amp;%s(|(&amp;(!(mail=*))(%s=%s))(mail=%s)))&quot; % (
</span><span class="cx">                         filterstr,
</span><del>-                        self.rdnSchema[recordType][&quot;attr&quot;],
</del><ins>+                        self.rdnSchema[recordType].get(&quot;attr&quot;, &quot;cn&quot;),
</ins><span class="cx">                         email.partition(&quot;@&quot;)[0],
</span><span class="cx">                         ldapEsc(email)
</span><span class="cx">                     )
</span><span class="cx">                 else:
</span><span class="cx">                     # emailAddresses can map to multiple LDAP fields
</span><del>-                    ldapFields = self.rdnSchema[recordType][&quot;mapping&quot;][&quot;emailAddresses&quot;]
</del><ins>+                    ldapFields = self.rdnSchema[recordType][&quot;mapping&quot;].get(&quot;emailAddresses&quot;, &quot;&quot;)
</ins><span class="cx">                     if isinstance(ldapFields, str):
</span><span class="cx">                         if ldapFields:
</span><span class="cx">                             subfilter = &quot;(%s=%s)&quot; % (ldapFields, ldapEsc(email))
</span><span class="lines">@@ -1112,7 +1134,7 @@
</span><span class="cx">             typeCounts[recordType] = 0
</span><span class="cx">             base = self.typeDNs[recordType]
</span><span class="cx">             scope = ldap.SCOPE_SUBTREE
</span><del>-            extraFilter = self.rdnSchema[recordType][&quot;filter&quot;]
</del><ins>+            extraFilter = self.rdnSchema[recordType].get(&quot;filter&quot;, &quot;&quot;)
</ins><span class="cx">             filterstr = buildFilterFromTokens(recordType, self.rdnSchema[recordType][&quot;mapping&quot;],
</span><span class="cx">                 tokens, extra=extraFilter)
</span><span class="cx"> 
</span><span class="lines">@@ -1453,7 +1475,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     filterStr = None
</span><del>-    tokens = [ldapEsc(t) for t in tokens if len(t) &gt; 2]
</del><ins>+    tokens = [ldapEsc(t) for t in tokens]
</ins><span class="cx">     if len(tokens) == 0:
</span><span class="cx">         return None
</span><span class="cx"> 
</span><span class="lines">@@ -1506,7 +1528,7 @@
</span><span class="cx">         guid, shortNames, authIDs, fullName,
</span><span class="cx">         firstName, lastName, emailAddresses,
</span><span class="cx">         uid, dn, memberGUIDs, extProxies, extReadOnlyProxies,
</span><del>-        attrs
</del><ins>+        attrs, **kwargs
</ins><span class="cx">     ):
</span><span class="cx">         super(LdapDirectoryRecord, self).__init__(
</span><span class="cx">             service               = service,
</span><span class="lines">@@ -1521,6 +1543,7 @@
</span><span class="cx">             extProxies            = extProxies,
</span><span class="cx">             extReadOnlyProxies    = extReadOnlyProxies,
</span><span class="cx">             uid                   = uid,
</span><ins>+            **kwargs
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         # Save attributes of dn and attrs in case you might need them later
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytesttest_aggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_aggregate.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_aggregate.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_aggregate.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -55,6 +55,7 @@
</span><span class="cx">     groups = property(_records(&quot;groups&quot;))
</span><span class="cx">     locations = property(_records(&quot;locations&quot;))
</span><span class="cx">     resources = property(_records(&quot;resources&quot;))
</span><ins>+    addresses = property(_records(&quot;addresses&quot;))
</ins><span class="cx"> 
</span><span class="cx">     recordTypePrefixes = tuple(s[0] for s in testServices)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytesttest_ldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_ldapdirectory.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_ldapdirectory.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_ldapdirectory.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -540,8 +540,6 @@
</span><span class="cx">                             &quot;recordName&quot;: &quot;cn&quot;,
</span><span class="cx">                             &quot;fullName&quot; : &quot;cn&quot;,
</span><span class="cx">                             &quot;emailAddresses&quot; : [&quot;mail&quot;, &quot;emailAliases&quot;],
</span><del>-                            &quot;firstName&quot; : &quot;givenName&quot;,
-                            &quot;lastName&quot; : &quot;sn&quot;,
</del><span class="cx">                         },
</span><span class="cx">                     },
</span><span class="cx">                     &quot;locations&quot;: {
</span><span class="lines">@@ -551,12 +549,11 @@
</span><span class="cx">                         &quot;filter&quot;: &quot;(objectClass=apple-resource)&quot;, # additional filter for this type
</span><span class="cx">                         &quot;calendarEnabledAttr&quot; : &quot;&quot;, # attribute controlling calendaring
</span><span class="cx">                         &quot;calendarEnabledValue&quot; : &quot;yes&quot;, # &quot;True&quot; value of above attribute
</span><ins>+                        &quot;associatedAddressAttr&quot; : &quot;assocAddr&quot;,
</ins><span class="cx">                         &quot;mapping&quot;: { # maps internal record names to LDAP
</span><span class="cx">                             &quot;recordName&quot;: &quot;cn&quot;,
</span><span class="cx">                             &quot;fullName&quot; : &quot;cn&quot;,
</span><span class="cx">                             &quot;emailAddresses&quot; : &quot;&quot;, # old style, single string
</span><del>-                            &quot;firstName&quot; : &quot;givenName&quot;,
-                            &quot;lastName&quot; : &quot;sn&quot;,
</del><span class="cx">                         },
</span><span class="cx">                     },
</span><span class="cx">                     &quot;resources&quot;: {
</span><span class="lines">@@ -570,10 +567,17 @@
</span><span class="cx">                             &quot;recordName&quot;: &quot;cn&quot;,
</span><span class="cx">                             &quot;fullName&quot; : &quot;cn&quot;,
</span><span class="cx">                             &quot;emailAddresses&quot; : [], # new style, array
</span><del>-                            &quot;firstName&quot; : &quot;givenName&quot;,
-                            &quot;lastName&quot; : &quot;sn&quot;,
</del><span class="cx">                         },
</span><span class="cx">                     },
</span><ins>+                    &quot;addresses&quot;: {
+                        &quot;rdn&quot;: &quot;cn=Buildings&quot;,
+                        &quot;geoAttr&quot; : &quot;coordinates&quot;,
+                        &quot;streetAddressAttr&quot; : &quot;postal&quot;,
+                        &quot;mapping&quot;: { # maps internal record names to LDAP
+                            &quot;recordName&quot;: &quot;cn&quot;,
+                            &quot;fullName&quot; : &quot;cn&quot;,
+                        },
+                    },
</ins><span class="cx">                 },
</span><span class="cx">                 &quot;groupSchema&quot;: {
</span><span class="cx">                     &quot;membersAttr&quot;: &quot;uniqueMember&quot;, # how members are specified
</span><span class="lines">@@ -1521,6 +1525,39 @@
</span><span class="cx">                 self.service.recordType_users)
</span><span class="cx">             self.assertEquals(record.guid, guid.upper())
</span><span class="cx"> 
</span><ins>+            # Location with associated Address
+
+            dn = &quot;cn=odtestlocation,cn=locations,dc=example,dc=com&quot;
+            guid = &quot;D3094652-344B-4633-8DB8-09639FA00FB6&quot;
+            attrs = {
+                &quot;apple-generateduid&quot;: [guid],
+                &quot;cn&quot;: [&quot;odtestlocation&quot;],
+                &quot;assocAddr&quot; : [&quot;6C6CD280-E6E3-11DF-9492-0800200C9A66&quot;],
+            }
+            record = self.service._ldapResultToRecord(dn, attrs,
+                self.service.recordType_locations)
+            self.assertEquals(record.extras, {
+                &quot;associatedAddress&quot;: &quot;6C6CD280-E6E3-11DF-9492-0800200C9A66&quot;
+            })
+           
+            # Address with street and geo
+
+            dn = &quot;cn=odtestaddress,cn=buildings,dc=example,dc=com&quot;
+            guid = &quot;6C6CD280-E6E3-11DF-9492-0800200C9A66&quot;
+            attrs = {
+                &quot;apple-generateduid&quot;: [guid],
+                &quot;cn&quot;: [&quot;odtestaddress&quot;],
+                &quot;coordinates&quot; : [&quot;geo:1,2&quot;],
+                &quot;postal&quot; : [&quot;1 Infinite Loop, Cupertino, CA&quot;],
+            }
+            record = self.service._ldapResultToRecord(dn, attrs,
+                self.service.recordType_addresses)
+            self.assertEquals(record.extras, {
+                &quot;geo&quot;: &quot;geo:1,2&quot;,
+                &quot;streetAddress&quot; : &quot;1 Infinite Loop, Cupertino, CA&quot;,
+            })
+           
+
</ins><span class="cx">         def test_listRecords(self):
</span><span class="cx">             &quot;&quot;&quot;
</span><span class="cx">             listRecords makes an LDAP query (with fake results in this test)
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytesttest_xmlfilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_xmlfile.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_xmlfile.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/test_xmlfile.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -34,7 +34,8 @@
</span><span class="cx">         DirectoryService.recordType_users,
</span><span class="cx">         DirectoryService.recordType_groups,
</span><span class="cx">         DirectoryService.recordType_locations,
</span><del>-        DirectoryService.recordType_resources
</del><ins>+        DirectoryService.recordType_resources,
+        DirectoryService.recordType_addresses,
</ins><span class="cx">     ))
</span><span class="cx"> 
</span><span class="cx">     users = {
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectorytestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/util.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/util.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/test/util.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -44,6 +44,10 @@
</span><span class="cx">     # Subclass should init this to a dict of resourcenames keys and dict values.
</span><span class="cx">     resources = {}
</span><span class="cx"> 
</span><ins>+    # Subclass should init this to a dict of addressname keys and dict values.
+    addresses = {}
+
+
</ins><span class="cx">     # Subclass should init this to an IDirectoryService implementation class.
</span><span class="cx">     def service(self):
</span><span class="cx">         &quot;&quot;&quot;
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryxmlaccountsparserpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlaccountsparser.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlaccountsparser.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlaccountsparser.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx"> ELEMENT_GROUP             = &quot;group&quot;
</span><span class="cx"> ELEMENT_LOCATION          = &quot;location&quot;
</span><span class="cx"> ELEMENT_RESOURCE          = &quot;resource&quot;
</span><ins>+ELEMENT_ADDRESS           = &quot;address&quot;
</ins><span class="cx"> 
</span><span class="cx"> ELEMENT_SHORTNAME         = &quot;uid&quot;
</span><span class="cx"> ELEMENT_GUID              = &quot;guid&quot;
</span><span class="lines">@@ -65,6 +66,7 @@
</span><span class="cx">     ELEMENT_GROUP    : DirectoryService.recordType_groups,
</span><span class="cx">     ELEMENT_LOCATION : DirectoryService.recordType_locations,
</span><span class="cx">     ELEMENT_RESOURCE : DirectoryService.recordType_resources,
</span><ins>+    ELEMENT_ADDRESS  : DirectoryService.recordType_addresses,
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> class XMLAccountsParser(object):
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavdirectoryxmlfilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlfile.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlfile.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/directory/xmlfile.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -71,6 +71,7 @@
</span><span class="cx">                 self.recordType_groups,
</span><span class="cx">                 self.recordType_locations,
</span><span class="cx">                 self.recordType_resources,
</span><ins>+                self.recordType_addresses,
</ins><span class="cx">             ),
</span><span class="cx">             'realmName' : '/Search',
</span><span class="cx">             'statSeconds' : 15,
</span><span class="lines">@@ -373,6 +374,7 @@
</span><span class="cx">             'groups'    : 'group',
</span><span class="cx">             'locations' : 'location',
</span><span class="cx">             'resources' : 'resource',
</span><ins>+            'addresses' : 'address',
</ins><span class="cx">         }
</span><span class="cx">         xmlType = xmlTypes[principal.recordType]
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/stdconfig.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/stdconfig.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/twistedcaldav/stdconfig.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> from twistedcaldav.util import getPasswordFromKeychain
</span><span class="cx"> from twistedcaldav.util import KeychainAccessError, KeychainPasswordNotFound
</span><span class="cx"> from twistedcaldav.util import computeProcessCount
</span><ins>+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
</ins><span class="cx"> 
</span><span class="cx"> from calendarserver.push.util import getAPNTopicFromCertificate
</span><span class="cx"> 
</span><span class="lines">@@ -166,7 +167,7 @@
</span><span class="cx"> DEFAULT_RESOURCE_PARAMS = {
</span><span class="cx">     &quot;twistedcaldav.directory.xmlfile.XMLDirectoryService&quot;: {
</span><span class="cx">         &quot;xmlFile&quot;: &quot;resources.xml&quot;,
</span><del>-        &quot;recordTypes&quot; : (&quot;locations&quot;, &quot;resources&quot;),
</del><ins>+        &quot;recordTypes&quot; : (&quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;),
</ins><span class="cx">     },
</span><span class="cx">     &quot;twistedcaldav.directory.appleopendirectory.OpenDirectoryService&quot;: {
</span><span class="cx">         &quot;node&quot;: &quot;/Search&quot;,
</span><span class="lines">@@ -583,10 +584,13 @@
</span><span class="cx"> 
</span><span class="cx">         &quot;Calendars&quot; : {
</span><span class="cx">             &quot;Enabled&quot;         : True, # Calendar on/off switch
</span><ins>+            &quot;IgnorePerUserProperties&quot; : [
+                &quot;X-APPLE-STRUCTURED-LOCATION&quot;,
+            ],
</ins><span class="cx">         },
</span><span class="cx">         &quot;AddressBooks&quot; : {
</span><span class="cx">             &quot;Enabled&quot;         : False, # Address Books on/off switch
</span><del>-        }
</del><ins>+        },
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     &quot;RestrictCalendarsToOneComponentType&quot; : True, # Only allow calendars to be created with a single component type
</span><span class="lines">@@ -1516,7 +1520,16 @@
</span><span class="cx">                         (direction,))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def _updateSharing(configDict, reloading=False):
+    #
+    # Sharing
+    #
</ins><span class="cx"> 
</span><ins>+    # Transfer configured non-per-user property names to PerUserDataFilter
+    for propertyName in configDict.Sharing.Calendars.IgnorePerUserProperties:
+        PerUserDataFilter.IGNORE_X_PROPERTIES.append(propertyName)
+
+
</ins><span class="cx"> def _updateServers(configDict, reloading=False):
</span><span class="cx">     from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
</span><span class="cx">     if configDict.Servers.Enabled:
</span><span class="lines">@@ -1594,6 +1607,7 @@
</span><span class="cx">     _updateLogLevels,
</span><span class="cx">     _updateNotifications,
</span><span class="cx">     _updateScheduling,
</span><ins>+    _updateSharing,
</ins><span class="cx">     _updateServers,
</span><span class="cx">     _updateCompliance,
</span><span class="cx">     )
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/sql.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/sql.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/sql.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -1938,6 +1938,43 @@
</span><span class="cx">             self._componentChanged = True
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def addStructuredLocation(self, component):
+        &quot;&quot;&quot;
+        Scan the component for ROOM attendees; if any are associated with an
+        address record which has street address and geo coordinates, add an
+        X-APPLE-STRUCTURED-LOCATION property and update the LOCATION property
+        to contain the name and street address.
+        &quot;&quot;&quot;
+        for sub in component.subcomponents():
+            for attendee in sub.getAllAttendeeProperties():
+                if attendee.parameterValue(&quot;CUTYPE&quot;) == &quot;ROOM&quot;:
+                    value = attendee.value()
+                    if value.startswith(&quot;urn:uuid:&quot;):
+                        guid = value[9:]
+                        loc = self.directoryService().recordWithGUID(guid)
+                        if loc is not None:
+                            guid = loc.extras.get(&quot;associatedAddress&quot;,
+                                None)
+                            if guid is not None:
+                                addr = self.directoryService().recordWithGUID(guid)
+                                if addr is not None:
+                                    street = addr.extras.get(&quot;streetAddress&quot;, &quot;&quot;)
+                                    geo = addr.extras.get(&quot;geo&quot;, &quot;&quot;)
+                                    if street and geo:
+                                        title = attendee.parameterValue(&quot;CN&quot;)
+                                        params = {
+                                            &quot;X-ADDRESS&quot; : street,
+                                            &quot;X-APPLE-RADIUS&quot; : &quot;71&quot;,
+                                            &quot;X-TITLE&quot; : title,
+                                        }
+                                        structured = Property(&quot;X-APPLE-STRUCTURED-LOCATION&quot;,
+                                            &quot;geo:%s&quot; % (geo,), params=params,
+                                            valuetype=PyCalendarValue.VALUETYPE_URI)
+                                        sub.replaceProperty(structured)
+                                        sub.replaceProperty(Property(&quot;LOCATION&quot;,
+                                            &quot;%s\n%s&quot; % (title, street)))
+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def doImplicitScheduling(self, component, inserting, internal_state, split_details=None):
</span><span class="cx"> 
</span><span class="lines">@@ -2150,6 +2187,9 @@
</span><span class="cx">             # Default/duplicate alarms
</span><span class="cx">             self.processAlarms(component, inserting)
</span><span class="cx"> 
</span><ins>+            # Process structured location
+            self.addStructuredLocation(component)
+
</ins><span class="cx">             # Do scheduling
</span><span class="cx">             implicit_result = (yield self.doImplicitScheduling(component, inserting, internal_state))
</span><span class="cx">             if isinstance(implicit_result, int):
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtxdavcaldavdatastoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/test_sql.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/test_sql.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/test_sql.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -2134,7 +2134,82 @@
</span><span class="cx">         self.assertEqual(len(self.flushLoggedErrors(InvalidOverriddenInstanceError)), 1)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def test_setComponent_structuredLocation(self):
+        &quot;&quot;&quot;
+        Verify ROOM attendees who have street address and geo information
+        within the directory will get X-APPLE-STRUCTURED-LOCATION properties
+        added, as well as updated LOCATION properties.
+        &quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+        data = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//Mac OS X 10.9.1//EN
+BEGIN:VEVENT
+UID:561F5DBB-3F38-4B3A-986F-DD05CBAF554F
+DTSTART;TZID=America/Los_Angeles:20131211T164500
+DTEND;TZID=America/Los_Angeles:20131211T174500
+ATTENDEE;CN=Conference Room One;CUTYPE=ROOM;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
+ T;SCHEDULE-STATUS=2.0:urn:uuid:room1
+ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01@example.com;PARTSTAT=AC
+ CEPTED:urn:uuid:user01
+CREATED:20131211T221854Z
+DTSTAMP:20131211T230632Z
+ORGANIZER;CN=User 01;EMAIL=user01@example.com:urn:uuid:user01
+RRULE:FREQ=DAILY;COUNT=5
+SEQUENCE:8
+SUMMARY:locations
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:561F5DBB-3F38-4B3A-986F-DD05CBAF554F
+RECURRENCE-ID;TZID=America/Los_Angeles:20131214T164500
+DTSTART;TZID=America/Los_Angeles:20131214T160000
+DTEND;TZID=America/Los_Angeles:20131214T170000
+ATTENDEE;CN=Conference Room Two;CUTYPE=ROOM;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
+ T;SCHEDULE-STATUS=2.0:urn:uuid:room2
+ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01@example.com;PARTSTAT=AC
+ CEPTED:urn:uuid:user01
+CREATED:20131211T221854Z
+DTSTAMP:20131211T230632Z
+ORGANIZER;CN=User 01;EMAIL=user01@example.com:urn:uuid:user01
+SEQUENCE:8
+SUMMARY:locations
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+
+        calendar = yield self.calendarUnderTest(name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        yield calendar.createCalendarObjectWithName(&quot;structured.ics&quot;,
+            Component.fromString(data))
+        cobj = yield self.calendarObjectUnderTest(name=&quot;structured.ics&quot;,
+            calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        comp = yield cobj.component()
+        components = list(comp.subcomponents())
+
+        # Check first component
+        locProp = components[0].getProperty(&quot;LOCATION&quot;)
+        self.assertEquals(locProp.value(),
+            &quot;Conference Room One\n1 Infinite Loop, Cupertino, CA 95014&quot;)
+        structProp = components[0].getProperty(&quot;X-APPLE-STRUCTURED-LOCATION&quot;)
+        self.assertEquals(structProp.value(),
+            &quot;geo:37.331741,-122.030333&quot;)
+
+        # Check second component
+        locProp = components[1].getProperty(&quot;LOCATION&quot;)
+        self.assertEquals(locProp.value(),
+            &quot;Conference Room Two\n2 Infinite Loop, Cupertino, CA 95014&quot;)
+        structProp = components[1].getProperty(&quot;X-APPLE-STRUCTURED-LOCATION&quot;)
+        self.assertEquals(structProp.value(),
+            &quot;geo:37.332633,-122.030502&quot;)
+
+        yield self.commit()
+
+
+
+
</ins><span class="cx"> class CalendarObjectSplitting(CommonCommonTests, unittest.TestCase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     CalendarObject splitting tests
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtxdavcaldavdatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/util.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/util.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/caldav/datastore/test/util.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -63,9 +63,11 @@
</span><span class="cx">         cutype=&quot;INDIVIDUAL&quot;,
</span><span class="cx">         locallyHosted=True,
</span><span class="cx">         thisServer=True,
</span><ins>+        extras={},
</ins><span class="cx">     ):
</span><span class="cx"> 
</span><del>-        super(TestCalendarStoreDirectoryRecord, self).__init__(uid, shortNames, fullName)
</del><ins>+        super(TestCalendarStoreDirectoryRecord, self).__init__(uid, shortNames,
+            fullName, extras=extras)
</ins><span class="cx">         self.uid = uid
</span><span class="cx">         self.shortNames = shortNames
</span><span class="cx">         self.fullName = fullName
</span><span class="lines">@@ -164,6 +166,36 @@
</span><span class="cx">     for uid in homes:
</span><span class="cx">         directory.addRecord(buildDirectoryRecord(uid))
</span><span class="cx"> 
</span><ins>+    # Structured Locations
+    directory.addRecord(TestCalendarStoreDirectoryRecord(
+        &quot;il1&quot;, (&quot;il1&quot;,), &quot;1 Infinite Loop&quot;, [],
+        extras={
+            &quot;geo&quot; : &quot;37.331741,-122.030333&quot;,
+            &quot;streetAddress&quot; : &quot;1 Infinite Loop, Cupertino, CA 95014&quot;,
+        }
+    ))
+    directory.addRecord(TestCalendarStoreDirectoryRecord(
+        &quot;il2&quot;, (&quot;il2&quot;,), &quot;2 Infinite Loop&quot;, [],
+        extras={
+            &quot;geo&quot; : &quot;37.332633,-122.030502&quot;,
+            &quot;streetAddress&quot; : &quot;2 Infinite Loop, Cupertino, CA 95014&quot;,
+        }
+    ))
+    directory.addRecord(TestCalendarStoreDirectoryRecord(
+        &quot;room1&quot;, (&quot;room1&quot;,), &quot;Conference Room One&quot;,
+        frozenset((&quot;urn:uuid:room1&quot;,)),
+        extras={
+            &quot;associatedAddress&quot; : &quot;il1&quot;,
+        }
+    ))
+    directory.addRecord(TestCalendarStoreDirectoryRecord(
+        &quot;room2&quot;, (&quot;room2&quot;,), &quot;Conference Room Two&quot;,
+        frozenset((&quot;urn:uuid:room2&quot;,)),
+        extras={
+            &quot;associatedAddress&quot; : &quot;il2&quot;,
+        }
+    ))
+
</ins><span class="cx">     return directory
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtxdavcommondatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/datastore/test/util.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/datastore/test/util.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/datastore/test/util.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -111,6 +111,11 @@
</span><span class="cx">     def recordWithUID(self, uid):
</span><span class="cx">         return self.records.get(uid)
</span><span class="cx"> 
</span><ins>+    def recordWithGUID(self, guid):
+        for record in self.records.itervalues():
+            if record.guid == guid:
+                return record
+        return None
</ins><span class="cx"> 
</span><span class="cx">     def addRecord(self, record):
</span><span class="cx">         self.records[record.uid] = record
</span><span class="lines">@@ -121,11 +126,13 @@
</span><span class="cx"> 
</span><span class="cx">     implements(IStoreDirectoryRecord)
</span><span class="cx"> 
</span><del>-    def __init__(self, uid, shortNames, fullName):
</del><ins>+    def __init__(self, uid, shortNames, fullName, extras={}):
</ins><span class="cx">         self.uid = uid
</span><ins>+        self.guid = uid
</ins><span class="cx">         self.shortNames = shortNames
</span><span class="cx">         self.fullName = fullName
</span><span class="cx">         self.displayName = self.fullName if self.fullName else self.shortNames[0]
</span><ins>+        self.extras = extras
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer52devtxdavcommonidirectoryservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/idirectoryservice.py (12242 => 12243)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/idirectoryservice.py        2014-01-06 21:46:00 UTC (rev 12242)
+++ CalendarServer/branches/release/CalendarServer-5.2-dev/txdav/common/idirectoryservice.py        2014-01-06 22:03:14 UTC (rev 12243)
</span><span class="lines">@@ -39,8 +39,15 @@
</span><span class="cx">         @rtype: L{IStoreDirectoryRecord}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    def recordWithGUID(guid): #@NoSelf
+        &quot;&quot;&quot;
+        Return the record for the specified store guid.
</ins><span class="cx"> 
</span><ins>+        @return: the record.
+        @rtype: L{IStoreDirectoryRecord}
+        &quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class IStoreDirectoryRecord(Interface):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Directory record object
</span></span></pre>
</div>
</div>

</body>
</html>