[CalendarServer-changes] [5080] CalendarServer/branches/users/glyph/contacts-server-merge

source_changes at macosforge.org source_changes at macosforge.org
Tue Feb 9 12:49:43 PST 2010


Revision: 5080
          http://trac.macosforge.org/projects/calendarserver/changeset/5080
Author:   glyph at apple.com
Date:     2010-02-09 12:49:43 -0800 (Tue, 09 Feb 2010)
Log Message:
-----------
Pull in changes from trunk again, resolving minor conflicts that have crept in.

Modified Paths:
--------------
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tap/caldav.py
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/principals.py
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/util.py
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/accounts-test.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-apple.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-test.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.txt
    CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/python
    CalendarServer/branches/users/glyph/contacts-server-merge/setup.py
    CalendarServer/branches/users/glyph/contacts-server-merge/support/Makefile.Apple
    CalendarServer/branches/users/glyph/contacts-server-merge/support/submit
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/aggregate.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/cachingdirectory.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/directory.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/accounts.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_aggregate.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_cachedirectory.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_opendirectory.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_proxyprincipalmembers.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/xmlfile.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/index.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/stdconfig.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/file.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/test/test_file.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/icalendarstore.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/idav.py

Added Paths:
-----------
    CalendarServer/branches/users/glyph/contacts-server-merge/bin/calendarserver_command_gateway
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/gateway.py
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/resources-test.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/doc/calendarserver_command_gateway.8
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_modify.py
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_resources.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py

Removed Paths:
-------------
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py
    CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py

Property Changed:
----------------
    CalendarServer/branches/users/glyph/contacts-server-merge/


Property changes on: CalendarServer/branches/users/glyph/contacts-server-merge
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/trunk:4971-5006
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/trunk:4971-5079

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/bin/calendarserver_command_gateway (from rev 5079, CalendarServer/trunk/bin/calendarserver_command_gateway)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/bin/calendarserver_command_gateway	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/bin/calendarserver_command_gateway	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+import sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+    if "PYTHONPATH" in globals():
+        sys.path.insert(0, PYTHONPATH)
+    else:
+        from os.path import dirname, abspath, join
+        from subprocess import Popen, PIPE
+
+        home = dirname(dirname(abspath(__file__)))
+        run = join(home, "run")
+
+        child = Popen((run, "-p"), stdout=PIPE)
+        path, stderr = child.communicate()
+
+        path = path.rstrip("\n")
+
+        if child.wait() == 0:
+            sys.path[0:0] = path.split(":")
+
+        sys.argv[1:1] = ["-f", join(home, "conf", "caldavd-dev.plist")]
+
+    from calendarserver.tools.gateway import main
+    main()

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tap/caldav.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tap/caldav.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -487,6 +487,20 @@
 
         directories.append(baseDirectory)
 
+        #
+        # Setup the Locations and Resources Service
+        #
+        if config.ResourceService.Enabled:
+            resourceClass = namedClass(config.ResourceService.type)
+
+            self.log_info("Configuring resource service of type: %s" % (resourceClass,))
+
+            resourceDirectory = resourceClass(config.ResourceService.params)
+            directories.append(resourceDirectory)
+
+        #
+        # Add sudoers directory
+        #
         sudoDirectory = None
 
         if config.SudoersFile and os.path.exists(config.SudoersFile):
@@ -1086,6 +1100,8 @@
         
                 if config.Memcached.MaxMemory is not 0:
                     memcachedArgv.extend(["-m", str(config.Memcached.MaxMemory)])
+                if config.UserName:
+                    memcachedArgv.extend(["-u", config.UserName])
         
                 memcachedArgv.extend(config.Memcached.Options)
         

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/gateway.py (from rev 5079, CalendarServer/trunk/calendarserver/tools/gateway.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/gateway.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/gateway.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,297 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+from getopt import getopt, GetoptError
+from grp import getgrnam
+from pwd import getpwnam
+import os
+import plistlib
+import sys
+import xml
+
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks
+from twisted.python.util import switchUID
+from twistedcaldav.config import config, ConfigurationError
+from twistedcaldav.directory.directory import DirectoryError
+from twisted.web2.dav import davxml
+
+from calendarserver.tools.util import loadConfig, getDirectory, setupMemcached, setupNotifications
+from calendarserver.tools.principals import principalForPrincipalID, proxySubprincipal, addProxy, removeProxy, ProxyError, ProxyWarning
+
+def usage(e=None):
+
+    name = os.path.basename(sys.argv[0])
+    print "usage: %s [options]" % (name,)
+    print ""
+    print "  TODO: describe usage"
+    print ""
+    print "options:"
+    print "  -h --help: print this help and exit"
+    print "  -f --config <path>: Specify caldavd.plist configuration path"
+    print ""
+
+    if e:
+        sys.exit(64)
+    else:
+        sys.exit(0)
+
+
+def main():
+
+    try:
+        (optargs, args) = getopt(
+            sys.argv[1:], "hf:", [
+                "help",
+                "config=",
+            ],
+        )
+    except GetoptError, e:
+        usage(e)
+
+    #
+    # Get configuration
+    #
+    configFileName = None
+
+    for opt, arg in optargs:
+        if opt in ("-h", "--help"):
+            usage()
+
+        elif opt in ("-f", "--config"):
+            configFileName = arg
+
+        else:
+            raise NotImplementedError(opt)
+
+    try:
+        loadConfig(configFileName)
+
+        # Shed privileges
+        if config.UserName and config.GroupName and os.getuid() == 0:
+            uid = getpwnam(config.UserName).pw_uid
+            gid = getgrnam(config.GroupName).gr_gid
+            switchUID(uid, uid, gid)
+
+        os.umask(config.umask)
+
+        try:
+            config.directory = getDirectory()
+        except DirectoryError, e:
+            abort(e)
+        setupMemcached(config)
+        setupNotifications(config)
+    except ConfigurationError, e:
+        abort(e)
+
+    #
+    # Read commands from stdin
+    #
+    rawInput = sys.stdin.read()
+    try:
+        plist = plistlib.readPlistFromString(rawInput)
+    except xml.parsers.expat.ExpatError, e:
+        abort(str(e))
+
+    # If the plist is an array, each element of the array is a separate
+    # command dictionary.
+    if isinstance(plist, list):
+        commands = plist
+    else:
+        commands = [plist]
+
+    runner = Runner(config.directory, commands)
+    runner.validate()
+
+    #
+    # Start the reactor
+    #
+    reactor.callLater(0, runner.run)
+    reactor.run()
+
+
+class Runner(object):
+
+    def __init__(self, directory, commands):
+        self.dir = directory
+        self.commands = commands
+
+    def validate(self):
+        # Make sure commands are valid
+        for command in self.commands:
+            if not command.has_key('command'):
+                abort("'command' missing from plist")
+            commandName = command['command']
+            methodName = "command_%s" % (commandName,)
+            if not hasattr(self, methodName):
+                abort("Unknown command '%s'" % (commandName,))
+
+    @inlineCallbacks
+    def run(self):
+        for command in self.commands:
+            commandName = command['command']
+            methodName = "command_%s" % (commandName,)
+            if hasattr(self, methodName):
+                (yield getattr(self, methodName)(command))
+            else:
+                abort("Unknown command '%s'" % (commandName,))
+
+        reactor.stop()
+
+    # Locations
+
+    def command_getLocationList(self, command):
+        respondWithRecordsOfType(self.dir, command, "locations")
+
+    def command_createLocation(self, command):
+
+        try:
+            self.dir.createRecord("locations", guid=command['GeneratedUID'],
+                shortNames=command['RecordName'], fullName=command['RealName'])
+        except DirectoryError, e:
+            abort(str(e))
+        respondWithRecordsOfType(self.dir, command, "locations")
+
+    def command_deleteLocation(self, command):
+        try:
+            self.dir.destroyRecord("locations", guid=command['GeneratedUID'])
+        except DirectoryError, e:
+            abort(str(e))
+        respondWithRecordsOfType(self.dir, command, "locations")
+
+    # Resources
+
+    def command_getResourceList(self, command):
+        respondWithRecordsOfType(self.dir, command, "resources")
+
+    def command_createResource(self, command):
+        try:
+            self.dir.createRecord("resources", guid=command['GeneratedUID'],
+                shortNames=command['RecordName'], fullName=command['RealName'])
+        except DirectoryError, e:
+            abort(str(e))
+        respondWithRecordsOfType(self.dir, command, "resources")
+
+    def command_deleteResource(self, command):
+        try:
+            self.dir.destroyRecord("resources", guid=command['GeneratedUID'])
+        except DirectoryError, e:
+            abort(str(e))
+        respondWithRecordsOfType(self.dir, command, "resources")
+
+    # Proxies
+
+    @inlineCallbacks
+    def command_listWriteProxies(self, command):
+        principal = principalForPrincipalID(command['Principal'], directory=self.dir)
+        (yield respondWithProxies(self.dir, command, principal, "write"))
+
+    @inlineCallbacks
+    def command_addWriteProxy(self, command):
+        principal = principalForPrincipalID(command['Principal'], directory=self.dir)
+        proxy = principalForPrincipalID(command['Proxy'], directory=self.dir)
+        try:
+            (yield addProxy(principal, "write", proxy))
+        except ProxyError, e:
+            abort(str(e))
+        except ProxyWarning, e:
+            pass
+        (yield respondWithProxies(self.dir, command, principal, "write"))
+
+    @inlineCallbacks
+    def command_removeWriteProxy(self, command):
+        principal = principalForPrincipalID(command['Principal'], directory=self.dir)
+        proxy = principalForPrincipalID(command['Proxy'], directory=self.dir)
+        try:
+            (yield removeProxy(principal, proxy, proxyTypes=("write",)))
+        except ProxyError, e:
+            abort(str(e))
+        except ProxyWarning, e:
+            pass
+        (yield respondWithProxies(self.dir, command, principal, "write"))
+
+    @inlineCallbacks
+    def command_listReadProxies(self, command):
+        principal = principalForPrincipalID(command['Principal'], directory=self.dir)
+        (yield respondWithProxies(self.dir, command, principal, "read"))
+
+    @inlineCallbacks
+    def command_addReadProxy(self, command):
+        principal = principalForPrincipalID(command['Principal'], directory=self.dir)
+        proxy = principalForPrincipalID(command['Proxy'], directory=self.dir)
+        try:
+            (yield addProxy(principal, "read", proxy))
+        except ProxyError, e:
+            abort(str(e))
+        except ProxyWarning, e:
+            pass
+        (yield respondWithProxies(self.dir, command, principal, "read"))
+
+    @inlineCallbacks
+    def command_removeReadProxy(self, command):
+        principal = principalForPrincipalID(command['Principal'], directory=self.dir)
+        proxy = principalForPrincipalID(command['Proxy'], directory=self.dir)
+        try:
+            (yield removeProxy(principal, proxy, proxyTypes=("read",)))
+        except ProxyError, e:
+            abort(str(e))
+        except ProxyWarning, e:
+            pass
+        (yield respondWithProxies(self.dir, command, principal, "read"))
+
+
+ at inlineCallbacks
+def respondWithProxies(directory, command, principal, proxyType):
+    proxies = []
+    subPrincipal = proxySubprincipal(principal, proxyType)
+    if subPrincipal is not None:
+        membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+        if membersProperty.children:
+            for member in membersProperty.children:
+                proxyPrincipal = principalForPrincipalID(str(member))
+                proxies.append(proxyPrincipal.record.guid)
+
+    respond(command, {
+        'Principal' : principal.record.guid, 'Proxies' : proxies
+    })
+
+
+def respondWithRecordsOfType(directory, command, recordType):
+    result = []
+    for record in directory.listRecords(recordType):
+        result.append( {
+            'GeneratedUID' : record.guid,
+            'RecordName' : [n for n in record.shortNames],
+            'RealName' : record.fullName,
+            'AutoSchedule' : record.autoSchedule,
+        } )
+    respond(command, result)
+
+def respond(command, result):
+    sys.stdout.write(plistlib.writePlistToString( { 'command' : command['command'], 'result' : result } ) )
+
+def abort(msg, status=1):
+    sys.stdout.write(plistlib.writePlistToString( { 'error' : msg, } ) )
+    try:
+        reactor.stop()
+    except RuntimeError:
+        pass
+    sys.exit(status)
+
+if __name__ == "__main__":
+    main()

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/principals.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/principals.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -33,15 +33,10 @@
 from twext.python.log import StandardIOObserver
 from twext.web2.dav.davxml import sname2qname, qname2sname
 
-from twistedcaldav import memcachepool
 from twistedcaldav.config import config, ConfigurationError
-from twistedcaldav.notify import installNotificationClient
-from twistedcaldav.static import CalendarHomeProvisioningFile
 from twistedcaldav.directory.directory import UnknownRecordTypeError, DirectoryError
 
-from calendarserver.tools.util import booleanArgument, autoDisableMemcached
-from calendarserver.tools.util import loadConfig, getDirectory
-from calendarserver.provision.root import RootResource
+from calendarserver.tools.util import loadConfig, getDirectory, setupMemcached, setupNotifications, booleanArgument
 
 def usage(e=None):
     if e:
@@ -210,7 +205,8 @@
             config.directory = getDirectory()
         except DirectoryError, e:
             abort(e)
-        autoDisableMemcached(config)
+        setupMemcached(config)
+        setupNotifications(config)
     except ConfigurationError, e:
         abort(e)
 
@@ -261,40 +257,6 @@
 @inlineCallbacks
 def run(principalIDs, actions):
     try:
-        #
-        # Connect to memcached, notifications
-        #
-        memcachepool.installPools(
-            config.Memcached.Pools,
-            config.Memcached.MaxClients
-        )
-        if config.Notifications.Enabled:
-            installNotificationClient(
-                config.Notifications.InternalNotificationHost,
-                config.Notifications.InternalNotificationPort,
-            )
-
-        #
-        # Wire up the resource hierarchy
-        #
-        principalCollection = config.directory.getPrincipalCollection()
-        root = RootResource(
-            config.DocumentRoot,
-            principalCollections=(principalCollection,),
-        )
-        root.putChild("principals", principalCollection)
-        calendarCollection = CalendarHomeProvisioningFile(
-            os.path.join(config.DocumentRoot, "calendars"),
-            config.directory, "/calendars/",
-        )
-        root.putChild("calendars", calendarCollection)
-
-        #
-        # Wrap root resource
-        #
-        # FIXME: not a fan -wsanchez
-        #root = ResourceWrapper(root)
-
         for principalID in principalIDs:
             # Resolve the given principal IDs to principals
             try:
@@ -317,6 +279,7 @@
         #
         reactor.stop()
 
+
 def principalForPrincipalID(principalID, checkOnly=False, directory=None):
     
     # Allow a directory parameter to be passed in, but default to config.directory
@@ -326,11 +289,19 @@
         directory = config.directory
 
     if principalID.startswith("/"):
-        raise ValueError("Can't resolve paths yet")
+        segments = principalID.strip("/").split("/")
+        if (len(segments) == 3 and
+            segments[0] == "principals" and segments[1] == "__uids__"):
+            uid = segments[2]
+        else:
+            raise ValueError("Can't resolve all paths yet")
 
         if checkOnly:
             return None
 
+        return directory.principalCollection.principalForUID(uid)
+
+
     if principalID.startswith("("):
         try:
             i = principalID.index(")")
@@ -363,7 +334,7 @@
         if checkOnly:
             return None
 
-        return directory.principalCollection.principalForUID(guid)
+        return directory.principalCollection.principalForUID(str(guid))
     except ValueError:
         pass
 
@@ -407,31 +378,40 @@
 
 @inlineCallbacks
 def action_addProxyPrincipal(principal, proxyType, proxyPrincipal):
+    try:
+        (yield addProxy(principal, proxyType, proxyPrincipal))
+        print "Added %s as a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
+    except ProxyError, e:
+        print "Error:", e
+    except ProxyWarning, e:
+        print e
+
+ at inlineCallbacks
+def addProxy(principal, proxyType, proxyPrincipal):
     proxyURL = proxyPrincipal.url()
 
     subPrincipal = proxySubprincipal(principal, proxyType)
     if subPrincipal is None:
-        sys.stderr.write("Unable to edit %s proxies for %s\n" % (proxyType, principal))
-        return
+        raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType, principal))
 
     membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
 
     for memberURL in membersProperty.children:
         if str(memberURL) == proxyURL:
-            print "%s is already a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
-            break
+            raise ProxyWarning("%s is already a %s proxy for %s" % (proxyPrincipal, proxyType, principal))
+
     else:
         memberURLs = list(membersProperty.children)
         memberURLs.append(davxml.HRef(proxyURL))
         membersProperty = davxml.GroupMemberSet(*memberURLs)
         (yield subPrincipal.writeProperty(membersProperty, None))
-        print "Added %s as a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
 
     proxyTypes = ["read", "write"]
     proxyTypes.remove(proxyType)
 
     (yield action_removeProxyPrincipal(principal, proxyPrincipal, proxyTypes=proxyTypes))
 
+
 @inlineCallbacks
 def action_removeProxy(principal, *proxyIDs, **kwargs):
     for proxyID in proxyIDs:
@@ -440,14 +420,23 @@
 
 @inlineCallbacks
 def action_removeProxyPrincipal(principal, proxyPrincipal, **kwargs):
+    try:
+        (yield removeProxy(principal, proxyPrincipal, **kwargs))
+    except ProxyError, e:
+        print "Error:", e
+    except ProxyWarning, e:
+        print e
+
+
+ at inlineCallbacks
+def removeProxy(principal, proxyPrincipal, **kwargs):
     proxyTypes = kwargs.get("proxyTypes", ("read", "write"))
     for proxyType in proxyTypes:
         proxyURL = proxyPrincipal.url()
 
         subPrincipal = proxySubprincipal(principal, proxyType)
         if subPrincipal is None:
-            sys.stderr.write("Unable to edit %s proxies for %s\n" % (proxyType, principal))
-            continue
+            raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType, principal))
 
         membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
 
@@ -462,8 +451,9 @@
 
         membersProperty = davxml.GroupMemberSet(*memberURLs)
         (yield subPrincipal.writeProperty(membersProperty, None))
-        print "Removed %s as a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
 
+
+
 @inlineCallbacks
 def action_setAutoSchedule(principal, autoSchedule):
     if autoSchedule and principal.record.recordType in ("users", "groups"):
@@ -530,5 +520,16 @@
         pass
     sys.exit(status)
 
