<!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>[13081] CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory</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/13081">13081</a></dd>
<dt>Author</dt> <dd>wsanchez@apple.com</dd>
<dt>Date</dt> <dd>2014-03-31 19:02:50 -0700 (Mon, 31 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Obsolete; no imports</pre>

<h3>Removed Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryidirectorypy">CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryxmlaccountsparserpy">CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryxmlfilepy">CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryidirectorypy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py (13080 => 13081)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py        2014-04-01 01:41:35 UTC (rev 13080)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py        2014-04-01 02:02:50 UTC (rev 13081)
</span><span class="lines">@@ -1,180 +0,0 @@
</span><del>-##
-# Copyright (c) 2006-2014 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-&quot;&quot;&quot;
-Directory service interfaces.
-&quot;&quot;&quot;
-
-__all__ = [
-    &quot;IDirectoryService&quot;,
-    &quot;IDirectoryRecord&quot;,
-]
-
-from zope.interface import Attribute, Interface
-
-class IDirectoryService(Interface):
-    &quot;&quot;&quot;
-    Directory Service
-    &quot;&quot;&quot;
-    realmName = Attribute(&quot;The name of the authentication realm this service represents.&quot;)
-    guid = Attribute(&quot;A GUID for this service.&quot;)
-
-    def recordTypes(): #@NoSelf
-        &quot;&quot;&quot;
-        @return: a sequence of strings denoting the record types that
-            are kept in the directory.  For example: C{[&quot;users&quot;,
-            &quot;groups&quot;, &quot;resources&quot;]}.
-        &quot;&quot;&quot;
-
-    def listRecords(recordType): #@NoSelf
-        &quot;&quot;&quot;
-        @param type: the type of records to retrieve.
-        @return: an iterable of records of the given type.
-        &quot;&quot;&quot;
-
-    def recordWithShortName(recordType, shortName): #@NoSelf
-        &quot;&quot;&quot;
-        @param recordType: the type of the record to look up.
-        @param shortName: the short name of the record to look up.
-        @return: an L{IDirectoryRecord} with the given short name, or
-            C{None} if no such record exists.
-        &quot;&quot;&quot;
-
-    def recordWithUID(uid): #@NoSelf
-        &quot;&quot;&quot;
-        @param uid: the UID of the record to look up.
-        @return: an L{IDirectoryRecord} with the given UID, or C{None}
-            if no such record exists.
-        &quot;&quot;&quot;
-
-    def recordWithGUID(guid): #@NoSelf
-        &quot;&quot;&quot;
-        @param guid: the GUID of the record to look up.
-        @return: an L{IDirectoryRecord} with the given GUID, or
-            C{None} if no such record exists.
-        &quot;&quot;&quot;
-
-    def recordWithCalendarUserAddress(address): #@NoSelf
-        &quot;&quot;&quot;
-        @param address: the calendar user address of the record to look up.
-        @type address: C{str}
-
-        @return: an L{IDirectoryRecord} with the given calendar user
-            address, or C{None} if no such record is found.  Note that
-            some directory services may not be able to locate records
-            by calendar user address, or may return partial results.
-            Note also that the calendar server may add to the list of
-            valid calendar user addresses for a user, and the
-            directory service may not be aware of these addresses.
-        &quot;&quot;&quot;
-
-    def recordWithCachedGroupsAlias(recordType, alias): #@NoSelf
-        &quot;&quot;&quot;
-        @param recordType: the type of the record to look up.
-        @param alias: the cached-groups alias of the record to look up.
-        @type alias: C{str}
-
-        @return: a deferred L{IDirectoryRecord} with the given cached-groups
-            alias, or C{None} if no such record is found.
-        &quot;&quot;&quot;
-
-    def recordsMatchingFields(fields): #@NoSelf
-        &quot;&quot;&quot;
-        @return: a deferred sequence of L{IDirectoryRecord}s which
-            match the given fields.
-        &quot;&quot;&quot;
-
-    def recordsMatchingTokens(tokens, context=None): #@NoSelf
-        &quot;&quot;&quot;
-        @param tokens: The tokens to search on
-        @type tokens: C{list} of C{str} (utf-8 bytes)
-
-        @param context: An indication of what the end user is searching for;
-            &quot;attendee&quot;, &quot;location&quot;, or None
-        @type context: C{str}
-
-        @return: a deferred sequence of L{IDirectoryRecord}s which match the
-            given tokens and optional context.
-
-            Each token is searched for within each record's full name and email
-            address; if each token is found within a record that record is
-            returned in the results.
-
-            If context is None, all record types are considered.  If context is
-            &quot;location&quot;, only locations are considered.  If context is
-            &quot;attendee&quot;, only users, groups, and resources are considered.
-        &quot;&quot;&quot;
-
-    def setRealm(realmName): #@NoSelf
-        &quot;&quot;&quot;
-        Set a new realm name for this (and nested services if any)
-
-        @param realmName: the realm name this service should use.
-        &quot;&quot;&quot;
-
-
-
-class IDirectoryRecord(Interface):
-    &quot;&quot;&quot;
-    Directory Record
-    &quot;&quot;&quot;
-    service = Attribute(&quot;The L{IDirectoryService} this record exists in.&quot;)
-    recordType = Attribute(&quot;The type of this record.&quot;)
-    guid = Attribute(&quot;The GUID of this record.&quot;)
-    uid = Attribute(&quot;The UID of this record.&quot;)
-    enabled = Attribute(&quot;Determines whether this record should allow a principal to be created.&quot;)
-    serverID = Attribute(&quot;Identifies the server that actually hosts data for the record.&quot;)
-    shortNames = Attribute(&quot;The names for this record.&quot;)
-    authIDs = Attribute(&quot;Alternative security identities for this record.&quot;)
-    fullName = Attribute(&quot;The full name of this record.&quot;)
-    firstName = Attribute(&quot;The first name of this record.&quot;)
-    lastName = Attribute(&quot;The last name of this record.&quot;)
-    emailAddresses = Attribute(&quot;The email addresses of this record.&quot;)
-    enabledForCalendaring = Attribute(&quot;Determines whether this record creates a principal with a calendar home.&quot;)
-    enabledForAddressBooks = Attribute(&quot;Determines whether this record creates a principal with an address book home.&quot;)
-    calendarUserAddresses = Attribute(
-        &quot;&quot;&quot;
-        An iterable of C{str}s representing calendar user addresses for this
-        L{IDirectoryRecord}.
-
-        A &quot;calendar user address&quot;, as defined by U{RFC 2445 section
-        4.3.3&lt;http://xml.resource.org/public/rfc/html/rfc2445.html#anchor50&gt;},
-        is simply an URI which identifies this user.  Some of these URIs are
-        relative references to URLs from the root of the calendar server's HTTP
-        hierarchy.
-        &quot;&quot;&quot;
-    )
-
-    def members(): #@NoSelf
-        &quot;&quot;&quot;
-        @return: an iterable of L{IDirectoryRecord}s for the members of this
-            (group) record.
-        &quot;&quot;&quot;
-
-    def groups(): #@NoSelf
-        &quot;&quot;&quot;
-        @return: an iterable of L{IDirectoryRecord}s for the groups this
-            record is a member of.
-        &quot;&quot;&quot;
-
-    def verifyCredentials(credentials): #@NoSelf
-        &quot;&quot;&quot;
-        Verify that the given credentials can authenticate the principal
-        represented by this record.
-        @param credentials: the credentials to authenticate with.
-        @return: C{True} if the given credentials match this record,
-            C{False} otherwise.
-        &quot;&quot;&quot;
</del></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryxmlaccountsparserpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py (13080 => 13081)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py        2014-04-01 01:41:35 UTC (rev 13080)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py        2014-04-01 02:02:50 UTC (rev 13081)
</span><span class="lines">@@ -1,283 +0,0 @@
</span><del>-##
-# Copyright (c) 2006-2014 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-
-&quot;&quot;&quot;
-XML based user/group/resource configuration file handling.
-&quot;&quot;&quot;
-
-__all__ = [
-    &quot;XMLAccountsParser&quot;,
-]
-
-from twext.python.filepath import CachingFilePath as FilePath
-
-from twext.python.log import Logger
-
-from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.directory.util import normalizeUUID
-from twistedcaldav.xmlutil import readXML
-
-import re
-import hashlib
-
-log = Logger()
-
-ELEMENT_ACCOUNTS = &quot;accounts&quot;
-ELEMENT_USER = &quot;user&quot;
-ELEMENT_GROUP = &quot;group&quot;
-ELEMENT_LOCATION = &quot;location&quot;
-ELEMENT_RESOURCE = &quot;resource&quot;
-ELEMENT_ADDRESS = &quot;address&quot;
-
-ELEMENT_SHORTNAME = &quot;uid&quot;
-ELEMENT_GUID = &quot;guid&quot;
-ELEMENT_PASSWORD = &quot;password&quot;
-ELEMENT_NAME = &quot;name&quot;
-ELEMENT_FIRST_NAME = &quot;first-name&quot;
-ELEMENT_LAST_NAME = &quot;last-name&quot;
-ELEMENT_EMAIL_ADDRESS = &quot;email-address&quot;
-ELEMENT_MEMBERS = &quot;members&quot;
-ELEMENT_MEMBER = &quot;member&quot;
-ELEMENT_EXTRAS = &quot;extras&quot;
-
-ATTRIBUTE_REALM = &quot;realm&quot;
-ATTRIBUTE_REPEAT = &quot;repeat&quot;
-ATTRIBUTE_RECORDTYPE = &quot;type&quot;
-
-VALUE_TRUE = &quot;true&quot;
-VALUE_FALSE = &quot;false&quot;
-
-RECORD_TYPES = {
-    ELEMENT_USER     : DirectoryService.recordType_users,
-    ELEMENT_GROUP    : DirectoryService.recordType_groups,
-    ELEMENT_LOCATION : DirectoryService.recordType_locations,
-    ELEMENT_RESOURCE : DirectoryService.recordType_resources,
-    ELEMENT_ADDRESS  : DirectoryService.recordType_addresses,
-}
-
-class XMLAccountsParser(object):
-    &quot;&quot;&quot;
-    XML account configuration file parser.
-    &quot;&quot;&quot;
-    def __repr__(self):
-        return &quot;&lt;%s %r&gt;&quot; % (self.__class__.__name__, self.xmlFile)
-
-
-    def __init__(self, xmlFile, externalUpdate=True):
-
-        if type(xmlFile) is str:
-            xmlFile = FilePath(xmlFile)
-
-        self.xmlFile = xmlFile
-        self.realm = None
-        self.items = {}
-
-        for recordType in RECORD_TYPES.values():
-            self.items[recordType] = {}
-
-        # Read in XML
-        try:
-            _ignore_tree, accounts_node = readXML(self.xmlFile.path, ELEMENT_ACCOUNTS)
-        except ValueError, e:
-            raise RuntimeError(&quot;XML parse error for '%s' because: %s&quot; % (self.xmlFile, e,))
-        self._parseXML(accounts_node)
-
-
-    def _parseXML(self, node):
-        &quot;&quot;&quot;
-        Parse the XML root node from the accounts configuration document.
-        @param node: the L{Node} to parse.
-        &quot;&quot;&quot;
-        self.realm = node.get(ATTRIBUTE_REALM, &quot;&quot;).encode(&quot;utf-8&quot;)
-
-        def updateMembership(group):
-            # Update group membership
-            for recordType, shortName in group.members:
-                item = self.items[recordType].get(shortName)
-                if item is not None:
-                    item.groups.add(group.shortNames[0])
-
-        for child in node:
-            try:
-                recordType = RECORD_TYPES[child.tag]
-            except KeyError:
-                raise RuntimeError(&quot;Unknown account type: %s&quot; % (child.tag,))
-
-            repeat = int(child.get(ATTRIBUTE_REPEAT, 0))
-
-            principal = XMLAccountRecord(recordType)
-            principal.parseXML(child)
-            if repeat &gt; 0:
-                for i in xrange(1, repeat + 1):
-                    newprincipal = principal.repeat(i)
-                    self.items[recordType][newprincipal.shortNames[0]] = newprincipal
-            else:
-                self.items[recordType][principal.shortNames[0]] = principal
-
-        # Do reverse membership mapping only after all records have been read in
-        for records in self.items.itervalues():
-            for principal in records.itervalues():
-                updateMembership(principal)
-
-
-
-class XMLAccountRecord (object):
-    &quot;&quot;&quot;
-    Contains provision information for one user.
-    &quot;&quot;&quot;
-    def __init__(self, recordType):
-        &quot;&quot;&quot;
-        @param recordType: record type for directory entry.
-        &quot;&quot;&quot;
-        self.recordType = recordType
-        self.shortNames = []
-        self.guid = None
-        self.password = None
-        self.fullName = None
-        self.firstName = None
-        self.lastName = None
-        self.emailAddresses = set()
-        self.members = set()
-        self.groups = set()
-        self.extras = {}
-
-
-    def repeat(self, ctr):
-        &quot;&quot;&quot;
-        Create another object like this but with all text items having % substitution
-        done on them with the numeric value provided.
-        @param ctr: an integer to substitute into text.
-        &quot;&quot;&quot;
-
-        # Regular expression which matches ~ followed by a number
-        matchNumber = re.compile(r&quot;(~\d+)&quot;)
-
-        def expand(text, ctr):
-            &quot;&quot;&quot;
-            Returns a string where ~&lt;number&gt; is replaced by the first &lt;number&gt;
-            characters from the md5 hexdigest of str(ctr), e.g.::
-
-                expand(&quot;~9 foo&quot;, 1)
-
-            returns::
-
-                &quot;c4ca4238a foo&quot;
-
-            ...since &quot;c4ca4238a&quot; is the first 9 characters of::
-
-                hashlib.md5(str(1)).hexdigest()
-
-            If &lt;number&gt; is larger than 32, the hash will repeat as needed.
-            &quot;&quot;&quot;
-            if text:
-                m = matchNumber.search(text)
-                if m:
-                    length = int(m.group(0)[1:])
-                    hash = hashlib.md5(str(ctr)).hexdigest()
-                    string = (hash * ((length / 32) + 1))[:-(32 - (length % 32))]
-                    return text.replace(m.group(0), string)
-            return text
-
-        shortNames = []
-        for shortName in self.shortNames:
-            if shortName.find(&quot;%&quot;) != -1:
-                shortNames.append(shortName % ctr)
-            else:
-                shortNames.append(shortName)
-        if self.guid and self.guid.find(&quot;%&quot;) != -1:
-            guid = self.guid % ctr
-        else:
-            guid = self.guid
-        if self.password.find(&quot;%&quot;) != -1:
-            password = self.password % ctr
-        else:
-            password = self.password
-        if self.fullName.find(&quot;%&quot;) != -1:
-            fullName = self.fullName % ctr
-        else:
-            fullName = self.fullName
-        fullName = expand(fullName, ctr)
-        if self.firstName and self.firstName.find(&quot;%&quot;) != -1:
-            firstName = self.firstName % ctr
-        else:
-            firstName = self.firstName
-        firstName = expand(firstName, ctr)
-        if self.lastName and self.lastName.find(&quot;%&quot;) != -1:
-            lastName = self.lastName % ctr
-        else:
-            lastName = self.lastName
-        lastName = expand(lastName, ctr)
-        emailAddresses = set()
-        for emailAddr in self.emailAddresses:
-            emailAddr = expand(emailAddr, ctr)
-            if emailAddr.find(&quot;%&quot;) != -1:
-                emailAddresses.add(emailAddr % ctr)
-            else:
-                emailAddresses.add(emailAddr)
-
-        result = XMLAccountRecord(self.recordType)
-        result.shortNames = shortNames
-        result.guid = normalizeUUID(guid)
-        result.password = password
-        result.fullName = fullName
-        result.firstName = firstName
-        result.lastName = lastName
-        result.emailAddresses = emailAddresses
-        result.members = self.members
-        result.extras = self.extras
-        return result
-
-
-    def parseXML(self, node):
-        for child in node:
-            if child.tag == ELEMENT_SHORTNAME:
-                self.shortNames.append(child.text.encode(&quot;utf-8&quot;))
-            elif child.tag == ELEMENT_GUID:
-                self.guid = normalizeUUID(child.text.encode(&quot;utf-8&quot;))
-                if len(self.guid) &lt; 4:
-                    self.guid += &quot;?&quot; * (4 - len(self.guid))
-            elif child.tag == ELEMENT_PASSWORD:
-                self.password = child.text.encode(&quot;utf-8&quot;)
-            elif child.tag == ELEMENT_NAME:
-                self.fullName = child.text.encode(&quot;utf-8&quot;)
-            elif child.tag == ELEMENT_FIRST_NAME:
-                self.firstName = child.text.encode(&quot;utf-8&quot;)
-            elif child.tag == ELEMENT_LAST_NAME:
-                self.lastName = child.text.encode(&quot;utf-8&quot;)
-            elif child.tag == ELEMENT_EMAIL_ADDRESS:
-                self.emailAddresses.add(child.text.encode(&quot;utf-8&quot;).lower())
-            elif child.tag == ELEMENT_MEMBERS:
-                self._parseMembers(child, self.members)
-            elif child.tag == ELEMENT_EXTRAS:
-                self._parseExtras(child, self.extras)
-            else:
-                raise RuntimeError(&quot;Unknown account attribute: %s&quot; % (child.tag,))
-
-        if not self.shortNames:
-            self.shortNames.append(self.guid)
-
-
-    def _parseMembers(self, node, addto):
-        for child in node:
-            if child.tag == ELEMENT_MEMBER:
-                recordType = child.get(ATTRIBUTE_RECORDTYPE, DirectoryService.recordType_users)
-                addto.add((recordType, child.text.encode(&quot;utf-8&quot;)))
-
-
-    def _parseExtras(self, node, addto):
-        for child in node:
-            addto[child.tag] = child.text.encode(&quot;utf-8&quot;)
</del></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryxmlfilepy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py (13080 => 13081)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py        2014-04-01 01:41:35 UTC (rev 13080)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py        2014-04-01 02:02:50 UTC (rev 13081)
</span><span class="lines">@@ -1,633 +0,0 @@
</span><del>-##
-# Copyright (c) 2006-2014 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-&quot;&quot;&quot;
-XML based user/group/resource directory service implementation.
-&quot;&quot;&quot;
-
-__all__ = [
-    &quot;XMLDirectoryService&quot;,
-]
-
-from time import time
-import grp
-import os
-import pwd
-import types
-
-from twisted.cred.credentials import UsernamePassword
-from txweb2.auth.digest import DigestedCredentials
-from twext.python.filepath import CachingFilePath as FilePath
-from twistedcaldav.config import config
-from twisted.internet.defer import succeed
-
-from twistedcaldav.config import fullServerPath
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError
-from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser, XMLAccountRecord
-from twistedcaldav.directory.util import normalizeUUID
-from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
-from twistedcaldav.xmlutil import addSubElement, createElement, elementToXML
-from uuid import uuid4
-
-
-class XMLDirectoryService(DirectoryService):
-    &quot;&quot;&quot;
-    XML based implementation of L{IDirectoryService}.
-    &quot;&quot;&quot;
-    baseGUID = &quot;9CA8DEC5-5A17-43A9-84A8-BE77C1FB9172&quot;
-
-    realmName = None
-
-    INDEX_TYPE_GUID = &quot;guid&quot;
-    INDEX_TYPE_SHORTNAME = &quot;shortname&quot;
-    INDEX_TYPE_CUA = &quot;cua&quot;
-    INDEX_TYPE_AUTHID = &quot;authid&quot;
-
-
-    def __repr__(self):
-        return &quot;&lt;%s %r: %r&gt;&quot; % (self.__class__.__name__, self.realmName, self.xmlFile)
-
-
-    def __init__(self, params, alwaysStat=False):
-
-        defaults = {
-            'xmlFile' : None,
-            'directoryBackedAddressBook': None,
-            'recordTypes' : (
-                self.recordType_users,
-                self.recordType_groups,
-                self.recordType_locations,
-                self.recordType_resources,
-                self.recordType_addresses,
-            ),
-            'realmName' : '/Search',
-            'statSeconds' : 15,
-            'augmentService' : None,
-            'groupMembershipCache' : None,
-        }
-        ignored = None
-        params = self.getParams(params, defaults, ignored)
-
-        self._recordTypes = params['recordTypes']
-        self.realmName = params['realmName']
-        self.statSeconds = params['statSeconds']
-        self.augmentService = params['augmentService']
-        self.groupMembershipCache = params['groupMembershipCache']
-
-        super(XMLDirectoryService, self).__init__()
-
-        xmlFile = fullServerPath(config.DataRoot, params.get(&quot;xmlFile&quot;))
-        if type(xmlFile) is str:
-            xmlFile = FilePath(xmlFile)
-
-        if not xmlFile.exists():
-            xmlFile.setContent(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
-
-&lt;accounts realm=&quot;%s&quot;&gt;
-&lt;/accounts&gt;
-&quot;&quot;&quot; % (self.realmName,))
-
-        uid = -1
-        if config.UserName:
-            try:
-                uid = pwd.getpwnam(config.UserName).pw_uid
-            except KeyError:
-                self.log.error(&quot;User not found: %s&quot; % (config.UserName,))
-
-        gid = -1
-        if config.GroupName:
-            try:
-                gid = grp.getgrnam(config.GroupName).gr_gid
-            except KeyError:
-                self.log.error(&quot;Group not found: %s&quot; % (config.GroupName,))
-
-        if uid != -1 and gid != -1:
-            os.chown(xmlFile.path, uid, gid)
-
-        self.xmlFile = xmlFile
-        self._fileInfo = None
-        self._lastCheck = 0
-        self._alwaysStat = alwaysStat
-        self.directoryBackedAddressBook = params.get('directoryBackedAddressBook')
-        self._initIndexes()
-        self._accounts()
-
-
-    def _initIndexes(self):
-        &quot;&quot;&quot;
-        Create empty indexes
-        &quot;&quot;&quot;
-        self.records = {}
-        self.recordIndexes = {}
-
-        for recordType in self.recordTypes():
-            self.records[recordType] = set()
-            self.recordIndexes[recordType] = {
-                self.INDEX_TYPE_GUID     : {},
-                self.INDEX_TYPE_SHORTNAME: {},
-                self.INDEX_TYPE_CUA      : {},
-                self.INDEX_TYPE_AUTHID   : {},
-            }
-
-
-    def _accounts(self):
-        &quot;&quot;&quot;
-        Parses XML file, creates XMLDirectoryRecords and indexes them, and
-        because some other code in this module still works directly with
-        XMLAccountRecords as returned by XMLAccountsParser, returns a list
-        of XMLAccountRecords.
-
-        The XML file is only stat'ed at most every self.statSeconds, and is
-        only reparsed if it's been modified.
-
-        FIXME: don't return XMLAccountRecords, and have any code in this module
-        which currently does work with XMLAccountRecords, modify such code to
-        use XMLDirectoryRecords instead.
-        &quot;&quot;&quot;
-        currentTime = time()
-        if self._alwaysStat or currentTime - self._lastCheck &gt; self.statSeconds:
-            self.xmlFile.restat()
-            self._lastCheck = currentTime
-            fileInfo = (self.xmlFile.getmtime(), self.xmlFile.getsize())
-            if fileInfo != self._fileInfo:
-                self._initIndexes()
-                parser = XMLAccountsParser(self.xmlFile)
-                self._parsedAccounts = parser.items
-                self.realmName = parser.realm
-                self._fileInfo = fileInfo
-
-                for accountDict in self._parsedAccounts.itervalues():
-                    for xmlAccountRecord in accountDict.itervalues():
-                        if xmlAccountRecord.recordType not in self.recordTypes():
-                            continue
-                        record = XMLDirectoryRecord(
-                            service=self,
-                            recordType=xmlAccountRecord.recordType,
-                            shortNames=tuple(xmlAccountRecord.shortNames),
-                            xmlPrincipal=xmlAccountRecord,
-                        )
-                        if self.augmentService is not None:
-                            d = self.augmentService.getAugmentRecord(record.guid,
-                                record.recordType)
-                            d.addCallback(lambda x: record.addAugmentInformation(x))
-
-                        self._addToIndex(record)
-
-        return self._parsedAccounts
-
-
-    def _addToIndex(self, record):
-        &quot;&quot;&quot;
-        Index the record by GUID, shortName(s), authID(s) and CUA(s)
-        &quot;&quot;&quot;
-
-        self.recordIndexes[record.recordType][self.INDEX_TYPE_GUID][record.guid] = record
-        for shortName in record.shortNames:
-            self.recordIndexes[record.recordType][self.INDEX_TYPE_SHORTNAME][shortName] = record
-        for authID in record.authIDs:
-            self.recordIndexes[record.recordType][self.INDEX_TYPE_AUTHID][authID] = record
-        for cua in record.calendarUserAddresses:
-            cua = normalizeCUAddr(cua)
-            self.recordIndexes[record.recordType][self.INDEX_TYPE_CUA][cua] = record
-        self.records[record.recordType].add(record)
-
-
-    def _removeFromIndex(self, record):
-        &quot;&quot;&quot;
-        Removes a record from all indexes.  Note this is only used for unit
-        testing, to simulate a user being removed from the directory.
-        &quot;&quot;&quot;
-        del self.recordIndexes[record.recordType][self.INDEX_TYPE_GUID][record.guid]
-        for shortName in record.shortNames:
-            del self.recordIndexes[record.recordType][self.INDEX_TYPE_SHORTNAME][shortName]
-        for authID in record.authIDs:
-            del self.recordIndexes[record.recordType][self.INDEX_TYPE_AUTHID][authID]
-        for cua in record.calendarUserAddresses:
-            cua = normalizeCUAddr(cua)
-            del self.recordIndexes[record.recordType][self.INDEX_TYPE_CUA][cua]
-        if record in self.records[record.recordType]:
-            self.records[record.recordType].remove(record)
-
-
-    def _lookupInIndex(self, recordType, indexType, key):
-        &quot;&quot;&quot;
-        Look for an existing record of the given recordType with the key for
-        the given index type.  Returns None if no match.
-        &quot;&quot;&quot;
-        self._accounts()
-        return self.recordIndexes.get(recordType, {}).get(indexType, {}).get(key, None)
-
-
-    def _initCaches(self):
-        &quot;&quot;&quot;
-        Invalidates the indexes
-        &quot;&quot;&quot;
-        self._lastCheck = 0
-        self._initIndexes()
-
-
-    def _forceReload(self):
-        &quot;&quot;&quot;
-        Invalidates the indexes, re-reads the XML file and re-indexes
-        &quot;&quot;&quot;
-        self._initCaches()
-        self._fileInfo = None
-        return self._accounts()
-
-
-    def recordWithShortName(self, recordType, shortName):
-        return self._lookupInIndex(recordType, self.INDEX_TYPE_SHORTNAME, shortName)
-
-
-    def recordWithAuthID(self, authID):
-        for recordType in self.recordTypes():
-            record = self._lookupInIndex(recordType, self.INDEX_TYPE_AUTHID, authID)
-            if record is not None:
-                return record
-        return None
-
-
-    def recordWithGUID(self, guid):
-        guid = normalizeUUID(guid)
-        for recordType in self.recordTypes():
-            record = self._lookupInIndex(recordType, self.INDEX_TYPE_GUID, guid)
-            if record is not None:
-                return record
-        return None
-
-    recordWithUID = recordWithGUID
-
-    def createCache(self):
-        &quot;&quot;&quot;
-        No-op to pacify addressbook backing.
-        &quot;&quot;&quot;
-
-    def recordTypes(self):
-        return self._recordTypes
-
-
-    def listRecords(self, recordType):
-        self._accounts()
-        return self.records[recordType]
-
-
-    def recordsMatchingFields(self, fields, operand=&quot;or&quot;, recordType=None):
-        # Default, brute force method search of underlying XML data
-
-        def fieldMatches(fieldValue, value, caseless, matchType):
-            if fieldValue is None:
-                return False
-            elif type(fieldValue) in types.StringTypes:
-                fieldValue = (fieldValue,)
-
-            for testValue in fieldValue:
-                if caseless:
-                    testValue = testValue.lower()
-                    value = value.lower()
-
-                if matchType == 'starts-with':
-                    if testValue.startswith(value):
-                        return True
-                elif matchType == 'contains':
-                    try:
-                        testValue.index(value)
-                        return True
-                    except ValueError:
-                        pass
-                else: # exact
-                    if testValue == value:
-                        return True
-
-            return False
-
-        def xmlPrincipalMatches(xmlPrincipal):
-            if operand == &quot;and&quot;:
-                for fieldName, value, caseless, matchType in fields:
-                    try:
-                        fieldValue = getattr(xmlPrincipal, fieldName)
-                        if not fieldMatches(fieldValue, value, caseless, matchType):
-                            return False
-                    except AttributeError:
-                        # No property =&gt; no match
-                        return False
-                # we hit on every property
-                return True
-            else: # &quot;or&quot;
-                for fieldName, value, caseless, matchType in fields:
-                    try:
-                        fieldValue = getattr(xmlPrincipal, fieldName)
-                        if fieldMatches(fieldValue, value, caseless, matchType):
-                            return True
-                    except AttributeError:
-                        # No value
-                        pass
-                # we didn't hit any
-                return False
-
-        if recordType is None:
-            recordTypes = list(self.recordTypes())
-        else:
-            recordTypes = (recordType,)
-
-        records = []
-        for recordType in recordTypes:
-            for xmlPrincipal in self._accounts()[recordType].itervalues():
-                if xmlPrincipalMatches(xmlPrincipal):
-
-                    # Load/cache record from its GUID
-                    record = self.recordWithGUID(xmlPrincipal.guid)
-                    if record:
-                        records.append(record)
-        return succeed(records)
-
-
-    def _addElement(self, parent, principal):
-        &quot;&quot;&quot;
-        Create an XML element from principal and add it as a child of parent
-        &quot;&quot;&quot;
-
-        # TODO: derive this from xmlaccountsparser.py
-        xmlTypes = {
-            'users'     : 'user',
-            'groups'    : 'group',
-            'locations' : 'location',
-            'resources' : 'resource',
-            'addresses' : 'address',
-        }
-        xmlType = xmlTypes[principal.recordType]
-
-        element = addSubElement(parent, xmlType)
-        for value in principal.shortNames:
-            addSubElement(element, &quot;uid&quot;, text=value.decode(&quot;utf-8&quot;))
-        addSubElement(element, &quot;guid&quot;, text=principal.guid)
-        if principal.fullName is not None:
-            addSubElement(element, &quot;name&quot;, text=principal.fullName.decode(&quot;utf-8&quot;))
-        if principal.firstName is not None:
-            addSubElement(element, &quot;first-name&quot;, text=principal.firstName.decode(&quot;utf-8&quot;))
-        if principal.lastName is not None:
-            addSubElement(element, &quot;last-name&quot;, text=principal.lastName.decode(&quot;utf-8&quot;))
-        for value in principal.emailAddresses:
-            addSubElement(element, &quot;email-address&quot;, text=value.decode(&quot;utf-8&quot;))
-        if principal.extras:
-            extrasElement = addSubElement(element, &quot;extras&quot;)
-            for key, value in principal.extras.iteritems():
-                addSubElement(extrasElement, key, text=value.decode(&quot;utf-8&quot;))
-
-        return element
-
-
-    def _persistRecords(self, element):
-
-        def indent(elem, level=0):
-            i = &quot;\n&quot; + level * &quot;  &quot;
-            if len(elem):
-                if not elem.text or not elem.text.strip():
-                    elem.text = i + &quot;  &quot;
-                if not elem.tail or not elem.tail.strip():
-                    elem.tail = i
-                for elem in elem:
-                    indent(elem, level + 1)
-                if not elem.tail or not elem.tail.strip():
-                    elem.tail = i
-            else:
-                if level and (not elem.tail or not elem.tail.strip()):
-                    elem.tail = i
-
-        indent(element)
-
-        self.xmlFile.setContent(elementToXML(element))
-
-        # Fix up the file ownership because setContent doesn't maintain it
-        uid = -1
-        if config.UserName:
-            try:
-                uid = pwd.getpwnam(config.UserName).pw_uid
-            except KeyError:
-                self.log.error(&quot;User not found: %s&quot; % (config.UserName,))
-
-        gid = -1
-        if config.GroupName:
-            try:
-                gid = grp.getgrnam(config.GroupName).gr_gid
-            except KeyError:
-                self.log.error(&quot;Group not found: %s&quot; % (config.GroupName,))
-
-        if uid != -1 and gid != -1:
-            os.chown(self.xmlFile.path, uid, gid)
-
-
-    def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
-        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwargs):
-        &quot;&quot;&quot;
-        Create and persist a record using the provided information.  In this
-        XML-based implementation, the xml accounts are read in and converted
-        to elementtree elements, a new element is added for the new record,
-        and the document is serialized to disk.
-        &quot;&quot;&quot;
-        if guid is None:
-            guid = str(uuid4())
-        guid = normalizeUUID(guid)
-
-        if not shortNames:
-            shortNames = (guid,)
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        accountsElement = createElement(&quot;accounts&quot;, realm=self.realmName)
-        for recType in self.recordTypes():
-            for xmlPrincipal in accounts[recType].itervalues():
-                if xmlPrincipal.guid == guid:
-                    raise DirectoryError(&quot;Duplicate guid: %s&quot; % (guid,))
-                for shortName in shortNames:
-                    if shortName in xmlPrincipal.shortNames:
-                        raise DirectoryError(&quot;Duplicate shortName: %s&quot; %
-                            (shortName,))
-                self._addElement(accountsElement, xmlPrincipal)
-
-        xmlPrincipal = XMLAccountRecord(recordType)
-        xmlPrincipal.shortNames = shortNames
-        xmlPrincipal.guid = guid
-        xmlPrincipal.password = password
-        xmlPrincipal.fullName = fullName
-        xmlPrincipal.firstName = firstName
-        xmlPrincipal.lastName = lastName
-        xmlPrincipal.emailAddresses = emailAddresses
-        xmlPrincipal.extras = kwargs
-        self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-        return self.recordWithGUID(guid)
-
-
-    def destroyRecord(self, recordType, guid=None):
-        &quot;&quot;&quot;
-        Remove the record matching guid.  In this XML-based implementation,
-        the xml accounts are read in and those not matching the given guid are
-        converted to elementtree elements, then the document is serialized to
-        disk.
-        &quot;&quot;&quot;
-
-        guid = normalizeUUID(guid)
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        accountsElement = createElement(&quot;accounts&quot;, realm=self.realmName)
-        for recType in self.recordTypes():
-
-            for xmlPrincipal in accounts[recType].itervalues():
-                if xmlPrincipal.guid != guid:
-                    self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-
-
-    def updateRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
-        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwargs):
-        &quot;&quot;&quot;
-        Update the record matching guid.  In this XML-based implementation,
-        the xml accounts are read in and converted to elementtree elements.
-        The account matching the given guid is replaced, then the document
-        is serialized to disk.
-        &quot;&quot;&quot;
-
-        guid = normalizeUUID(guid)
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        accountsElement = createElement(&quot;accounts&quot;, realm=self.realmName)
-        for recType in self.recordTypes():
-
-            for xmlPrincipal in accounts[recType].itervalues():
-                if xmlPrincipal.guid == guid:
-                    # Replace this record
-                    xmlPrincipal.shortNames = shortNames
-                    xmlPrincipal.password = password
-                    xmlPrincipal.fullName = fullName
-                    xmlPrincipal.firstName = firstName
-                    xmlPrincipal.lastName = lastName
-                    xmlPrincipal.emailAddresses = emailAddresses
-                    xmlPrincipal.extras = kwargs
-                    self._addElement(accountsElement, xmlPrincipal)
-                else:
-                    self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-        return self.recordWithGUID(guid)
-
-
-    def createRecords(self, data):
-        &quot;&quot;&quot;
-        Create records in bulk
-        &quot;&quot;&quot;
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        knownGUIDs = {}
-        knownShortNames = {}
-
-        accountsElement = createElement(&quot;accounts&quot;, realm=self.realmName)
-        for recType in self.recordTypes():
-            for xmlPrincipal in accounts[recType].itervalues():
-                self._addElement(accountsElement, xmlPrincipal)
-                knownGUIDs[xmlPrincipal.guid] = 1
-                for shortName in xmlPrincipal.shortNames:
-                    knownShortNames[shortName] = 1
-
-        for recordType, recordData in data:
-            guid = recordData[&quot;guid&quot;]
-            if guid is None:
-                guid = str(uuid4())
-
-            shortNames = recordData[&quot;shortNames&quot;]
-            if not shortNames:
-                shortNames = (guid,)
-
-            if guid in knownGUIDs:
-                raise DirectoryError(&quot;Duplicate guid: %s&quot; % (guid,))
-
-            for shortName in shortNames:
-                if shortName in knownShortNames:
-                    raise DirectoryError(&quot;Duplicate shortName: %s&quot; %
-                        (shortName,))
-
-            xmlPrincipal = XMLAccountRecord(recordType)
-            xmlPrincipal.shortNames = shortNames
-            xmlPrincipal.guid = guid
-            xmlPrincipal.fullName = recordData[&quot;fullName&quot;]
-            self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-
-
-
-class XMLDirectoryRecord(DirectoryRecord):
-    &quot;&quot;&quot;
-    XML based implementation implementation of L{IDirectoryRecord}.
-    &quot;&quot;&quot;
-    def __init__(self, service, recordType, shortNames, xmlPrincipal):
-        super(XMLDirectoryRecord, self).__init__(
-            service=service,
-            recordType=recordType,
-            guid=xmlPrincipal.guid,
-            shortNames=shortNames,
-            fullName=xmlPrincipal.fullName,
-            firstName=xmlPrincipal.firstName,
-            lastName=xmlPrincipal.lastName,
-            emailAddresses=xmlPrincipal.emailAddresses,
-            **xmlPrincipal.extras
-        )
-
-        self.password = xmlPrincipal.password
-        self._members = xmlPrincipal.members
-        self._groups = xmlPrincipal.groups
-
-
-    def members(self):
-        for recordType, shortName in self._members:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-
-    def groups(self):
-        for shortName in self._groups:
-            yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
-
-
-    def memberGUIDs(self):
-        results = set()
-        for recordType, shortName in self._members:
-            record = self.service.recordWithShortName(recordType, shortName)
-            results.add(record.guid)
-        return results
-
-
-    def verifyCredentials(self, credentials):
-        if self.enabled:
-            if isinstance(credentials, UsernamePassword):
-                return credentials.password == self.password
-            if isinstance(credentials, DigestedCredentials):
-                return credentials.checkPassword(self.password)
-
-        return super(XMLDirectoryRecord, self).verifyCredentials(credentials)
</del></span></pre>
</div>
</div>

</body>
</html>