+class ProxyError(Exception):
+    """
+    Raised when proxy assignments cannot be performed
+    """
+
+class ProxyWarning(Exception):
+    """
+    Raised for harmless proxy assignment failures such as trying to add a
+    duplicate or remove a non-existent assignment.
+    """
+
 if __name__ == "__main__":
     main()

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/__init__.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,19 +0,0 @@
-##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-"""
-Tests for the calendarserver.tools module.
-"""

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py (from rev 5079, CalendarServer/trunk/calendarserver/tools/test/__init__.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/__init__.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Tests for the calendarserver.tools module.
+"""

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/augments.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2009-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
-
-<augments>
-  <record>
-    <guid>user01</guid>
-    <enable>true</enable>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <guid>user02</guid>
-    <enable>false</enable>
-    <enable-calendar>false</enable-calendar>
-  </record>
-  <record>
-    <guid>location01</guid>
-    <enable>true</enable>
-    <enable-calendar>true</enable-calendar>
-  </record>
-</augments>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml (from rev 5079, CalendarServer/trunk/calendarserver/tools/test/gateway/augments.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/augments.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
+
+<augments>
+  <record>
+    <guid>user01</guid>
+    <enable>true</enable>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <guid>user02</guid>
+    <enable>false</enable>
+    <enable-calendar>false</enable-calendar>
+  </record>
+  <record>
+    <guid>location01</guid>
+    <enable>true</enable>
+    <enable-calendar>true</enable-calendar>
+  </record>
+</augments>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,760 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    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 "AS IS" 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.
-  -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-  <dict>
-
-    <!--
-        Public network address information
-
-        This is the server's public network address, which is provided to
-        clients in URLs and the like.  It may or may not be the network
-        address that the server is listening to directly, though it is by
-        default.  For example, it may be the address of a load balancer or
-        proxy which forwards connections to the server.
-      -->
-
-    <!-- Network host name [empty = system host name] -->
-    <key>ServerHostName</key>
-    <string></string> <!-- The hostname clients use when connecting -->
-
-    <!-- HTTP port [0 = disable HTTP] -->
-    <key>HTTPPort</key>
-    <integer>8008</integer>
-
-    <!-- SSL port [0 = disable HTTPS] -->
-    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
-    <key>SSLPort</key>
-    <integer>8443</integer>
-
-    <!-- Redirect non-SSL ports to an SSL port -->
-    <key>RedirectHTTPToHTTPS</key>
-    <false/>
-
-    <!--
-        Network address configuration information
-
-        This configures the actual network address that the server binds to.
-      -->
-
-    <!-- List of IP addresses to bind to [empty = all] -->
-    <key>BindAddresses</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
-    <key>BindHTTPPorts</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
-    <key>BindSSLPorts</key>
-    <array>
-    </array>
-
-
-    <!--
-        Data Store
-      -->
-
-    <!-- Data root -->
-    <key>DataRoot</key>
-    <string>%(DataRoot)s</string>
-
-    <!-- Document root -->
-    <key>DocumentRoot</key>
-    <string>%(DocumentRoot)s</string>
-
-    <!-- Child aliases -->
-    <key>Aliases</key>
-    <dict>
-      <!--
-      <key>foo</key>
-      <dict>
-        <key>path</key>
-        <string>/path/to/foo</string>
-      </dict>
-       -->
-    </dict>
-
-    <!-- User quota (in bytes) -->
-    <key>UserQuota</key>
-    <integer>104857600</integer><!-- 100Mb -->
-
-    <!-- Attachment size limit (in bytes) -->
-    <key>MaximumAttachmentSize</key>
-    <integer>1048576</integer><!-- 1Mb -->
-
-    <!-- Maximum number of unique attendees per entire event -->
-    <!-- 0 for no limit -->
-    <key>MaxAttendeesPerInstance</key>
-    <integer>100</integer>
-
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
-
-
-    <!--
-        Directory service
-
-        A directory service provides information about principals (eg.
-        users, groups, locations and resources) to the server.
-
-        A variety of directory services are available for use.
-      -->
-
-    <!-- XML File Directory Service -->
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>%(DirectoryXMLFile)s</string>
-        <key>recordTypes</key>
-        <array>
-            <string>users</string>
-            <string>groups</string>
-        </array>
-      </dict>
-    </dict>
-
-    <!-- XML File Resource Service -->
-    <key>ResourceService</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>%(ResourceXMLFile)s</string>
-        <key>recordTypes</key>
-        <array>
-            <string>resources</string>
-            <string>locations</string>
-        </array>
-        <key>cacheTimeout</key>
-        <integer>30</integer>
-      </dict>
-    </dict>
-    
-    <!-- Open Directory Service (Mac OS X) -->
-    <!--
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>node</key>
-        <string>/Search</string>
-        <key>cacheTimeout</key>
-        <integer>30</integer>
-      </dict>
-    </dict>
-    -->
-
-    <!--
-        Augment service
-
-        Augments for the directory service records to add calendar specific attributes.
-
-        A variety of augment services are available for use.
-        When using a partitioned server, a service that can be accessed from each host will be needed.
-      -->
-
-    <!-- XML File Augment Service -->
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFiles</key>
-        <array>
-	      <string>%(AugmentXMLFile)s</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Sqlite Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>/etc/caldavd/augments.sqlite</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- PostgreSQL Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>augments</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- Sqlite ProxyDB Service -->
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>%(ProxyDBFile)s</string>
-      </dict>
-    </dict>
-
-    <!-- PostgreSQL ProxyDB Service -->
-    <!--
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>proxies</string>
-      </dict>
-    </dict>
-     -->
-
-	<key>ProxyLoadFromFile</key>
-    <string>conf/auth/proxies-test.xml</string>
-
-    <!--
-        Special principals
-
-        These principals are granted special access and/or perform
-        special roles on the server.
-      -->
-
-    <!-- Principals with "DAV:all" access (relative URLs) -->
-    <key>AdminPrincipals</key>
-    <array>
-      <string>/principals/__uids__/admin/</string>
-    </array>
-
-    <!-- Principals with "DAV:read" access (relative URLs) -->
-    <key>ReadPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
-    </array>
-
-    <!-- Principals that can pose as other principals -->
-    <key>SudoersFile</key>
-    <string>conf/sudoers.plist</string>
-
-    <!-- Create "proxy access" principals -->
-    <key>EnableProxyPrincipals</key>
-    <true/>
-
-
-    <!--
-        Permissions
-      -->
-
-    <!-- Anonymous read access for root resource -->
-    <key>EnableAnonymousReadRoot</key>
-    <true/>
-
-    <!-- Anonymous read access for resource hierarchy -->
-    <key>EnableAnonymousReadNav</key>
-    <false/>
-
-    <!-- Enables directory listings for principals -->
-    <key>EnablePrincipalListings</key>
-    <true/>
-
-    <!-- Render calendar collections as a monolithic iCalendar object -->
-    <key>EnableMonolithicCalendars</key>
-    <true/>
-
-
-    <!--
-        Authentication
-      -->
-
-    <key>Authentication</key>
-    <dict>
-
-      <!-- Clear text; best avoided -->
-      <key>Basic</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-      </dict>
-
-      <!-- Digest challenge/response -->
-      <key>Digest</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Algorithm</key>
-        <string>md5</string>
-        <key>Qop</key>
-        <string></string>
-      </dict>
-
-      <!-- Kerberos/SPNEGO -->
-      <key>Kerberos</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>ServicePrincipal</key>
-        <string></string>
-      </dict>
-
-      <!-- Wikiserver authentication (Mac OS X) -->
-      <key>Wiki</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Cookie</key>
-        <string>sessionID</string>
-        <key>URL</key>
-        <string>http://127.0.0.1/RPC2</string>
-        <key>UserMethod</key>
-        <string>userForSession</string>
-        <key>WikiMethod</key>
-        <string>accessLevelForUserWikiCalendar</string>
-      </dict>
-
-    </dict>
-
-
-    <!--
-        Logging
-      -->
-
-    <!-- Apache-style access log -->
-    <key>AccessLogFile</key>
-    <string>logs/access.log</string>
-    <key>RotateAccessLog</key>
-    <false/>
-
-    <!-- Server activity log -->
-    <key>ErrorLogFile</key>
-    <string>logs/error.log</string>
-
-    <!-- Log levels -->
-    <key>DefaultLogLevel</key>
-    <string>info</string> <!-- debug, info, warn, error -->
-
-    <!-- Log level overrides for specific functionality -->
-    <key>LogLevels</key>
-    <dict>
-      <!--
-      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
-      <string>debug</string>
-      -->
-    </dict>
-
-    <!-- Global server stats --> 
-    <key>GlobalStatsSocket</key> 
-    <string>logs/caldavd-stats.sock</string> 
-
-    <!-- Global server stats logging period --> 
-    <key>GlobalStatsLoggingPeriod</key> 
-    <integer>60</integer> 
-
-    <!-- Global server stats logging frequency [0 = disable stats] --> 
-    <key>GlobalStatsLoggingFrequency</key> 
-    <integer>12</integer>
-
-    <!-- Server statistics file -->
-    <key>ServerStatsFile</key>
-    <string>logs/stats.plist</string>
-
-    <!-- Server process ID file -->
-    <key>PIDFile</key>
-    <string>logs/caldavd.pid</string>
-
-
-    <!--
-        Accounting
-      -->
-
-    <!-- Enable accounting for certain operations -->
-    <key>AccountingCategories</key>
-    <dict>
-      <key>iTIP</key>
-      <false/>
-      <key>HTTP</key>
-      <false/>
-    </dict>
-    <!-- Enable accounting for specific principals -->
-    <key>AccountingPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
-    </array>
-
-
-    <!--
-        SSL/TLS
-      -->
-
-    <!-- Public key -->
-    <key>SSLCertificate</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-    <!-- SSL authority chain (for intermediate certs) -->
-    <key>SSLAuthorityChain</key>
-    <string></string>
-
-    <!-- Private key -->
-    <key>SSLPrivateKey</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-
-    <!--
-        Process management
-      -->
-
-    <key>UserName</key>
-    <string></string>
-
-    <key>GroupName</key>
-    <string></string>
-
-    <key>ProcessType</key>
-    <string>Combined</string>
-
-    <key>MultiProcess</key>
-    <dict>
-      <key>ProcessCount</key>
-      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
-    </dict>
-
-
-    <!--
-        Notifications
-      -->
-
-    <key>Notifications</key>
-    <dict>
-      <!-- Time spent coalescing notifications before delivery -->
-      <key>CoalesceSeconds</key>
-      <integer>3</integer>
-
-      <key>InternalNotificationHost</key>
-      <string>localhost</string>
-
-      <key>InternalNotificationPort</key>
-      <integer>62309</integer>
-
-      <key>Services</key>
-      <dict>
-        <key>SimpleLineNotifier</key>
-        <dict>
-          <!-- Simple line notification service (for testing) -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-          <key>Port</key>
-          <integer>62308</integer>
-        </dict>
-
-        <key>XMPPNotifier</key>
-        <dict>
-          <!-- XMPP notification service -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.XMPPNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-
-          <!-- XMPP host and port to contact -->
-          <key>Host</key>
-          <string>xmpp.host.name</string>
-          <key>Port</key>
-          <integer>5222</integer>
-
-          <!-- Jabber ID and password for the server -->
-          <key>JID</key>
-          <string>jid at xmpp.host.name/resource</string>
-          <key>Password</key>
-          <string>password_goes_here</string>
-
-          <!-- PubSub service address -->
-          <key>ServiceAddress</key>
-          <string>pubsub.xmpp.host.name</string>
-
-          <key>NodeConfiguration</key>
-          <dict>
-            <key>pubsub#deliver_payloads</key>
-            <string>1</string>
-            <key>pubsub#persist_items</key>
-            <string>1</string>
-          </dict>
-
-          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
-          <key>KeepAliveSeconds</key>
-          <integer>120</integer>
-
-          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
-          <key>HeartbeatMinutes</key>
-          <integer>30</integer>
-
-          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
-          <key>AllowedJIDs</key>
-          <array>
-            <!--
-            <string>*.example.com</string>
-             -->
-          </array>
-        </dict>
-      </dict>
-    </dict>
-
-
-    <!--
-        Server-to-server protocol
-      -->
-
-    <key>Scheduling</key>
-    <dict>
-
-      <!-- CalDAV protocol options -->
-      <key>CalDAV</key>
-      <dict>
-        <key>EmailDomain</key>
-        <string></string>
-        <key>HTTPDomain</key>
-        <string></string>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>OldDraftCompatibility</key>
-        <true/>
-        <key>ScheduleTagCompatibility</key>
-        <true/>
-        <key>EnablePrivateComments</key>
-        <true/>
-      </dict>
-
-      <!-- iSchedule protocol options -->
-      <key>iSchedule</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>Servers</key>
-        <string>conf/servertoserver-test.xml</string>
-      </dict>
-
-      <!-- iMIP protocol options -->
-      <key>iMIP</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>MailGatewayServer</key>
-        <string>localhost</string>
-        <key>MailGatewayPort</key>
-        <integer>62310</integer>
-        <key>Sending</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>587</integer>
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>Address</key>
-          <string></string> <!-- Address email will be sent from -->
-        </dict>
-        <key>Receiving</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>995</integer>
-          <key>Type</key>
-          <string></string> <!-- Either "pop" or "imap" -->
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>PollingSeconds</key>
-          <integer>30</integer>
-        </dict>
-        <key>AddressPatterns</key>
-        <array>
-          <string>mailto:.*</string>
-        </array>
-      </dict>
-
-	  <!-- General options for scheduling -->
-	  <key>Options</key>
-	  <dict>
-        <key>AllowGroupAsOrganizer</key>
-        <false/>
-        <key>AllowLocationAsOrganizer</key>
-        <false/>
-        <key>AllowResourceAsOrganizer</key>
-        <false/>
-       </dict>
-
-    </dict>
-
-
-    <!--
-        Free-busy URL protocol
-      -->
-
-    <key>FreeBusyURL</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>TimePeriod</key>
-      <integer>14</integer>
-      <key>AnonymousAccess</key>
-      <false/>
-    </dict>
-
-
-    <!--
-        Non-standard CalDAV extensions
-      -->
-
-    <!-- Calendar Drop Box -->
-    <key>EnableDropBox</key>
-    <true/>
-
-    <!-- Private Events -->
-    <key>EnablePrivateEvents</key>
-    <true/>
-
-    <!-- Timezone Service -->
-    <key>EnableTimezoneService</key>
-    <true/>
-
-
-    <!--
-        Miscellaneous items
-      -->
-
-    <!-- Service ACLs (Mac OS X) -->
-    <key>EnableSACLs</key>
-    <false/>
-
-    <!-- Web-based administration -->
-    <key>EnableWebAdmin</key>
-    <true/>
-
-    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
-    <key>ResponseCompression</key>
-    <false/>
-    
-    <!-- The retry-after value (in seconds) to return with a 503 error. -->
-    <key>HTTPRetryAfter</key>
-    <integer>180</integer>
-
-    <!-- A unix socket used for communication between the child and master processes.
-         An empty value tells the server to use a tcp socket instead. -->
-    <key>ControlSocket</key>
-    <string>logs/caldavd.sock</string>
-
-    <!-- Support for Memcached -->
-    <key>Memcached</key>
-    <dict>
-      <key>MaxClients</key>
-      <integer>5</integer>
-      <key>memcached</key>
-      <string>memcached</string> <!-- Find in PATH -->
-      <key>Options</key>
-      <array>
-        <!--<string>-vv</string>-->
-      </array>
-      <key>Pools</key>
-        <dict>
-        <key>Default</key>
-            <dict>
-                <key>ClientEnabled</key>
-                <false/>
-                <key>ServerEnabled</key>
-                <false/>
-            </dict>
-        </dict>
-    </dict>
-
-    <!-- Response Caching -->
-    <key>ResponseCacheTimeout</key>
-    <integer>30</integer> <!-- in minutes -->
-
-
-    <!--
-        Twisted
-      -->
-
-    <key>Twisted</key>
-    <dict>
-      <key>twistd</key>
-      <string>../Twisted/bin/twistd</string>
-    </dict>
-
-
-    <key>Localization</key>
-    <dict>
-      <key>LocalesDirectory</key>
-      <string>locales</string>
-      <key>Language</key>
-      <string>English</string>
-    </dict>
-
-
-  </dict>
-</plist>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist (from rev 5079, CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/caldavd.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,760 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    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 "AS IS" 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.
+  -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+
+    <!--
+        Public network address information
+
+        This is the server's public network address, which is provided to
+        clients in URLs and the like.  It may or may not be the network
+        address that the server is listening to directly, though it is by
+        default.  For example, it may be the address of a load balancer or
+        proxy which forwards connections to the server.
+      -->
+
+    <!-- Network host name [empty = system host name] -->
+    <key>ServerHostName</key>
+    <string></string> <!-- The hostname clients use when connecting -->
+
+    <!-- HTTP port [0 = disable HTTP] -->
+    <key>HTTPPort</key>
+    <integer>8008</integer>
+
+    <!-- SSL port [0 = disable HTTPS] -->
+    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+    <key>SSLPort</key>
+    <integer>8443</integer>
+
+    <!-- Redirect non-SSL ports to an SSL port -->
+    <key>RedirectHTTPToHTTPS</key>
+    <false/>
+
+    <!--
+        Network address configuration information
+
+        This configures the actual network address that the server binds to.
+      -->
+
+    <!-- List of IP addresses to bind to [empty = all] -->
+    <key>BindAddresses</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+    <key>BindHTTPPorts</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+    <key>BindSSLPorts</key>
+    <array>
+    </array>
+
+
+    <!--
+        Data Store
+      -->
+
+    <!-- Data root -->
+    <key>DataRoot</key>
+    <string>%(DataRoot)s</string>
+
+    <!-- Document root -->
+    <key>DocumentRoot</key>
+    <string>%(DocumentRoot)s</string>
+
+    <!-- Child aliases -->
+    <key>Aliases</key>
+    <dict>
+      <!--
+      <key>foo</key>
+      <dict>
+        <key>path</key>
+        <string>/path/to/foo</string>
+      </dict>
+       -->
+    </dict>
+
+    <!-- User quota (in bytes) -->
+    <key>UserQuota</key>
+    <integer>104857600</integer><!-- 100Mb -->
+
+    <!-- Attachment size limit (in bytes) -->
+    <key>MaximumAttachmentSize</key>
+    <integer>1048576</integer><!-- 1Mb -->
+
+    <!-- Maximum number of unique attendees per entire event -->
+    <!-- 0 for no limit -->
+    <key>MaxAttendeesPerInstance</key>
+    <integer>100</integer>
+
+    <!-- Maximum number of instances allowed for a single RRULE -->
+    <!-- 0 for no limit -->
+    <key>MaxInstancesForRRULE</key>
+    <integer>400</integer>
+
+
+    <!--
+        Directory service
+
+        A directory service provides information about principals (eg.
+        users, groups, locations and resources) to the server.
+
+        A variety of directory services are available for use.
+      -->
+
+    <!-- XML File Directory Service -->
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>%(DirectoryXMLFile)s</string>
+        <key>recordTypes</key>
+        <array>
+            <string>users</string>
+            <string>groups</string>
+        </array>
+      </dict>
+    </dict>
+
+    <!-- XML File Resource Service -->
+    <key>ResourceService</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>%(ResourceXMLFile)s</string>
+        <key>recordTypes</key>
+        <array>
+            <string>resources</string>
+            <string>locations</string>
+        </array>
+        <key>cacheTimeout</key>
+        <integer>30</integer>
+      </dict>
+    </dict>
+    
+    <!-- Open Directory Service (Mac OS X) -->
+    <!--
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>node</key>
+        <string>/Search</string>
+        <key>cacheTimeout</key>
+        <integer>30</integer>
+      </dict>
+    </dict>
+    -->
+
+    <!--
+        Augment service
+
+        Augments for the directory service records to add calendar specific attributes.
+
+        A variety of augment services are available for use.
+        When using a partitioned server, a service that can be accessed from each host will be needed.
+      -->
+
+    <!-- XML File Augment Service -->
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFiles</key>
+        <array>
+	      <string>%(AugmentXMLFile)s</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Sqlite Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>/etc/caldavd/augments.sqlite</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- PostgreSQL Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>augments</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- Sqlite ProxyDB Service -->
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>%(ProxyDBFile)s</string>
+      </dict>
+    </dict>
+
+    <!-- PostgreSQL ProxyDB Service -->
+    <!--
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>proxies</string>
+      </dict>
+    </dict>
+     -->
+
+	<key>ProxyLoadFromFile</key>
+    <string>conf/auth/proxies-test.xml</string>
+
+    <!--
+        Special principals
+
+        These principals are granted special access and/or perform
+        special roles on the server.
+      -->
+
+    <!-- Principals with "DAV:all" access (relative URLs) -->
+    <key>AdminPrincipals</key>
+    <array>
+      <string>/principals/__uids__/admin/</string>
+    </array>
+
+    <!-- Principals with "DAV:read" access (relative URLs) -->
+    <key>ReadPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
+    </array>
+
+    <!-- Principals that can pose as other principals -->
+    <key>SudoersFile</key>
+    <string>conf/sudoers.plist</string>
+
+    <!-- Create "proxy access" principals -->
+    <key>EnableProxyPrincipals</key>
+    <true/>
+
+
+    <!--
+        Permissions
+      -->
+
+    <!-- Anonymous read access for root resource -->
+    <key>EnableAnonymousReadRoot</key>
+    <true/>
+
+    <!-- Anonymous read access for resource hierarchy -->
+    <key>EnableAnonymousReadNav</key>
+    <false/>
+
+    <!-- Enables directory listings for principals -->
+    <key>EnablePrincipalListings</key>
+    <true/>
+
+    <!-- Render calendar collections as a monolithic iCalendar object -->
+    <key>EnableMonolithicCalendars</key>
+    <true/>
+
+
+    <!--
+        Authentication
+      -->
+
+    <key>Authentication</key>
+    <dict>
+
+      <!-- Clear text; best avoided -->
+      <key>Basic</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+      </dict>
+
+      <!-- Digest challenge/response -->
+      <key>Digest</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Algorithm</key>
+        <string>md5</string>
+        <key>Qop</key>
+        <string></string>
+      </dict>
+
+      <!-- Kerberos/SPNEGO -->
+      <key>Kerberos</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>ServicePrincipal</key>
+        <string></string>
+      </dict>
+
+      <!-- Wikiserver authentication (Mac OS X) -->
+      <key>Wiki</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Cookie</key>
+        <string>sessionID</string>
+        <key>URL</key>
+        <string>http://127.0.0.1/RPC2</string>
+        <key>UserMethod</key>
+        <string>userForSession</string>
+        <key>WikiMethod</key>
+        <string>accessLevelForUserWikiCalendar</string>
+      </dict>
+
+    </dict>
+
+
+    <!--
+        Logging
+      -->
+
+    <!-- Apache-style access log -->
+    <key>AccessLogFile</key>
+    <string>logs/access.log</string>
+    <key>RotateAccessLog</key>
+    <false/>
+
+    <!-- Server activity log -->
+    <key>ErrorLogFile</key>
+    <string>logs/error.log</string>
+
+    <!-- Log levels -->
+    <key>DefaultLogLevel</key>
+    <string>info</string> <!-- debug, info, warn, error -->
+
+    <!-- Log level overrides for specific functionality -->
+    <key>LogLevels</key>
+    <dict>
+      <!--
+      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
+      <string>debug</string>
+      -->
+    </dict>
+
+    <!-- Global server stats --> 
+    <key>GlobalStatsSocket</key> 
+    <string>logs/caldavd-stats.sock</string> 
+
+    <!-- Global server stats logging period --> 
+    <key>GlobalStatsLoggingPeriod</key> 
+    <integer>60</integer> 
+
+    <!-- Global server stats logging frequency [0 = disable stats] --> 
+    <key>GlobalStatsLoggingFrequency</key> 
+    <integer>12</integer>
+
+    <!-- Server statistics file -->
+    <key>ServerStatsFile</key>
+    <string>logs/stats.plist</string>
+
+    <!-- Server process ID file -->
+    <key>PIDFile</key>
+    <string>logs/caldavd.pid</string>
+
+
+    <!--
+        Accounting
+      -->
+
+    <!-- Enable accounting for certain operations -->
+    <key>AccountingCategories</key>
+    <dict>
+      <key>iTIP</key>
+      <false/>
+      <key>HTTP</key>
+      <false/>
+    </dict>
+    <!-- Enable accounting for specific principals -->
+    <key>AccountingPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
+    </array>
+
+
+    <!--
+        SSL/TLS
+      -->
+
+    <!-- Public key -->
+    <key>SSLCertificate</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+    <!-- SSL authority chain (for intermediate certs) -->
+    <key>SSLAuthorityChain</key>
+    <string></string>
+
+    <!-- Private key -->
+    <key>SSLPrivateKey</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+
+    <!--
+        Process management
+      -->
+
+    <key>UserName</key>
+    <string></string>
+
+    <key>GroupName</key>
+    <string></string>
+
+    <key>ProcessType</key>
+    <string>Combined</string>
+
+    <key>MultiProcess</key>
+    <dict>
+      <key>ProcessCount</key>
+      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
+    </dict>
+
+
+    <!--
+        Notifications
+      -->
+
+    <key>Notifications</key>
+    <dict>
+      <!-- Time spent coalescing notifications before delivery -->
+      <key>CoalesceSeconds</key>
+      <integer>3</integer>
+
+      <key>InternalNotificationHost</key>
+      <string>localhost</string>
+
+      <key>InternalNotificationPort</key>
+      <integer>62309</integer>
+
+      <key>Services</key>
+      <dict>
+        <key>SimpleLineNotifier</key>
+        <dict>
+          <!-- Simple line notification service (for testing) -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+          <key>Port</key>
+          <integer>62308</integer>
+        </dict>
+
+        <key>XMPPNotifier</key>
+        <dict>
+          <!-- XMPP notification service -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.XMPPNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+
+          <!-- XMPP host and port to contact -->
+          <key>Host</key>
+          <string>xmpp.host.name</string>
+          <key>Port</key>
+          <integer>5222</integer>
+
+          <!-- Jabber ID and password for the server -->
+          <key>JID</key>
+          <string>jid at xmpp.host.name/resource</string>
+          <key>Password</key>
+          <string>password_goes_here</string>
+
+          <!-- PubSub service address -->
+          <key>ServiceAddress</key>
+          <string>pubsub.xmpp.host.name</string>
+
+          <key>NodeConfiguration</key>
+          <dict>
+            <key>pubsub#deliver_payloads</key>
+            <string>1</string>
+            <key>pubsub#persist_items</key>
+            <string>1</string>
+          </dict>
+
+          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
+          <key>KeepAliveSeconds</key>
+          <integer>120</integer>
+
+          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
+          <key>HeartbeatMinutes</key>
+          <integer>30</integer>
+
+          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
+          <key>AllowedJIDs</key>
+          <array>
+            <!--
+            <string>*.example.com</string>
+             -->
+          </array>
+        </dict>
+      </dict>
+    </dict>
+
+
+    <!--
+        Server-to-server protocol
+      -->
+
+    <key>Scheduling</key>
+    <dict>
+
+      <!-- CalDAV protocol options -->
+      <key>CalDAV</key>
+      <dict>
+        <key>EmailDomain</key>
+        <string></string>
+        <key>HTTPDomain</key>
+        <string></string>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>OldDraftCompatibility</key>
+        <true/>
+        <key>ScheduleTagCompatibility</key>
+        <true/>
+        <key>EnablePrivateComments</key>
+        <true/>
+      </dict>
+
+      <!-- iSchedule protocol options -->
+      <key>iSchedule</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>Servers</key>
+        <string>conf/servertoserver-test.xml</string>
+      </dict>
+
+      <!-- iMIP protocol options -->
+      <key>iMIP</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>MailGatewayServer</key>
+        <string>localhost</string>
+        <key>MailGatewayPort</key>
+        <integer>62310</integer>
+        <key>Sending</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>587</integer>
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>Address</key>
+          <string></string> <!-- Address email will be sent from -->
+        </dict>
+        <key>Receiving</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>995</integer>
+          <key>Type</key>
+          <string></string> <!-- Either "pop" or "imap" -->
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>PollingSeconds</key>
+          <integer>30</integer>
+        </dict>
+        <key>AddressPatterns</key>
+        <array>
+          <string>mailto:.*</string>
+        </array>
+      </dict>
+
+	  <!-- General options for scheduling -->
+	  <key>Options</key>
+	  <dict>
+        <key>AllowGroupAsOrganizer</key>
+        <false/>
+        <key>AllowLocationAsOrganizer</key>
+        <false/>
+        <key>AllowResourceAsOrganizer</key>
+        <false/>
+       </dict>
+
+    </dict>
+
+
+    <!--
+        Free-busy URL protocol
+      -->
+
+    <key>FreeBusyURL</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>TimePeriod</key>
+      <integer>14</integer>
+      <key>AnonymousAccess</key>
+      <false/>
+    </dict>
+
+
+    <!--
+        Non-standard CalDAV extensions
+      -->
+
+    <!-- Calendar Drop Box -->
+    <key>EnableDropBox</key>
+    <true/>
+
+    <!-- Private Events -->
+    <key>EnablePrivateEvents</key>
+    <true/>
+
+    <!-- Timezone Service -->
+    <key>EnableTimezoneService</key>
+    <true/>
+
+
+    <!--
+        Miscellaneous items
+      -->
+
+    <!-- Service ACLs (Mac OS X) -->
+    <key>EnableSACLs</key>
+    <false/>
+
+    <!-- Web-based administration -->
+    <key>EnableWebAdmin</key>
+    <true/>
+
+    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+    <key>ResponseCompression</key>
+    <false/>
+    
+    <!-- The retry-after value (in seconds) to return with a 503 error. -->
+    <key>HTTPRetryAfter</key>
+    <integer>180</integer>
+
+    <!-- A unix socket used for communication between the child and master processes.
+         An empty value tells the server to use a tcp socket instead. -->
+    <key>ControlSocket</key>
+    <string>logs/caldavd.sock</string>
+
+    <!-- Support for Memcached -->
+    <key>Memcached</key>
+    <dict>
+      <key>MaxClients</key>
+      <integer>5</integer>
+      <key>memcached</key>
+      <string>memcached</string> <!-- Find in PATH -->
+      <key>Options</key>
+      <array>
+        <!--<string>-vv</string>-->
+      </array>
+      <key>Pools</key>
+        <dict>
+        <key>Default</key>
+            <dict>
+                <key>ClientEnabled</key>
+                <false/>
+                <key>ServerEnabled</key>
+                <false/>
+            </dict>
+        </dict>
+    </dict>
+
+    <!-- Response Caching -->
+    <key>ResponseCacheTimeout</key>
+    <integer>30</integer> <!-- in minutes -->
+
+
+    <!--
+        Twisted
+      -->
+
+    <key>Twisted</key>
+    <dict>
+      <key>twistd</key>
+      <string>../Twisted/bin/twistd</string>
+    </dict>
+
+
+    <key>Localization</key>
+    <dict>
+      <key>LocalesDirectory</key>
+      <string>locales</string>
+      <key>Language</key>
+      <string>English</string>
+    </dict>
+
+
+  </dict>
+</plist>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/resources-locations.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <location repeat="10">
-    <uid>location%02d</uid>
-    <guid>location%02d</guid>
-    <password>location%02d</password>
-    <name>Room %02d</name>
-  </location>
-  <resource repeat="10">
-    <uid>resource%02d</uid>
-    <guid>resource%02d</guid>
-    <password>resource%02d</password>
-    <name>Resource %02d</name>
-  </resource>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml (from rev 5079, CalendarServer/trunk/calendarserver/tools/test/gateway/resources-locations.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/resources-locations.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <location repeat="10">
+    <uid>location%02d</uid>
+    <guid>location%02d</guid>
+    <password>location%02d</password>
+    <name>Room %02d</name>
+  </location>
+  <resource repeat="10">
+    <uid>resource%02d</uid>
+    <guid>resource%02d</guid>
+    <password>resource%02d</password>
+    <name>Resource %02d</name>
+  </resource>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/users-groups.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <user repeat="10">
-    <uid>user%02d</uid>
-    <guid>user%02d</guid>
-    <password>test</password>
-    <name>Test User %02d</name>
-    <first-name>Test</first-name>
-    <last-name>User %02d</last-name>
-  </user>
-  <group>
-    <uid>testgroup1</uid>
-    <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
-    <password>test</password>
-    <name>Group 01</name>
-    <members>
-      <member type="users">user01</member>
-      <member type="users">user02</member>
-    </members>
-  </group>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml (from rev 5079, CalendarServer/trunk/calendarserver/tools/test/gateway/users-groups.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/gateway/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <user repeat="10">
+    <uid>user%02d</uid>
+    <guid>user%02d</guid>
+    <password>test</password>
+    <name>Test User %02d</name>
+    <first-name>Test</first-name>
+    <last-name>User %02d</last-name>
+  </user>
+  <group>
+    <uid>testgroup1</uid>
+    <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
+    <password>test</password>
+    <name>Group 01</name>
+    <members>
+      <member type="users">user01</member>
+      <member type="users">user02</member>
+    </members>
+  </group>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,405 +0,0 @@
-##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-import os
-import plistlib
-import xml
-
-from twisted.python.filepath import FilePath
-from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
-from twisted.internet.error import ProcessDone
-from twisted.internet.protocol import ProcessProtocol
-
-from twistedcaldav.config import config
-from twistedcaldav.test.util import TestCase
-from calendarserver.tools.util import getDirectory
-
-
-class ErrorOutput(Exception):
-    """
-    The process produced some error output and exited with a non-zero exit
-    code.
-    """
-
-
-class CapturingProcessProtocol(ProcessProtocol):
-    """
-    A L{ProcessProtocol} that captures its output and error.
-
-    @ivar output: a C{list} of all C{str}s received to stderr.
-
-    @ivar error: a C{list} of all C{str}s received to stderr.
-    """
-
-    def __init__(self, deferred, inputData):
-        """
-        Initialize a L{CapturingProcessProtocol}.
-
-        @param deferred: the L{Deferred} to fire when the process is complete.
-
-        @param inputData: a C{str} to feed to the subprocess's stdin.
-        """
-        self.deferred = deferred
-        self.input = inputData
-        self.output = []
-        self.error = []
-
-
-    def connectionMade(self):
-        """
-        The process started; feed its input on stdin.
-        """
-        self.transport.write(self.input)
-        self.transport.closeStdin()
-
-
-    def outReceived(self, data):
-        """
-        Some output was received on stdout.
-        """
-        self.output.append(data)
-
-
-    def errReceived(self, data):
-        """
-        Some output was received on stderr.
-        """
-        self.error.append(data)
-        # Attempt to exit promptly if a traceback is displayed, so we don't
-        # deal with timeouts.
-        lines = ''.join(self.error).split("\n")
-        if len(lines) > 1:
-            errorReportLine = lines[-2].split(": ", 1)
-            if len(errorReportLine) == 2 and ' ' not in errorReportLine[0] and '\t' not in errorReportLine[0]:
-                self.transport.signalProcess("TERM")
-
-
-    def processEnded(self, why):
-        """
-        The process is over, fire the Deferred with the output.
-        """
-        if why.check(ProcessDone) and not self.error:
-            self.deferred.callback(''.join(self.output))
-        else:
-            self.deferred.errback(ErrorOutput(''.join(self.error)))
-
-
-class GatewayTestCase(TestCase):
-
-    def setUp(self):
-        testRoot = os.path.join(os.path.dirname(__file__), "gateway")
-        templateName = os.path.join(testRoot, "caldavd.plist")
-        templateFile = open(templateName)
-        template = templateFile.read()
-        templateFile.close()
-
-        tmpDir = FilePath(self.mktemp())
-        tmpDir.makedirs()
-        dataRoot = tmpDir.child("data")
-        dataRoot.makedirs()
-        docRoot = tmpDir.child("documents")
-        docRoot.makedirs()
-
-        # Copy xml files to a temp directory because they may get modified
-
-        origUsersFile = FilePath(os.path.join(os.path.dirname(__file__),
-            "gateway", "users-groups.xml"))
-        copyUsersFile = tmpDir.child("users-groups.xml")
-        origUsersFile.copyTo(copyUsersFile)
-
-        origResourcesFile = FilePath(os.path.join(os.path.dirname(__file__),
-            "gateway", "resources-locations.xml"))
-        copyResourcesFile = tmpDir.child("resources-locations.xml")
-        origResourcesFile.copyTo(copyResourcesFile)
-
-        origAugmentFile = FilePath(os.path.join(os.path.dirname(__file__),
-            "gateway", "augments.xml"))
-        copyAugmentFile = tmpDir.child("augments.xml")
-        origAugmentFile.copyTo(copyAugmentFile)
-
-        proxyFile = tmpDir.child("proxies.sqlite")
-
-        newConfig = template % {
-            'DataRoot' : dataRoot.path,
-            'DocumentRoot' : docRoot.path,
-            'DirectoryXMLFile' : copyUsersFile.path,
-            'ResourceXMLFile' : copyResourcesFile.path,
-            'AugmentXMLFile' : copyAugmentFile.path,
-            'ProxyDBFile' : proxyFile.path,
-        }
-        configFilePath = tmpDir.child("caldavd.plist")
-        configFilePath.setContent(newConfig)
-
-        self.configFileName = configFilePath.path
-        config.load(self.configFileName)
-
-        super(GatewayTestCase, self).setUp()
-
-        # Make sure trial puts the reactor in the right state, by letting it
-        # run one reactor iteration.  (Ignore me, please.)
-        d = Deferred()
-        reactor.callLater(0, d.callback, True)
-        return d
-
-    @inlineCallbacks
-    def runCommand(self, command):
-        """
-        Run the given command by feeding it as standard input to
-        calendarserver_command_gateway in a subprocess.
-        """
-        sourceRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
-        python = os.path.join(sourceRoot, "python")
-        gateway = os.path.join(sourceRoot, "bin", "calendarserver_command_gateway")
-
-        args = [python, gateway, "-f", self.configFileName]
-        cwd = sourceRoot
-
-        deferred = Deferred()
-        reactor.spawnProcess(CapturingProcessProtocol(deferred, command), python, args, env=os.environ, path=cwd)
-        output = yield deferred
-        try:
-            plist = plistlib.readPlistFromString(output)
-        except xml.parsers.expat.ExpatError, e:
-            print "Error (%s) parsing (%s)" % (e, output)
-            raise
-
-        returnValue(plist)
-
-    @inlineCallbacks
-    def test_getLocationList(self):
-        results = yield self.runCommand(command_getLocationList)
-        self.assertEquals(len(results['result']), 10)
-
-    @inlineCallbacks
-    def test_getResourceList(self):
-        results = yield self.runCommand(command_getResourceList)
-        self.assertEquals(len(results['result']), 10)
-
-    @inlineCallbacks
-    def test_createLocation(self):
-        directory = getDirectory()
-
-        record = directory.recordWithUID("createdlocation01")
-        self.assertEquals(record, None)
-
-        yield self.runCommand(command_createLocation)
-
-        directory.flushCaches()
-        record = directory.recordWithUID("createdlocation01")
-        self.assertNotEquals(record, None)
-
-    @inlineCallbacks
-    def test_destroyRecord(self):
-        directory = getDirectory()
-
-        record = directory.recordWithUID("location01")
-        self.assertNotEquals(record, None)
-
-        yield self.runCommand(command_deleteLocation)
-
-        directory.flushCaches()
-        record = directory.recordWithUID("location01")
-        self.assertEquals(record, None)
-
-    @inlineCallbacks
-    def test_addWriteProxy(self):
-        results = yield self.runCommand(command_addWriteProxy)
-        self.assertEquals(len(results['result']['Proxies']), 1)
-
-    @inlineCallbacks
-    def test_removeWriteProxy(self):
-        results = yield self.runCommand(command_addWriteProxy)
-        results = yield self.runCommand(command_removeWriteProxy)
-        self.assertEquals(len(results['result']['Proxies']), 0)
-
-
-
-command_deleteLocation = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>deleteLocation</string>
-        <key>GeneratedUID</key>
-        <string>guidoffice3</string>
-</dict>
-</plist>
-"""
-
-command_addReadProxy = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>addReadProxy</string>
-        <key>Principal</key>
-        <string>locations:location01</string>
-        <key>Proxy</key>
-        <string>users:user03</string>
-</dict>
-</plist>
-"""
-
-command_addWriteProxy = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>addWriteProxy</string>
-        <key>Principal</key>
-        <string>locations:location01</string>
-        <key>Proxy</key>
-        <string>users:user01</string>
-</dict>
-</plist>
-"""
-
-command_createLocation = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>createLocation</string>
-        <key>AutoSchedule</key>
-        <true/>
-        <key>GeneratedUID</key>
-        <string>createdlocation01</string>
-        <key>RealName</key>
-        <string>Created Location 01</string>
-        <key>RecordName</key>
-        <array>
-                <string>createdlocation01</string>
-        </array>
-</dict>
-</plist>
-"""
-
-command_createResource = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>createResource</string>
-        <key>AutoSchedule</key>
-        <true/>
-        <key>GeneratedUID</key>
-        <string>guidlaptop1</string>
-        <key>RealName</key>
-        <string>Laptop 1</string>
-        <key>RecordName</key>
-        <array>
-                <string>laptop1</string>
-        </array>
-</dict>
-</plist>
-"""
-
-command_deleteLocation = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>deleteLocation</string>
-        <key>GeneratedUID</key>
-        <string>location01</string>
-</dict>
-</plist>
-"""
-
-command_deleteResource = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>deleteResource</string>
-        <key>GeneratedUID</key>
-        <string>guidlaptop1</string>
-</dict>
-</plist>
-"""
-
-command_getLocationList = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>getLocationList</string>
-</dict>
-</plist>
-"""
-
-command_getResourceList = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>getResourceList</string>
-</dict>
-</plist>
-"""
-
-command_listReadProxies = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>listReadProxies</string>
-        <key>Principal</key>
-        <string>locations:location01</string>
-</dict>
-</plist>
-"""
-
-command_listWriteProxies = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>listWriteProxies</string>
-        <key>Principal</key>
-        <string>locations:location01</string>
-</dict>
-</plist>
-"""
-
-command_removeReadProxy = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>removeReadProxy</string>
-        <key>Principal</key>
-        <string>locations:location01</string>
-        <key>Proxy</key>
-        <string>users:user03</string>
-</dict>
-</plist>
-"""
-
-command_removeWriteProxy = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>removeWriteProxy</string>
-        <key>Principal</key>
-        <string>locations:location01</string>
-        <key>Proxy</key>
-        <string>users:user01</string>
-</dict>
-</plist>
-"""

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py (from rev 5079, CalendarServer/trunk/calendarserver/tools/test/test_gateway.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/test/test_gateway.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,405 @@
+##
+# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+import os
+import plistlib
+import xml
+
+from twisted.python.filepath import FilePath
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
+from twisted.internet.error import ProcessDone
+from twisted.internet.protocol import ProcessProtocol
+
+from twistedcaldav.config import config
+from twistedcaldav.test.util import TestCase
+from calendarserver.tools.util import getDirectory
+
+
+class ErrorOutput(Exception):
+    """
+    The process produced some error output and exited with a non-zero exit
+    code.
+    """
+
+
+class CapturingProcessProtocol(ProcessProtocol):
+    """
+    A L{ProcessProtocol} that captures its output and error.
+
+    @ivar output: a C{list} of all C{str}s received to stderr.
+
+    @ivar error: a C{list} of all C{str}s received to stderr.
+    """
+
+    def __init__(self, deferred, inputData):
+        """
+        Initialize a L{CapturingProcessProtocol}.
+
+        @param deferred: the L{Deferred} to fire when the process is complete.
+
+        @param inputData: a C{str} to feed to the subprocess's stdin.
+        """
+        self.deferred = deferred
+        self.input = inputData
+        self.output = []
+        self.error = []
+
+
+    def connectionMade(self):
+        """
+        The process started; feed its input on stdin.
+        """
+        self.transport.write(self.input)
+        self.transport.closeStdin()
+
+
+    def outReceived(self, data):
+        """
+        Some output was received on stdout.
+        """
+        self.output.append(data)
+
+
+    def errReceived(self, data):
+        """
+        Some output was received on stderr.
+        """
+        self.error.append(data)
+        # Attempt to exit promptly if a traceback is displayed, so we don't
+        # deal with timeouts.
+        lines = ''.join(self.error).split("\n")
+        if len(lines) > 1:
+            errorReportLine = lines[-2].split(": ", 1)
+            if len(errorReportLine) == 2 and ' ' not in errorReportLine[0] and '\t' not in errorReportLine[0]:
+                self.transport.signalProcess("TERM")
+
+
+    def processEnded(self, why):
+        """
+        The process is over, fire the Deferred with the output.
+        """
+        if why.check(ProcessDone) and not self.error:
+            self.deferred.callback(''.join(self.output))
+        else:
+            self.deferred.errback(ErrorOutput(''.join(self.error)))
+
+
+class GatewayTestCase(TestCase):
+
+    def setUp(self):
+        testRoot = os.path.join(os.path.dirname(__file__), "gateway")
+        templateName = os.path.join(testRoot, "caldavd.plist")
+        templateFile = open(templateName)
+        template = templateFile.read()
+        templateFile.close()
+
+        tmpDir = FilePath(self.mktemp())
+        tmpDir.makedirs()
+        dataRoot = tmpDir.child("data")
+        dataRoot.makedirs()
+        docRoot = tmpDir.child("documents")
+        docRoot.makedirs()
+
+        # Copy xml files to a temp directory because they may get modified
+
+        origUsersFile = FilePath(os.path.join(os.path.dirname(__file__),
+            "gateway", "users-groups.xml"))
+        copyUsersFile = tmpDir.child("users-groups.xml")
+        origUsersFile.copyTo(copyUsersFile)
+
+        origResourcesFile = FilePath(os.path.join(os.path.dirname(__file__),
+            "gateway", "resources-locations.xml"))
+        copyResourcesFile = tmpDir.child("resources-locations.xml")
+        origResourcesFile.copyTo(copyResourcesFile)
+
+        origAugmentFile = FilePath(os.path.join(os.path.dirname(__file__),
+            "gateway", "augments.xml"))
+        copyAugmentFile = tmpDir.child("augments.xml")
+        origAugmentFile.copyTo(copyAugmentFile)
+
+        proxyFile = tmpDir.child("proxies.sqlite")
+
+        newConfig = template % {
+            'DataRoot' : dataRoot.path,
+            'DocumentRoot' : docRoot.path,
+            'DirectoryXMLFile' : copyUsersFile.path,
+            'ResourceXMLFile' : copyResourcesFile.path,
+            'AugmentXMLFile' : copyAugmentFile.path,
+            'ProxyDBFile' : proxyFile.path,
+        }
+        configFilePath = tmpDir.child("caldavd.plist")
+        configFilePath.setContent(newConfig)
+
+        self.configFileName = configFilePath.path
+        config.load(self.configFileName)
+
+        super(GatewayTestCase, self).setUp()
+
+        # Make sure trial puts the reactor in the right state, by letting it
+        # run one reactor iteration.  (Ignore me, please.)
+        d = Deferred()
+        reactor.callLater(0, d.callback, True)
+        return d
+
+    @inlineCallbacks
+    def runCommand(self, command):
+        """
+        Run the given command by feeding it as standard input to
+        calendarserver_command_gateway in a subprocess.
+        """
+        sourceRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
+        python = os.path.join(sourceRoot, "python")
+        gateway = os.path.join(sourceRoot, "bin", "calendarserver_command_gateway")
+
+        args = [python, gateway, "-f", self.configFileName]
+        cwd = sourceRoot
+
+        deferred = Deferred()
+        reactor.spawnProcess(CapturingProcessProtocol(deferred, command), python, args, env=os.environ, path=cwd)
+        output = yield deferred
+        try:
+            plist = plistlib.readPlistFromString(output)
+        except xml.parsers.expat.ExpatError, e:
+            print "Error (%s) parsing (%s)" % (e, output)
+            raise
+
+        returnValue(plist)
+
+    @inlineCallbacks
+    def test_getLocationList(self):
+        results = yield self.runCommand(command_getLocationList)
+        self.assertEquals(len(results['result']), 10)
+
+    @inlineCallbacks
+    def test_getResourceList(self):
+        results = yield self.runCommand(command_getResourceList)
+        self.assertEquals(len(results['result']), 10)
+
+    @inlineCallbacks
+    def test_createLocation(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID("createdlocation01")
+        self.assertEquals(record, None)
+
+        yield self.runCommand(command_createLocation)
+
+        directory.flushCaches()
+        record = directory.recordWithUID("createdlocation01")
+        self.assertNotEquals(record, None)
+
+    @inlineCallbacks
+    def test_destroyRecord(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID("location01")
+        self.assertNotEquals(record, None)
+
+        yield self.runCommand(command_deleteLocation)
+
+        directory.flushCaches()
+        record = directory.recordWithUID("location01")
+        self.assertEquals(record, None)
+
+    @inlineCallbacks
+    def test_addWriteProxy(self):
+        results = yield self.runCommand(command_addWriteProxy)
+        self.assertEquals(len(results['result']['Proxies']), 1)
+
+    @inlineCallbacks
+    def test_removeWriteProxy(self):
+        results = yield self.runCommand(command_addWriteProxy)
+        results = yield self.runCommand(command_removeWriteProxy)
+        self.assertEquals(len(results['result']['Proxies']), 0)
+
+
+
+command_deleteLocation = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>deleteLocation</string>
+        <key>GeneratedUID</key>
+        <string>guidoffice3</string>
+</dict>
+</plist>
+"""
+
+command_addReadProxy = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>addReadProxy</string>
+        <key>Principal</key>
+        <string>locations:location01</string>
+        <key>Proxy</key>
+        <string>users:user03</string>
+</dict>
+</plist>
+"""
+
+command_addWriteProxy = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>addWriteProxy</string>
+        <key>Principal</key>
+        <string>locations:location01</string>
+        <key>Proxy</key>
+        <string>users:user01</string>
+</dict>
+</plist>
+"""
+
+command_createLocation = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>createLocation</string>
+        <key>AutoSchedule</key>
+        <true/>
+        <key>GeneratedUID</key>
+        <string>createdlocation01</string>
+        <key>RealName</key>
+        <string>Created Location 01</string>
+        <key>RecordName</key>
+        <array>
+                <string>createdlocation01</string>
+        </array>
+</dict>
+</plist>
+"""
+
+command_createResource = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>createResource</string>
+        <key>AutoSchedule</key>
+        <true/>
+        <key>GeneratedUID</key>
+        <string>guidlaptop1</string>
+        <key>RealName</key>
+        <string>Laptop 1</string>
+        <key>RecordName</key>
+        <array>
+                <string>laptop1</string>
+        </array>
+</dict>
+</plist>
+"""
+
+command_deleteLocation = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>deleteLocation</string>
+        <key>GeneratedUID</key>
+        <string>location01</string>
+</dict>
+</plist>
+"""
+
+command_deleteResource = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>deleteResource</string>
+        <key>GeneratedUID</key>
+        <string>guidlaptop1</string>
+</dict>
+</plist>
+"""
+
+command_getLocationList = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>getLocationList</string>
+</dict>
+</plist>
+"""
+
+command_getResourceList = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>getResourceList</string>
+</dict>
+</plist>
+"""
+
+command_listReadProxies = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>listReadProxies</string>
+        <key>Principal</key>
+        <string>locations:location01</string>
+</dict>
+</plist>
+"""
+
+command_listWriteProxies = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>listWriteProxies</string>
+        <key>Principal</key>
+        <string>locations:location01</string>
+</dict>
+</plist>
+"""
+
+command_removeReadProxy = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>removeReadProxy</string>
+        <key>Principal</key>
+        <string>locations:location01</string>
+        <key>Proxy</key>
+        <string>users:user03</string>
+</dict>
+</plist>
+"""
+
+command_removeWriteProxy = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>removeWriteProxy</string>
+        <key>Principal</key>
+        <string>locations:location01</string>
+        <key>Proxy</key>
+        <string>users:user01</string>
+</dict>
+</plist>
+"""

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/util.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/calendarserver/tools/util.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -24,15 +24,21 @@
 
 import os
 from time import sleep
+import socket
 
 from twisted.python.reflect import namedClass
 
-import socket
+from calendarserver.provision.root import RootResource
+from twistedcaldav import memcachepool
 from twistedcaldav.config import config, ConfigurationError
 from twistedcaldav.directory import augment, calendaruserproxy
+from twistedcaldav.directory.aggregate import AggregateDirectoryService
 from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
+from twistedcaldav.notify import installNotificationClient
+from twistedcaldav.static import CalendarHomeProvisioningFile
 from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
 
+
 def loadConfig(configFileName):
     if configFileName is None:
         configFileName = DEFAULT_CONFIG_FILE
@@ -45,9 +51,8 @@
     return config
 
 def getDirectory():
-    BaseDirectoryService = namedClass(config.DirectoryService.type)
 
-    class MyDirectoryService (BaseDirectoryService):
+    class MyDirectoryService (AggregateDirectoryService):
         def getPrincipalCollection(self):
             if not hasattr(self, "_principalCollection"):
                 #
@@ -97,12 +102,38 @@
     calendaruserproxy.ProxyDBService = proxydbClass(**config.ProxyDBService.params)
 
     # Wait for directory service to become available
-    directory = MyDirectoryService(config.DirectoryService.params)
+    BaseDirectoryService = namedClass(config.DirectoryService.type)
+    directory = BaseDirectoryService(config.DirectoryService.params)
     while not directory.isAvailable():
         sleep(5)
 
-    return directory
 
+    directories = [directory]
+
+    if config.ResourceService.Enabled:
+        resourceClass = namedClass(config.ResourceService.type)
+        resourceDirectory = resourceClass(config.ResourceService.params)
+        directories.append(resourceDirectory)
+
+    aggregate = MyDirectoryService(directories)
+
+    #
+    # Wire up the resource hierarchy
+    #
+    principalCollection = aggregate.getPrincipalCollection()
+    root = RootResource(
+        config.DocumentRoot,
+        principalCollections=(principalCollection,),
+    )
+    root.putChild("principals", principalCollection)
+    calendarCollection = CalendarHomeProvisioningFile(
+        os.path.join(config.DocumentRoot, "calendars"),
+        aggregate, "/calendars/",
+    )
+    root.putChild("calendars", calendarCollection)
+
+    return aggregate
+
 class DummyDirectoryService (DirectoryService):
     realmName = ""
     baseGUID = "51856FD4-5023-4890-94FE-4356C4AAC3E4"
@@ -147,3 +178,25 @@
 
     except socket.error:
         config.Memcached.Pools.Default.ClientEnabled = False
+
+
+def setupMemcached(config):
+    #
+    # Connect to memcached
+    #
+    memcachepool.installPools(
+        config.Memcached.Pools,
+        config.Memcached.MaxClients
+    )
+    autoDisableMemcached(config)
+
+def setupNotifications(config):
+    #
+    # Connect to notifications
+    #
+    if config.Notifications.Enabled:
+        installNotificationClient(
+            config.Notifications.InternalNotificationHost,
+            config.Notifications.InternalNotificationPort,
+        )
+

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/accounts-test.xml
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/accounts-test.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/accounts-test.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -53,18 +53,6 @@
     <first-name>Public</first-name>
     <last-name>%02d</last-name>
   </user>
-  <location repeat="10">
-    <uid>location%02d</uid>
-    <guid>location%02d</guid>
-    <password>location%02d</password>
-    <name>Room %02d</name>
-  </location>
-  <resource repeat="10">
-    <uid>resource%02d</uid>
-    <guid>resource%02d</guid>
-    <password>resource%02d</password>
-    <name>Resource %02d</name>
-  </resource>
   <group>
     <uid>group01</uid>
     <guid>group01</guid>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/resources-test.xml (from rev 5079, CalendarServer/trunk/conf/auth/resources-test.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/resources-test.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/auth/resources-test.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <location repeat="10">
+    <uid>location%02d</uid>
+    <guid>location%02d</guid>
+    <password>location%02d</password>
+    <name>Room %02d</name>
+  </location>
+  <resource repeat="10">
+    <uid>resource%02d</uid>
+    <guid>resource%02d</guid>
+    <password>resource%02d</password>
+    <name>Resource %02d</name>
+  </resource>
+</accounts>

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-apple.plist	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-apple.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -159,6 +159,22 @@
     </dict>
 
 
+    <!-- Resource and Locations service -->
+    <key>ResourceService</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>/Library/CalendarServer/Data/resources.xml</string>
+      </dict>
+    </dict>
+
+
     <!--
         Augment service
 
@@ -224,7 +240,7 @@
       <key>params</key>
       <dict>
         <key>dbpath</key>
-        <string>/etc/caldavd/proxies.sqlite</string>
+        <string>/Library/CalendarServer/Data/proxies.sqlite</string>
       </dict>
     </dict>
 

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-test.plist	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/caldavd-test.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -131,9 +131,34 @@
       <dict>
         <key>xmlFile</key>
         <string>conf/auth/accounts-test.xml</string>
+        <key>recordTypes</key>
+        <array>
+            <string>users</string>
+            <string>groups</string>
+        </array>
       </dict>
     </dict>
     
+    <!-- Resource and Location Service -->
+    <key>ResourceService</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>conf/auth/resources-test.xml</string>
+        <key>recordTypes</key>
+        <array>
+            <string>locations</string>
+            <string>resources</string>
+        </array>
+      </dict>
+    </dict>
+    
     <!-- Open Directory Service (Mac OS X) -->
     <!--
     <key>DirectoryService</key>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist
===================================================================
--- CalendarServer/trunk/conf/resources/caldavd-resources.plist	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,747 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    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 "AS IS" 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.
-  -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-  <dict>
-
-    <!--
-        Public network address information
-
-        This is the server's public network address, which is provided to
-        clients in URLs and the like.  It may or may not be the network
-        address that the server is listening to directly, though it is by
-        default.  For example, it may be the address of a load balancer or
-        proxy which forwards connections to the server.
-      -->
-
-    <!-- Network host name [empty = system host name] -->
-    <key>ServerHostName</key>
-    <string></string> <!-- The hostname clients use when connecting -->
-
-    <!-- HTTP port [0 = disable HTTP] -->
-    <key>HTTPPort</key>
-    <integer>8008</integer>
-
-    <!-- SSL port [0 = disable HTTPS] -->
-    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
-    <key>SSLPort</key>
-    <integer>8443</integer>
-
-    <!-- Redirect non-SSL ports to an SSL port -->
-    <key>RedirectHTTPToHTTPS</key>
-    <false/>
-
-    <!--
-        Network address configuration information
-
-        This configures the actual network address that the server binds to.
-      -->
-
-    <!-- List of IP addresses to bind to [empty = all] -->
-    <key>BindAddresses</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
-    <key>BindHTTPPorts</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
-    <key>BindSSLPorts</key>
-    <array>
-    </array>
-
-
-    <!--
-        Data Store
-      -->
-
-    <!-- Data root -->
-    <key>DataRoot</key>
-    <string>data/</string>
-
-    <!-- Document root -->
-    <key>DocumentRoot</key>
-    <string>twistedcaldav/test/data/</string>
-
-    <!-- Child aliases -->
-    <key>Aliases</key>
-    <dict>
-      <!--
-      <key>foo</key>
-      <dict>
-        <key>path</key>
-        <string>/path/to/foo</string>
-      </dict>
-       -->
-    </dict>
-
-    <!-- User quota (in bytes) -->
-    <key>UserQuota</key>
-    <integer>104857600</integer><!-- 100Mb -->
-
-    <!-- Attachment size limit (in bytes) -->
-    <key>MaximumAttachmentSize</key>
-    <integer>1048576</integer><!-- 1Mb -->
-
-    <!-- Maximum number of unique attendees per entire event -->
-    <!-- 0 for no limit -->
-    <key>MaxAttendeesPerInstance</key>
-    <integer>100</integer>
-
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
-
-
-    <!--
-        Directory service
-
-        A directory service provides information about principals (eg.
-        users, groups, locations and resources) to the server.
-
-        A variety of directory services are available for use.
-      -->
-
-    <!-- XML File Directory Service -->
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>conf/resources/users-groups.xml</string>
-        <key>recordTypes</key>
-        <array>
-            <string>users</string>
-            <string>groups</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <key>ResourceService</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>conf/resources/locations-resources.xml</string>
-        <key>recordTypes</key>
-        <array>
-            <string>locations</string>
-            <string>resources</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Open Directory Service (Mac OS X) -->
-    <!--
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>node</key>
-        <string>/Search</string>
-        <key>cacheTimeout</key>
-        <integer>30</integer>
-      </dict>
-    </dict>
-    -->
-
-    <!--
-        Augment service
-
-        Augments for the directory service records to add calendar specific attributes.
-
-        A variety of augment services are available for use.
-        When using a partitioned server, a service that can be accessed from each host will be needed.
-      -->
-
-    <!-- XML File Augment Service -->
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFiles</key>
-        <array>
-	      <string>conf/auth/augments-test.xml</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Sqlite Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>/etc/caldavd/augments.sqlite</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- PostgreSQL Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>augments</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- Sqlite ProxyDB Service -->
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>data/proxies.sqlite</string>
-      </dict>
-    </dict>
-
-    <!-- PostgreSQL ProxyDB Service -->
-    <!--
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>proxies</string>
-      </dict>
-    </dict>
-     -->
-
-	<key>ProxyLoadFromFile</key>
-    <string>conf/auth/proxies-test.xml</string>
-
-    <!--
-        Special principals
-
-        These principals are granted special access and/or perform
-        special roles on the server.
-      -->
-
-    <!-- Principals with "DAV:all" access (relative URLs) -->
-    <key>AdminPrincipals</key>
-    <array>
-      <string>/principals/__uids__/admin/</string>
-    </array>
-
-    <!-- Principals with "DAV:read" access (relative URLs) -->
-    <key>ReadPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
-    </array>
-
-    <!-- Principals that can pose as other principals -->
-    <key>SudoersFile</key>
-    <string>conf/sudoers.plist</string>
-
-    <!-- Create "proxy access" principals -->
-    <key>EnableProxyPrincipals</key>
-    <true/>
-
-
-    <!--
-        Permissions
-      -->
-
-    <!-- Anonymous read access for root resource -->
-    <key>EnableAnonymousReadRoot</key>
-    <true/>
-
-    <!-- Anonymous read access for resource hierarchy -->
-    <key>EnableAnonymousReadNav</key>
-    <false/>
-
-    <!-- Enables directory listings for principals -->
-    <key>EnablePrincipalListings</key>
-    <true/>
-
-    <!-- Render calendar collections as a monolithic iCalendar object -->
-    <key>EnableMonolithicCalendars</key>
-    <true/>
-
-
-    <!--
-        Authentication
-      -->
-
-    <key>Authentication</key>
-    <dict>
-
-      <!-- Clear text; best avoided -->
-      <key>Basic</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-      </dict>
-
-      <!-- Digest challenge/response -->
-      <key>Digest</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Algorithm</key>
-        <string>md5</string>
-        <key>Qop</key>
-        <string></string>
-      </dict>
-
-      <!-- Kerberos/SPNEGO -->
-      <key>Kerberos</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>ServicePrincipal</key>
-        <string></string>
-      </dict>
-
-      <!-- Wikiserver authentication (Mac OS X) -->
-      <key>Wiki</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Cookie</key>
-        <string>sessionID</string>
-        <key>URL</key>
-        <string>http://127.0.0.1/RPC2</string>
-        <key>UserMethod</key>
-        <string>userForSession</string>
-        <key>WikiMethod</key>
-        <string>accessLevelForUserWikiCalendar</string>
-      </dict>
-
-    </dict>
-
-
-    <!--
-        Logging
-      -->
-
-    <!-- Apache-style access log -->
-    <key>AccessLogFile</key>
-    <string>logs/access.log</string>
-    <key>RotateAccessLog</key>
-    <false/>
-
-    <!-- Server activity log -->
-    <key>ErrorLogFile</key>
-    <string>logs/error.log</string>
-
-    <!-- Log levels -->
-    <key>DefaultLogLevel</key>
-    <string>info</string> <!-- debug, info, warn, error -->
-
-    <!-- Log level overrides for specific functionality -->
-    <key>LogLevels</key>
-    <dict>
-      <!--
-      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
-      <string>debug</string>
-      -->
-    </dict>
-
-    <!-- Global server stats --> 
-    <key>GlobalStatsSocket</key> 
-    <string>logs/caldavd-stats.sock</string> 
-
-    <!-- Global server stats logging period --> 
-    <key>GlobalStatsLoggingPeriod</key> 
-    <integer>60</integer> 
-
-    <!-- Global server stats logging frequency [0 = disable stats] --> 
-    <key>GlobalStatsLoggingFrequency</key> 
-    <integer>12</integer>
-
-    <!-- Server statistics file -->
-    <key>ServerStatsFile</key>
-    <string>logs/stats.plist</string>
-
-    <!-- Server process ID file -->
-    <key>PIDFile</key>
-    <string>logs/caldavd.pid</string>
-
-
-    <!--
-        Accounting
-      -->
-
-    <!-- Enable accounting for certain operations -->
-    <key>AccountingCategories</key>
-    <dict>
-      <key>iTIP</key>
-      <false/>
-      <key>HTTP</key>
-      <false/>
-    </dict>
-    <!-- Enable accounting for specific principals -->
-    <key>AccountingPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
-    </array>
-
-
-    <!--
-        SSL/TLS
-      -->
-
-    <!-- Public key -->
-    <key>SSLCertificate</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-    <!-- SSL authority chain (for intermediate certs) -->
-    <key>SSLAuthorityChain</key>
-    <string></string>
-
-    <!-- Private key -->
-    <key>SSLPrivateKey</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-
-    <!--
-        Process management
-      -->
-
-    <key>UserName</key>
-    <string></string>
-
-    <key>GroupName</key>
-    <string></string>
-
-    <key>ProcessType</key>
-    <string>Combined</string>
-
-    <key>MultiProcess</key>
-    <dict>
-      <key>ProcessCount</key>
-      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
-    </dict>
-
-
-    <!--
-        Notifications
-      -->
-
-    <key>Notifications</key>
-    <dict>
-      <!-- Time spent coalescing notifications before delivery -->
-      <key>CoalesceSeconds</key>
-      <integer>3</integer>
-
-      <key>InternalNotificationHost</key>
-      <string>localhost</string>
-
-      <key>InternalNotificationPort</key>
-      <integer>62309</integer>
-
-      <key>Services</key>
-      <dict>
-        <key>SimpleLineNotifier</key>
-        <dict>
-          <!-- Simple line notification service (for testing) -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-          <key>Port</key>
-          <integer>62308</integer>
-        </dict>
-
-        <key>XMPPNotifier</key>
-        <dict>
-          <!-- XMPP notification service -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.XMPPNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-
-          <!-- XMPP host and port to contact -->
-          <key>Host</key>
-          <string>xmpp.host.name</string>
-          <key>Port</key>
-          <integer>5222</integer>
-
-          <!-- Jabber ID and password for the server -->
-          <key>JID</key>
-          <string>jid at xmpp.host.name/resource</string>
-          <key>Password</key>
-          <string>password_goes_here</string>
-
-          <!-- PubSub service address -->
-          <key>ServiceAddress</key>
-          <string>pubsub.xmpp.host.name</string>
-
-          <key>NodeConfiguration</key>
-          <dict>
-            <key>pubsub#deliver_payloads</key>
-            <string>1</string>
-            <key>pubsub#persist_items</key>
-            <string>1</string>
-          </dict>
-
-          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
-          <key>KeepAliveSeconds</key>
-          <integer>120</integer>
-
-          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
-          <key>HeartbeatMinutes</key>
-          <integer>30</integer>
-
-          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
-          <key>AllowedJIDs</key>
-          <array>
-            <!--
-            <string>*.example.com</string>
-             -->
-          </array>
-        </dict>
-      </dict>
-    </dict>
-
-
-    <!--
-        Server-to-server protocol
-      -->
-
-    <key>Scheduling</key>
-    <dict>
-
-      <!-- CalDAV protocol options -->
-      <key>CalDAV</key>
-      <dict>
-        <key>EmailDomain</key>
-        <string></string>
-        <key>HTTPDomain</key>
-        <string></string>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>OldDraftCompatibility</key>
-        <true/>
-        <key>ScheduleTagCompatibility</key>
-        <true/>
-        <key>EnablePrivateComments</key>
-        <true/>
-      </dict>
-
-      <!-- iSchedule protocol options -->
-      <key>iSchedule</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>Servers</key>
-        <string>conf/servertoserver-test.xml</string>
-      </dict>
-
-      <!-- iMIP protocol options -->
-      <key>iMIP</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>MailGatewayServer</key>
-        <string>localhost</string>
-        <key>MailGatewayPort</key>
-        <integer>62310</integer>
-        <key>Sending</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>587</integer>
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>Address</key>
-          <string></string> <!-- Address email will be sent from -->
-        </dict>
-        <key>Receiving</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>995</integer>
-          <key>Type</key>
-          <string></string> <!-- Either "pop" or "imap" -->
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>PollingSeconds</key>
-          <integer>30</integer>
-        </dict>
-        <key>AddressPatterns</key>
-        <array>
-          <string>mailto:.*</string>
-        </array>
-      </dict>
-
-	  <!-- General options for scheduling -->
-	  <key>Options</key>
-	  <dict>
-        <key>AllowGroupAsOrganizer</key>
-        <false/>
-        <key>AllowLocationAsOrganizer</key>
-        <false/>
-        <key>AllowResourceAsOrganizer</key>
-        <false/>
-       </dict>
-
-    </dict>
-
-
-    <!--
-        Free-busy URL protocol
-      -->
-
-    <key>FreeBusyURL</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>TimePeriod</key>
-      <integer>14</integer>
-      <key>AnonymousAccess</key>
-      <false/>
-    </dict>
-
-
-    <!--
-        Non-standard CalDAV extensions
-      -->
-
-    <!-- Calendar Drop Box -->
-    <key>EnableDropBox</key>
-    <true/>
-
-    <!-- Private Events -->
-    <key>EnablePrivateEvents</key>
-    <true/>
-
-    <!-- Timezone Service -->
-    <key>EnableTimezoneService</key>
-    <true/>
-
-
-    <!--
-        Miscellaneous items
-      -->
-
-    <!-- Service ACLs (Mac OS X) -->
-    <key>EnableSACLs</key>
-    <false/>
-
-    <!-- Web-based administration -->
-    <key>EnableWebAdmin</key>
-    <true/>
-
-    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
-    <key>ResponseCompression</key>
-    <false/>
-    
-    <!-- The retry-after value (in seconds) to return with a 503 error. -->
-    <key>HTTPRetryAfter</key>
-    <integer>180</integer>
-
-    <!-- A unix socket used for communication between the child and master processes.
-         An empty value tells the server to use a tcp socket instead. -->
-    <key>ControlSocket</key>
-    <string>logs/caldavd.sock</string>
-
-    <!-- Support for Memcached -->
-    <key>Memcached</key>
-    <dict>
-      <key>MaxClients</key>
-      <integer>5</integer>
-      <key>memcached</key>
-      <string>memcached</string> <!-- Find in PATH -->
-      <key>Options</key>
-      <array>
-        <!--<string>-vv</string>-->
-      </array>
-    </dict>
-
-    <!-- Response Caching -->
-    <key>ResponseCacheTimeout</key>
-    <integer>30</integer> <!-- in minutes -->
-
-
-    <!--
-        Twisted
-      -->
-
-    <key>Twisted</key>
-    <dict>
-      <key>twistd</key>
-      <string>../Twisted/bin/twistd</string>
-    </dict>
-
-
-    <key>Localization</key>
-    <dict>
-      <key>LocalesDirectory</key>
-      <string>locales</string>
-      <key>Language</key>
-      <string>English</string>
-    </dict>
-
-
-  </dict>
-</plist>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist (from rev 5079, CalendarServer/trunk/conf/resources/caldavd-resources.plist)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/caldavd-resources.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,747 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    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 "AS IS" 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.
+  -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+
+    <!--
+        Public network address information
+
+        This is the server's public network address, which is provided to
+        clients in URLs and the like.  It may or may not be the network
+        address that the server is listening to directly, though it is by
+        default.  For example, it may be the address of a load balancer or
+        proxy which forwards connections to the server.
+      -->
+
+    <!-- Network host name [empty = system host name] -->
+    <key>ServerHostName</key>
+    <string></string> <!-- The hostname clients use when connecting -->
+
+    <!-- HTTP port [0 = disable HTTP] -->
+    <key>HTTPPort</key>
+    <integer>8008</integer>
+
+    <!-- SSL port [0 = disable HTTPS] -->
+    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+    <key>SSLPort</key>
+    <integer>8443</integer>
+
+    <!-- Redirect non-SSL ports to an SSL port -->
+    <key>RedirectHTTPToHTTPS</key>
+    <false/>
+
+    <!--
+        Network address configuration information
+
+        This configures the actual network address that the server binds to.
+      -->
+
+    <!-- List of IP addresses to bind to [empty = all] -->
+    <key>BindAddresses</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+    <key>BindHTTPPorts</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+    <key>BindSSLPorts</key>
+    <array>
+    </array>
+
+
+    <!--
+        Data Store
+      -->
+
+    <!-- Data root -->
+    <key>DataRoot</key>
+    <string>data/</string>
+
+    <!-- Document root -->
+    <key>DocumentRoot</key>
+    <string>twistedcaldav/test/data/</string>
+
+    <!-- Child aliases -->
+    <key>Aliases</key>
+    <dict>
+      <!--
+      <key>foo</key>
+      <dict>
+        <key>path</key>
+        <string>/path/to/foo</string>
+      </dict>
+       -->
+    </dict>
+
+    <!-- User quota (in bytes) -->
+    <key>UserQuota</key>
+    <integer>104857600</integer><!-- 100Mb -->
+
+    <!-- Attachment size limit (in bytes) -->
+    <key>MaximumAttachmentSize</key>
+    <integer>1048576</integer><!-- 1Mb -->
+
+    <!-- Maximum number of unique attendees per entire event -->
+    <!-- 0 for no limit -->
+    <key>MaxAttendeesPerInstance</key>
+    <integer>100</integer>
+
+    <!-- Maximum number of instances allowed for a single RRULE -->
+    <!-- 0 for no limit -->
+    <key>MaxInstancesForRRULE</key>
+    <integer>400</integer>
+
+
+    <!--
+        Directory service
+
+        A directory service provides information about principals (eg.
+        users, groups, locations and resources) to the server.
+
+        A variety of directory services are available for use.
+      -->
+
+    <!-- XML File Directory Service -->
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>conf/resources/users-groups.xml</string>
+        <key>recordTypes</key>
+        <array>
+            <string>users</string>
+            <string>groups</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <key>ResourceService</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>conf/resources/locations-resources.xml</string>
+        <key>recordTypes</key>
+        <array>
+            <string>locations</string>
+            <string>resources</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Open Directory Service (Mac OS X) -->
+    <!--
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>node</key>
+        <string>/Search</string>
+        <key>cacheTimeout</key>
+        <integer>30</integer>
+      </dict>
+    </dict>
+    -->
+
+    <!--
+        Augment service
+
+        Augments for the directory service records to add calendar specific attributes.
+
+        A variety of augment services are available for use.
+        When using a partitioned server, a service that can be accessed from each host will be needed.
+      -->
+
+    <!-- XML File Augment Service -->
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFiles</key>
+        <array>
+	      <string>conf/auth/augments-test.xml</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Sqlite Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>/etc/caldavd/augments.sqlite</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- PostgreSQL Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>augments</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- Sqlite ProxyDB Service -->
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>data/proxies.sqlite</string>
+      </dict>
+    </dict>
+
+    <!-- PostgreSQL ProxyDB Service -->
+    <!--
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>proxies</string>
+      </dict>
+    </dict>
+     -->
+
+	<key>ProxyLoadFromFile</key>
+    <string>conf/auth/proxies-test.xml</string>
+
+    <!--
+        Special principals
+
+        These principals are granted special access and/or perform
+        special roles on the server.
+      -->
+
+    <!-- Principals with "DAV:all" access (relative URLs) -->
+    <key>AdminPrincipals</key>
+    <array>
+      <string>/principals/__uids__/admin/</string>
+    </array>
+
+    <!-- Principals with "DAV:read" access (relative URLs) -->
+    <key>ReadPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
+    </array>
+
+    <!-- Principals that can pose as other principals -->
+    <key>SudoersFile</key>
+    <string>conf/sudoers.plist</string>
+
+    <!-- Create "proxy access" principals -->
+    <key>EnableProxyPrincipals</key>
+    <true/>
+
+
+    <!--
+        Permissions
+      -->
+
+    <!-- Anonymous read access for root resource -->
+    <key>EnableAnonymousReadRoot</key>
+    <true/>
+
+    <!-- Anonymous read access for resource hierarchy -->
+    <key>EnableAnonymousReadNav</key>
+    <false/>
+
+    <!-- Enables directory listings for principals -->
+    <key>EnablePrincipalListings</key>
+    <true/>
+
+    <!-- Render calendar collections as a monolithic iCalendar object -->
+    <key>EnableMonolithicCalendars</key>
+    <true/>
+
+
+    <!--
+        Authentication
+      -->
+
+    <key>Authentication</key>
+    <dict>
+
+      <!-- Clear text; best avoided -->
+      <key>Basic</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+      </dict>
+
+      <!-- Digest challenge/response -->
+      <key>Digest</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Algorithm</key>
+        <string>md5</string>
+        <key>Qop</key>
+        <string></string>
+      </dict>
+
+      <!-- Kerberos/SPNEGO -->
+      <key>Kerberos</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>ServicePrincipal</key>
+        <string></string>
+      </dict>
+
+      <!-- Wikiserver authentication (Mac OS X) -->
+      <key>Wiki</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Cookie</key>
+        <string>sessionID</string>
+        <key>URL</key>
+        <string>http://127.0.0.1/RPC2</string>
+        <key>UserMethod</key>
+        <string>userForSession</string>
+        <key>WikiMethod</key>
+        <string>accessLevelForUserWikiCalendar</string>
+      </dict>
+
+    </dict>
+
+
+    <!--
+        Logging
+      -->
+
+    <!-- Apache-style access log -->
+    <key>AccessLogFile</key>
+    <string>logs/access.log</string>
+    <key>RotateAccessLog</key>
+    <false/>
+
+    <!-- Server activity log -->
+    <key>ErrorLogFile</key>
+    <string>logs/error.log</string>
+
+    <!-- Log levels -->
+    <key>DefaultLogLevel</key>
+    <string>info</string> <!-- debug, info, warn, error -->
+
+    <!-- Log level overrides for specific functionality -->
+    <key>LogLevels</key>
+    <dict>
+      <!--
+      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
+      <string>debug</string>
+      -->
+    </dict>
+
+    <!-- Global server stats --> 
+    <key>GlobalStatsSocket</key> 
+    <string>logs/caldavd-stats.sock</string> 
+
+    <!-- Global server stats logging period --> 
+    <key>GlobalStatsLoggingPeriod</key> 
+    <integer>60</integer> 
+
+    <!-- Global server stats logging frequency [0 = disable stats] --> 
+    <key>GlobalStatsLoggingFrequency</key> 
+    <integer>12</integer>
+
+    <!-- Server statistics file -->
+    <key>ServerStatsFile</key>
+    <string>logs/stats.plist</string>
+
+    <!-- Server process ID file -->
+    <key>PIDFile</key>
+    <string>logs/caldavd.pid</string>
+
+
+    <!--
+        Accounting
+      -->
+
+    <!-- Enable accounting for certain operations -->
+    <key>AccountingCategories</key>
+    <dict>
+      <key>iTIP</key>
+      <false/>
+      <key>HTTP</key>
+      <false/>
+    </dict>
+    <!-- Enable accounting for specific principals -->
+    <key>AccountingPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
+    </array>
+
+
+    <!--
+        SSL/TLS
+      -->
+
+    <!-- Public key -->
+    <key>SSLCertificate</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+    <!-- SSL authority chain (for intermediate certs) -->
+    <key>SSLAuthorityChain</key>
+    <string></string>
+
+    <!-- Private key -->
+    <key>SSLPrivateKey</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+
+    <!--
+        Process management
+      -->
+
+    <key>UserName</key>
+    <string></string>
+
+    <key>GroupName</key>
+    <string></string>
+
+    <key>ProcessType</key>
+    <string>Combined</string>
+
+    <key>MultiProcess</key>
+    <dict>
+      <key>ProcessCount</key>
+      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
+    </dict>
+
+
+    <!--
+        Notifications
+      -->
+
+    <key>Notifications</key>
+    <dict>
+      <!-- Time spent coalescing notifications before delivery -->
+      <key>CoalesceSeconds</key>
+      <integer>3</integer>
+
+      <key>InternalNotificationHost</key>
+      <string>localhost</string>
+
+      <key>InternalNotificationPort</key>
+      <integer>62309</integer>
+
+      <key>Services</key>
+      <dict>
+        <key>SimpleLineNotifier</key>
+        <dict>
+          <!-- Simple line notification service (for testing) -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+          <key>Port</key>
+          <integer>62308</integer>
+        </dict>
+
+        <key>XMPPNotifier</key>
+        <dict>
+          <!-- XMPP notification service -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.XMPPNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+
+          <!-- XMPP host and port to contact -->
+          <key>Host</key>
+          <string>xmpp.host.name</string>
+          <key>Port</key>
+          <integer>5222</integer>
+
+          <!-- Jabber ID and password for the server -->
+          <key>JID</key>
+          <string>jid at xmpp.host.name/resource</string>
+          <key>Password</key>
+          <string>password_goes_here</string>
+
+          <!-- PubSub service address -->
+          <key>ServiceAddress</key>
+          <string>pubsub.xmpp.host.name</string>
+
+          <key>NodeConfiguration</key>
+          <dict>
+            <key>pubsub#deliver_payloads</key>
+            <string>1</string>
+            <key>pubsub#persist_items</key>
+            <string>1</string>
+          </dict>
+
+          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
+          <key>KeepAliveSeconds</key>
+          <integer>120</integer>
+
+          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
+          <key>HeartbeatMinutes</key>
+          <integer>30</integer>
+
+          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
+          <key>AllowedJIDs</key>
+          <array>
+            <!--
+            <string>*.example.com</string>
+             -->
+          </array>
+        </dict>
+      </dict>
+    </dict>
+
+
+    <!--
+        Server-to-server protocol
+      -->
+
+    <key>Scheduling</key>
+    <dict>
+
+      <!-- CalDAV protocol options -->
+      <key>CalDAV</key>
+      <dict>
+        <key>EmailDomain</key>
+        <string></string>
+        <key>HTTPDomain</key>
+        <string></string>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>OldDraftCompatibility</key>
+        <true/>
+        <key>ScheduleTagCompatibility</key>
+        <true/>
+        <key>EnablePrivateComments</key>
+        <true/>
+      </dict>
+
+      <!-- iSchedule protocol options -->
+      <key>iSchedule</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>Servers</key>
+        <string>conf/servertoserver-test.xml</string>
+      </dict>
+
+      <!-- iMIP protocol options -->
+      <key>iMIP</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>MailGatewayServer</key>
+        <string>localhost</string>
+        <key>MailGatewayPort</key>
+        <integer>62310</integer>
+        <key>Sending</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>587</integer>
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>Address</key>
+          <string></string> <!-- Address email will be sent from -->
+        </dict>
+        <key>Receiving</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>995</integer>
+          <key>Type</key>
+          <string></string> <!-- Either "pop" or "imap" -->
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>PollingSeconds</key>
+          <integer>30</integer>
+        </dict>
+        <key>AddressPatterns</key>
+        <array>
+          <string>mailto:.*</string>
+        </array>
+      </dict>
+
+	  <!-- General options for scheduling -->
+	  <key>Options</key>
+	  <dict>
+        <key>AllowGroupAsOrganizer</key>
+        <false/>
+        <key>AllowLocationAsOrganizer</key>
+        <false/>
+        <key>AllowResourceAsOrganizer</key>
+        <false/>
+       </dict>
+
+    </dict>
+
+
+    <!--
+        Free-busy URL protocol
+      -->
+
+    <key>FreeBusyURL</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>TimePeriod</key>
+      <integer>14</integer>
+      <key>AnonymousAccess</key>
+      <false/>
+    </dict>
+
+
+    <!--
+        Non-standard CalDAV extensions
+      -->
+
+    <!-- Calendar Drop Box -->
+    <key>EnableDropBox</key>
+    <true/>
+
+    <!-- Private Events -->
+    <key>EnablePrivateEvents</key>
+    <true/>
+
+    <!-- Timezone Service -->
+    <key>EnableTimezoneService</key>
+    <true/>
+
+
+    <!--
+        Miscellaneous items
+      -->
+
+    <!-- Service ACLs (Mac OS X) -->
+    <key>EnableSACLs</key>
+    <false/>
+
+    <!-- Web-based administration -->
+    <key>EnableWebAdmin</key>
+    <true/>
+
+    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+    <key>ResponseCompression</key>
+    <false/>
+    
+    <!-- The retry-after value (in seconds) to return with a 503 error. -->
+    <key>HTTPRetryAfter</key>
+    <integer>180</integer>
+
+    <!-- A unix socket used for communication between the child and master processes.
+         An empty value tells the server to use a tcp socket instead. -->
+    <key>ControlSocket</key>
+    <string>logs/caldavd.sock</string>
+
+    <!-- Support for Memcached -->
+    <key>Memcached</key>
+    <dict>
+      <key>MaxClients</key>
+      <integer>5</integer>
+      <key>memcached</key>
+      <string>memcached</string> <!-- Find in PATH -->
+      <key>Options</key>
+      <array>
+        <!--<string>-vv</string>-->
+      </array>
+    </dict>
+
+    <!-- Response Caching -->
+    <key>ResponseCacheTimeout</key>
+    <integer>30</integer> <!-- in minutes -->
+
+
+    <!--
+        Twisted
+      -->
+
+    <key>Twisted</key>
+    <dict>
+      <key>twistd</key>
+      <string>../Twisted/bin/twistd</string>
+    </dict>
+
+
+    <key>Localization</key>
+    <dict>
+      <key>LocalesDirectory</key>
+      <string>locales</string>
+      <key>Language</key>
+      <string>English</string>
+    </dict>
+
+
+  </dict>
+</plist>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml
===================================================================
--- CalendarServer/trunk/conf/resources/locations-resources-orig.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <location repeat="10">
-    <uid>location%02d</uid>
-    <guid>location%02d</guid>
-    <password>location%02d</password>
-    <name>Room %02d</name>
-  </location>
-  <resource repeat="10">
-    <uid>resource%02d</uid>
-    <guid>resource%02d</guid>
-    <password>resource%02d</password>
-    <name>Resource %02d</name>
-  </resource>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml (from rev 5079, CalendarServer/trunk/conf/resources/locations-resources-orig.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources-orig.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <location repeat="10">
+    <uid>location%02d</uid>
+    <guid>location%02d</guid>
+    <password>location%02d</password>
+    <name>Room %02d</name>
+  </location>
+  <resource repeat="10">
+    <uid>resource%02d</uid>
+    <guid>resource%02d</guid>
+    <password>resource%02d</password>
+    <name>Resource %02d</name>
+  </resource>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml
===================================================================
--- CalendarServer/trunk/conf/resources/locations-resources.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <location repeat="10">
-    <uid>location%02d</uid>
-    <guid>location%02d</guid>
-    <password>location%02d</password>
-    <name>Room %02d</name>
-  </location>
-  <resource repeat="10">
-    <uid>resource%02d</uid>
-    <guid>resource%02d</guid>
-    <password>resource%02d</password>
-    <name>Resource %02d</name>
-  </resource>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml (from rev 5079, CalendarServer/trunk/conf/resources/locations-resources.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/locations-resources.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <location repeat="10">
+    <uid>location%02d</uid>
+    <guid>location%02d</guid>
+    <password>location%02d</password>
+    <name>Room %02d</name>
+  </location>
+  <resource repeat="10">
+    <uid>resource%02d</uid>
+    <guid>resource%02d</guid>
+    <password>resource%02d</password>
+    <name>Resource %02d</name>
+  </resource>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml
===================================================================
--- CalendarServer/trunk/conf/resources/users-groups.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <user>
-    <uid>admin</uid>
-    <guid>admin</guid>
-    <password>admin</password>
-    <name>Super User</name>
-    <first-name>Super</first-name>
-    <last-name>User</last-name>
-  </user>
-  <user>
-    <uid>apprentice</uid>
-    <guid>apprentice</guid>
-    <password>apprentice</password>
-    <name>Apprentice Super User</name>
-    <first-name>Apprentice</first-name>
-    <last-name>Super User</last-name>
-  </user>
-  <user repeat="99">
-    <uid>user%02d</uid>
-    <uid>User %02d</uid>
-    <guid>user%02d</guid>
-    <password>user%02d</password>
-    <name>User %02d</name>
-    <first-name>User</first-name>
-    <last-name>%02d</last-name>
-    <email-address>user%02d at example.com</email-address>
-  </user>
-  <user repeat="10">
-    <uid>public%02d</uid>
-    <guid>public%02d</guid>
-    <password>public%02d</password>
-    <name>Public %02d</name>
-    <first-name>Public</first-name>
-    <last-name>%02d</last-name>
-  </user>
-  <group>
-    <uid>group01</uid>
-    <guid>group01</guid>
-    <password>group01</password>
-    <name>Group 01</name>
-    <members>
-      <member type="users">user01</member>
-    </members>
-  </group>
-  <group>
-    <uid>group02</uid>
-    <guid>group02</guid>
-    <password>group02</password>
-    <name>Group 02</name>
-    <members>
-      <member type="users">user06</member>
-      <member type="users">user07</member>
-    </members>
-  </group>
-  <group>
-    <uid>group03</uid>
-    <guid>group03</guid>
-    <password>group03</password>
-    <name>Group 03</name>
-    <members>
-      <member type="users">user08</member>
-      <member type="users">user09</member>
-    </members>
-  </group>
-  <group>
-    <uid>group04</uid>
-    <guid>group04</guid>
-    <password>group04</password>
-    <name>Group 04</name>
-    <members>
-      <member type="groups">group02</member>
-      <member type="groups">group03</member>
-      <member type="users">user10</member>
-    </members>
-  </group>
-  <group>
-    <uid>disabledgroup</uid>
-    <guid>disabledgroup</guid>
-    <password>disabledgroup</password>
-    <name>Disabled Group</name>
-    <members>
-      <member type="users">user01</member>
-    </members>
-  </group>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml (from rev 5079, CalendarServer/trunk/conf/resources/users-groups.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <user>
+    <uid>admin</uid>
+    <guid>admin</guid>
+    <password>admin</password>
+    <name>Super User</name>
+    <first-name>Super</first-name>
+    <last-name>User</last-name>
+  </user>
+  <user>
+    <uid>apprentice</uid>
+    <guid>apprentice</guid>
+    <password>apprentice</password>
+    <name>Apprentice Super User</name>
+    <first-name>Apprentice</first-name>
+    <last-name>Super User</last-name>
+  </user>
+  <user repeat="99">
+    <uid>user%02d</uid>
+    <uid>User %02d</uid>
+    <guid>user%02d</guid>
+    <password>user%02d</password>
+    <name>User %02d</name>
+    <first-name>User</first-name>
+    <last-name>%02d</last-name>
+    <email-address>user%02d at example.com</email-address>
+  </user>
+  <user repeat="10">
+    <uid>public%02d</uid>
+    <guid>public%02d</guid>
+    <password>public%02d</password>
+    <name>Public %02d</name>
+    <first-name>Public</first-name>
+    <last-name>%02d</last-name>
+  </user>
+  <group>
+    <uid>group01</uid>
+    <guid>group01</guid>
+    <password>group01</password>
+    <name>Group 01</name>
+    <members>
+      <member type="users">user01</member>
+    </members>
+  </group>
+  <group>
+    <uid>group02</uid>
+    <guid>group02</guid>
+    <password>group02</password>
+    <name>Group 02</name>
+    <members>
+      <member type="users">user06</member>
+      <member type="users">user07</member>
+    </members>
+  </group>
+  <group>
+    <uid>group03</uid>
+    <guid>group03</guid>
+    <password>group03</password>
+    <name>Group 03</name>
+    <members>
+      <member type="users">user08</member>
+      <member type="users">user09</member>
+    </members>
+  </group>
+  <group>
+    <uid>group04</uid>
+    <guid>group04</guid>
+    <password>group04</password>
+    <name>Group 04</name>
+    <members>
+      <member type="groups">group02</member>
+      <member type="groups">group03</member>
+      <member type="users">user10</member>
+    </members>
+  </group>
+  <group>
+    <uid>disabledgroup</uid>
+    <guid>disabledgroup</guid>
+    <password>disabledgroup</password>
+    <name>Disabled Group</name>
+    <members>
+      <member type="users">user01</member>
+    </members>
+  </group>
+</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources.xml (from rev 5079, CalendarServer/trunk/conf/resources.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/conf/resources.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="/Search">
+</accounts>

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.txt
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.txt	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.txt	2010-02-09 20:49:43 UTC (rev 5080)
@@ -3,11 +3,10 @@
 
 Calendar Server Extension                                       C. Daboo
                                                                    Apple
-                                                       December 18, 2007
+                                                        February 3, 2010
 
 
                  Private Calendar Components in CalDAV
-                       icalendar-privateevents-01
 
 Abstract
 
@@ -34,7 +33,7 @@
          4.2.2.1.  Changing the X-CALENDARSERVER-ACCESS value . . . .  5
          4.2.2.2.  X-CALENDARSERVER-ACCESS set to PUBLIC  . . . . . .  5
          4.2.2.3.  X-CALENDARSERVER-ACCESS set to PRIVATE . . . . . .  5
-         4.2.2.4.  X-CALENDARSERVER-ACCESS set to CONIFDENTIAL  . . .  6
+         4.2.2.4.  X-CALENDARSERVER-ACCESS set to CONFIDENTIAL  . . .  6
          4.2.2.5.  X-CALENDARSERVER-ACCESS set to RESTRICTED  . . . .  6
        4.2.3.  Changes to WebDAV Privileges when
                X-CALENDARSERVER-ACCESS is used  . . . . . . . . . . .  7
@@ -47,20 +46,21 @@
    7.  Normative References . . . . . . . . . . . . . . . . . . . . . 11
    Appendix A.  Acknowledgments . . . . . . . . . . . . . . . . . . . 12
    Appendix B.  Change History  . . . . . . . . . . . . . . . . . . . 12
-   Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 12
+   Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 13
 
 
 
 
+
 Daboo                                                           [Page 1]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
 1.  Introduction
 
    Internet calendaring and scheduling standards are defined by
-   iCalendar [RFC2445] and iTIP [RFC2446].  The CalDAV [RFC4791]
+   iCalendar [RFC5545] and iTIP [RFC5546].  The CalDAV [RFC4791]
    standard defines a way to access calendar data stored on a server.
    CalDAV uses WebDAV ACLs [RFC3744] to allow a calendar user to grant
    rights to other users to see the calendar data stored on the server.
@@ -110,7 +110,7 @@
 
 Daboo                                                           [Page 2]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    1.  None right now.
@@ -166,7 +166,7 @@
 
 Daboo                                                           [Page 3]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    store or when sent via an iTIP message.  When used on a CalDAV
@@ -222,7 +222,7 @@
 
 Daboo                                                           [Page 4]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
 4.2.1.1.  Example: Using OPTIONS for the Discovery of Private Event
@@ -278,12 +278,12 @@
 
 Daboo                                                           [Page 5]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    indicated in the DAV:owner property on the calendar resource.
 
-4.2.2.4.  X-CALENDARSERVER-ACCESS set to CONIFDENTIAL
+4.2.2.4.  X-CALENDARSERVER-ACCESS set to CONFIDENTIAL
 
    In addition to normal WebDAV access control, a calendar user
    authorized as a principal that is not the DAV:owner of the calendar
@@ -334,7 +334,7 @@
 
 Daboo                                                           [Page 6]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    +-----------+-------------------------------------------------------+
@@ -390,7 +390,7 @@
 
 Daboo                                                           [Page 7]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    | CONFIDENTIAL | The DAV:write privilege MUST be denied to all      |
@@ -446,7 +446,7 @@
 
 Daboo                                                           [Page 8]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    |              | REPORT -          | Any CALDAV:calendar-data       |
@@ -502,7 +502,7 @@
 
 Daboo                                                           [Page 9]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
    |              | REPORT -          | Normal ACLs apply.             |
@@ -558,7 +558,7 @@
 
 Daboo                                                          [Page 10]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
 5.  Security Considerations
@@ -604,9 +604,9 @@
 7.  Normative References
 
    [I-D.desruisseaux-caldav-sched]
-              Desruisseaux, B., "Scheduling Extensions to CalDAV",
-              draft-desruisseaux-caldav-sched-03 (work in progress),
-              January 2007.
+              Daboo, C. and B. Desruisseaux, "CalDAV Scheduling
+              Extensions to WebDAV", draft-desruisseaux-caldav-sched-08
+              (work in progress), August 2009.
 
    [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
 
@@ -614,20 +614,11 @@
 
 Daboo                                                          [Page 11]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
               Requirement Levels", BCP 14, RFC 2119, March 1997.
 
-   [RFC2445]  Dawson, F. and Stenerson, D., "Internet Calendaring and
-              Scheduling Core Object Specification (iCalendar)",
-              RFC 2445, November 1998.
-
-   [RFC2446]  Silverberg, S., Mansour, S., Dawson, F., and R. Hopson,
-              "iCalendar Transport-Independent Interoperability Protocol
-              (iTIP) Scheduling Events, BusyTime, To-dos and Journal
-              Entries", RFC 2446, November 1998.
-
    [RFC3744]  Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web
               Distributed Authoring and Versioning (WebDAV)
               Access Control Protocol", RFC 3744, May 2004.
@@ -636,15 +627,30 @@
               "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791,
               March 2007.
 
+   [RFC5545]  Desruisseaux, B., "Internet Calendaring and Scheduling
+              Core Object Specification (iCalendar)", RFC 5545,
+              September 2009.
 
+   [RFC5546]  Daboo, C., "iCalendar Transport-Independent
+              Interoperability Protocol (iTIP)", RFC 5546,
+              December 2009.
+
+
 Appendix A.  Acknowledgments
 
    This specification is the result of discussions between the Apple
-   calendar server and client teams.
+   calendar server and client teams.  Also thanks to Filip Navara for
+   comments.
 
 
 Appendix B.  Change History
 
+   Changes since -01
+
+   1.  Fixed typo.
+
+   2.  Updated to new 5545 and 5546 specs.
+
    Changes since -00
 
    1.  Added security text on how to deal with unrecognized values.
@@ -662,15 +668,9 @@
 
 
 
-
-
-
-
-
-
 Daboo                                                          [Page 12]
 
-                   CalDAV Private Calendar Components      December 2007
+                   CalDAV Private Calendar Components      February 2010
 
 
 Author's Address

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.xml
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/doc/Extensions/caldav-privateevents.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE rfc SYSTEM 'rfc2629.dtd' [
 <!ENTITY rfc2119 PUBLIC '' 'bibxml/reference.RFC.2119.xml'>
-<!ENTITY rfc2445 PUBLIC '' 'bibxml/reference.RFC.2445.xml'>
-<!ENTITY rfc2446 PUBLIC '' 'bibxml/reference.RFC.2446.xml'>
 <!ENTITY rfc3744 PUBLIC '' 'bibxml/reference.RFC.3744.xml'>
 <!ENTITY rfc4791 PUBLIC '' 'bibxml/reference.RFC.4791.xml'>
+<!ENTITY rfc5545 PUBLIC '' 'bibxml/reference.RFC.5545.xml'>
+<!ENTITY rfc5546 PUBLIC '' 'bibxml/reference.RFC.5546.xml'>
 <!ENTITY I-D.desruisseaux-caldav-sched PUBLIC '' 'bibxml3/reference.I-D.desruisseaux-caldav-sched.xml'>
 ]> 
 <?rfc toc="yes"?>
@@ -17,7 +17,7 @@
 <?rfc compact="yes"?>
 <?rfc subcompact="no"?>
 <?rfc private="Calendar Server Extension"?>
-<rfc ipr="none" docName='icalendar-privateevents-01'>
+<rfc ipr="none" docName='icalendar-privateevents-02'>
     <front>
         <title abbrev="CalDAV Private Calendar Components">Private Calendar Components in CalDAV</title> 
         <author initials="C." surname="Daboo" fullname="Cyrus Daboo">
@@ -36,7 +36,7 @@
                 <uri>http://www.apple.com/</uri>
             </address>
         </author>
-        <date year='2007'/>
+        <date />
         <abstract>
             <t>
                 This document defines an extension to CalDAV that enables a client to mark events with an access classification (e.g., "private") so that other calendar users have restricted rights to view the data in the calendar component.
@@ -46,7 +46,7 @@
     <middle>
         <section title='Introduction'>
             <t>
-                Internet calendaring and scheduling standards are defined by <xref target="RFC2445">iCalendar</xref> and <xref target="RFC2446">iTIP</xref>. The <xref target="RFC4791">CalDAV</xref> standard defines a way to access calendar data stored on a server. CalDAV uses <xref target="RFC3744">WebDAV ACLs</xref> to allow a calendar user to grant rights to other users to see the calendar data stored on the server. This is an "all or nothing" behavior, i.e. if another user is granted the DAV:read privilege to a calendar component, then that user can read all the calendar data in the calendar resource stored on the server.
+                Internet calendaring and scheduling standards are defined by <xref target="RFC5545">iCalendar</xref> and <xref target="RFC5546">iTIP</xref>. The <xref target="RFC4791">CalDAV</xref> standard defines a way to access calendar data stored on a server. CalDAV uses <xref target="RFC3744">WebDAV ACLs</xref> to allow a calendar user to grant rights to other users to see the calendar data stored on the server. This is an "all or nothing" behavior, i.e. if another user is granted the DAV:read privilege to a calendar component, then that user can read all the calendar data in the calendar resource stored on the server.
             </t>
             <t>
                 It is often the case that a calendar user wants to give "restricted" access to portions of the calendar data. e.g., allow another calendar user to see only the start and end times of an event, but not other information (such as summary, description, location, attendee list etc). There is currently no way to do that with CalDAV.
@@ -192,7 +192,7 @@
 						<t>There are no additional restrictions beyond normal WebDAV access control applied to the calendar resource for this access restriction. Note that in this case the server will explicitly set WebDAV ACLs to prevent access to the data by any principal other than the one indicated in the DAV:owner property on the calendar resource.
 						</t>
 					</section>
-					<section title="X-CALENDARSERVER-ACCESS set to CONIFDENTIAL" anchor="confidential">
+					<section title="X-CALENDARSERVER-ACCESS set to CONFIDENTIAL" anchor="confidential">
 						<t>In addition to normal WebDAV access control, a calendar user authorized as a principal that is not the DAV:owner of the calendar resource can retrieve or match on only the following iCalendar properties (assuming these properties actually occur in the calendar object):
 						</t>
 						<texttable>
@@ -529,10 +529,10 @@
     <back>
         <references title='Normative References'>
             &rfc2119;
-            &rfc2445;
-            &rfc2446;
             &rfc3744;
             &rfc4791;
+            &rfc5545;
+            &rfc5546;
             &I-D.desruisseaux-caldav-sched; 
         </references>
 <!--
@@ -541,10 +541,16 @@
 -->
         <section title='Acknowledgments'>
             <t>
-                This specification is the result of discussions between the Apple calendar server and client teams.
+                This specification is the result of discussions between the Apple calendar server and client teams. Also thanks to Filip Navara for comments.
             </t>
         </section>
         <section title='Change History'>
+          <t>Changes since -01
+            <list style='numbers'>
+              <t>Fixed typo.</t>
+              <t>Updated to new 5545 and 5546 specs.</t>
+            </list>
+		  </t>
           <t>Changes since -00
             <list style='numbers'>
               <t>Added security text on how to deal with unrecognized values.</t>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/doc/calendarserver_command_gateway.8 (from rev 5079, CalendarServer/trunk/doc/calendarserver_command_gateway.8)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/doc/calendarserver_command_gateway.8	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/doc/calendarserver_command_gateway.8	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,44 @@
+.\"
+.\" Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+.\"
+.\" Licensed under the Apache License, Version 2.0 (the "License");
+.\" 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 "AS IS" 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.
+.\"
+.\" The following requests are required for all man pages.
+.Dd June 17, 2009
+.Dt CALENDARSERVER_COMMAND_GATEWAY 8
+.Os
+.Sh NAME
+.Nm calendarserver_command_gateway
+.Nd Darwin Calendar Server Manager Gateway
+.Sh SYNOPSIS
+.Nm
+.Op Fl -config Ar file
+.Sh DESCRIPTION
+.Nm
+is a gateway between Apple OS X Server Admin and the calendar server, and is not
+meant to be run by end users.  It processes commands in plist form from stdin
+and writes results in plist form to stdout.
+.Pp
+.Sh OPTIONS
+.Bl -tag -width flag
+.It Fl h, -help
+Displays usage information
+.It Fl f, -config Ar FILE
+Use the Calendar Server configuration specified in the given file.  Defaults to /etc/caldavd/caldavd.plist.
+.El
+.Pp
+.Sh FILES
+.Bl -tag -width flag
+.It /etc/caldavd/caldavd.plist
+The Calendar Server configuration file.
+.El

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/python
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/python	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/python	2010-02-09 20:49:43 UTC (rev 5080)
@@ -4,4 +4,4 @@
 
 export PYTHONPATH="$("${wd}/run" -p)";
 
-python "$@";
+exec python "$@";

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/setup.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/setup.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/setup.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -109,6 +109,7 @@
                          "bin/caldavd",
                          "bin/calendarserver_export",
                          "bin/calendarserver_manage_principals",
+                         "bin/calendarserver_command_gateway",
                          "bin/carddavd",
                        ],
     data_files       = [ ("caldavd", ["conf/caldavd.plist"]),

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/support/Makefile.Apple
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/support/Makefile.Apple	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/support/Makefile.Apple	2010-02-09 20:49:43 UTC (rev 5080)
@@ -104,6 +104,7 @@
 	$(_v) $(INSTALL_FILE) "$(Sources)/doc/carddavd.8"                          "$(DSTROOT)$(MANDIR)/man8"
 	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_export.8"            "$(DSTROOT)$(MANDIR)/man8"
 	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_manage_principals.8" "$(DSTROOT)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_command_gateway.8"   "$(DSTROOT)$(MANDIR)/man8"
 	$(_v) gzip -9 -f "$(DSTROOT)$(MANDIR)/man8/"*.[0-9]
 	@echo "Installing launchd config..."
 	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(NSLOCALDIR)/$(NSLIBRARYSUBDIR)/$(Project)"
@@ -140,16 +141,20 @@
 	@echo "Extracting source for $(notdir $<)..."
 	$(_v) $(MKDIR) -p "$(BuildDirectory)"
 	$(_v) $(RMDIR) "$@"
-	$(_v) $(TAR) -C "$(BuildDirectory)" -xzf $<
+	$(_v) $(TAR) -C "$(BuildDirectory)" -xzf "$<"
 
 %.tgz: ../%
 	@echo "Archiving sources for $(notdir $<)..."
+	$(_v) if [ -f "$</setup.py" ] && grep setuptools "$</setup.py" > /dev/null; then \
+	        echo "Working around setuptools' stupid need to download a new version."; \
+	        cd "$<" && $(PYTHON) "setup.py" --help >/dev/null; \
+	      fi
 	$(_v) $(TAR) -C "$(dir $<)"        \
 	          --exclude=.svn           \
 	          --exclude=build          \
 	          --exclude=_trial_temp    \
 	          --exclude=dropin.cache   \
-	          -czf $@ "$(notdir $<)"
+	          -czf "$@" "$(notdir $<)"
 
 #
 # Open Source Hooey

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/support/submit
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/support/submit	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/support/submit	2010-02-09 20:49:43 UTC (rev 5080)
@@ -177,7 +177,6 @@
 
   sudo ~rc/bin/buildit "${wc}" CALENDARSERVER_CACHE_DEPS="${CALENDARSERVER_CACHE_DEPS-${wd}/.dependencies}" \
     $(file /System/Library/Frameworks/Python.framework/Versions/Current/Python | sed -n -e 's|^.*(for architecture \([^)][^)]*\).*$|-arch \1|p' | sed 's|ppc7400|ppc|') \
-    -release SnowLeopard \
     ${merge_flags};
 
   if "${package}"; then

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/aggregate.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/aggregate.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -159,12 +159,20 @@
 
     def _queryAll(self, query, *args):
         for service in self._recordTypes.values():
-            record = getattr(service, query)(*args)
+            try:
+                record = getattr(service, query)(*args)
+            except UnknownRecordTypeError:
+                record = None
             if record is not None:
                 return record
         else:
             return None
 
+    def flushCaches(self):
+        for service in self._recordTypes.values():
+            if hasattr(service, "_initCaches"):
+                service._initCaches()
+
     userRecordTypes = [DirectoryService.recordType_users]
 
     def requestAvatarId(self, credentials):
@@ -182,6 +190,28 @@
                     results.append(result)
         return results
 
+    def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
+        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+        uid=None, password=None, **kwds):
+        service = self.serviceForRecordType(recordType)
+        return service.createRecord(recordType, guid=guid,
+            shortNames=shortNames, authIDs=authIDs, fullName=fullName,
+            firstName=firstName, lastName=lastName,
+            emailAddresses=emailAddresses, uid=uid, password=password, **kwds)
+
+    def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+        uid=None, password=None, **kwds):
+        service = self.serviceForRecordType(recordType)
+        return service.updateRecord(recordType, guid, shortNames=shortNames,
+            authIDs=authIDs, fullName=fullName, firstName=firstName,
+            lastName=lastName, emailAddresses=emailAddresses, uid=uid,
+            password=password, **kwds)
+
+    def destroyRecord(self, recordType, guid):
+        service = self.serviceForRecordType(recordType)
+        return service.destroyRecord(recordType, guid)
+
 class DuplicateRecordTypeError(DirectoryError):
     """
     Duplicate record type.

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/appleopendirectory.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/appleopendirectory.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -64,6 +64,12 @@
         defaults = {
             'node' : '/Search',
             'cacheTimeout' : 30,
+            'recordTypes' : (
+                self.recordType_users,
+                self.recordType_groups,
+                self.recordType_locations,
+                self.recordType_resources,
+            ),
         }
         ignored = (
             'requireComputerRecord',
@@ -72,6 +78,8 @@
         )
         params = self.getParams(params, defaults, ignored)
 
+        self._recordTypes = params['recordTypes']
+
         super(OpenDirectoryService, self).__init__(params['cacheTimeout'])
 
         try:
@@ -161,12 +169,7 @@
                 yield GUID
 
     def recordTypes(self):
-        return (
-            self.recordType_users,
-            self.recordType_groups,
-            self.recordType_locations,
-            self.recordType_resources,
-        )
+        return self._recordTypes
 
     def groupsForGUID(self, guid):
         

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/cachingdirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/cachingdirectory.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/cachingdirectory.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -73,6 +73,7 @@
             CachingDirectoryService.INDEX_TYPE_CUA    : {},
             CachingDirectoryService.INDEX_TYPE_AUTHID   : {},
         }
+        self.directoryService = directoryService
 
     def addRecord(self, record, indexType, indexKey, useMemcache=True,
         neverExpire=False):
@@ -92,7 +93,9 @@
         for indexType, indexKey in indexTypes:
             self.recordsIndexedBy[indexType][indexKey] = record
             if useMemcache:
-                key = "dir|%s|%s" % (indexType, indexKey)
+                key = "dir|%s|%s|%s|%s" % (self.directoryService.baseGUID,
+                    indexType, indexKey,
+                    "|".join(self.directoryService.recordTypes()))
                 self.log_debug("Memcache: storing %s" % (key,))
                 try:
                     self.directoryService.memcacheSet(key, record)
@@ -150,7 +153,8 @@
         
         self.cacheTimeout = cacheTimeout * 60
 
-        self._initCaches(cacheClass)
+        self.cacheClass = cacheClass
+        self._initCaches()
 
         super(CachingDirectoryService, self).__init__()
 
@@ -200,9 +204,9 @@
                 raise DirectoryMemcacheError("Failed to read from memcache")
         return record
 
-    def _initCaches(self, cacheClass):
+    def _initCaches(self):
         self._recordCaches = dict([
-            (recordType, cacheClass(self, recordType))
+            (recordType, self.cacheClass(self, recordType))
             for recordType in self.recordTypes()
         ])
             
@@ -291,7 +295,8 @@
             
             # Check memcache
             if config.Memcached.Pools.Default.ClientEnabled:
-                key = "dir|%s|%s" % (indexType, indexKey)
+                key = "dir|%s|%s|%s|%s" % (self.baseGUID, indexType, indexKey,
+                    "|".join(self.recordTypes()))
                 self.log_debug("Memcache: checking %s" % (key,))
 
                 try:
@@ -328,7 +333,6 @@
                 self.log_debug("Found record for attribute '%s' with value '%s'" % (indexType, indexKey,))
                 return record
 
-
             # Add to negative cache with timestamp
             self.log_debug("Failed to fault record for attribute '%s' with value '%s'" % (indexType, indexKey,))
             self._disabledKeys[indexType][indexKey] = time.time()

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/directory.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/directory.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -297,6 +297,29 @@
         return result
 
 
+    def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
+        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+        uid=None, password=None, **kwds):
+        """
+        Create/persist a directory record based on the given values
+        """
+        raise NotImplementedError("Subclass must implement createRecord")
+
+    def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+        uid=None, password=None, **kwds):
+        """
+        Update/persist a directory record based on the given values
+        """
+        raise NotImplementedError("Subclass must implement updateRecord")
+
+    def destroyRecord(self, recordType, guid):
+        """
+        Remove a directory record from the directory
+        """
+        raise NotImplementedError("Subclass must implement destroyRecord")
+
+
 class DirectoryRecord(LoggingMixIn):
     implements(IDirectoryRecord)
 

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/principal.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/principal.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -539,10 +539,11 @@
 
         assert record is not None, "Principal must have a directory record"
 
-        url = joinURL(parent.principalCollectionURL(), record.guid) + slash
 
         self.record = record
         self.parent = parent
+
+        url = joinURL(parent.principalCollectionURL(), self.principalUID()) + slash
         self._url   = url
 
         self._alternate_urls = tuple([

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/accounts.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/accounts.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -54,13 +54,6 @@
     <email-address>dreid at example.com</email-address>
   </user>
   <user>
-    <uid>sagen</uid>
-    <guid>34360DAD-9BDE-4508-3C34-250394720345</guid>
-    <password>negas</password>
-    <name>Morgen Sagen</name>
-    <email-address>sagen at example.com</email-address>
-  </user>
-  <user>
     <uid>nocalendar</uid>
     <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
     <password>radnelacon</password>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/modify/augments.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2009-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
-
-<augments>
-  <record>
-    <guid>user01</guid>
-    <enable>true</enable>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <guid>user02</guid>
-    <enable>false</enable>
-    <enable-calendar>false</enable-calendar>
-  </record>
-</augments>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/modify/augments.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/augments.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
+
+<augments>
+  <record>
+    <guid>user01</guid>
+    <enable>true</enable>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <guid>user02</guid>
+    <enable>false</enable>
+    <enable-calendar>false</enable-calendar>
+  </record>
+</augments>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/modify/caldavd.plist	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,748 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    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 "AS IS" 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.
-  -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-  <dict>
-
-    <!--
-        Public network address information
-
-        This is the server's public network address, which is provided to
-        clients in URLs and the like.  It may or may not be the network
-        address that the server is listening to directly, though it is by
-        default.  For example, it may be the address of a load balancer or
-        proxy which forwards connections to the server.
-      -->
-
-    <!-- Network host name [empty = system host name] -->
-    <key>ServerHostName</key>
-    <string></string> <!-- The hostname clients use when connecting -->
-
-    <!-- HTTP port [0 = disable HTTP] -->
-    <key>HTTPPort</key>
-    <integer>8008</integer>
-
-    <!-- SSL port [0 = disable HTTPS] -->
-    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
-    <key>SSLPort</key>
-    <integer>8443</integer>
-
-    <!-- Redirect non-SSL ports to an SSL port -->
-    <key>RedirectHTTPToHTTPS</key>
-    <false/>
-
-    <!--
-        Network address configuration information
-
-        This configures the actual network address that the server binds to.
-      -->
-
-    <!-- List of IP addresses to bind to [empty = all] -->
-    <key>BindAddresses</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
-    <key>BindHTTPPorts</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
-    <key>BindSSLPorts</key>
-    <array>
-    </array>
-
-
-    <!--
-        Data Store
-      -->
-
-    <!-- Data root -->
-    <key>DataRoot</key>
-    <string>data/</string>
-
-    <!-- Document root -->
-    <key>DocumentRoot</key>
-    <string>twistedcaldav/test/data/</string>
-
-    <!-- Child aliases -->
-    <key>Aliases</key>
-    <dict>
-      <!--
-      <key>foo</key>
-      <dict>
-        <key>path</key>
-        <string>/path/to/foo</string>
-      </dict>
-       -->
-    </dict>
-
-    <!-- User quota (in bytes) -->
-    <key>UserQuota</key>
-    <integer>104857600</integer><!-- 100Mb -->
-
-    <!-- Attachment size limit (in bytes) -->
-    <key>MaximumAttachmentSize</key>
-    <integer>1048576</integer><!-- 1Mb -->
-
-    <!-- Maximum number of unique attendees per entire event -->
-    <!-- 0 for no limit -->
-    <key>MaxAttendeesPerInstance</key>
-    <integer>100</integer>
-
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
-
-
-    <!--
-        Directory service
-
-        A directory service provides information about principals (eg.
-        users, groups, locations and resources) to the server.
-
-        A variety of directory services are available for use.
-      -->
-
-    <!-- XML File Directory Service -->
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>SET IN TEST</string>
-        <key>recordTypes</key>
-        <array>
-            <string>users</string>
-            <string>groups</string>
-        </array>
-      </dict>
-    </dict>
-
-    <!-- XML File Resource Service -->
-    <key>ResourceService</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>SET IN TEST</string>
-        <key>recordTypes</key>
-        <array>
-            <string>resources</string>
-            <string>locations</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Open Directory Service (Mac OS X) -->
-    <!--
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>node</key>
-        <string>/Search</string>
-        <key>cacheTimeout</key>
-        <integer>30</integer>
-      </dict>
-    </dict>
-    -->
-
-    <!--
-        Augment service
-
-        Augments for the directory service records to add calendar specific attributes.
-
-        A variety of augment services are available for use.
-        When using a partitioned server, a service that can be accessed from each host will be needed.
-      -->
-
-    <!-- XML File Augment Service -->
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFiles</key>
-        <array>
-	      <string>SET IN TEST</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Sqlite Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>/etc/caldavd/augments.sqlite</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- PostgreSQL Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>augments</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- Sqlite ProxyDB Service -->
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>data/proxies.sqlite</string>
-      </dict>
-    </dict>
-
-    <!-- PostgreSQL ProxyDB Service -->
-    <!--
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>proxies</string>
-      </dict>
-    </dict>
-     -->
-
-	<key>ProxyLoadFromFile</key>
-    <string>conf/auth/proxies-test.xml</string>
-
-    <!--
-        Special principals
-
-        These principals are granted special access and/or perform
-        special roles on the server.
-      -->
-
-    <!-- Principals with "DAV:all" access (relative URLs) -->
-    <key>AdminPrincipals</key>
-    <array>
-      <string>/principals/__uids__/admin/</string>
-    </array>
-
-    <!-- Principals with "DAV:read" access (relative URLs) -->
-    <key>ReadPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
-    </array>
-
-    <!-- Principals that can pose as other principals -->
-    <key>SudoersFile</key>
-    <string>conf/sudoers.plist</string>
-
-    <!-- Create "proxy access" principals -->
-    <key>EnableProxyPrincipals</key>
-    <true/>
-
-
-    <!--
-        Permissions
-      -->
-
-    <!-- Anonymous read access for root resource -->
-    <key>EnableAnonymousReadRoot</key>
-    <true/>
-
-    <!-- Anonymous read access for resource hierarchy -->
-    <key>EnableAnonymousReadNav</key>
-    <false/>
-
-    <!-- Enables directory listings for principals -->
-    <key>EnablePrincipalListings</key>
-    <true/>
-
-    <!-- Render calendar collections as a monolithic iCalendar object -->
-    <key>EnableMonolithicCalendars</key>
-    <true/>
-
-
-    <!--
-        Authentication
-      -->
-
-    <key>Authentication</key>
-    <dict>
-
-      <!-- Clear text; best avoided -->
-      <key>Basic</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-      </dict>
-
-      <!-- Digest challenge/response -->
-      <key>Digest</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Algorithm</key>
-        <string>md5</string>
-        <key>Qop</key>
-        <string></string>
-      </dict>
-
-      <!-- Kerberos/SPNEGO -->
-      <key>Kerberos</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>ServicePrincipal</key>
-        <string></string>
-      </dict>
-
-      <!-- Wikiserver authentication (Mac OS X) -->
-      <key>Wiki</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Cookie</key>
-        <string>sessionID</string>
-        <key>URL</key>
-        <string>http://127.0.0.1/RPC2</string>
-        <key>UserMethod</key>
-        <string>userForSession</string>
-        <key>WikiMethod</key>
-        <string>accessLevelForUserWikiCalendar</string>
-      </dict>
-
-    </dict>
-
-
-    <!--
-        Logging
-      -->
-
-    <!-- Apache-style access log -->
-    <key>AccessLogFile</key>
-    <string>logs/access.log</string>
-    <key>RotateAccessLog</key>
-    <false/>
-
-    <!-- Server activity log -->
-    <key>ErrorLogFile</key>
-    <string>logs/error.log</string>
-
-    <!-- Log levels -->
-    <key>DefaultLogLevel</key>
-    <string>info</string> <!-- debug, info, warn, error -->
-
-    <!-- Log level overrides for specific functionality -->
-    <key>LogLevels</key>
-    <dict>
-      <!--
-      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
-      <string>debug</string>
-      -->
-    </dict>
-
-    <!-- Global server stats --> 
-    <key>GlobalStatsSocket</key> 
-    <string>logs/caldavd-stats.sock</string> 
-
-    <!-- Global server stats logging period --> 
-    <key>GlobalStatsLoggingPeriod</key> 
-    <integer>60</integer> 
-
-    <!-- Global server stats logging frequency [0 = disable stats] --> 
-    <key>GlobalStatsLoggingFrequency</key> 
-    <integer>12</integer>
-
-    <!-- Server statistics file -->
-    <key>ServerStatsFile</key>
-    <string>logs/stats.plist</string>
-
-    <!-- Server process ID file -->
-    <key>PIDFile</key>
-    <string>logs/caldavd.pid</string>
-
-
-    <!--
-        Accounting
-      -->
-
-    <!-- Enable accounting for certain operations -->
-    <key>AccountingCategories</key>
-    <dict>
-      <key>iTIP</key>
-      <false/>
-      <key>HTTP</key>
-      <false/>
-    </dict>
-    <!-- Enable accounting for specific principals -->
-    <key>AccountingPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
-    </array>
-
-
-    <!--
-        SSL/TLS
-      -->
-
-    <!-- Public key -->
-    <key>SSLCertificate</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-    <!-- SSL authority chain (for intermediate certs) -->
-    <key>SSLAuthorityChain</key>
-    <string></string>
-
-    <!-- Private key -->
-    <key>SSLPrivateKey</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-
-    <!--
-        Process management
-      -->
-
-    <key>UserName</key>
-    <string></string>
-
-    <key>GroupName</key>
-    <string></string>
-
-    <key>ProcessType</key>
-    <string>Combined</string>
-
-    <key>MultiProcess</key>
-    <dict>
-      <key>ProcessCount</key>
-      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
-    </dict>
-
-
-    <!--
-        Notifications
-      -->
-
-    <key>Notifications</key>
-    <dict>
-      <!-- Time spent coalescing notifications before delivery -->
-      <key>CoalesceSeconds</key>
-      <integer>3</integer>
-
-      <key>InternalNotificationHost</key>
-      <string>localhost</string>
-
-      <key>InternalNotificationPort</key>
-      <integer>62309</integer>
-
-      <key>Services</key>
-      <dict>
-        <key>SimpleLineNotifier</key>
-        <dict>
-          <!-- Simple line notification service (for testing) -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-          <key>Port</key>
-          <integer>62308</integer>
-        </dict>
-
-        <key>XMPPNotifier</key>
-        <dict>
-          <!-- XMPP notification service -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.XMPPNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-
-          <!-- XMPP host and port to contact -->
-          <key>Host</key>
-          <string>xmpp.host.name</string>
-          <key>Port</key>
-          <integer>5222</integer>
-
-          <!-- Jabber ID and password for the server -->
-          <key>JID</key>
-          <string>jid at xmpp.host.name/resource</string>
-          <key>Password</key>
-          <string>password_goes_here</string>
-
-          <!-- PubSub service address -->
-          <key>ServiceAddress</key>
-          <string>pubsub.xmpp.host.name</string>
-
-          <key>NodeConfiguration</key>
-          <dict>
-            <key>pubsub#deliver_payloads</key>
-            <string>1</string>
-            <key>pubsub#persist_items</key>
-            <string>1</string>
-          </dict>
-
-          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
-          <key>KeepAliveSeconds</key>
-          <integer>120</integer>
-
-          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
-          <key>HeartbeatMinutes</key>
-          <integer>30</integer>
-
-          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
-          <key>AllowedJIDs</key>
-          <array>
-            <!--
-            <string>*.example.com</string>
-             -->
-          </array>
-        </dict>
-      </dict>
-    </dict>
-
-
-    <!--
-        Server-to-server protocol
-      -->
-
-    <key>Scheduling</key>
-    <dict>
-
-      <!-- CalDAV protocol options -->
-      <key>CalDAV</key>
-      <dict>
-        <key>EmailDomain</key>
-        <string></string>
-        <key>HTTPDomain</key>
-        <string></string>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>OldDraftCompatibility</key>
-        <true/>
-        <key>ScheduleTagCompatibility</key>
-        <true/>
-        <key>EnablePrivateComments</key>
-        <true/>
-      </dict>
-
-      <!-- iSchedule protocol options -->
-      <key>iSchedule</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>Servers</key>
-        <string>conf/servertoserver-test.xml</string>
-      </dict>
-
-      <!-- iMIP protocol options -->
-      <key>iMIP</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>MailGatewayServer</key>
-        <string>localhost</string>
-        <key>MailGatewayPort</key>
-        <integer>62310</integer>
-        <key>Sending</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>587</integer>
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>Address</key>
-          <string></string> <!-- Address email will be sent from -->
-        </dict>
-        <key>Receiving</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>995</integer>
-          <key>Type</key>
-          <string></string> <!-- Either "pop" or "imap" -->
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>PollingSeconds</key>
-          <integer>30</integer>
-        </dict>
-        <key>AddressPatterns</key>
-        <array>
-          <string>mailto:.*</string>
-        </array>
-      </dict>
-
-	  <!-- General options for scheduling -->
-	  <key>Options</key>
-	  <dict>
-        <key>AllowGroupAsOrganizer</key>
-        <false/>
-        <key>AllowLocationAsOrganizer</key>
-        <false/>
-        <key>AllowResourceAsOrganizer</key>
-        <false/>
-       </dict>
-
-    </dict>
-
-
-    <!--
-        Free-busy URL protocol
-      -->
-
-    <key>FreeBusyURL</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>TimePeriod</key>
-      <integer>14</integer>
-      <key>AnonymousAccess</key>
-      <false/>
-    </dict>
-
-
-    <!--
-        Non-standard CalDAV extensions
-      -->
-
-    <!-- Calendar Drop Box -->
-    <key>EnableDropBox</key>
-    <true/>
-
-    <!-- Private Events -->
-    <key>EnablePrivateEvents</key>
-    <true/>
-
-    <!-- Timezone Service -->
-    <key>EnableTimezoneService</key>
-    <true/>
-
-
-    <!--
-        Miscellaneous items
-      -->
-
-    <!-- Service ACLs (Mac OS X) -->
-    <key>EnableSACLs</key>
-    <false/>
-
-    <!-- Web-based administration -->
-    <key>EnableWebAdmin</key>
-    <true/>
-
-    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
-    <key>ResponseCompression</key>
-    <false/>
-    
-    <!-- The retry-after value (in seconds) to return with a 503 error. -->
-    <key>HTTPRetryAfter</key>
-    <integer>180</integer>
-
-    <!-- A unix socket used for communication between the child and master processes.
-         An empty value tells the server to use a tcp socket instead. -->
-    <key>ControlSocket</key>
-    <string>logs/caldavd.sock</string>
-
-    <!-- Support for Memcached -->
-    <key>Memcached</key>
-    <dict>
-      <key>MaxClients</key>
-      <integer>5</integer>
-      <key>memcached</key>
-      <string>memcached</string> <!-- Find in PATH -->
-      <key>Options</key>
-      <array>
-        <!--<string>-vv</string>-->
-      </array>
-    </dict>
-
-    <!-- Response Caching -->
-    <key>ResponseCacheTimeout</key>
-    <integer>30</integer> <!-- in minutes -->
-
-
-    <!--
-        Twisted
-      -->
-
-    <key>Twisted</key>
-    <dict>
-      <key>twistd</key>
-      <string>../Twisted/bin/twistd</string>
-    </dict>
-
-
-    <key>Localization</key>
-    <dict>
-      <key>LocalesDirectory</key>
-      <string>locales</string>
-      <key>Language</key>
-      <string>English</string>
-    </dict>
-
-
-  </dict>
-</plist>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/modify/caldavd.plist)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/caldavd.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,748 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    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 "AS IS" 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.
+  -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+
+    <!--
+        Public network address information
+
+        This is the server's public network address, which is provided to
+        clients in URLs and the like.  It may or may not be the network
+        address that the server is listening to directly, though it is by
+        default.  For example, it may be the address of a load balancer or
+        proxy which forwards connections to the server.
+      -->
+
+    <!-- Network host name [empty = system host name] -->
+    <key>ServerHostName</key>
+    <string></string> <!-- The hostname clients use when connecting -->
+
+    <!-- HTTP port [0 = disable HTTP] -->
+    <key>HTTPPort</key>
+    <integer>8008</integer>
+
+    <!-- SSL port [0 = disable HTTPS] -->
+    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+    <key>SSLPort</key>
+    <integer>8443</integer>
+
+    <!-- Redirect non-SSL ports to an SSL port -->
+    <key>RedirectHTTPToHTTPS</key>
+    <false/>
+
+    <!--
+        Network address configuration information
+
+        This configures the actual network address that the server binds to.
+      -->
+
+    <!-- List of IP addresses to bind to [empty = all] -->
+    <key>BindAddresses</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+    <key>BindHTTPPorts</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+    <key>BindSSLPorts</key>
+    <array>
+    </array>
+
+
+    <!--
+        Data Store
+      -->
+
+    <!-- Data root -->
+    <key>DataRoot</key>
+    <string>data/</string>
+
+    <!-- Document root -->
+    <key>DocumentRoot</key>
+    <string>twistedcaldav/test/data/</string>
+
+    <!-- Child aliases -->
+    <key>Aliases</key>
+    <dict>
+      <!--
+      <key>foo</key>
+      <dict>
+        <key>path</key>
+        <string>/path/to/foo</string>
+      </dict>
+       -->
+    </dict>
+
+    <!-- User quota (in bytes) -->
+    <key>UserQuota</key>
+    <integer>104857600</integer><!-- 100Mb -->
+
+    <!-- Attachment size limit (in bytes) -->
+    <key>MaximumAttachmentSize</key>
+    <integer>1048576</integer><!-- 1Mb -->
+
+    <!-- Maximum number of unique attendees per entire event -->
+    <!-- 0 for no limit -->
+    <key>MaxAttendeesPerInstance</key>
+    <integer>100</integer>
+
+    <!-- Maximum number of instances allowed for a single RRULE -->
+    <!-- 0 for no limit -->
+    <key>MaxInstancesForRRULE</key>
+    <integer>400</integer>
+
+
+    <!--
+        Directory service
+
+        A directory service provides information about principals (eg.
+        users, groups, locations and resources) to the server.
+
+        A variety of directory services are available for use.
+      -->
+
+    <!-- XML File Directory Service -->
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>SET IN TEST</string>
+        <key>recordTypes</key>
+        <array>
+            <string>users</string>
+            <string>groups</string>
+        </array>
+      </dict>
+    </dict>
+
+    <!-- XML File Resource Service -->
+    <key>ResourceService</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>SET IN TEST</string>
+        <key>recordTypes</key>
+        <array>
+            <string>resources</string>
+            <string>locations</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Open Directory Service (Mac OS X) -->
+    <!--
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>node</key>
+        <string>/Search</string>
+        <key>cacheTimeout</key>
+        <integer>30</integer>
+      </dict>
+    </dict>
+    -->
+
+    <!--
+        Augment service
+
+        Augments for the directory service records to add calendar specific attributes.
+
+        A variety of augment services are available for use.
+        When using a partitioned server, a service that can be accessed from each host will be needed.
+      -->
+
+    <!-- XML File Augment Service -->
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFiles</key>
+        <array>
+	      <string>SET IN TEST</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Sqlite Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>/etc/caldavd/augments.sqlite</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- PostgreSQL Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>augments</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- Sqlite ProxyDB Service -->
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>data/proxies.sqlite</string>
+      </dict>
+    </dict>
+
+    <!-- PostgreSQL ProxyDB Service -->
+    <!--
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>proxies</string>
+      </dict>
+    </dict>
+     -->
+
+	<key>ProxyLoadFromFile</key>
+    <string>conf/auth/proxies-test.xml</string>
+
+    <!--
+        Special principals
+
+        These principals are granted special access and/or perform
+        special roles on the server.
+      -->
+
+    <!-- Principals with "DAV:all" access (relative URLs) -->
+    <key>AdminPrincipals</key>
+    <array>
+      <string>/principals/__uids__/admin/</string>
+    </array>
+
+    <!-- Principals with "DAV:read" access (relative URLs) -->
+    <key>ReadPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
+    </array>
+
+    <!-- Principals that can pose as other principals -->
+    <key>SudoersFile</key>
+    <string>conf/sudoers.plist</string>
+
+    <!-- Create "proxy access" principals -->
+    <key>EnableProxyPrincipals</key>
+    <true/>
+
+
+    <!--
+        Permissions
+      -->
+
+    <!-- Anonymous read access for root resource -->
+    <key>EnableAnonymousReadRoot</key>
+    <true/>
+
+    <!-- Anonymous read access for resource hierarchy -->
+    <key>EnableAnonymousReadNav</key>
+    <false/>
+
+    <!-- Enables directory listings for principals -->
+    <key>EnablePrincipalListings</key>
+    <true/>
+
+    <!-- Render calendar collections as a monolithic iCalendar object -->
+    <key>EnableMonolithicCalendars</key>
+    <true/>
+
+
+    <!--
+        Authentication
+      -->
+
+    <key>Authentication</key>
+    <dict>
+
+      <!-- Clear text; best avoided -->
+      <key>Basic</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+      </dict>
+
+      <!-- Digest challenge/response -->
+      <key>Digest</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Algorithm</key>
+        <string>md5</string>
+        <key>Qop</key>
+        <string></string>
+      </dict>
+
+      <!-- Kerberos/SPNEGO -->
+      <key>Kerberos</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>ServicePrincipal</key>
+        <string></string>
+      </dict>
+
+      <!-- Wikiserver authentication (Mac OS X) -->
+      <key>Wiki</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Cookie</key>
+        <string>sessionID</string>
+        <key>URL</key>
+        <string>http://127.0.0.1/RPC2</string>
+        <key>UserMethod</key>
+        <string>userForSession</string>
+        <key>WikiMethod</key>
+        <string>accessLevelForUserWikiCalendar</string>
+      </dict>
+
+    </dict>
+
+
+    <!--
+        Logging
+      -->
+
+    <!-- Apache-style access log -->
+    <key>AccessLogFile</key>
+    <string>logs/access.log</string>
+    <key>RotateAccessLog</key>
+    <false/>
+
+    <!-- Server activity log -->
+    <key>ErrorLogFile</key>
+    <string>logs/error.log</string>
+
+    <!-- Log levels -->
+    <key>DefaultLogLevel</key>
+    <string>info</string> <!-- debug, info, warn, error -->
+
+    <!-- Log level overrides for specific functionality -->
+    <key>LogLevels</key>
+    <dict>
+      <!--
+      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
+      <string>debug</string>
+      -->
+    </dict>
+
+    <!-- Global server stats --> 
+    <key>GlobalStatsSocket</key> 
+    <string>logs/caldavd-stats.sock</string> 
+
+    <!-- Global server stats logging period --> 
+    <key>GlobalStatsLoggingPeriod</key> 
+    <integer>60</integer> 
+
+    <!-- Global server stats logging frequency [0 = disable stats] --> 
+    <key>GlobalStatsLoggingFrequency</key> 
+    <integer>12</integer>
+
+    <!-- Server statistics file -->
+    <key>ServerStatsFile</key>
+    <string>logs/stats.plist</string>
+
+    <!-- Server process ID file -->
+    <key>PIDFile</key>
+    <string>logs/caldavd.pid</string>
+
+
+    <!--
+        Accounting
+      -->
+
+    <!-- Enable accounting for certain operations -->
+    <key>AccountingCategories</key>
+    <dict>
+      <key>iTIP</key>
+      <false/>
+      <key>HTTP</key>
+      <false/>
+    </dict>
+    <!-- Enable accounting for specific principals -->
+    <key>AccountingPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
+    </array>
+
+
+    <!--
+        SSL/TLS
+      -->
+
+    <!-- Public key -->
+    <key>SSLCertificate</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+    <!-- SSL authority chain (for intermediate certs) -->
+    <key>SSLAuthorityChain</key>
+    <string></string>
+
+    <!-- Private key -->
+    <key>SSLPrivateKey</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+
+    <!--
+        Process management
+      -->
+
+    <key>UserName</key>
+    <string></string>
+
+    <key>GroupName</key>
+    <string></string>
+
+    <key>ProcessType</key>
+    <string>Combined</string>
+
+    <key>MultiProcess</key>
+    <dict>
+      <key>ProcessCount</key>
+      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
+    </dict>
+
+
+    <!--
+        Notifications
+      -->
+
+    <key>Notifications</key>
+    <dict>
+      <!-- Time spent coalescing notifications before delivery -->
+      <key>CoalesceSeconds</key>
+      <integer>3</integer>
+
+      <key>InternalNotificationHost</key>
+      <string>localhost</string>
+
+      <key>InternalNotificationPort</key>
+      <integer>62309</integer>
+
+      <key>Services</key>
+      <dict>
+        <key>SimpleLineNotifier</key>
+        <dict>
+          <!-- Simple line notification service (for testing) -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+          <key>Port</key>
+          <integer>62308</integer>
+        </dict>
+
+        <key>XMPPNotifier</key>
+        <dict>
+          <!-- XMPP notification service -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.XMPPNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+
+          <!-- XMPP host and port to contact -->
+          <key>Host</key>
+          <string>xmpp.host.name</string>
+          <key>Port</key>
+          <integer>5222</integer>
+
+          <!-- Jabber ID and password for the server -->
+          <key>JID</key>
+          <string>jid at xmpp.host.name/resource</string>
+          <key>Password</key>
+          <string>password_goes_here</string>
+
+          <!-- PubSub service address -->
+          <key>ServiceAddress</key>
+          <string>pubsub.xmpp.host.name</string>
+
+          <key>NodeConfiguration</key>
+          <dict>
+            <key>pubsub#deliver_payloads</key>
+            <string>1</string>
+            <key>pubsub#persist_items</key>
+            <string>1</string>
+          </dict>
+
+          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
+          <key>KeepAliveSeconds</key>
+          <integer>120</integer>
+
+          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
+          <key>HeartbeatMinutes</key>
+          <integer>30</integer>
+
+          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
+          <key>AllowedJIDs</key>
+          <array>
+            <!--
+            <string>*.example.com</string>
+             -->
+          </array>
+        </dict>
+      </dict>
+    </dict>
+
+
+    <!--
+        Server-to-server protocol
+      -->
+
+    <key>Scheduling</key>
+    <dict>
+
+      <!-- CalDAV protocol options -->
+      <key>CalDAV</key>
+      <dict>
+        <key>EmailDomain</key>
+        <string></string>
+        <key>HTTPDomain</key>
+        <string></string>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>OldDraftCompatibility</key>
+        <true/>
+        <key>ScheduleTagCompatibility</key>
+        <true/>
+        <key>EnablePrivateComments</key>
+        <true/>
+      </dict>
+
+      <!-- iSchedule protocol options -->
+      <key>iSchedule</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>Servers</key>
+        <string>conf/servertoserver-test.xml</string>
+      </dict>
+
+      <!-- iMIP protocol options -->
+      <key>iMIP</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>MailGatewayServer</key>
+        <string>localhost</string>
+        <key>MailGatewayPort</key>
+        <integer>62310</integer>
+        <key>Sending</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>587</integer>
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>Address</key>
+          <string></string> <!-- Address email will be sent from -->
+        </dict>
+        <key>Receiving</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>995</integer>
+          <key>Type</key>
+          <string></string> <!-- Either "pop" or "imap" -->
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>PollingSeconds</key>
+          <integer>30</integer>
+        </dict>
+        <key>AddressPatterns</key>
+        <array>
+          <string>mailto:.*</string>
+        </array>
+      </dict>
+
+	  <!-- General options for scheduling -->
+	  <key>Options</key>
+	  <dict>
+        <key>AllowGroupAsOrganizer</key>
+        <false/>
+        <key>AllowLocationAsOrganizer</key>
+        <false/>
+        <key>AllowResourceAsOrganizer</key>
+        <false/>
+       </dict>
+
+    </dict>
+
+
+    <!--
+        Free-busy URL protocol
+      -->
+
+    <key>FreeBusyURL</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>TimePeriod</key>
+      <integer>14</integer>
+      <key>AnonymousAccess</key>
+      <false/>
+    </dict>
+
+
+    <!--
+        Non-standard CalDAV extensions
+      -->
+
+    <!-- Calendar Drop Box -->
+    <key>EnableDropBox</key>
+    <true/>
+
+    <!-- Private Events -->
+    <key>EnablePrivateEvents</key>
+    <true/>
+
+    <!-- Timezone Service -->
+    <key>EnableTimezoneService</key>
+    <true/>
+
+
+    <!--
+        Miscellaneous items
+      -->
+
+    <!-- Service ACLs (Mac OS X) -->
+    <key>EnableSACLs</key>
+    <false/>
+
+    <!-- Web-based administration -->
+    <key>EnableWebAdmin</key>
+    <true/>
+
+    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+    <key>ResponseCompression</key>
+    <false/>
+    
+    <!-- The retry-after value (in seconds) to return with a 503 error. -->
+    <key>HTTPRetryAfter</key>
+    <integer>180</integer>
+
+    <!-- A unix socket used for communication between the child and master processes.
+         An empty value tells the server to use a tcp socket instead. -->
+    <key>ControlSocket</key>
+    <string>logs/caldavd.sock</string>
+
+    <!-- Support for Memcached -->
+    <key>Memcached</key>
+    <dict>
+      <key>MaxClients</key>
+      <integer>5</integer>
+      <key>memcached</key>
+      <string>memcached</string> <!-- Find in PATH -->
+      <key>Options</key>
+      <array>
+        <!--<string>-vv</string>-->
+      </array>
+    </dict>
+
+    <!-- Response Caching -->
+    <key>ResponseCacheTimeout</key>
+    <integer>30</integer> <!-- in minutes -->
+
+
+    <!--
+        Twisted
+      -->
+
+    <key>Twisted</key>
+    <dict>
+      <key>twistd</key>
+      <string>../Twisted/bin/twistd</string>
+    </dict>
+
+
+    <key>Localization</key>
+    <dict>
+      <key>LocalesDirectory</key>
+      <string>locales</string>
+      <key>Language</key>
+      <string>English</string>
+    </dict>
+
+
+  </dict>
+</plist>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/modify/resources-locations.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <location>
-    <uid>location01</uid>
-    <guid>location01</guid>
-    <password>test</password>
-    <name>Room 01</name>
-  </location>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/modify/resources-locations.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/resources-locations.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <location>
+    <uid>location01</uid>
+    <guid>location01</guid>
+    <password>test</password>
+    <name>Room 01</name>
+  </location>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/modify/users-groups.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <user repeat="10">
-    <uid>user%02d</uid>
-    <guid>user%02d</guid>
-    <password>test</password>
-    <name>Test User %02d</name>
-    <first-name>Test</first-name>
-    <last-name>User %02d</last-name>
-  </user>
-  <group>
-    <uid>testgroup1</uid>
-    <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
-    <password>test</password>
-    <name>Group 01</name>
-    <members>
-      <member type="users">user01</member>
-      <member type="users">user02</member>
-    </members>
-  </group>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/modify/users-groups.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/modify/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <user repeat="10">
+    <uid>user%02d</uid>
+    <guid>user%02d</guid>
+    <password>test</password>
+    <name>Test User %02d</name>
+    <first-name>Test</first-name>
+    <last-name>User %02d</last-name>
+  </user>
+  <group>
+    <uid>testgroup1</uid>
+    <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
+    <password>test</password>
+    <name>Group 01</name>
+    <members>
+      <member type="users">user01</member>
+      <member type="users">user02</member>
+    </members>
+  </group>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources/augments.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2009-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
-
-<augments>
-  <record>
-    <guid>user01</guid>
-    <enable>true</enable>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <guid>user02</guid>
-    <enable>false</enable>
-    <enable-calendar>false</enable-calendar>
-  </record>
-  <record>
-    <guid>resource01</guid>
-    <enable>true</enable>
-    <enable-calendar>true</enable-calendar>
-    <auto-schedule>true</auto-schedule>
-  </record>
-  <record>
-    <guid>resource02</guid>
-    <enable>false</enable>
-    <enable-calendar>false</enable-calendar>
-    <auto-schedule>false</auto-schedule>
-  </record>
-</augments>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/resources/augments.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/augments.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
+
+<augments>
+  <record>
+    <guid>user01</guid>
+    <enable>true</enable>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <guid>user02</guid>
+    <enable>false</enable>
+    <enable-calendar>false</enable-calendar>
+  </record>
+  <record>
+    <guid>resource01</guid>
+    <enable>true</enable>
+    <enable-calendar>true</enable-calendar>
+    <auto-schedule>true</auto-schedule>
+  </record>
+  <record>
+    <guid>resource02</guid>
+    <enable>false</enable>
+    <enable-calendar>false</enable-calendar>
+    <auto-schedule>false</auto-schedule>
+  </record>
+</augments>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources/caldavd.plist	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,748 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    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 "AS IS" 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.
-  -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-  <dict>
-
-    <!--
-        Public network address information
-
-        This is the server's public network address, which is provided to
-        clients in URLs and the like.  It may or may not be the network
-        address that the server is listening to directly, though it is by
-        default.  For example, it may be the address of a load balancer or
-        proxy which forwards connections to the server.
-      -->
-
-    <!-- Network host name [empty = system host name] -->
-    <key>ServerHostName</key>
-    <string></string> <!-- The hostname clients use when connecting -->
-
-    <!-- HTTP port [0 = disable HTTP] -->
-    <key>HTTPPort</key>
-    <integer>8008</integer>
-
-    <!-- SSL port [0 = disable HTTPS] -->
-    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
-    <key>SSLPort</key>
-    <integer>8443</integer>
-
-    <!-- Redirect non-SSL ports to an SSL port -->
-    <key>RedirectHTTPToHTTPS</key>
-    <false/>
-
-    <!--
-        Network address configuration information
-
-        This configures the actual network address that the server binds to.
-      -->
-
-    <!-- List of IP addresses to bind to [empty = all] -->
-    <key>BindAddresses</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
-    <key>BindHTTPPorts</key>
-    <array>
-    </array>
-
-    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
-    <key>BindSSLPorts</key>
-    <array>
-    </array>
-
-
-    <!--
-        Data Store
-      -->
-
-    <!-- Data root -->
-    <key>DataRoot</key>
-    <string>data/</string>
-
-    <!-- Document root -->
-    <key>DocumentRoot</key>
-    <string>twistedcaldav/test/data/</string>
-
-    <!-- Child aliases -->
-    <key>Aliases</key>
-    <dict>
-      <!--
-      <key>foo</key>
-      <dict>
-        <key>path</key>
-        <string>/path/to/foo</string>
-      </dict>
-       -->
-    </dict>
-
-    <!-- User quota (in bytes) -->
-    <key>UserQuota</key>
-    <integer>104857600</integer><!-- 100Mb -->
-
-    <!-- Attachment size limit (in bytes) -->
-    <key>MaximumAttachmentSize</key>
-    <integer>1048576</integer><!-- 1Mb -->
-
-    <!-- Maximum number of unique attendees per entire event -->
-    <!-- 0 for no limit -->
-    <key>MaxAttendeesPerInstance</key>
-    <integer>100</integer>
-
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
-
-
-    <!--
-        Directory service
-
-        A directory service provides information about principals (eg.
-        users, groups, locations and resources) to the server.
-
-        A variety of directory services are available for use.
-      -->
-
-    <!-- XML File Directory Service -->
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>SET IN TEST</string>
-        <key>recordTypes</key>
-        <array>
-            <string>users</string>
-            <string>groups</string>
-        </array>
-      </dict>
-    </dict>
-
-    <!-- XML File Resource Service -->
-    <key>ResourceService</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>type</key>
-      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFile</key>
-        <string>SET IN TEST</string>
-        <key>recordTypes</key>
-        <array>
-            <string>resources</string>
-            <string>locations</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Open Directory Service (Mac OS X) -->
-    <!--
-    <key>DirectoryService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
-      
-      <key>params</key>
-      <dict>
-        <key>node</key>
-        <string>/Search</string>
-        <key>cacheTimeout</key>
-        <integer>30</integer>
-      </dict>
-    </dict>
-    -->
-
-    <!--
-        Augment service
-
-        Augments for the directory service records to add calendar specific attributes.
-
-        A variety of augment services are available for use.
-        When using a partitioned server, a service that can be accessed from each host will be needed.
-      -->
-
-    <!-- XML File Augment Service -->
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>xmlFiles</key>
-        <array>
-	      <string>SET IN TEST</string>
-        </array>
-      </dict>
-    </dict>
-    
-    <!-- Sqlite Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>/etc/caldavd/augments.sqlite</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- PostgreSQL Augment Service -->
-    <!--
-    <key>AugmentService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>augments</string>
-      </dict>
-    </dict>
-     -->
-
-    <!-- Sqlite ProxyDB Service -->
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>dbpath</key>
-        <string>data/proxies.sqlite</string>
-      </dict>
-    </dict>
-
-    <!-- PostgreSQL ProxyDB Service -->
-    <!--
-    <key>ProxyDBService</key>
-    <dict>
-      <key>type</key>
-      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-      
-      <key>params</key>
-      <dict>
-        <key>host</key>
-        <string>localhost</string>
-        <key>database</key>
-        <string>proxies</string>
-      </dict>
-    </dict>
-     -->
-
-	<key>ProxyLoadFromFile</key>
-    <string>conf/auth/proxies-test.xml</string>
-
-    <!--
-        Special principals
-
-        These principals are granted special access and/or perform
-        special roles on the server.
-      -->
-
-    <!-- Principals with "DAV:all" access (relative URLs) -->
-    <key>AdminPrincipals</key>
-    <array>
-      <string>/principals/__uids__/admin/</string>
-    </array>
-
-    <!-- Principals with "DAV:read" access (relative URLs) -->
-    <key>ReadPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
-    </array>
-
-    <!-- Principals that can pose as other principals -->
-    <key>SudoersFile</key>
-    <string>conf/sudoers.plist</string>
-
-    <!-- Create "proxy access" principals -->
-    <key>EnableProxyPrincipals</key>
-    <true/>
-
-
-    <!--
-        Permissions
-      -->
-
-    <!-- Anonymous read access for root resource -->
-    <key>EnableAnonymousReadRoot</key>
-    <true/>
-
-    <!-- Anonymous read access for resource hierarchy -->
-    <key>EnableAnonymousReadNav</key>
-    <false/>
-
-    <!-- Enables directory listings for principals -->
-    <key>EnablePrincipalListings</key>
-    <true/>
-
-    <!-- Render calendar collections as a monolithic iCalendar object -->
-    <key>EnableMonolithicCalendars</key>
-    <true/>
-
-
-    <!--
-        Authentication
-      -->
-
-    <key>Authentication</key>
-    <dict>
-
-      <!-- Clear text; best avoided -->
-      <key>Basic</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-      </dict>
-
-      <!-- Digest challenge/response -->
-      <key>Digest</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Algorithm</key>
-        <string>md5</string>
-        <key>Qop</key>
-        <string></string>
-      </dict>
-
-      <!-- Kerberos/SPNEGO -->
-      <key>Kerberos</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>ServicePrincipal</key>
-        <string></string>
-      </dict>
-
-      <!-- Wikiserver authentication (Mac OS X) -->
-      <key>Wiki</key>
-      <dict>
-        <key>Enabled</key>
-        <true/>
-        <key>Cookie</key>
-        <string>sessionID</string>
-        <key>URL</key>
-        <string>http://127.0.0.1/RPC2</string>
-        <key>UserMethod</key>
-        <string>userForSession</string>
-        <key>WikiMethod</key>
-        <string>accessLevelForUserWikiCalendar</string>
-      </dict>
-
-    </dict>
-
-
-    <!--
-        Logging
-      -->
-
-    <!-- Apache-style access log -->
-    <key>AccessLogFile</key>
-    <string>logs/access.log</string>
-    <key>RotateAccessLog</key>
-    <false/>
-
-    <!-- Server activity log -->
-    <key>ErrorLogFile</key>
-    <string>logs/error.log</string>
-
-    <!-- Log levels -->
-    <key>DefaultLogLevel</key>
-    <string>info</string> <!-- debug, info, warn, error -->
-
-    <!-- Log level overrides for specific functionality -->
-    <key>LogLevels</key>
-    <dict>
-      <!--
-      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
-      <string>debug</string>
-      -->
-    </dict>
-
-    <!-- Global server stats --> 
-    <key>GlobalStatsSocket</key> 
-    <string>logs/caldavd-stats.sock</string> 
-
-    <!-- Global server stats logging period --> 
-    <key>GlobalStatsLoggingPeriod</key> 
-    <integer>60</integer> 
-
-    <!-- Global server stats logging frequency [0 = disable stats] --> 
-    <key>GlobalStatsLoggingFrequency</key> 
-    <integer>12</integer>
-
-    <!-- Server statistics file -->
-    <key>ServerStatsFile</key>
-    <string>logs/stats.plist</string>
-
-    <!-- Server process ID file -->
-    <key>PIDFile</key>
-    <string>logs/caldavd.pid</string>
-
-
-    <!--
-        Accounting
-      -->
-
-    <!-- Enable accounting for certain operations -->
-    <key>AccountingCategories</key>
-    <dict>
-      <key>iTIP</key>
-      <false/>
-      <key>HTTP</key>
-      <false/>
-    </dict>
-    <!-- Enable accounting for specific principals -->
-    <key>AccountingPrincipals</key>
-    <array>
-      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
-    </array>
-
-
-    <!--
-        SSL/TLS
-      -->
-
-    <!-- Public key -->
-    <key>SSLCertificate</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-    <!-- SSL authority chain (for intermediate certs) -->
-    <key>SSLAuthorityChain</key>
-    <string></string>
-
-    <!-- Private key -->
-    <key>SSLPrivateKey</key>
-    <string>twistedcaldav/test/data/server.pem</string>
-
-
-    <!--
-        Process management
-      -->
-
-    <key>UserName</key>
-    <string></string>
-
-    <key>GroupName</key>
-    <string></string>
-
-    <key>ProcessType</key>
-    <string>Combined</string>
-
-    <key>MultiProcess</key>
-    <dict>
-      <key>ProcessCount</key>
-      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
-    </dict>
-
-
-    <!--
-        Notifications
-      -->
-
-    <key>Notifications</key>
-    <dict>
-      <!-- Time spent coalescing notifications before delivery -->
-      <key>CoalesceSeconds</key>
-      <integer>3</integer>
-
-      <key>InternalNotificationHost</key>
-      <string>localhost</string>
-
-      <key>InternalNotificationPort</key>
-      <integer>62309</integer>
-
-      <key>Services</key>
-      <dict>
-        <key>SimpleLineNotifier</key>
-        <dict>
-          <!-- Simple line notification service (for testing) -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-          <key>Port</key>
-          <integer>62308</integer>
-        </dict>
-
-        <key>XMPPNotifier</key>
-        <dict>
-          <!-- XMPP notification service -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.XMPPNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-
-          <!-- XMPP host and port to contact -->
-          <key>Host</key>
-          <string>xmpp.host.name</string>
-          <key>Port</key>
-          <integer>5222</integer>
-
-          <!-- Jabber ID and password for the server -->
-          <key>JID</key>
-          <string>jid at xmpp.host.name/resource</string>
-          <key>Password</key>
-          <string>password_goes_here</string>
-
-          <!-- PubSub service address -->
-          <key>ServiceAddress</key>
-          <string>pubsub.xmpp.host.name</string>
-
-          <key>NodeConfiguration</key>
-          <dict>
-            <key>pubsub#deliver_payloads</key>
-            <string>1</string>
-            <key>pubsub#persist_items</key>
-            <string>1</string>
-          </dict>
-
-          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
-          <key>KeepAliveSeconds</key>
-          <integer>120</integer>
-
-          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
-          <key>HeartbeatMinutes</key>
-          <integer>30</integer>
-
-          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
-          <key>AllowedJIDs</key>
-          <array>
-            <!--
-            <string>*.example.com</string>
-             -->
-          </array>
-        </dict>
-      </dict>
-    </dict>
-
-
-    <!--
-        Server-to-server protocol
-      -->
-
-    <key>Scheduling</key>
-    <dict>
-
-      <!-- CalDAV protocol options -->
-      <key>CalDAV</key>
-      <dict>
-        <key>EmailDomain</key>
-        <string></string>
-        <key>HTTPDomain</key>
-        <string></string>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>OldDraftCompatibility</key>
-        <true/>
-        <key>ScheduleTagCompatibility</key>
-        <true/>
-        <key>EnablePrivateComments</key>
-        <true/>
-      </dict>
-
-      <!-- iSchedule protocol options -->
-      <key>iSchedule</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>AddressPatterns</key>
-        <array>
-        </array>
-        <key>Servers</key>
-        <string>conf/servertoserver-test.xml</string>
-      </dict>
-
-      <!-- iMIP protocol options -->
-      <key>iMIP</key>
-      <dict>
-        <key>Enabled</key>
-        <false/>
-        <key>MailGatewayServer</key>
-        <string>localhost</string>
-        <key>MailGatewayPort</key>
-        <integer>62310</integer>
-        <key>Sending</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>587</integer>
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>Address</key>
-          <string></string> <!-- Address email will be sent from -->
-        </dict>
-        <key>Receiving</key>
-        <dict>
-          <key>Server</key>
-          <string></string>
-          <key>Port</key>
-          <integer>995</integer>
-          <key>Type</key>
-          <string></string> <!-- Either "pop" or "imap" -->
-          <key>UseSSL</key>
-          <true/>
-          <key>Username</key>
-          <string></string>
-          <key>Password</key>
-          <string></string>
-          <key>PollingSeconds</key>
-          <integer>30</integer>
-        </dict>
-        <key>AddressPatterns</key>
-        <array>
-          <string>mailto:.*</string>
-        </array>
-      </dict>
-
-	  <!-- General options for scheduling -->
-	  <key>Options</key>
-	  <dict>
-        <key>AllowGroupAsOrganizer</key>
-        <false/>
-        <key>AllowLocationAsOrganizer</key>
-        <false/>
-        <key>AllowResourceAsOrganizer</key>
-        <false/>
-       </dict>
-
-    </dict>
-
-
-    <!--
-        Free-busy URL protocol
-      -->
-
-    <key>FreeBusyURL</key>
-    <dict>
-      <key>Enabled</key>
-      <true/>
-      <key>TimePeriod</key>
-      <integer>14</integer>
-      <key>AnonymousAccess</key>
-      <false/>
-    </dict>
-
-
-    <!--
-        Non-standard CalDAV extensions
-      -->
-
-    <!-- Calendar Drop Box -->
-    <key>EnableDropBox</key>
-    <true/>
-
-    <!-- Private Events -->
-    <key>EnablePrivateEvents</key>
-    <true/>
-
-    <!-- Timezone Service -->
-    <key>EnableTimezoneService</key>
-    <true/>
-
-
-    <!--
-        Miscellaneous items
-      -->
-
-    <!-- Service ACLs (Mac OS X) -->
-    <key>EnableSACLs</key>
-    <false/>
-
-    <!-- Web-based administration -->
-    <key>EnableWebAdmin</key>
-    <true/>
-
-    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
-    <key>ResponseCompression</key>
-    <false/>
-    
-    <!-- The retry-after value (in seconds) to return with a 503 error. -->
-    <key>HTTPRetryAfter</key>
-    <integer>180</integer>
-
-    <!-- A unix socket used for communication between the child and master processes.
-         An empty value tells the server to use a tcp socket instead. -->
-    <key>ControlSocket</key>
-    <string>logs/caldavd.sock</string>
-
-    <!-- Support for Memcached -->
-    <key>Memcached</key>
-    <dict>
-      <key>MaxClients</key>
-      <integer>5</integer>
-      <key>memcached</key>
-      <string>memcached</string> <!-- Find in PATH -->
-      <key>Options</key>
-      <array>
-        <!--<string>-vv</string>-->
-      </array>
-    </dict>
-
-    <!-- Response Caching -->
-    <key>ResponseCacheTimeout</key>
-    <integer>30</integer> <!-- in minutes -->
-
-
-    <!--
-        Twisted
-      -->
-
-    <key>Twisted</key>
-    <dict>
-      <key>twistd</key>
-      <string>../Twisted/bin/twistd</string>
-    </dict>
-
-
-    <key>Localization</key>
-    <dict>
-      <key>LocalesDirectory</key>
-      <string>locales</string>
-      <key>Language</key>
-      <string>English</string>
-    </dict>
-
-
-  </dict>
-</plist>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/resources/caldavd.plist)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/caldavd.plist	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,748 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    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 "AS IS" 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.
+  -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+
+    <!--
+        Public network address information
+
+        This is the server's public network address, which is provided to
+        clients in URLs and the like.  It may or may not be the network
+        address that the server is listening to directly, though it is by
+        default.  For example, it may be the address of a load balancer or
+        proxy which forwards connections to the server.
+      -->
+
+    <!-- Network host name [empty = system host name] -->
+    <key>ServerHostName</key>
+    <string></string> <!-- The hostname clients use when connecting -->
+
+    <!-- HTTP port [0 = disable HTTP] -->
+    <key>HTTPPort</key>
+    <integer>8008</integer>
+
+    <!-- SSL port [0 = disable HTTPS] -->
+    <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+    <key>SSLPort</key>
+    <integer>8443</integer>
+
+    <!-- Redirect non-SSL ports to an SSL port -->
+    <key>RedirectHTTPToHTTPS</key>
+    <false/>
+
+    <!--
+        Network address configuration information
+
+        This configures the actual network address that the server binds to.
+      -->
+
+    <!-- List of IP addresses to bind to [empty = all] -->
+    <key>BindAddresses</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+    <key>BindHTTPPorts</key>
+    <array>
+    </array>
+
+    <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+    <key>BindSSLPorts</key>
+    <array>
+    </array>
+
+
+    <!--
+        Data Store
+      -->
+
+    <!-- Data root -->
+    <key>DataRoot</key>
+    <string>data/</string>
+
+    <!-- Document root -->
+    <key>DocumentRoot</key>
+    <string>twistedcaldav/test/data/</string>
+
+    <!-- Child aliases -->
+    <key>Aliases</key>
+    <dict>
+      <!--
+      <key>foo</key>
+      <dict>
+        <key>path</key>
+        <string>/path/to/foo</string>
+      </dict>
+       -->
+    </dict>
+
+    <!-- User quota (in bytes) -->
+    <key>UserQuota</key>
+    <integer>104857600</integer><!-- 100Mb -->
+
+    <!-- Attachment size limit (in bytes) -->
+    <key>MaximumAttachmentSize</key>
+    <integer>1048576</integer><!-- 1Mb -->
+
+    <!-- Maximum number of unique attendees per entire event -->
+    <!-- 0 for no limit -->
+    <key>MaxAttendeesPerInstance</key>
+    <integer>100</integer>
+
+    <!-- Maximum number of instances allowed for a single RRULE -->
+    <!-- 0 for no limit -->
+    <key>MaxInstancesForRRULE</key>
+    <integer>400</integer>
+
+
+    <!--
+        Directory service
+
+        A directory service provides information about principals (eg.
+        users, groups, locations and resources) to the server.
+
+        A variety of directory services are available for use.
+      -->
+
+    <!-- XML File Directory Service -->
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>SET IN TEST</string>
+        <key>recordTypes</key>
+        <array>
+            <string>users</string>
+            <string>groups</string>
+        </array>
+      </dict>
+    </dict>
+
+    <!-- XML File Resource Service -->
+    <key>ResourceService</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>type</key>
+      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFile</key>
+        <string>SET IN TEST</string>
+        <key>recordTypes</key>
+        <array>
+            <string>resources</string>
+            <string>locations</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Open Directory Service (Mac OS X) -->
+    <!--
+    <key>DirectoryService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
+      
+      <key>params</key>
+      <dict>
+        <key>node</key>
+        <string>/Search</string>
+        <key>cacheTimeout</key>
+        <integer>30</integer>
+      </dict>
+    </dict>
+    -->
+
+    <!--
+        Augment service
+
+        Augments for the directory service records to add calendar specific attributes.
+
+        A variety of augment services are available for use.
+        When using a partitioned server, a service that can be accessed from each host will be needed.
+      -->
+
+    <!-- XML File Augment Service -->
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>xmlFiles</key>
+        <array>
+	      <string>SET IN TEST</string>
+        </array>
+      </dict>
+    </dict>
+    
+    <!-- Sqlite Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>/etc/caldavd/augments.sqlite</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- PostgreSQL Augment Service -->
+    <!--
+    <key>AugmentService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>augments</string>
+      </dict>
+    </dict>
+     -->
+
+    <!-- Sqlite ProxyDB Service -->
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>dbpath</key>
+        <string>data/proxies.sqlite</string>
+      </dict>
+    </dict>
+
+    <!-- PostgreSQL ProxyDB Service -->
+    <!--
+    <key>ProxyDBService</key>
+    <dict>
+      <key>type</key>
+      <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
+      
+      <key>params</key>
+      <dict>
+        <key>host</key>
+        <string>localhost</string>
+        <key>database</key>
+        <string>proxies</string>
+      </dict>
+    </dict>
+     -->
+
+	<key>ProxyLoadFromFile</key>
+    <string>conf/auth/proxies-test.xml</string>
+
+    <!--
+        Special principals
+
+        These principals are granted special access and/or perform
+        special roles on the server.
+      -->
+
+    <!-- Principals with "DAV:all" access (relative URLs) -->
+    <key>AdminPrincipals</key>
+    <array>
+      <string>/principals/__uids__/admin/</string>
+    </array>
+
+    <!-- Principals with "DAV:read" access (relative URLs) -->
+    <key>ReadPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
+    </array>
+
+    <!-- Principals that can pose as other principals -->
+    <key>SudoersFile</key>
+    <string>conf/sudoers.plist</string>
+
+    <!-- Create "proxy access" principals -->
+    <key>EnableProxyPrincipals</key>
+    <true/>
+
+
+    <!--
+        Permissions
+      -->
+
+    <!-- Anonymous read access for root resource -->
+    <key>EnableAnonymousReadRoot</key>
+    <true/>
+
+    <!-- Anonymous read access for resource hierarchy -->
+    <key>EnableAnonymousReadNav</key>
+    <false/>
+
+    <!-- Enables directory listings for principals -->
+    <key>EnablePrincipalListings</key>
+    <true/>
+
+    <!-- Render calendar collections as a monolithic iCalendar object -->
+    <key>EnableMonolithicCalendars</key>
+    <true/>
+
+
+    <!--
+        Authentication
+      -->
+
+    <key>Authentication</key>
+    <dict>
+
+      <!-- Clear text; best avoided -->
+      <key>Basic</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+      </dict>
+
+      <!-- Digest challenge/response -->
+      <key>Digest</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Algorithm</key>
+        <string>md5</string>
+        <key>Qop</key>
+        <string></string>
+      </dict>
+
+      <!-- Kerberos/SPNEGO -->
+      <key>Kerberos</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>ServicePrincipal</key>
+        <string></string>
+      </dict>
+
+      <!-- Wikiserver authentication (Mac OS X) -->
+      <key>Wiki</key>
+      <dict>
+        <key>Enabled</key>
+        <true/>
+        <key>Cookie</key>
+        <string>sessionID</string>
+        <key>URL</key>
+        <string>http://127.0.0.1/RPC2</string>
+        <key>UserMethod</key>
+        <string>userForSession</string>
+        <key>WikiMethod</key>
+        <string>accessLevelForUserWikiCalendar</string>
+      </dict>
+
+    </dict>
+
+
+    <!--
+        Logging
+      -->
+
+    <!-- Apache-style access log -->
+    <key>AccessLogFile</key>
+    <string>logs/access.log</string>
+    <key>RotateAccessLog</key>
+    <false/>
+
+    <!-- Server activity log -->
+    <key>ErrorLogFile</key>
+    <string>logs/error.log</string>
+
+    <!-- Log levels -->
+    <key>DefaultLogLevel</key>
+    <string>info</string> <!-- debug, info, warn, error -->
+
+    <!-- Log level overrides for specific functionality -->
+    <key>LogLevels</key>
+    <dict>
+      <!--
+      <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
+      <string>debug</string>
+      -->
+    </dict>
+
+    <!-- Global server stats --> 
+    <key>GlobalStatsSocket</key> 
+    <string>logs/caldavd-stats.sock</string> 
+
+    <!-- Global server stats logging period --> 
+    <key>GlobalStatsLoggingPeriod</key> 
+    <integer>60</integer> 
+
+    <!-- Global server stats logging frequency [0 = disable stats] --> 
+    <key>GlobalStatsLoggingFrequency</key> 
+    <integer>12</integer>
+
+    <!-- Server statistics file -->
+    <key>ServerStatsFile</key>
+    <string>logs/stats.plist</string>
+
+    <!-- Server process ID file -->
+    <key>PIDFile</key>
+    <string>logs/caldavd.pid</string>
+
+
+    <!--
+        Accounting
+      -->
+
+    <!-- Enable accounting for certain operations -->
+    <key>AccountingCategories</key>
+    <dict>
+      <key>iTIP</key>
+      <false/>
+      <key>HTTP</key>
+      <false/>
+    </dict>
+    <!-- Enable accounting for specific principals -->
+    <key>AccountingPrincipals</key>
+    <array>
+      <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
+    </array>
+
+
+    <!--
+        SSL/TLS
+      -->
+
+    <!-- Public key -->
+    <key>SSLCertificate</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+    <!-- SSL authority chain (for intermediate certs) -->
+    <key>SSLAuthorityChain</key>
+    <string></string>
+
+    <!-- Private key -->
+    <key>SSLPrivateKey</key>
+    <string>twistedcaldav/test/data/server.pem</string>
+
+
+    <!--
+        Process management
+      -->
+
+    <key>UserName</key>
+    <string></string>
+
+    <key>GroupName</key>
+    <string></string>
+
+    <key>ProcessType</key>
+    <string>Combined</string>
+
+    <key>MultiProcess</key>
+    <dict>
+      <key>ProcessCount</key>
+      <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
+    </dict>
+
+
+    <!--
+        Notifications
+      -->
+
+    <key>Notifications</key>
+    <dict>
+      <!-- Time spent coalescing notifications before delivery -->
+      <key>CoalesceSeconds</key>
+      <integer>3</integer>
+
+      <key>InternalNotificationHost</key>
+      <string>localhost</string>
+
+      <key>InternalNotificationPort</key>
+      <integer>62309</integer>
+
+      <key>Services</key>
+      <dict>
+        <key>SimpleLineNotifier</key>
+        <dict>
+          <!-- Simple line notification service (for testing) -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+          <key>Port</key>
+          <integer>62308</integer>
+        </dict>
+
+        <key>XMPPNotifier</key>
+        <dict>
+          <!-- XMPP notification service -->
+          <key>Service</key>
+          <string>twistedcaldav.notify.XMPPNotifierService</string>
+          <key>Enabled</key>
+          <false/>
+
+          <!-- XMPP host and port to contact -->
+          <key>Host</key>
+          <string>xmpp.host.name</string>
+          <key>Port</key>
+          <integer>5222</integer>
+
+          <!-- Jabber ID and password for the server -->
+          <key>JID</key>
+          <string>jid at xmpp.host.name/resource</string>
+          <key>Password</key>
+          <string>password_goes_here</string>
+
+          <!-- PubSub service address -->
+          <key>ServiceAddress</key>
+          <string>pubsub.xmpp.host.name</string>
+
+          <key>NodeConfiguration</key>
+          <dict>
+            <key>pubsub#deliver_payloads</key>
+            <string>1</string>
+            <key>pubsub#persist_items</key>
+            <string>1</string>
+          </dict>
+
+          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
+          <key>KeepAliveSeconds</key>
+          <integer>120</integer>
+
+          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
+          <key>HeartbeatMinutes</key>
+          <integer>30</integer>
+
+          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
+          <key>AllowedJIDs</key>
+          <array>
+            <!--
+            <string>*.example.com</string>
+             -->
+          </array>
+        </dict>
+      </dict>
+    </dict>
+
+
+    <!--
+        Server-to-server protocol
+      -->
+
+    <key>Scheduling</key>
+    <dict>
+
+      <!-- CalDAV protocol options -->
+      <key>CalDAV</key>
+      <dict>
+        <key>EmailDomain</key>
+        <string></string>
+        <key>HTTPDomain</key>
+        <string></string>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>OldDraftCompatibility</key>
+        <true/>
+        <key>ScheduleTagCompatibility</key>
+        <true/>
+        <key>EnablePrivateComments</key>
+        <true/>
+      </dict>
+
+      <!-- iSchedule protocol options -->
+      <key>iSchedule</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>AddressPatterns</key>
+        <array>
+        </array>
+        <key>Servers</key>
+        <string>conf/servertoserver-test.xml</string>
+      </dict>
+
+      <!-- iMIP protocol options -->
+      <key>iMIP</key>
+      <dict>
+        <key>Enabled</key>
+        <false/>
+        <key>MailGatewayServer</key>
+        <string>localhost</string>
+        <key>MailGatewayPort</key>
+        <integer>62310</integer>
+        <key>Sending</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>587</integer>
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>Address</key>
+          <string></string> <!-- Address email will be sent from -->
+        </dict>
+        <key>Receiving</key>
+        <dict>
+          <key>Server</key>
+          <string></string>
+          <key>Port</key>
+          <integer>995</integer>
+          <key>Type</key>
+          <string></string> <!-- Either "pop" or "imap" -->
+          <key>UseSSL</key>
+          <true/>
+          <key>Username</key>
+          <string></string>
+          <key>Password</key>
+          <string></string>
+          <key>PollingSeconds</key>
+          <integer>30</integer>
+        </dict>
+        <key>AddressPatterns</key>
+        <array>
+          <string>mailto:.*</string>
+        </array>
+      </dict>
+
+	  <!-- General options for scheduling -->
+	  <key>Options</key>
+	  <dict>
+        <key>AllowGroupAsOrganizer</key>
+        <false/>
+        <key>AllowLocationAsOrganizer</key>
+        <false/>
+        <key>AllowResourceAsOrganizer</key>
+        <false/>
+       </dict>
+
+    </dict>
+
+
+    <!--
+        Free-busy URL protocol
+      -->
+
+    <key>FreeBusyURL</key>
+    <dict>
+      <key>Enabled</key>
+      <true/>
+      <key>TimePeriod</key>
+      <integer>14</integer>
+      <key>AnonymousAccess</key>
+      <false/>
+    </dict>
+
+
+    <!--
+        Non-standard CalDAV extensions
+      -->
+
+    <!-- Calendar Drop Box -->
+    <key>EnableDropBox</key>
+    <true/>
+
+    <!-- Private Events -->
+    <key>EnablePrivateEvents</key>
+    <true/>
+
+    <!-- Timezone Service -->
+    <key>EnableTimezoneService</key>
+    <true/>
+
+
+    <!--
+        Miscellaneous items
+      -->
+
+    <!-- Service ACLs (Mac OS X) -->
+    <key>EnableSACLs</key>
+    <false/>
+
+    <!-- Web-based administration -->
+    <key>EnableWebAdmin</key>
+    <true/>
+
+    <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+    <key>ResponseCompression</key>
+    <false/>
+    
+    <!-- The retry-after value (in seconds) to return with a 503 error. -->
+    <key>HTTPRetryAfter</key>
+    <integer>180</integer>
+
+    <!-- A unix socket used for communication between the child and master processes.
+         An empty value tells the server to use a tcp socket instead. -->
+    <key>ControlSocket</key>
+    <string>logs/caldavd.sock</string>
+
+    <!-- Support for Memcached -->
+    <key>Memcached</key>
+    <dict>
+      <key>MaxClients</key>
+      <integer>5</integer>
+      <key>memcached</key>
+      <string>memcached</string> <!-- Find in PATH -->
+      <key>Options</key>
+      <array>
+        <!--<string>-vv</string>-->
+      </array>
+    </dict>
+
+    <!-- Response Caching -->
+    <key>ResponseCacheTimeout</key>
+    <integer>30</integer> <!-- in minutes -->
+
+
+    <!--
+        Twisted
+      -->
+
+    <key>Twisted</key>
+    <dict>
+      <key>twistd</key>
+      <string>../Twisted/bin/twistd</string>
+    </dict>
+
+
+    <key>Localization</key>
+    <dict>
+      <key>LocalesDirectory</key>
+      <string>locales</string>
+      <key>Language</key>
+      <string>English</string>
+    </dict>
+
+
+  </dict>
+</plist>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources/resources-locations.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <location repeat="10">
-    <uid>location%02d</uid>
-    <guid>location%02d</guid>
-    <password>location%02d</password>
-    <name>Room %02d</name>
-  </location>
-  <resource repeat="10">
-    <uid>resource%02d</uid>
-    <guid>resource%02d</guid>
-    <password>resource%02d</password>
-    <name>Resource %02d</name>
-  </resource>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/resources/resources-locations.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/resources-locations.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <location repeat="10">
+    <uid>location%02d</uid>
+    <guid>location%02d</guid>
+    <password>location%02d</password>
+    <name>Room %02d</name>
+  </location>
+  <resource repeat="10">
+    <uid>resource%02d</uid>
+    <guid>resource%02d</guid>
+    <password>resource%02d</password>
+    <name>Resource %02d</name>
+  </resource>
+</accounts>

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources/users-groups.xml	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2010 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-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 "AS IS" 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.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
-<accounts realm="Test Realm">
-  <user repeat="10">
-    <uid>user%02d</uid>
-    <guid>user%02d</guid>
-    <password>test</password>
-    <name>Test User %02d</name>
-    <first-name>Test</first-name>
-    <last-name>User %02d</last-name>
-  </user>
-  <group>
-    <uid>testgroup1</uid>
-    <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
-    <password>test</password>
-    <name>Group 01</name>
-    <members>
-      <member type="users">user01</member>
-      <member type="users">user02</member>
-    </members>
-  </group>
-</accounts>

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/resources/users-groups.xml)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/resources/users-groups.xml	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+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 "AS IS" 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+  <user repeat="10">
+    <uid>user%02d</uid>
+    <guid>user%02d</guid>
+    <password>test</password>
+    <name>Test User %02d</name>
+    <first-name>Test</first-name>
+    <last-name>User %02d</last-name>
+  </user>
+  <group>
+    <uid>testgroup1</uid>
+    <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
+    <password>test</password>
+    <name>Group 01</name>
+    <members>
+      <member type="users">user01</member>
+      <member type="users">user02</member>
+    </members>
+  </group>
+</accounts>

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_aggregate.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_aggregate.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_aggregate.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -22,7 +22,6 @@
 import twistedcaldav.directory.test.util
 from twistedcaldav.directory import augment
 
-apache_prefix = "apache:"
 xml_prefix = "xml:"
 
 testServices = (

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_cachedirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_cachedirectory.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_cachedirectory.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -14,14 +14,16 @@
 # limitations under the License.
 ##
 
-from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
-    CachingDirectoryRecord, DictRecordTypeCache
-from twistedcaldav.test.util import TestCase
+from uuid import uuid4
+
+from twistedcaldav.directory.cachingdirectory import CachingDirectoryService
+from twistedcaldav.directory.cachingdirectory import CachingDirectoryRecord
 from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.directory.util import uuidFromName
-from uuid import uuid4
 from twistedcaldav.directory.augment import AugmentRecord
+from twistedcaldav.test.util import TestCase
 
+
 class TestDirectoryService (CachingDirectoryService):
 
     realmName = "Dummy Realm"
@@ -86,7 +88,7 @@
         self.service.queried = False
 
     def loadRecords(self, records):
-        self.service._initCaches(DictRecordTypeCache)
+        self.service._initCaches()
         self.service.fakerecords = records
         self.service.queried = False
 

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_modify.py (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/test_modify.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_modify.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_modify.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,122 @@
+##
+# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+import os
+from twistedcaldav.config import config
+from twistedcaldav.test.util import TestCase
+from calendarserver.tools.util import getDirectory
+from twisted.python.filepath import FilePath
+from twistedcaldav.directory.directory import DirectoryError
+
+
+class ModificationTestCase(TestCase):
+
+    def setUp(self):
+        testRoot = os.path.join(os.path.dirname(__file__), "modify")
+        configFileName = os.path.join(testRoot, "caldavd.plist")
+        config.load(configFileName)
+
+        usersFile = os.path.join(testRoot, "users-groups.xml")
+        config.DirectoryService.params.xmlFile = usersFile
+
+        # Copy xml file containgin locations/resources to a temp file because
+        # we're going to be modifying it during testing
+
+        origResourcesFile = FilePath(os.path.join(os.path.dirname(__file__),
+            "modify", "resources-locations.xml"))
+        copyResourcesFile = FilePath(self.mktemp())
+        origResourcesFile.copyTo(copyResourcesFile)
+        config.ResourceService.params.xmlFile = copyResourcesFile
+
+        augmentsFile = os.path.join(testRoot, "augments.xml")
+        config.AugmentService.params.xmlFiles = (augmentsFile,)
+
+        super(ModificationTestCase, self).setUp()
+
+    def test_createRecord(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record, None)
+
+        directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+
+        record = directory.recordWithUID("resource01")
+        self.assertNotEquals(record, None)
+
+        directory.createRecord("resources", "resource02", shortNames=("resource02",), uid="resource02")
+
+        record = directory.recordWithUID("resource02")
+        self.assertNotEquals(record, None)
+
+        # Make sure old records are still there:
+        record = directory.recordWithUID("resource01")
+        self.assertNotEquals(record, None)
+        record = directory.recordWithUID("location01")
+        self.assertNotEquals(record, None)
+
+    def test_destroyRecord(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record, None)
+
+        directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+
+        record = directory.recordWithUID("resource01")
+        self.assertNotEquals(record, None)
+
+        directory.destroyRecord("resources", "resource01")
+
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record, None)
+
+        # Make sure old records are still there:
+        record = directory.recordWithUID("location01")
+        self.assertNotEquals(record, None)
+
+    def test_updateRecord(self):
+        directory = getDirectory()
+
+        directory.createRecord("resources", "resource01",
+            shortNames=("resource01",), uid="resource01",
+            fullName="Resource number 1")
+
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record.fullName, "Resource number 1")
+
+        directory.updateRecord("resources", "resource01",
+            shortNames=("resource01", "r01"), uid="resource01",
+            fullName="Resource #1", firstName="First", lastName="Last",
+            emailAddresses=("resource01 at example.com", "r01 at example.com"))
+
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record.fullName, "Resource #1")
+        self.assertEquals(record.firstName, "First")
+        self.assertEquals(record.lastName, "Last")
+        self.assertEquals(set(record.shortNames), set(["resource01", "r01"]))
+        self.assertEquals(record.emailAddresses,
+            set(["resource01 at example.com", "r01 at example.com"]))
+
+        # Make sure old records are still there:
+        record = directory.recordWithUID("location01")
+        self.assertNotEquals(record, None)
+
+    def test_createDuplicateRecord(self):
+        directory = getDirectory()
+
+        directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+        self.assertRaises(DirectoryError, directory.createRecord, "resources", "resource01", shortNames=("resource01",), uid="resource01")

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_opendirectory.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_opendirectory.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -261,3 +261,18 @@
             self.assertEquals(len(results), 2)
             for record in results:
                 self.assertTrue(isinstance(record, OpenDirectoryRecord))
+
+
+    class OpenDirectorySubset (OpenDirectory):
+        """
+        Test the recordTypes subset feature of Apple OpenDirectoryService.
+        """
+        recordTypes = set((
+            DirectoryService.recordType_users,
+            DirectoryService.recordType_groups,
+        ))
+
+        def setUp(self):
+            super(OpenDirectorySubset, self).setUp()
+            self._service = OpenDirectoryService({'node' : "/Search", 'recordTypes' : (DirectoryService.recordType_users, DirectoryService.recordType_groups)}, dosetup=False)
+            augment.AugmentService = augment.AugmentXMLDB(xmlFiles=())

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -295,6 +295,7 @@
             changedCount = 0
             def changed(self):
                 self.changedCount += 1
+                return succeed(None)
 
         user = self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo")
 

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_resources.py (from rev 5079, CalendarServer/trunk/twistedcaldav/directory/test/test_resources.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_resources.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_resources.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,77 @@
+##
+# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+import os
+from twistedcaldav.config import config
+from twistedcaldav.test.util import TestCase
+from calendarserver.tools.util import getDirectory
+
+class ResourcesTestCase(TestCase):
+
+    def setUp(self):
+        testRoot = os.path.join(os.path.dirname(__file__), "resources")
+        configFileName = os.path.join(testRoot, "caldavd.plist")
+        config.load(configFileName)
+
+        xmlFile = os.path.join(testRoot, "users-groups.xml")
+        config.DirectoryService.params.xmlFile = xmlFile
+
+        xmlFile = os.path.join(testRoot, "resources-locations.xml")
+        config.ResourceService.params.xmlFile = xmlFile
+
+        xmlFile = os.path.join(testRoot, "augments.xml")
+        config.AugmentService.params.xmlFiles = (xmlFile,)
+
+        super(ResourcesTestCase, self).setUp()
+
+# Uh, what's this testing?
+#    def test_loadConfig(self):
+#        directory = getDirectory()
+
+    def test_recordInPrimaryDirectory(self):
+        directory = getDirectory()
+
+        # Look up a user, which comes out of primary directory service
+        record = directory.recordWithUID("user01")
+        self.assertNotEquals(record, None)
+
+    def test_recordInSupplementalDirectory(self):
+        directory = getDirectory()
+
+        # Look up a resource, which comes out of locations/resources service
+        record = directory.recordWithUID("resource01")
+        self.assertNotEquals(record, None)
+
+    def test_augments(self):
+        directory = getDirectory()
+
+        # Primary directory
+        record = directory.recordWithUID("user01")
+        self.assertEquals(record.enabled, True)
+        self.assertEquals(record.enabledForCalendaring, True)
+        record = directory.recordWithUID("user02")
+        self.assertEquals(record.enabled, False)
+        self.assertEquals(record.enabledForCalendaring, False)
+
+        # Supplemental directory
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record.enabled, True)
+        self.assertEquals(record.enabledForCalendaring, True)
+        self.assertEquals(record.autoSchedule, True)
+        record = directory.recordWithUID("resource02")
+        self.assertEquals(record.enabled, False)
+        self.assertEquals(record.enabledForCalendaring, False)
+        self.assertEquals(record.autoSchedule, False)

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_xmlfile.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/test/test_xmlfile.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -18,6 +18,7 @@
 
 from twisted.python.filepath import FilePath
 
+from twistedcaldav.test.util import TestCase
 from twistedcaldav.directory import augment
 from twistedcaldav.directory.directory import DirectoryService
 import twistedcaldav.directory.test.util
@@ -194,11 +195,13 @@
 <accounts realm="Test Realm">
   <group>
     <uid>enabled</uid>
+    <guid>enabled</guid>
     <password>enabled</password>
     <name>Enabled</name>
   </group>
   <group>
     <uid>disabled</uid>
+    <guid>disabled</guid>
     <password>disabled</password>
     <name>Disabled</name>
   </group>
@@ -225,3 +228,16 @@
         self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "enabled").enabledForCalendaring)
         self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "disabled").enabledForCalendaring)
 
+
+class XMLFileSubset (XMLFileBase, TestCase):
+    """
+    Test the recordTypes subset feature of XMLFile service.
+    """
+    recordTypes = set((
+        DirectoryService.recordType_users,
+        DirectoryService.recordType_groups,
+    ))
+
+    def test_recordTypesSubset(self):
+        directory = XMLDirectoryService({'xmlFile' : self.xmlFile(), 'recordTypes' : (DirectoryService.recordType_users, DirectoryService.recordType_groups)}, alwaysStat=True)
+        self.assertEquals(set(("users", "groups")), set(directory.recordTypes()))

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/xmlfile.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/directory/xmlfile.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -24,17 +24,22 @@
 
 from time import time
 import types
+import os, pwd, grp
 
 from twisted.cred.credentials import UsernamePassword
 from twisted.web2.auth.digest import DigestedCredentials
 from twisted.python.filepath import FilePath
+from twistedcaldav.config import config
 
 from twistedcaldav.directory import augment
-from twistedcaldav.directory.directory import DirectoryService
+from twistedcaldav.directory.directory import DirectoryService, DirectoryError
 from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
     CachingDirectoryRecord
-from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
+from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser, XMLAccountRecord
+import xml.etree.ElementTree as ET
+from uuid import uuid4
 
+
 class XMLDirectoryService(CachingDirectoryService):
     """
     XML based implementation of L{IDirectoryService}.
@@ -50,17 +55,53 @@
 
         defaults = {
             'xmlFile' : None,
-            'directoryBackedAddressBook': None
+            'directoryBackedAddressBook': None,
+            'recordTypes' : (
+                self.recordType_users,
+                self.recordType_groups,
+                self.recordType_locations,
+                self.recordType_resources,
+            ),
+            'cacheTimeout' : 30,
+            'realmName' : '/Search',
         }
         ignored = None
         params = self.getParams(params, defaults, ignored)
 
-        super(XMLDirectoryService, self).__init__()
+        self._recordTypes = params['recordTypes']
+        self.realmName = params['realmName']
 
+        super(XMLDirectoryService, self).__init__(params['cacheTimeout'])
+
         xmlFile = params.get("xmlFile")
         if type(xmlFile) is str:
             xmlFile = FilePath(xmlFile)
 
+        if not xmlFile.exists():
+            xmlFile.setContent("""<?xml version="1.0" encoding="utf-8"?>
+
+<accounts realm="%s">
+</accounts>
+""" % (self.realmName,))
+
+        uid = -1
+        if config.UserName:
+            try:
+                uid = pwd.getpwnam(config.UserName).pw_uid
+            except KeyError:
+                self.log_error("User not found: %s" % (config.UserName,))
+
+        gid = -1
+        if config.GroupName:
+            try:
+                gid = grp.getgrnam(config.GroupName).gr_gid
+            except KeyError:
+                self.log_error("Group not found: %s" % (config.GroupName,))
+
+        if uid != -1 and gid != -1:
+            os.chown(xmlFile.path, uid, gid)
+
+
         self.xmlFile = xmlFile
         self._fileInfo = None
         self._lastCheck = 0
@@ -77,16 +118,27 @@
         
 
     def recordTypes(self):
-        recordTypes = (
-            DirectoryService.recordType_users,
-            DirectoryService.recordType_groups,
-            DirectoryService.recordType_locations,
-            DirectoryService.recordType_resources
-        )
-        return recordTypes
+        return self._recordTypes
 
+    def listRecords(self, recordType):
+        self._lastCheck = 0
+        for xmlPrincipal in self._accounts()[recordType].itervalues():
+            record = self.recordWithGUID(xmlPrincipal.guid)
+            if record is not None:
+                yield record
+
     def queryDirectory(self, recordTypes, indexType, indexKey):
-        
+        """
+        If the query is a miss, re-read from the XML file and try again
+        """
+        if not self._queryDirectory(recordTypes, indexType, indexKey):
+            self._lastCheck = 0
+            self._queryDirectory(recordTypes, indexType, indexKey)
+
+    def _queryDirectory(self, recordTypes, indexType, indexKey):
+
+        anyMatches = False
+
         for recordType in recordTypes:
             for xmlPrincipal in self._accounts()[recordType].itervalues():
                 record = XMLDirectoryRecord(
@@ -96,13 +148,6 @@
                     xmlPrincipal  = xmlPrincipal,
                 )
 
-                record = XMLDirectoryRecord(
-                    service       = self,
-                    recordType    = recordType,
-                    shortNames    = tuple(xmlPrincipal.shortNames),
-                    xmlPrincipal  = xmlPrincipal,
-                )
-                
                 # Look up augment information
                 # TODO: this needs to be deferred but for now we hard code the deferred result because
                 # we know it is completing immediately.
@@ -118,7 +163,10 @@
                     matched = indexKey in record.calendarUserAddresses
                 
                 if matched:
+                    anyMatches = True
                     self.recordCacheForType(recordType).addRecord(record, indexType, indexKey)
+
+        return anyMatches
             
     def recordsMatchingFields(self, fields, operand="or", recordType=None):
         # Default, brute force method search of underlying XML data
@@ -187,6 +235,10 @@
                     if record:
                         yield record
 
+    def _initCaches(self):
+        super(XMLDirectoryService, self)._initCaches()
+        self._lastCheck = 0
+
     def _accounts(self):
         currentTime = time()
         if self._alwaysStat or currentTime - self._lastCheck > 60:
@@ -200,6 +252,155 @@
                 self._fileInfo = fileInfo
         return self._parsedAccounts
 
+
+    def _addElement(self, parent, principal):
+        """
+        Create an XML element from principal and add it as a child of parent
+        """
+
+        # TODO: derive this from xmlaccountsparser.py
+        xmlTypes = {
+            'users'     : 'user',
+            'groups'    : 'group',
+            'locations' : 'location',
+            'resources' : 'resource',
+        }
+        xmlType = xmlTypes[principal.recordType]
+
+        element = ET.SubElement(parent, xmlType)
+        for value in principal.shortNames:
+            ET.SubElement(element, "uid").text = value
+        ET.SubElement(element, "guid").text = principal.guid
+        ET.SubElement(element, "name").text = principal.fullName
+        ET.SubElement(element, "first-name").text = principal.firstName
+        ET.SubElement(element, "last-name").text = principal.lastName
+        for value in principal.emailAddresses:
+            ET.SubElement(element, "email-address").text = value
+
+        return element
+
+
+    def _persistRecords(self, element):
+
+        def indent(elem, level=0):
+            i = "\n" + level*"  "
+            if len(elem):
+                if not elem.text or not elem.text.strip():
+                    elem.text = i + "  "
+                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)
+
+        # TODO: make this robust:
+        ET.ElementTree(element).write(self.xmlFile.path)
+
+        # Reload
+        self._initCaches() # nuke local cache
+        self._lastCheck = 0
+        self._accounts()
+        # TODO: nuke memcache entries, or prepopulate them
+
+
+    def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
+        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+        uid=None, password=None, **kwds):
+        """
+        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.
+        """
+
+        if guid is None:
+            guid = str(uuid4())
+
+        # Make sure latest XML records are read in
+        self._lastCheck = 0
+        accounts = self._accounts()
+
+        accountsElement = ET.Element("accounts", realm=self.realmName)
+        for recType in self.recordTypes():
+            for xmlPrincipal in accounts[recType].itervalues():
+                if xmlPrincipal.guid == guid:
+                    raise DirectoryError("Duplicate guid: %s" % (guid,))
+                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
+        self._addElement(accountsElement, xmlPrincipal)
+
+        self._persistRecords(accountsElement)
+
+
+    def destroyRecord(self, recordType, guid):
+        """
+        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.
+        """
+
+        # Make sure latest XML records are read in
+        self._lastCheck = 0
+        accounts = self._accounts()
+
+        accountsElement = ET.Element("accounts", 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)
+
+
+    def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+        uid=None, password=None, **kwds):
+        """
+        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.
+        """
+
+        # Make sure latest XML records are read in
+        self._lastCheck = 0
+        accounts = self._accounts()
+
+        accountsElement = ET.Element("accounts", 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
+                    self._addElement(accountsElement, xmlPrincipal)
+                else:
+                    self._addElement(accountsElement, xmlPrincipal)
+
+        self._persistRecords(accountsElement)
+
+
 class XMLDirectoryRecord(CachingDirectoryRecord):
     """
     XML based implementation implementation of L{IDirectoryRecord}.

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/index.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/index.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/index.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -829,9 +829,11 @@
         assert resource.isCalendarCollection(), "non-calendar collection resource %s has no index." % (resource,)
         super(Index, self).__init__(resource)
 
-        if config.Memcached.Pools.Default.ClientEnabled:
+        if (
+            hasattr(config, "Memcached") and
+            config.Memcached.Pools.Default.ClientEnabled
+        ):
             self.reserver = MemcachedUIDReserver(self)
-
         else:
             self.reserver = SQLUIDReserver(self)
 

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/stdconfig.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/twistedcaldav/stdconfig.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -39,13 +39,25 @@
 DEFAULT_SERVICE_PARAMS = {
     "twistedcaldav.directory.xmlfile.XMLDirectoryService": {
         "xmlFile": "/etc/caldavd/accounts.xml",
+        "cacheTimeout": 30,
+        "recordTypes": ("users", "groups"),
     },
     "twistedcaldav.directory.appleopendirectory.OpenDirectoryService": {
         "node": "/Search",
         "cacheTimeout": 30,
+        "recordTypes": ("users", "groups"),
     },
 }
 
+DEFAULT_RESOURCE_PARAMS = {
+    "twistedcaldav.directory.xmlfile.XMLDirectoryService": {
+        "xmlFile": "/etc/caldavd/resources.xml",
+        "cacheTimeout": 30,
+        "realmName": "/Search",
+        "recordTypes" : ("locations", "resources"),
+    },
+}
+
 DEFAULT_AUGMENT_PARAMS = {
     "twistedcaldav.directory.augment.AugmentXMLDB": {
         "xmlFiles": ["/etc/caldavd/augments.xml",],
@@ -174,6 +186,18 @@
     },
 
     #
+    # Locations and Resources service
+    #
+    #    Supplements the directory service with information about locations
+    #    and resources.
+    #
+    "ResourceService": {
+        "Enabled" : False,
+        "type": "twistedcaldav.directory.xmlfile.XMLDirectoryService",
+        "params": DEFAULT_RESOURCE_PARAMS["twistedcaldav.directory.xmlfile.XMLDirectoryService"],
+    },
+
+    #
     # Augment service
     #
     #    Augments for the directory service records to add calendar specific attributes.

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/file.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/file.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -30,23 +30,29 @@
 from zope.interface import implements
 
 from twisted.python.filepath import FilePath
+from twisted.internet.defer import inlineCallbacks
 
 from twext.log import LoggingMixIn
-from twext.python.icalendar import Component as iComponent, InvalidICalendarDataError
+from twext.python.icalendar import Component as iComponent
+from twext.python.icalendar import InvalidICalendarDataError
 
+from txdav.propertystore.xattr import PropertyStore
+
 from txcaldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject
-#from txcaldav.icalendarstore import CalendarStoreError
-#from txcaldav.icalendarstore import AlreadyExistsError
-#from txcaldav.icalendarstore import CalendarAlreadyExistsError
-#from txcaldav.icalendarstore import CalendarObjectNameAlreadyExistsError
-#from txcaldav.icalendarstore import CalendarObjectUIDAlreadyExistsError
+from txcaldav.icalendarstore import CalendarNameNotAllowedError
+from txcaldav.icalendarstore import CalendarObjectNameNotAllowedError
+from txcaldav.icalendarstore import CalendarAlreadyExistsError
+from txcaldav.icalendarstore import CalendarObjectNameAlreadyExistsError
 from txcaldav.icalendarstore import NotFoundError
-#from txcaldav.icalendarstore import NoSuchCalendarError
+from txcaldav.icalendarstore import NoSuchCalendarError
 from txcaldav.icalendarstore import NoSuchCalendarObjectError
-#from txcaldav.icalendarstore import InvalidCalendarComponentError
+from txcaldav.icalendarstore import InvalidCalendarComponentError
 from txcaldav.icalendarstore import InternalDataStoreError
 
+from twistedcaldav.index import Index as OldIndex
+from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
 
+
 class CalendarStore(LoggingMixIn):
     # FIXME: Do we need an interface?
 
@@ -65,13 +71,21 @@
             # be CalendarStoreNotFoundError.
             raise NotFoundError("No such calendar store")
 
-    def __str__(self):
-        return "<%s: %s>" % (self.__class__, self.path)
+    def __repr__(self):
+        return "<%s: %s>" % (self.__class__.__name__, self.path.path)
 
     def calendarHomeWithUID(self, uid):
-        return CalendarHome(self.path.child(uid), self)
+        if uid.startswith("."):
+            return None
 
+        childPath = self.path.child(uid)
 
+        if childPath.isdir():
+            return CalendarHome(childPath, self)
+        else:
+            return None
+
+
 class CalendarHome(LoggingMixIn):
     implements(ICalendarHome)
 
@@ -81,8 +95,8 @@
         self.path = path
         self.calendarStore = calendarStore
 
-    def __str__(self):
-        return "<%s: %s>" % (self.__class__, self.path)
+    def __repr__(self):
+        return "<%s: %s>" % (self.__class__.__name__, self.path)
 
     def uid(self):
         return self.path.basename()
@@ -95,16 +109,44 @@
         )
 
     def calendarWithName(self, name):
-        return Calendar(self.path.child(name), self)
+        if name.startswith("."):
+            return None
 
+        childPath = self.path.child(name)
+        if childPath.isdir():
+            return Calendar(childPath, self)
+        else:
+            return None
+
     def createCalendarWithName(self, name):
-        raise NotImplementedError()
+        if name.startswith("."):
+            raise CalendarNameNotAllowedError(name)
 
+        childPath = self.path.child(name)
+
+        try:
+            childPath.createDirectory()
+        except (IOError, OSError), e:
+            if e.errno == errno.EEXIST:
+                raise CalendarAlreadyExistsError(name)
+            raise
+
     def removeCalendarWithName(self, name):
-        raise NotImplementedError()
+        if name.startswith("."):
+            raise NoSuchCalendarError(name)
 
+        childPath = self.path.child(name)
+        try:
+            childPath.remove()
+        except (IOError, OSError), e:
+            if e.errno == errno.ENOENT:
+                raise NoSuchCalendarError(name)
+            raise
+
     def properties(self):
-        raise NotImplementedError()
+        if not hasattr(self, "_properties"):
+            self._properties = PropertyStore(self.path)
+        return self._properties
 
 
 class Calendar(LoggingMixIn):
@@ -116,40 +158,94 @@
         self.path = path
         self.calendarHome = calendarHome
 
-    def __str__(self):
-        return "<%s: %s>" % (self.__class__, self.path)
+    def __repr__(self):
+        return "<%s: %s>" % (self.__class__.__name__, self.path.path)
 
+    def index(self):
+        if not hasattr(self, "_index"):
+            self._index = Index(self)
+        return self._index
+
     def name(self):
         return self.path.basename()
 
     def ownerCalendarHome(self):
         return self.calendarHome
 
-    def calendarObjects(self):
+    def _calendarObjects_index(self):
+        return self.index().calendarObjects()
+
+    def _calendarObjects_listdir(self):
         return (
             self.calendarObjectWithName(name)
             for name in self.path.listdir()
             if not name.startswith(".")
         )
 
+    calendarObjects = _calendarObjects_index
+
     def calendarObjectWithName(self, name):
-        return CalendarObject(self.path.child(name), self)
+        childPath = self.path.child(name)
+        if childPath.isfile():
+            return CalendarObject(childPath, self)
+        else:
+            return None
 
     def calendarObjectWithUID(self, uid):
         raise NotImplementedError()
 
     def createCalendarObjectWithName(self, name, component):
-        raise NotImplementedError()
+        if name.startswith("."):
+            raise CalendarObjectNameNotAllowedError(name)
 
+        childPath = self.path.child(name)
+        if childPath.exists():
+            raise CalendarObjectNameAlreadyExistsError(name)
+
+        calendarObject = CalendarObject(childPath, self)
+        calendarObject.setComponent(component)
+
     def removeCalendarObjectWithName(self, name):
-        raise NotImplementedError()
+        if name.startswith("."):
+            raise NoSuchCalendarObjectError(name)
 
+        childPath = self.path.child(name)
+        if childPath.isfile():
+            childPath.remove()
+        else:
+            raise NoSuchCalendarObjectError(name)
+
     def removeCalendarObjectWithUID(self, uid):
         raise NotImplementedError()
 
     def syncToken(self):
         raise NotImplementedError()
 
+    @inlineCallbacks
+    def _updateSyncToken(self, reset=False):
+        return
+
+        lock = MemcacheLock("Calendar", self.fp.path, timeout=60.0)
+        try:
+            try:
+                yield lock.acquire()
+            except MemcacheLockTimeoutError:
+                raise InternalDataStoreError("Timed out on calendar lock")
+
+            def newToken():
+                raise NotImplementedError()
+
+            if reset:
+                token = newToken()
+
+            raise NotImplementedError(token)
+
+        finally:
+            yield lock.clean()
+
+
+        raise NotImplementedError()
+
     def calendarObjectsInTimeRange(self, start, end, timeZone):
         raise NotImplementedError()
 
@@ -157,7 +253,9 @@
         raise NotImplementedError()
 
     def properties(self):
-        raise NotImplementedError()
+        if not hasattr(self, "_properties"):
+            self._properties = PropertyStore(self.path)
+        return self._properties
 
 
 class CalendarObject(LoggingMixIn):
@@ -167,15 +265,41 @@
         self.path = path
         self.calendar = calendar
 
-    def __str__(self):
-        return "<%s: %s>" % (self.__class__, self.path)
+    def __repr__(self):
+        return "<%s: %s>" % (self.__class__.__name__, self.path.path)
 
     def name(self):
         return self.path.basename()
 
     def setComponent(self, component):
-        raise NotImplementedError()
+        if not isinstance(component, iComponent):
+            raise TypeError(iComponent)
 
+        try:
+            if component.resourceUID() != self.uid():
+                raise InvalidCalendarComponentError(
+                    "UID may not change (%s != %s)" % (
+                        component.resourceUID(), self.uid()
+                     )
+                )
+        except NoSuchCalendarObjectError:
+            pass
+
+        try:
+            component.validateForCalDAV()
+        except InvalidICalendarDataError, e:
+            raise InvalidCalendarComponentError(e)
+
+        self._component = component
+        if hasattr(self, "_text"):
+            del self._text
+
+        fh = self.path.open("w")
+        try:
+            fh.write(str(component))
+        finally:
+            fh.close()
+
     def component(self):
         if not hasattr(self, "_component"):
             text = self.iCalendarText()
@@ -242,4 +366,55 @@
         return self.component().getOrganizer()
 
     def properties(self):
-        raise NotImplementedError()
+        if not hasattr(self, "_properties"):
+            self._properties = PropertyStore(self.path)
+        return self._properties
+
+class Index (object):
+    #
+    # OK, here's where we get ugly.
+    # The index code needs to be rewritten also, but in the meantime...
+    #
+    class StubResource(object):
+        """
+        Just enough resource to keep the Index class going.
+        """
+        def __init__(self, calendar):
+            self.calendar = calendar
+            self.fp = self.calendar.path
+
+        def isCalendarCollection(self):
+            return True
+
+        def getChild(self, name):
+            calendarObject = self.calendar.calendarObjectWithName(name)
+            if calendarObject:
+                class ChildResource(object):
+                    def __init__(self, calendarObject):
+                        self.calendarObject = calendarObject
+
+                    def iCalendar(self):
+                        return self.calendarObject.component()
+
+                return ChildResource(calendarObject)
+            else:
+                return None
+
+        def bumpSyncToken(self, reset=False):
+            return self.calendar._updateSyncToken(reset)
+
+
+    def __init__(self, calendar):
+        self.calendar = calendar
+        self._oldIndex = OldIndex(Index.StubResource(calendar))
+
+    def calendarObjects(self):
+        calendar = self.calendar
+        for name, uid, componentType in self._oldIndex.bruteForceSearch():
+            calendarObject = calendar.calendarObjectWithName(name)
+
+            # Precache what we found in the index
+            calendarObject._uid = uid
+            calendarObject._componentType = componentType
+
+            yield calendarObject

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/test/test_file.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/calendarstore/test/test_file.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -27,14 +27,18 @@
 
 from txdav.idav import IPropertyStore
 
-from txcaldav.icalendarstore import ICalendarHome
-from txcaldav.icalendarstore import ICalendar
-from txcaldav.icalendarstore import ICalendarObject
+from txcaldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject
+from txcaldav.icalendarstore import CalendarNameNotAllowedError
+from txcaldav.icalendarstore import CalendarObjectNameNotAllowedError
+from txcaldav.icalendarstore import CalendarAlreadyExistsError
+from txcaldav.icalendarstore import CalendarObjectNameAlreadyExistsError
+from txcaldav.icalendarstore import CalendarObjectUIDAlreadyExistsError
+from txcaldav.icalendarstore import NoSuchCalendarError
+from txcaldav.icalendarstore import NoSuchCalendarObjectError
+from txcaldav.icalendarstore import InvalidCalendarComponentError
 
-from txcaldav.calendarstore.file import CalendarStore
-from txcaldav.calendarstore.file import CalendarHome
-from txcaldav.calendarstore.file import Calendar
-from txcaldav.calendarstore.file import CalendarObject
+from txcaldav.calendarstore.file import CalendarStore, CalendarHome
+from txcaldav.calendarstore.file import Calendar, CalendarObject
 
 storePath = FilePath(__file__).parent().child("calendar_store")
 
@@ -50,38 +54,182 @@
     "3.ics",
 )
 
+event4_text = (
+    "BEGIN:VCALENDAR\r\n"
+      "VERSION:2.0\r\n"
+      "PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
+      "CALSCALE:GREGORIAN\r\n"
+      "BEGIN:VTIMEZONE\r\n"
+        "TZID:US/Pacific\r\n"
+        "BEGIN:DAYLIGHT\r\n"
+          "TZOFFSETFROM:-0800\r\n"
+          "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n"
+          "DTSTART:20070311T020000\r\n"
+          "TZNAME:PDT\r\n"
+          "TZOFFSETTO:-0700\r\n"
+        "END:DAYLIGHT\r\n"
+        "BEGIN:STANDARD\r\n"
+          "TZOFFSETFROM:-0700\r\n"
+          "RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n"
+          "DTSTART:20071104T020000\r\n"
+          "TZNAME:PST\r\n"
+          "TZOFFSETTO:-0800\r\n"
+        "END:STANDARD\r\n"
+      "END:VTIMEZONE\r\n"
+      "BEGIN:VEVENT\r\n"
+        "CREATED:20100203T013849Z\r\n"
+        "UID:uid4\r\n"
+        "DTEND;TZID=US/Pacific:20100207T173000\r\n"
+        "TRANSP:OPAQUE\r\n"
+        "SUMMARY:New Event\r\n"
+        "DTSTART;TZID=US/Pacific:20100207T170000\r\n"
+        "DTSTAMP:20100203T013909Z\r\n"
+        "SEQUENCE:3\r\n"
+        "BEGIN:VALARM\r\n"
+          "X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
+          "TRIGGER:-PT20M\r\n"
+          "ATTACH;VALUE=URI:Basso\r\n"
+          "ACTION:AUDIO\r\n"
+        "END:VALARM\r\n"
+      "END:VEVENT\r\n"
+    "END:VCALENDAR\r\n"
+)
+
+event1modified_text = event4_text.replace(
+    "\r\nUID:uid4\r\n",
+    "\r\nUID:uid1\r\n"
+)
+
+event4notCalDAV_text = (
+    "BEGIN:VCALENDAR\r\n"
+      "VERSION:2.0\r\n"
+      "PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
+      "CALSCALE:GREGORIAN\r\n"
+      "BEGIN:VEVENT\r\n"
+        "CREATED:20100203T013849Z\r\n"
+        "UID:4\r\n"
+        "DTEND;TZID=US/Pacific:20100207T173000\r\n" # TZID without VTIMEZONE
+        "TRANSP:OPAQUE\r\n"
+        "SUMMARY:New Event\r\n"
+        "DTSTART;TZID=US/Pacific:20100207T170000\r\n"
+        "DTSTAMP:20100203T013909Z\r\n"
+        "SEQUENCE:3\r\n"
+        "BEGIN:VALARM\r\n"
+          "X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
+          "TRIGGER:-PT20M\r\n"
+          "ATTACH;VALUE=URI:Basso\r\n"
+          "ACTION:AUDIO\r\n"
+        "END:VALARM\r\n"
+      "END:VEVENT\r\n"
+    "END:VCALENDAR\r\n"
+)
+
+
+def _todo(f, why):
+    f.todo = why
+    return f
+
+featureUnimplemented = lambda f: _todo(f, "Feature unimplemented")
+testUnimplemented = lambda f: _todo(f, "Test unimplemented")
+todo = lambda why: lambda f: _todo(f, why)
+
+
+class PropertiesTestMixin(object):
+    def test_properties(self):
+        properties = self.home1.properties()
+
+        self.failUnless(
+            IPropertyStore.providedBy(properties),
+            properties
+        )
+
+
+def setUpCalendarStore(test):
+    test.root = FilePath(test.mktemp())
+    test.root.createDirectory()
+
+    calendarPath = test.root.child("store")
+    storePath.copyTo(calendarPath)
+
+    test.calendarStore = CalendarStore(calendarPath)
+
+def setUpHome1(test):
+    setUpCalendarStore(test)
+    test.home1 = test.calendarStore.calendarHomeWithUID("home1")
+
+def setUpCalendar1(test):
+    setUpHome1(test)
+    test.calendar1 = test.home1.calendarWithName("calendar_1")
+
+
 class CalendarStoreTest(unittest.TestCase):
     def setUp(self):
-        self.calendarStore = CalendarStore(storePath)
+        setUpCalendarStore(self)
 
     # FIXME: If we define an interface
     #def test_interface(self):
+    #    """
+    #    Interface is completed and conforming.
+    #    """
     #    try:
     #        verifyObject(ICalendarStore, self.calendarstore)
     #    except BrokenMethodImplementation, e:
     #        self.fail(e)
 
     def test_init(self):
-        assert isinstance(self.calendarStore.path, FilePath), self.calendarStore.path
+        """
+        Ivars are correctly initialized.
+        """
+        self.failUnless(
+            isinstance(self.calendarStore.path, FilePath),
+            self.calendarStore.path
+        )
 
-    def test_calendarHomeWithUID(self):
+    def test_calendarHomeWithUID_exists(self):
+        """
+        Find an existing calendar home by UID.
+        """
         calendarHome = self.calendarStore.calendarHomeWithUID("home1")
 
-        assert isinstance(calendarHome, CalendarHome)
+        self.failUnless(isinstance(calendarHome, CalendarHome))
 
+    def test_calendarHomeWithUID_absent(self):
+        """
+        Missing calendar home.
+        """
+        self.assertEquals(
+            self.calendarStore.calendarHomeWithUID("xyzzy"),
+            None
+        )
 
-class CalendarHomeTest(unittest.TestCase):
+    def test_calendarHomeWithUID_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no UIDs may start with ".".
+        """
+        self.assertEquals(
+            self.calendarStore.calendarHomeWithUID("xyzzy"),
+            None
+        )
+
+
+class CalendarHomeTest(unittest.TestCase, PropertiesTestMixin):
     def setUp(self):
-        self.calendarStore = CalendarStore(storePath)
-        self.home1 = self.calendarStore.calendarHomeWithUID("home1")
+        setUpHome1(self)
 
     def test_interface(self):
+        """
+        Interface is completed and conforming.
+        """
         try:
             verifyObject(ICalendarHome, self.home1)
         except BrokenMethodImplementation, e:
             self.fail(e)
 
     def test_init(self):
+        """
+        Ivars are correctly initialized.
+        """
         self.failUnless(
             isinstance(self.home1.path, FilePath),
             self.home1.path
@@ -92,54 +240,128 @@
         )
 
     def test_uid(self):
+        """
+        UID is correct.
+        """
         self.assertEquals(self.home1.uid(), "home1")
 
     def test_calendars(self):
+        """
+        Find all of the calendars.
+        """
+        # Add a dot directory to make sure we don't find it
+        self.home1.path.child(".foo").createDirectory()
+
         calendars = tuple(self.home1.calendars())
 
         for calendar in calendars:
-            self.failUnless(isinstance(calendar, Calendar))
+            self.failUnless(isinstance(calendar, Calendar), calendar)
 
         self.assertEquals(
             tuple(c.name() for c in calendars),
             home1_calendarNames
         )
 
-    def test_calendarWithName(self):
+    def test_calendarWithName_exists(self):
+        """
+        Find existing calendar by name.
+        """
         for name in home1_calendarNames:
             calendar = self.home1.calendarWithName(name)
-            self.failUnless(isinstance(calendar, Calendar))
+            self.failUnless(isinstance(calendar, Calendar), calendar)
             self.assertEquals(calendar.name(), name)
 
-    def test_createCalendarWithName(self):
-        raise NotImplementedError()
-    test_createCalendarWithName.todo = "Unimplemented"
+    def test_calendarWithName_absent(self):
+        """
+        Missing calendar.
+        """
+        self.assertEquals(self.home1.calendarWithName("xyzzy"), None)
 
-    def test_removeCalendarWithName(self):
-        raise NotImplementedError()
-    test_removeCalendarWithName.todo = "Unimplemented"
+    def test_calendarWithName_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no calendar names may start with ".".
+        """
+        name = ".foo"
+        self.home1.path.child(name).createDirectory()
+        self.assertEquals(self.home1.calendarWithName(name), None)
 
-    def test_properties(self):
-        properties = self.home1.properties()
+    def test_createCalendarWithName_absent(self):
+        """
+        Create a new calendar.
+        """
+        name = "new"
+        assert self.home1.calendarWithName(name) is None
+        self.home1.createCalendarWithName(name)
+        self.failUnless(self.home1.calendarWithName(name) is not None)
 
-        # FIXME: check specific class later?
-        self.failUnless(IPropertyStore.providedBy(properties))
-    test_properties.todo = "Unimplemented"
+    def test_createCalendarWithName_exists(self):
+        """
+        Attempt to create an existing calendar should raise.
+        """
+        for name in home1_calendarNames:
+            self.assertRaises(
+                CalendarAlreadyExistsError,
+                self.home1.createCalendarWithName, name
+            )
 
+    def test_createCalendarWithName_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no calendar names may start with ".".
+        """
+        self.assertRaises(
+            CalendarNameNotAllowedError,
+            self.home1.createCalendarWithName, ".foo"
+        )
 
-class CalendarTest(unittest.TestCase):
+    def test_removeCalendarWithName_exists(self):
+        """
+        Remove an existing calendar.
+        """
+        for name in home1_calendarNames:
+            assert self.home1.calendarWithName(name) is not None
+            self.home1.removeCalendarWithName(name)
+            self.assertEquals(self.home1.calendarWithName(name), None)
+
+    def test_removeCalendarWithName_absent(self):
+        """
+        Attempt to remove an non-existing calendar should raise.
+        """
+        self.assertRaises(
+            NoSuchCalendarError,
+            self.home1.removeCalendarWithName, "xyzzy"
+        )
+
+    def test_removeCalendarWithName_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no calendar names may start with ".".
+        """
+        name = ".foo"
+        self.home1.path.child(name).createDirectory()
+        self.assertRaises(
+            NoSuchCalendarError,
+            self.home1.removeCalendarWithName, name
+        )
+
+class CalendarTest(unittest.TestCase, PropertiesTestMixin):
     def setUp(self):
-        self.calendarStore = CalendarStore(storePath)
-        self.home1 = self.calendarStore.calendarHomeWithUID("home1")
-        self.calendar1 = self.home1.calendarWithName("calendar_1")
+        setUpCalendar1(self)
 
     def test_interface(self):
+        """
+        Interface is completed and conforming.
+        """
         try:
             verifyObject(ICalendar, self.calendar1)
         except BrokenMethodImplementation, e:
             self.fail(e)
 
     def test_init(self):
+        """
+        Ivars are correctly initialized.
+        """
         self.failUnless(
             isinstance(self.calendar1.path, FilePath),
             self.calendar1
@@ -150,79 +372,268 @@
         )
 
     def test_name(self):
+        """
+        Name is correct.
+        """
         self.assertEquals(self.calendar1.name(), "calendar_1")
 
     def test_ownerCalendarHome(self):
+        """
+        Owner is correct.
+        """
         # Note that here we know that home1 owns calendar1
         self.assertEquals(
             self.calendar1.ownerCalendarHome().uid(),
             self.home1.uid()
         )
 
-    def test_calendarObjects(self):
-        calendarObjects = tuple(self.calendar1.calendarObjects())
+    def _test_calendarObjects(self, which):
+        # Add a dot file to make sure we don't find it
+        self.home1.path.child(".foo").createDirectory()
 
+        methodName = "_calendarObjects_%s" % (which,)
+        method = getattr(self.calendar1, methodName)
+        calendarObjects = tuple(method())
+
         for calendarObject in calendarObjects:
-            self.failUnless(isinstance(calendarObject, CalendarObject))
+            self.failUnless(
+                isinstance(calendarObject, CalendarObject),
+                calendarObject
+            )
 
         self.assertEquals(
             tuple(o.name() for o in calendarObjects),
             calendar1_objectNames
         )
 
-    def test_calendarObjectWithName(self):
+    def test_calendarObjects_listdir(self):
+        """
+        Find all of the calendar objects using the listdir
+        implementation.
+        """
+        return self._test_calendarObjects("listdir")
+
+    @todo("Index is missing 1.ics?")
+    def test_calendarObjects_index(self):
+        """
+        Find all of the calendar objects using the index
+        implementation.
+        """
+        return self._test_calendarObjects("index")
+
+    def test_calendarObjectWithName_exists(self):
+        """
+        Find existing calendar object by name.
+        """
         for name in calendar1_objectNames:
             calendarObject = self.calendar1.calendarObjectWithName(name)
-            self.failUnless(isinstance(calendarObject, CalendarObject))
+            self.failUnless(
+                isinstance(calendarObject, CalendarObject),
+                calendarObject
+            )
             self.assertEquals(calendarObject.name(), name)
 
-    def test_calendarObjectWithUID(self):
-        raise NotImplementedError()
-    test_calendarObjectWithUID.todo = "Unimplemented"
+    def test_calendarObjectWithName_absent(self):
+        """
+        Missing calendar object.
+        """
+        self.assertEquals(self.calendar1.calendarObjectWithName("xyzzy"), None)
 
-    def test_createCalendarObjectWithName(self):
-        raise NotImplementedError()
-    test_createCalendarObjectWithName.todo = "Unimplemented"
+    def test_calendarObjectWithName_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no calendar object names may start with
+        ".".
+        """
+        name = ".foo.ics"
+        self.home1.path.child(name).touch()
+        self.assertEquals(self.calendar1.calendarObjectWithName(name), None)
 
-    def test_removeCalendarComponentWithName(self):
-        raise NotImplementedError()
-    test_removeCalendarComponentWithName.todo = "Unimplemented"
+    @featureUnimplemented
+    def test_calendarObjectWithUID_exists(self):
+        """
+        Find existing calendar object by name.
+        """
+        calendarObject = self.calendar1.calendarObjectWithUID("1")
+        self.failUnless(
+            isinstance(calendarObject, CalendarObject),
+            calendarObject
+        )
+        self.assertEquals(
+            calendarObject.component(),
+            self.calendar1.calendarObjectWithName("1.ics").component()
+        )
 
-    def test_removeCalendarComponentWithUID(self):
-        raise NotImplementedError()
-    test_removeCalendarComponentWithUID.todo = "Unimplemented"
+    @featureUnimplemented
+    def test_calendarObjectWithUID_absent(self):
+        """
+        Missing calendar object.
+        """
+        self.assertEquals(self.calendar1.calendarObjectWithUID("xyzzy"), None)
 
+    def test_createCalendarObjectWithName_absent(self):
+        """
+        Create a new calendar object.
+        """
+        name = "4.ics"
+        assert self.calendar1.calendarObjectWithName(name) is None
+        component = iComponent.fromString(event4_text)
+        self.calendar1.createCalendarObjectWithName(name, component)
+
+        calendarObject = self.calendar1.calendarObjectWithName(name)
+        self.assertEquals(calendarObject.component(), component)
+
+    def test_createCalendarObjectWithName_exists(self):
+        """
+        Attempt to create an existing calendar object should raise.
+        """
+        self.assertRaises(
+            CalendarObjectNameAlreadyExistsError,
+            self.calendar1.createCalendarObjectWithName,
+            "1.ics", iComponent.fromString(event4_text)
+        )
+
+    def test_createCalendarObjectWithName_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no calendar object names may start with
+        ".".
+        """
+        self.assertRaises(
+            CalendarObjectNameNotAllowedError,
+            self.calendar1.createCalendarObjectWithName,
+            ".foo", iComponent.fromString(event4_text)
+        )
+
+    @featureUnimplemented
+    def test_createCalendarObjectWithName_uidconflict(self):
+        """
+        Attempt to create a calendar object with a conflicting UID
+        should raise.
+        """
+        name = "foo.ics"
+        assert self.calendar1.calendarObjectWithName(name) is None
+        component = iComponent.fromString(event1modified_text)
+        self.assertRaises(
+            CalendarObjectUIDAlreadyExistsError,
+            self.calendar1.createCalendarObjectWithName,
+            name, component
+        )
+
+    def test_createCalendarObjectWithName_invalid(self):
+        """
+        Attempt to create a calendar object with a invalid iCalendar text
+        should raise.
+        """
+        self.assertRaises(
+            InvalidCalendarComponentError,
+            self.calendar1.createCalendarObjectWithName,
+            "new", iComponent.fromString(event4notCalDAV_text)
+        )
+
+    def test_removeCalendarObjectWithName_exists(self):
+        """
+        Remove an existing calendar object.
+        """
+        for name in calendar1_objectNames:
+            assert self.calendar1.calendarObjectWithName(name) is not None
+            self.calendar1.removeCalendarObjectWithName(name)
+            self.assertEquals(
+                self.calendar1.calendarObjectWithName(name),
+                None
+            )
+
+    def test_removeCalendarObjectWithName_absent(self):
+        """
+        Attempt to remove an non-existing calendar object should raise.
+        """
+        self.assertRaises(
+            NoSuchCalendarObjectError,
+            self.calendar1.removeCalendarObjectWithName, "xyzzy"
+        )
+
+    def test_removeCalendarObjectWithName_dot(self):
+        """
+        Filenames starting with "." are reserved by this
+        implementation, so no calendar object names may start with
+        ".".
+        """
+        name = ".foo"
+        self.calendar1.path.child(name).touch()
+        self.assertRaises(
+            NoSuchCalendarObjectError,
+            self.calendar1.removeCalendarObjectWithName, name
+        )
+
+    @featureUnimplemented
+    def test_removeCalendarObjectWithUID_exists(self):
+        """
+        Remove an existing calendar object.
+        """
+        for name in calendar1_objectNames:
+            uid = name.rstrip(".ics")
+            assert self.calendar1.calendarObjectWithUID(uid) is not None
+            self.calendar1.removeCalendarObjectWithUID(uid)
+            self.assertEquals(
+                self.calendar1.calendarObjectWithUID(uid),
+                None
+            )
+            self.assertEquals(
+                self.calendar1.calendarObjectWithName(name),
+                None
+            )
+
+    @featureUnimplemented
+    def test_removeCalendarObjectWithUID_absent(self):
+        """
+        Attempt to remove an non-existing calendar object should raise.
+        """
+        self.assertRaises(
+            NoSuchCalendarObjectError,
+            self.calendar1.removeCalendarObjectWithUID, "xyzzy"
+        )
+
+    @testUnimplemented
     def test_syncToken(self):
+        """
+        Sync token is correct.
+        """
         raise NotImplementedError()
-    test_syncToken.todo = "Unimplemented"
 
+    @testUnimplemented
     def test_calendarObjectsInTimeRange(self):
+        """
+        Find calendar objects occuring in a given time range.
+        """
         raise NotImplementedError()
-    test_calendarObjectsInTimeRange.todo = "Unimplemented"
 
+    @testUnimplemented
     def test_calendarObjectsSinceToken(self):
+        """
+        Find calendar objects that have been modified since a given
+        sync token.
+        """
         raise NotImplementedError()
-    test_calendarObjectsSinceToken.todo = "Unimplemented"
 
-    def test_properties(self):
-        raise NotImplementedError()
-    test_properties.todo = "Unimplemented"
 
-
-class CalendarObjectTest(unittest.TestCase):
+class CalendarObjectTest(unittest.TestCase, PropertiesTestMixin):
     def setUp(self):
-        self.calendarStore = CalendarStore(storePath)
-        self.home1 = self.calendarStore.calendarHomeWithUID("home1")
-        self.calendar1 = self.home1.calendarWithName("calendar_1")
+        setUpCalendar1(self)
         self.object1 = self.calendar1.calendarObjectWithName("1.ics")
 
     def test_interface(self):
+        """
+        Interface is completed and conforming.
+        """
         try:
             verifyObject(ICalendarObject, self.object1)
         except BrokenMethodImplementation, e:
             self.fail(e)
 
     def test_init(self):
+        """
+        Ivars are correctly initialized.
+        """
         self.failUnless(
             isinstance(self.object1.path, FilePath),
             self.object1.path
@@ -233,13 +644,48 @@
         )
 
     def test_name(self):
+        """
+        Name is correct.
+        """
         self.assertEquals(self.object1.name(), "1.ics")
 
     def test_setComponent(self):
-        raise NotImplementedError()
-    test_setComponent.todo = "Unimplemented"
+        """
+        Rewrite component.
+        """
+        component = iComponent.fromString(event1modified_text)
 
+        calendarObject = self.calendar1.calendarObjectWithName("1.ics")
+        oldComponent = calendarObject.component() # Trigger caching
+        assert component != oldComponent
+        calendarObject.setComponent(component)
+        self.assertEquals(calendarObject.component(), component)
+
+        # Also check a new instance
+        calendarObject = self.calendar1.calendarObjectWithName("1.ics")
+        self.assertEquals(calendarObject.component(), component)
+
+    def test_setComponent_uidchanged(self):
+        component = iComponent.fromString(event4_text)
+
+        calendarObject = self.calendar1.calendarObjectWithName("1.ics")
+        self.assertRaises(
+            InvalidCalendarComponentError,
+            calendarObject.setComponent, component
+        )
+
+    def test_setComponent_invalid(self):
+        calendarObject = self.calendar1.calendarObjectWithName("1.ics")
+        self.assertRaises(
+            InvalidCalendarComponentError,
+            calendarObject.setComponent,
+            iComponent.fromString(event4notCalDAV_text)
+        )
+
     def test_component(self):
+        """
+        Component is correct.
+        """
         component = self.object1.component()
 
         self.failUnless(
@@ -252,6 +698,9 @@
         self.assertEquals(component.resourceUID(), "uid1")
 
     def text_iCalendarText(self):
+        """
+        iCalendar text is correct.
+        """
         text = self.object1.iCalendarText()
 
         self.failUnless(text.startswith("BEGIN:VCALENDAR\r\n"))
@@ -259,14 +708,22 @@
         self.failUnless(text.endswith("\r\nEND:VCALENDAR\r\n"))
 
     def test_uid(self):
+        """
+        UID is correct.
+        """
         self.assertEquals(self.object1.uid(), "uid1")
 
     def test_componentType(self):
+        """
+        Component type is correct.
+        """
         self.assertEquals(self.object1.componentType(), "VEVENT")
 
     def test_organizer(self):
-        self.assertEquals(self.object1.organizer(), "mailto:wsanchez at apple.com")
-
-    def test_properties(self):
-        raise NotImplementedError()
-    test_properties.todo = "Unimplemented"
+        """
+        Organizer is correct.
+        """
+        self.assertEquals(
+            self.object1.organizer(),
+            "mailto:wsanchez at apple.com"
+        )

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/icalendarstore.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txcaldav/icalendarstore.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -18,6 +18,10 @@
 Calendar store interfaces
 """
 
+# FIXME:  Still to do:
+# - Where to defer?
+# - commit() and abort()
+
 __all__ = [
     # Exceptions
     "CalendarStoreError",

Modified: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/idav.py
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txdav/idav.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/idav.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -19,13 +19,37 @@
 """
 
 __all__ = [
+    "PropertyStoreError",
+    "IPropertyName",
     "IPropertyStore",
 ]
 
-#from zope.interface import Attribute, Interface
+from zope.interface import Attribute, Interface
 
 from zope.interface.common.mapping import IMapping
 
+
+class PropertyStoreError(RuntimeError):
+    """
+    Property store error.
+    """
+
+
+class IPropertyName(Interface):
+    """
+    Property name.
+    """
+    namespace = Attribute("Namespace")
+    name      = Attribute("Name")
+
+    def toString():
+        """
+        Returns the string representation of the property name.
+
+        @return: a string
+        """
+
+
 class IPropertyStore(IMapping):
     """
     WebDAV property store

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py
===================================================================
--- CalendarServer/trunk/txdav/propertystore/__init__.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,19 +0,0 @@
-##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-"""
-WebDAV property support for Twisted.
-"""

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py (from rev 5079, CalendarServer/trunk/txdav/propertystore/__init__.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/__init__.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+"""
+WebDAV property support for Twisted.
+"""

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py
===================================================================
--- CalendarServer/trunk/txdav/propertystore/base.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,169 +0,0 @@
-##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-"""
-Base property store.
-"""
-
-__all__ = [
-    "AbstractPropertyStore",
-]
-
-from zope.interface import implements
-
-from twext.log import LoggingMixIn
-
-from txdav.idav import IPropertyStore, IPropertyName
-
-
-class PropertyName(LoggingMixIn):
-    """
-    Property name.
-    """
-    implements(IPropertyName)
-
-    @staticmethod
-    def fromString(sname):
-        index = sname.find("}")
-
-        if (index is -1 or not len(sname) > index or not sname[0] == "{"):
-            raise TypeError("Invalid sname: %r" % (sname,))
-
-        return (sname[1:index], sname[index+1:])
-
-    def __init__(self, namespace, name):
-        self.namespace = namespace
-        self.name = name
-
-    def __hash__(self):
-        return hash((self.namespace, self.name))
-
-    def __repr__(self):
-        return "<%s: %s>" % (
-            self.__class__.__name__,
-            self.toString(),
-        )
-
-    def toString(self):
-        return "{%s}%s" % (self.namespace, self.name)
-
-
-class AbstractPropertyStore(LoggingMixIn):
-    """
-    Base property store.
-    """
-    implements(IPropertyStore)
-
-    #
-    # Subclasses must override these
-    #
-
-    def __delitem__(self, key):
-        raise NotImplementedError()
-
-    def __getitem__(self, key):
-        raise NotImplementedError()
-
-    def __contains__(self, key):
-        raise NotImplementedError()
-
-    def __setitem__(key, value):
-        raise NotImplementedError()
-
-    def __iter__(self):
-        raise NotImplementedError()
-
-    def __len__(self):
-        raise NotImplementedError()
-
-    def flush(self):
-        raise NotImplementedError()
-
-    def abort(self):
-        raise NotImplementedError()
-
-    #
-    # Subclasses may override these
-    #
-
-    def len(self, key):
-        return self.__len__(key)
-
-    def clear(self):
-        for key in self.__iter__():
-            self.__delitem__(key)
-
-    def get(self, key, default=None):
-        if self.__contains__(key):
-            return self.__getitem__(key)
-        else:
-            return default
-
-    def iter(self):
-        return self.__iter__()
-
-    def iteritems(self):
-        return (
-            (key, self.get(key))
-            for key in self.__iter__()
-        )
-
-    def items(self):
-        return list(self.iteritems())
-
-    iterkeys = iter
-    __iterkeys__ = iter
-
-    def keys(self):
-        return tuple(self.__iter__())
-
-    def itervalues(self):
-        return (
-            self.get(key)
-            for key in self.__iter__()
-        )
-
-    def values(self):
-        return list(self.itervalues())
-
-    def pop(self, key, default=None):
-        try:
-            value = self.__getitem__(key)
-        except KeyError:
-            if default is None:
-                raise
-            return default
-
-        self.__delitem__(key)
-
-        return value
-
-    def popitem(self):
-        for key in self.__iter__():
-            self.__delitem__(key)
-            break
-
-    def setdefault(self, key, default=None):
-        if self.__contains__(key):
-            return key
-
-        self.__setitem__(key, default)
-
-        return default
-
-    def update(other=None):
-        # FIXME
-        raise NotImplementedError()

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py (from rev 5079, CalendarServer/trunk/txdav/propertystore/base.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/base.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,169 @@
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Base property store.
+"""
+
+__all__ = [
+    "AbstractPropertyStore",
+]
+
+from zope.interface import implements
+
+from twext.log import LoggingMixIn
+
+from txdav.idav import IPropertyStore, IPropertyName
+
+
+class PropertyName(LoggingMixIn):
+    """
+    Property name.
+    """
+    implements(IPropertyName)
+
+    @staticmethod
+    def fromString(sname):
+        index = sname.find("}")
+
+        if (index is -1 or not len(sname) > index or not sname[0] == "{"):
+            raise TypeError("Invalid sname: %r" % (sname,))
+
+        return (sname[1:index], sname[index+1:])
+
+    def __init__(self, namespace, name):
+        self.namespace = namespace
+        self.name = name
+
+    def __hash__(self):
+        return hash((self.namespace, self.name))
+
+    def __repr__(self):
+        return "<%s: %s>" % (
+            self.__class__.__name__,
+            self.toString(),
+        )
+
+    def toString(self):
+        return "{%s}%s" % (self.namespace, self.name)
+
+
+class AbstractPropertyStore(LoggingMixIn):
+    """
+    Base property store.
+    """
+    implements(IPropertyStore)
+
+    #
+    # Subclasses must override these
+    #
+
+    def __delitem__(self, key):
+        raise NotImplementedError()
+
+    def __getitem__(self, key):
+        raise NotImplementedError()
+
+    def __contains__(self, key):
+        raise NotImplementedError()
+
+    def __setitem__(key, value):
+        raise NotImplementedError()
+
+    def __iter__(self):
+        raise NotImplementedError()
+
+    def __len__(self):
+        raise NotImplementedError()
+
+    def flush(self):
+        raise NotImplementedError()
+
+    def abort(self):
+        raise NotImplementedError()
+
+    #
+    # Subclasses may override these
+    #
+
+    def len(self, key):
+        return self.__len__(key)
+
+    def clear(self):
+        for key in self.__iter__():
+            self.__delitem__(key)
+
+    def get(self, key, default=None):
+        if self.__contains__(key):
+            return self.__getitem__(key)
+        else:
+            return default
+
+    def iter(self):
+        return self.__iter__()
+
+    def iteritems(self):
+        return (
+            (key, self.get(key))
+            for key in self.__iter__()
+        )
+
+    def items(self):
+        return list(self.iteritems())
+
+    iterkeys = iter
+    __iterkeys__ = iter
+
+    def keys(self):
+        return tuple(self.__iter__())
+
+    def itervalues(self):
+        return (
+            self.get(key)
+            for key in self.__iter__()
+        )
+
+    def values(self):
+        return list(self.itervalues())
+
+    def pop(self, key, default=None):
+        try:
+            value = self.__getitem__(key)
+        except KeyError:
+            if default is None:
+                raise
+            return default
+
+        self.__delitem__(key)
+
+        return value
+
+    def popitem(self):
+        for key in self.__iter__():
+            self.__delitem__(key)
+            break
+
+    def setdefault(self, key, default=None):
+        if self.__contains__(key):
+            return key
+
+        self.__setitem__(key, default)
+
+        return default
+
+    def update(other=None):
+        # FIXME
+        raise NotImplementedError()

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py
===================================================================
--- CalendarServer/trunk/txdav/propertystore/test/__init__.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,19 +0,0 @@
-##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-"""
-Property store tests.
-"""

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py (from rev 5079, CalendarServer/trunk/txdav/propertystore/test/__init__.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/__init__.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Property store tests.
+"""

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py
===================================================================
--- CalendarServer/trunk/txdav/propertystore/test/test_xattr.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,52 +0,0 @@
-##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-"""
-Property store tests.
-"""
-
-from zope.interface.verify import verifyObject, BrokenMethodImplementation
-
-#from twisted.python.filepath import FilePath
-from twisted.trial import unittest
-
-from txdav.idav import IPropertyStore
-from txdav.propertystore.xattr import PropertyStore
-
-
-class PropertyStoreTest(unittest.TestCase):
-    def test_interface(self):
-        raise NotImplementedError()
-
-        store = PropertyStore()
-
-        try:
-            verifyObject(IPropertyStore, store)
-        except BrokenMethodImplementation, e:
-            self.fail(e)
-    test_interface.todo = "Unimplemented"
-
-    def test_init(self):
-        raise NotImplementedError()
-    test_init.todo = "Unimplemented"
-
-    def test_flush(self):
-        raise NotImplementedError()
-    test_flush.todo = "Unimplemented"
-
-    def test_abort(self):
-        raise NotImplementedError()
-    test_abort.todo = "Unimplemented"

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py (from rev 5079, CalendarServer/trunk/txdav/propertystore/test/test_xattr.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/test/test_xattr.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,52 @@
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Property store tests.
+"""
+
+from zope.interface.verify import verifyObject, BrokenMethodImplementation
+
+#from twisted.python.filepath import FilePath
+from twisted.trial import unittest
+
+from txdav.idav import IPropertyStore
+from txdav.propertystore.xattr import PropertyStore
+
+
+class PropertyStoreTest(unittest.TestCase):
+    def test_interface(self):
+        raise NotImplementedError()
+
+        store = PropertyStore()
+
+        try:
+            verifyObject(IPropertyStore, store)
+        except BrokenMethodImplementation, e:
+            self.fail(e)
+    test_interface.todo = "Unimplemented"
+
+    def test_init(self):
+        raise NotImplementedError()
+    test_init.todo = "Unimplemented"
+
+    def test_flush(self):
+        raise NotImplementedError()
+    test_flush.todo = "Unimplemented"
+
+    def test_abort(self):
+        raise NotImplementedError()
+    test_abort.todo = "Unimplemented"

Deleted: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py
===================================================================
--- CalendarServer/trunk/txdav/propertystore/xattr.py	2010-02-09 18:35:39 UTC (rev 5079)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -1,207 +0,0 @@
-##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# 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 "AS IS" 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.
-##
-
-"""
-Property store using filesystem extended attributes.
-"""
-
-from __future__ import absolute_import
-
-__all__ = [
-    "PropertyStore",
-]
-
-import sys
-import errno
-import urllib
-from zlib import compress, decompress, error as ZlibError
-from cPickle import UnpicklingError, loads as unpickle
-
-import xattr
-
-if getattr(xattr, "xattr", None) is None:
-    raise ImportError("wrong xattr package imported")
-
-from twisted.web2.dav.davxml import WebDAVDocument
-
-from txdav.propertystore.base import AbstractPropertyStore, PropertyName
-from txdav.idav import PropertyStoreError
-
-
-#
-# RFC 2518 Section 12.13.1 says that removal of non-existing property
-# is not an error.  python-xattr on Linux fails with ENODATA in this
-# case.  On OS X, the xattr library fails with ENOATTR, which CPython
-# does not expose.  Its value is 93.
-#
-if sys.platform is "darwin":
-    if hasattr(errno, "ENOATTR"):
-        _ERRNO_NO_ATTR = errno.ENOATTR
-    else:
-        _ERRNO_NO_ATTR = 93
-else:
-    _ERRNO_NO_ATTR = errno.ENODATA
-
-
-class PropertyStore(AbstractPropertyStore):
-    """
-    Property store using filesystem extended attributes.
-
-    This implementation uses Bob Ippolito's xattr package, available from::
-
-        http://undefined.org/python/#xattr
-    """
-    #
-    # Dead properties are stored as extended attributes on disk.  In order to
-    # avoid conflicts with other attributes, prefix dead property names.
-    #
-    deadPropertyXattrPrefix = "WebDAV:"
-
-    # Linux seems to require that attribute names use a "user." prefix.
-    if sys.platform == "linux2":
-        deadPropertyXattrPrefix = "user.%s" % (deadPropertyXattrPrefix,)
-
-    @classmethod
-    def _encodeKey(cls, name):
-        result = urllib.quote(name.toString(), safe="{}:")
-        r = cls.deadPropertyXattrPrefix + result
-        return r
-
-    @classmethod
-    def _decodeKey(cls, name):
-        return PropertyName.fromString(
-            urllib.unquote(name[len(cls.deadPropertyXattrPrefix):])
-        )
-
-    def __init__(self, path):
-        self.path = path
-        self.attrs = xattr.xattr(path.path)
-        self.removed = set()
-        self.modified = {}
-
-    def __str__(self):
-        return "<%s %s>" % (self.__class__.__name__, self.path.path)
-
-    #
-    # Accessors
-    #
-
-    def __delitem__(self, key):
-        if key in self.modified:
-            del self.modified[key]
-        self.removed.add(key)
-
-    def __getitem__(self, key):
-        if key in self.modified:
-            return self.modified[key]
-
-        if key in self.removed:
-            raise KeyError(key)
-
-        try:
-            data = self.attrs[self._encodeKey(key)]
-        except IOError, e:
-            if e.errno in _ERRNO_NO_ATTR:
-                raise KeyError(key)
-            raise PropertyStoreError(e)
-
-        #
-        # Unserialize XML data from an xattr.  The storage format has changed
-        # over time:
-        #
-        #  1- Started with XML
-        #  2- Started compressing the XML due to limits on xattr size
-        #  3- Switched to pickle which is faster, still compressing
-        #  4- Back to compressed XML for interoperability, size
-        #
-        # We only write the current format, but we also read the old
-        # ones for compatibility.
-        #
-        legacy = False
-
-        try:
-            data = decompress(data)
-        except ZlibError:
-            legacy = True
-
-        try:
-            doc = WebDAVDocument.fromString(data)
-        except ValueError:
-            try:
-                doc = unpickle(data)
-            except UnpicklingError:
-                msg = "Invalid property value stored on server: %s %s" % (
-                    key.toString(), data
-                )
-                self.log_error(msg)
-                raise PropertyStoreError(msg)
-            else:
-                legacy = True
-
-        if legacy:
-            self.set(doc.root_element)
-
-        return doc.root_element
-
-    def __contains__(self, key):
-        if key in self.modified:
-            return True
-        if key in self.removed:
-            return False
-        return self._encodeKey(key) in self.attrs
-
-    def __setitem__(self, key, value):
-        if key in self.removed:
-            self.removed.remove(key)
-        self.modified[key] = value
-
-    def __iter__(self):
-        seen = set()
-
-        for key in self.attrs:
-            key = self._decodeKey(key)
-            if key not in self.removed:
-                seen.add(key)
-                yield key
-
-        for key in self.modified:
-            if key not in seen:
-                yield key
-
-    def __len__(self):
-        return len(self.attrs)
-
-    #
-    # I/O
-    #
-
-    def flush(self):
-        attrs    = self.attrs
-        removed  = self.removed
-        modified = self.modified
-
-        for key in removed:
-            assert key not in modified
-            del attrs[self._encodeKey(key)]
-
-        for key in modified:
-            assert key not in removed
-            value = modified[key]
-            attrs[self._encodeKey(key)] = compress(value.toxml())
-
-    def abort(self):
-        self.removed.clear()
-        self.modified.clear()

Copied: CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py (from rev 5079, CalendarServer/trunk/txdav/propertystore/xattr.py)
===================================================================
--- CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/contacts-server-merge/txdav/propertystore/xattr.py	2010-02-09 20:49:43 UTC (rev 5080)
@@ -0,0 +1,207 @@
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Property store using filesystem extended attributes.
+"""
+
+from __future__ import absolute_import
+
+__all__ = [
+    "PropertyStore",
+]
+
+import sys
+import errno
+import urllib
+from zlib import compress, decompress, error as ZlibError
+from cPickle import UnpicklingError, loads as unpickle
+
+import xattr
+
+if getattr(xattr, "xattr", None) is None:
+    raise ImportError("wrong xattr package imported")
+
+from twisted.web2.dav.davxml import WebDAVDocument
+
+from txdav.propertystore.base import AbstractPropertyStore, PropertyName
+from txdav.idav import PropertyStoreError
+
+
+#
+# RFC 2518 Section 12.13.1 says that removal of non-existing property
+# is not an error.  python-xattr on Linux fails with ENODATA in this
+# case.  On OS X, the xattr library fails with ENOATTR, which CPython
+# does not expose.  Its value is 93.
+#
+if sys.platform is "darwin":
+    if hasattr(errno, "ENOATTR"):
+        _ERRNO_NO_ATTR = errno.ENOATTR
+    else:
+        _ERRNO_NO_ATTR = 93
+else:
+    _ERRNO_NO_ATTR = errno.ENODATA
+
+
+class PropertyStore(AbstractPropertyStore):
+    """
+    Property store using filesystem extended attributes.
+
+    This implementation uses Bob Ippolito's xattr package, available from::
+
+        http://undefined.org/python/#xattr
+    """
+    #
+    # Dead properties are stored as extended attributes on disk.  In order to
+    # avoid conflicts with other attributes, prefix dead property names.
+    #
+    deadPropertyXattrPrefix = "WebDAV:"
+
+    # Linux seems to require that attribute names use a "user." prefix.
+    if sys.platform == "linux2":
+        deadPropertyXattrPrefix = "user.%s" % (deadPropertyXattrPrefix,)
+
+    @classmethod
+    def _encodeKey(cls, name):
+        result = urllib.quote(name.toString(), safe="{}:")
+        r = cls.deadPropertyXattrPrefix + result
+        return r
+
+    @classmethod
+    def _decodeKey(cls, name):
+        return PropertyName.fromString(
+            urllib.unquote(name[len(cls.deadPropertyXattrPrefix):])
+        )
+
+    def __init__(self, path):
+        self.path = path
+        self.attrs = xattr.xattr(path.path)
+        self.removed = set()
+        self.modified = {}
+
+    def __str__(self):
+        return "<%s %s>" % (self.__class__.__name__, self.path.path)
+
+    #
+    # Accessors
+    #
+
+    def __delitem__(self, key):
+        if key in self.modified:
+            del self.modified[key]
+        self.removed.add(key)
+
+    def __getitem__(self, key):
+        if key in self.modified:
+            return self.modified[key]
+
+        if key in self.removed:
+            raise KeyError(key)
+
+        try:
+            data = self.attrs[self._encodeKey(key)]
+        except IOError, e:
+            if e.errno in _ERRNO_NO_ATTR:
+                raise KeyError(key)
+            raise PropertyStoreError(e)
+
+        #
+        # Unserialize XML data from an xattr.  The storage format has changed
+        # over time:
+        #
+        #  1- Started with XML
+        #  2- Started compressing the XML due to limits on xattr size
+        #  3- Switched to pickle which is faster, still compressing
+        #  4- Back to compressed XML for interoperability, size
+        #
+        # We only write the current format, but we also read the old
+        # ones for compatibility.
+        #
+        legacy = False
+
+        try:
+            data = decompress(data)
+        except ZlibError:
+            legacy = True
+
+        try:
+            doc = WebDAVDocument.fromString(data)
+        except ValueError:
+            try:
+                doc = unpickle(data)
+            except UnpicklingError:
+                msg = "Invalid property value stored on server: %s %s" % (
+                    key.toString(), data
+                )
+                self.log_error(msg)
+                raise PropertyStoreError(msg)
+            else:
+                legacy = True
+
+        if legacy:
+            self.set(doc.root_element)
+
+        return doc.root_element
+
+    def __contains__(self, key):
+        if key in self.modified:
+            return True
+        if key in self.removed:
+            return False
+        return self._encodeKey(key) in self.attrs
+
+    def __setitem__(self, key, value):
+        if key in self.removed:
+            self.removed.remove(key)
+        self.modified[key] = value
+
+    def __iter__(self):
+        seen = set()
+
+        for key in self.attrs:
+            key = self._decodeKey(key)
+            if key not in self.removed:
+                seen.add(key)
+                yield key
+
+        for key in self.modified:
+            if key not in seen:
+                yield key
+
+    def __len__(self):
+        return len(self.attrs)
+
+    #
+    # I/O
+    #
+
+    def flush(self):
+        attrs    = self.attrs
+        removed  = self.removed
+        modified = self.modified
+
+        for key in removed:
+            assert key not in modified
+            del attrs[self._encodeKey(key)]
+
+        for key in modified:
+            assert key not in removed
+            value = modified[key]
+            attrs[self._encodeKey(key)] = compress(value.toxml())
+
+    def abort(self):
+        self.removed.clear()
+        self.modified.clear()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100209/fd214be3/attachment-0001.html>


More information about the calendarserver-changes mailing list