[CalendarServer-changes] [11787] CalendarServer/branches/users/gaya/directorybacker
source_changes at macosforge.org
source_changes at macosforge.org
Thu Oct 3 15:51:14 PDT 2013
Revision: 11787
http://trac.calendarserver.org//changeset/11787
Author: gaya at apple.com
Date: 2013-10-03 15:51:14 -0700 (Thu, 03 Oct 2013)
Log Message:
-----------
merge part 2
Modified Paths:
--------------
CalendarServer/branches/users/gaya/directorybacker/lib-patches/cx_Oracle/nclob-fixes-and-prefetch.patch
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/scheduling_store/caldav/resource.py
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Africa/Casablanca.ics
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Pacific/Fakaofo.ics
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/delivery.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/test/test_delivery.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/cuaddress.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/freebusy.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icaldiff.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icalsplitter.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/implicit.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/delivery.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/dkim.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/localservers.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/resource.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/scheduler.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_dkim.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/itip.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/processing.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_pocessing.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_utils.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/utils.py
Added Paths:
-----------
CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/agent.py
CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/test/test_agent.py
CalendarServer/branches/users/gaya/directorybacker/contrib/performance/loadtest/benchmarks.json
CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.txt
CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.xml
CalendarServer/branches/users/gaya/directorybacker/doc/calendarserver_manage_timezones.8
CalendarServer/branches/users/gaya/directorybacker/support/gendocs
CalendarServer/branches/users/gaya/directorybacker/twext/python/launchd.py
CalendarServer/branches/users/gaya/directorybacker/twext/python/test/test_launchd.py
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/directory/test/augments-normalization.xml
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Khandyga.ics
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Ust-Nera.ics
CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Europe/Busingen.ics
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/schedule.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_implicit.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_schedule.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/util.py
CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/icalendardirectoryservice.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_dump.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v17.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v18.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v17.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v18.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v20.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v21.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_17_to_18.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_20_to_21.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_21_to_22.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_17_to_18.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_19_to_20.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_20_to_21.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_21_to_22.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_2_to_3.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py
CalendarServer/branches/users/gaya/directorybacker/txdav/common/idirectoryservice.py
Added: CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/agent.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/agent.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/agent.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,438 @@
+#!/usr/bin/env python
+# -*- test-case-name: calendarserver.tools.test.test_agent -*-
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+A service spawned on-demand by launchd, meant to handle configuration requests
+from Server.app. When a request comes in on the socket specified in the launchd
+agent.plist, launchd will run "caldavd -t Agent" which ends up creating this
+service. Requests are made using HTTP POSTS to /gateway, and are authenticated
+by OpenDirectory.
+"""
+
+from __future__ import print_function
+
+import cStringIO
+import socket
+
+from calendarserver.tap.util import getRootResource
+from twext.python.plistlib import readPlistFromString, writePlistToString
+from twisted.application.internet import StreamServerEndpointService
+from twisted.cred.checkers import ICredentialsChecker
+from twisted.cred.credentials import IUsernameHashedPassword
+from twisted.cred.error import UnauthorizedLogin
+from twisted.cred.portal import IRealm, Portal
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed, fail
+from twisted.internet.endpoints import AdoptedStreamServerEndpoint
+from twisted.internet.protocol import Factory
+from twisted.protocols import amp
+from twisted.web.guard import HTTPAuthSessionWrapper, DigestCredentialFactory
+from twisted.web.resource import IResource, Resource, ForbiddenResource
+from twisted.web.server import Site, NOT_DONE_YET
+from zope.interface import implements
+
+from twext.python.launchd import getLaunchDSocketFDs
+from twext.python.log import Logger
+log = Logger()
+
+
+
+class DirectoryServiceChecker:
+ """
+ A checker that knows how to ask OpenDirectory to authenticate via Digest
+ """
+ implements(ICredentialsChecker)
+
+ credentialInterfaces = (IUsernameHashedPassword,)
+
+ from calendarserver.platform.darwin.od import opendirectory
+ directoryModule = opendirectory
+
+ def __init__(self, node):
+ """
+ @param node: the name of the OpenDirectory node to use, e.g. /Local/Default
+ """
+ self.node = node
+ self.directory = self.directoryModule.odInit(node)
+
+ def requestAvatarId(self, credentials):
+ record = self.directoryModule.getUserRecord(self.directory, credentials.username)
+
+ if record is not None:
+ try:
+ if "algorithm" not in credentials.fields:
+ credentials.fields["algorithm"] = "md5"
+
+ challenge = 'Digest realm="%(realm)s", nonce="%(nonce)s", algorithm=%(algorithm)s' % credentials.fields
+
+ response = (
+ 'Digest username="%(username)s", '
+ 'realm="%(realm)s", '
+ 'nonce="%(nonce)s", '
+ 'uri="%(uri)s", '
+ 'response="%(response)s",'
+ 'algorithm=%(algorithm)s'
+ ) % credentials.fields
+
+ except KeyError as e:
+ log.error(
+ "OpenDirectory (node=%s) error while performing digest authentication for user %s: "
+ "missing digest response field: %s in: %s"
+ % (self.node, credentials.username, e, credentials.fields)
+ )
+ return fail(UnauthorizedLogin())
+
+ try:
+ if self.directoryModule.authenticateUserDigest(self.directory,
+ self.node,
+ credentials.username,
+ challenge,
+ response,
+ credentials.method
+ ):
+ return succeed(credentials.username)
+ else:
+ log.error("Failed digest auth with response: %s" % (response,))
+ return fail(UnauthorizedLogin())
+ except Exception as e:
+ log.error(
+ "OpenDirectory error while performing digest authentication for user %s: %s"
+ % (credentials.username, e)
+ )
+ return fail(UnauthorizedLogin())
+
+ else:
+ return fail(UnauthorizedLogin())
+
+
+class CustomDigestCredentialFactory(DigestCredentialFactory):
+ """
+ DigestCredentialFactory without qop, to interop with OD.
+ """
+
+ def getChallenge(self, address):
+ result = DigestCredentialFactory.getChallenge(self, address)
+ del result["qop"]
+ return result
+
+
+class AgentRealm(object):
+ """
+ Only allow a specified list of avatar IDs to access the site
+ """
+ implements(IRealm)
+
+ def __init__(self, root, allowedAvatarIds):
+ """
+ @param root: The root resource of the site
+ @param allowedAvatarIds: The list of IDs to allow access to
+ """
+ self.root = root
+ self.allowedAvatarIds = allowedAvatarIds
+
+ def requestAvatar(self, avatarId, mind, *interfaces):
+ if IResource in interfaces:
+ if avatarId in self.allowedAvatarIds:
+ return (IResource, self.root, lambda: None)
+ else:
+ return (IResource, ForbiddenResource(), lambda: None)
+
+ raise NotImplementedError()
+
+
+
+class AgentGatewayResource(Resource):
+ """
+ The gateway resource which forwards incoming requests through gateway.Runner.
+ """
+ isLeaf = True
+
+ def __init__(self, store, davRootResource, directory, inactivityDetector):
+ """
+ @param store: an already opened store
+ @param davRootResource: the root resource, required for principal
+ operations
+ @param directory: a directory service
+ @param inactivityDetector: the InactivityDetector to tell when requests
+ come in
+ """
+ Resource.__init__(self)
+ self.store = store
+ self.davRootResource = davRootResource
+ self.directory = directory
+ self.inactivityDetector = inactivityDetector
+
+ def render_POST(self, request):
+ """
+ Take the body of the POST request and feed it to gateway.Runner();
+ return the result as the response body.
+ """
+
+ self.inactivityDetector.activity()
+
+ def onSuccess(result, output):
+ txt = output.getvalue()
+ output.close()
+ request.write(txt)
+ request.finish()
+
+ def onError(failure):
+ message = failure.getErrorMessage()
+ tbStringIO = cStringIO.StringIO()
+ failure.printTraceback(file=tbStringIO)
+ tbString = tbStringIO.getvalue()
+ tbStringIO.close()
+ error = {
+ "Error" : message,
+ "Traceback" : tbString,
+ }
+ log.error("command failed %s" % (failure,))
+ request.write(writePlistToString(error))
+ request.finish()
+
+ from calendarserver.tools.gateway import Runner
+ body = request.content.read()
+ command = readPlistFromString(body)
+ output = cStringIO.StringIO()
+ runner = Runner(self.davRootResource, self.directory, self.store,
+ [command], output=output)
+ d = runner.run()
+ d.addCallback(onSuccess, output)
+ d.addErrback(onError)
+ return NOT_DONE_YET
+
+
+
+
+
+def makeAgentService(store):
+ """
+ Returns a service which will process GatewayAMPCommands, using a socket
+ file descripter acquired by launchd
+
+ @param store: an already opened store
+ @returns: service
+ """
+ from twisted.internet import reactor
+
+ sockets = getLaunchDSocketFDs()
+ fd = sockets["AgentSocket"][0]
+
+ family = socket.AF_INET
+ endpoint = AdoptedStreamServerEndpoint(reactor, fd, family)
+
+ from twistedcaldav.config import config
+ davRootResource = getRootResource(config, store)
+ directory = davRootResource.getDirectory()
+
+ def becameInactive():
+ log.warn("Agent inactive; shutting down")
+ reactor.stop()
+
+ inactivityDetector = InactivityDetector(reactor,
+ config.AgentInactivityTimeoutSeconds, becameInactive)
+ root = Resource()
+ root.putChild("gateway", AgentGatewayResource(store,
+ davRootResource, directory, inactivityDetector))
+
+ realmName = "/Local/Default"
+ portal = Portal(AgentRealm(root, ["com.apple.calendarserver"]),
+ [DirectoryServiceChecker(realmName)])
+ credentialFactory = CustomDigestCredentialFactory("md5", realmName)
+ wrapper = HTTPAuthSessionWrapper(portal, [credentialFactory])
+
+ site = Site(wrapper)
+
+ return StreamServerEndpointService(endpoint, site)
+
+
+
+class InactivityDetector(object):
+ """
+ If no 'activity' takes place for a specified amount of time, a method
+ will get called. Activity causes the inactivity time threshold to be
+ reset.
+ """
+
+ def __init__(self, reactor, timeoutSeconds, becameInactive):
+ """
+ @param reactor: the reactor
+ @timeoutSeconds: the number of seconds considered to mean inactive
+ @becameInactive: the method to call (with no arguments) when
+ inactivity is reached
+ """
+ self._reactor = reactor
+ self._timeoutSeconds = timeoutSeconds
+ self._becameInactive = becameInactive
+
+ if self._timeoutSeconds > 0:
+ self._delayedCall = self._reactor.callLater(self._timeoutSeconds,
+ self._inactivityThresholdReached)
+
+
+ def _inactivityThresholdReached(self):
+ """
+ The delayed call has fired. We're inactive. Call the becameInactive
+ method.
+ """
+ self._becameInactive()
+
+
+ def activity(self):
+ """
+ Call this to let the InactivityMonitor that there has been activity.
+ It will reset the timeout.
+ """
+ if self._timeoutSeconds > 0:
+ if self._delayedCall.active():
+ self._delayedCall.reset(self._timeoutSeconds)
+ else:
+ self._delayedCall = self._reactor.callLater(self._timeoutSeconds,
+ self._inactivityThresholdReached)
+
+
+ def stop(self):
+ """
+ Cancels the delayed call
+ """
+ if self._timeoutSeconds > 0:
+ if self._delayedCall.active():
+ self._delayedCall.cancel()
+
+
+
+#
+# Alternate implementation using AMP instead of HTTP
+#
+
+class GatewayAMPCommand(amp.Command):
+ """
+ A command to be executed by gateway.Runner
+ """
+ arguments = [('command', amp.String())]
+ response = [('result', amp.String())]
+
+
+class GatewayAMPProtocol(amp.AMP):
+ """
+ Passes commands to gateway.Runner and returns the results
+ """
+
+ def __init__(self, store, davRootResource, directory):
+ """
+ @param store: an already opened store
+ @param davRootResource: the root resource, required for principal
+ operations
+ @param directory: a directory service
+ """
+ amp.AMP.__init__(self)
+ self.store = store
+ self.davRootResource = davRootResource
+ self.directory = directory
+
+
+ @GatewayAMPCommand.responder
+ @inlineCallbacks
+ def gatewayCommandReceived(self, command):
+ """
+ Process a command via gateway.Runner
+
+ @param command: GatewayAMPCommand
+ @returns: a deferred returning a dict
+ """
+ command = readPlistFromString(command)
+ output = cStringIO.StringIO()
+ from calendarserver.tools.gateway import Runner
+ runner = Runner(self.davRootResource, self.directory, self.store,
+ [command], output=output)
+
+ try:
+ yield runner.run()
+ result = output.getvalue()
+ output.close()
+ except Exception as e:
+ error = {
+ "Error" : str(e),
+ }
+ result = writePlistToString(error)
+
+ output.close()
+ returnValue(dict(result=result))
+
+
+class GatewayAMPFactory(Factory):
+ """
+ Builds GatewayAMPProtocols
+ """
+ protocol = GatewayAMPProtocol
+
+ def __init__(self, store):
+ """
+ @param store: an already opened store
+ """
+ self.store = store
+ from twistedcaldav.config import config
+ self.davRootResource = getRootResource(config, self.store)
+ self.directory = self.davRootResource.getDirectory()
+
+ def buildProtocol(self, addr):
+ return GatewayAMPProtocol(self.store, self.davRootResource,
+ self.directory)
+
+
+
+#
+# A test AMP client
+#
+
+command = """<?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>getLocationAndResourceList</string>
+</dict>
+</plist>"""
+
+def getList():
+ # For the sample client, below:
+ from twisted.internet import reactor
+ from twisted.internet.protocol import ClientCreator
+
+ creator = ClientCreator(reactor, amp.AMP)
+ host = '127.0.0.1'
+ import sys
+ if len(sys.argv) > 1:
+ host = sys.argv[1]
+ d = creator.connectTCP(host, 62308)
+
+ def connected(ampProto):
+ return ampProto.callRemote(GatewayAMPCommand, command=command)
+ d.addCallback(connected)
+
+ def resulted(result):
+ return result['result']
+ d.addCallback(resulted)
+
+ def done(result):
+ print('Done: %s' % (result,))
+ reactor.stop()
+ d.addCallback(done)
+ reactor.run()
+
+if __name__ == '__main__':
+ getList()
Added: CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/test/test_agent.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/test/test_agent.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/calendarserver/tools/test/test_agent.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,184 @@
+##
+# Copyright (c) 2013 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.
+##
+
+try:
+ from calendarserver.tools.agent import AgentRealm
+ from calendarserver.tools.agent import CustomDigestCredentialFactory
+ from calendarserver.tools.agent import DirectoryServiceChecker
+ from calendarserver.tools.agent import InactivityDetector
+ from twistedcaldav.test.util import TestCase
+ from twisted.internet.defer import inlineCallbacks
+ from twisted.internet.task import Clock
+ from twisted.cred.error import UnauthorizedLogin
+ from twisted.web.resource import IResource
+ from twisted.web.resource import ForbiddenResource
+ RUN_TESTS = True
+except ImportError:
+ RUN_TESTS = False
+
+
+
+
+if RUN_TESTS:
+ class AgentTestCase(TestCase):
+
+ def test_CustomDigestCredentialFactory(self):
+ f = CustomDigestCredentialFactory("md5", "/Local/Default")
+ challenge = f.getChallenge(FakeRequest())
+ self.assertTrue("qop" not in challenge)
+ self.assertEquals(challenge["algorithm"], "md5")
+ self.assertEquals(challenge["realm"], "/Local/Default")
+
+ @inlineCallbacks
+ def test_DirectoryServiceChecker(self):
+ c = DirectoryServiceChecker("/Local/Default")
+ fakeOpenDirectory = FakeOpenDirectory()
+ c.directoryModule = fakeOpenDirectory
+
+ fields = {
+ "username" : "foo",
+ "realm" : "/Local/Default",
+ "nonce" : 1,
+ "uri" : "/gateway",
+ "response" : "abc",
+ "algorithm" : "md5",
+ }
+ creds = FakeCredentials("foo", fields)
+
+ # Record does not exist:
+ fakeOpenDirectory.returnThisRecord(None)
+ try:
+ yield c.requestAvatarId(creds)
+ except UnauthorizedLogin:
+ pass
+ else:
+ self.fail("Didn't raise UnauthorizedLogin")
+
+
+ # Record exists, but invalid credentials
+ fakeOpenDirectory.returnThisRecord("fooRecord")
+ fakeOpenDirectory.returnThisAuthResponse(False)
+ try:
+ yield c.requestAvatarId(creds)
+ except UnauthorizedLogin:
+ pass
+ else:
+ self.fail("Didn't raise UnauthorizedLogin")
+
+
+ # Record exists, valid credentials
+ fakeOpenDirectory.returnThisRecord("fooRecord")
+ fakeOpenDirectory.returnThisAuthResponse(True)
+ avatar = (yield c.requestAvatarId(creds))
+ self.assertEquals(avatar, "foo")
+
+
+ # Record exists, but missing fields in credentials
+ del creds.fields["nonce"]
+ fakeOpenDirectory.returnThisRecord("fooRecord")
+ fakeOpenDirectory.returnThisAuthResponse(False)
+ try:
+ yield c.requestAvatarId(creds)
+ except UnauthorizedLogin:
+ pass
+ else:
+ self.fail("Didn't raise UnauthorizedLogin")
+
+
+ def test_AgentRealm(self):
+ realm = AgentRealm("root", ["abc"])
+
+ # Valid avatar
+ interface, resource, ignored = realm.requestAvatar("abc", None, IResource)
+ self.assertEquals(resource, "root")
+
+ # Not allowed avatar
+ interface, resource, ignored = realm.requestAvatar("def", None, IResource)
+ self.assertTrue(isinstance(resource, ForbiddenResource))
+
+ # Interface unhandled
+ try:
+ realm.requestAvatar("def", None, None)
+ except NotImplementedError:
+ pass
+ else:
+ self.fail("Didn't raise NotImplementedError")
+
+
+
+ class InactivityDectectorTestCase(TestCase):
+
+ def test_inactivity(self):
+ clock = Clock()
+
+ self.inactivityReached = False
+ def becameInactive():
+ self.inactivityReached = True
+
+ id = InactivityDetector(clock, 5, becameInactive)
+
+ # After 3 seconds, not inactive
+ clock.advance(3)
+ self.assertFalse(self.inactivityReached)
+
+ # Activity happens, pushing out the inactivity threshold
+ id.activity()
+ clock.advance(3)
+ self.assertFalse(self.inactivityReached)
+
+ # Time passes without activity
+ clock.advance(3)
+ self.assertTrue(self.inactivityReached)
+
+ id.stop()
+
+ # Verify a timeout of 0 does not ever fire
+ id = InactivityDetector(clock, 0, becameInactive)
+ self.assertEquals(clock.getDelayedCalls(), [])
+
+
+ class FakeRequest(object):
+
+ def getClientIP(self):
+ return "127.0.0.1"
+
+
+
+ class FakeOpenDirectory(object):
+
+ def returnThisRecord(self, response):
+ self.recordResponse = response
+
+ def getUserRecord(self, ignored, username):
+ return self.recordResponse
+
+ def returnThisAuthResponse(self, response):
+ self.authResponse = response
+
+ def authenticateUserDigest(self, ignored, node, username, challenge, response,
+ method):
+ return self.authResponse
+
+ ODNSerror = "Error"
+
+
+
+ class FakeCredentials(object):
+
+ def __init__(self, username, fields):
+ self.username = username
+ self.fields = fields
+ self.method = "POST"
Added: CalendarServer/branches/users/gaya/directorybacker/contrib/performance/loadtest/benchmarks.json
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/contrib/performance/loadtest/benchmarks.json (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/contrib/performance/loadtest/benchmarks.json 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,42 @@
+{
+ "GET{event}" : {"mean": 0.100, "weight": 1.000},
+
+ "PUT{event}" : {"mean": 0.250, "weight": 1.000},
+ "PUT{attendee-small}" : {"mean": 0.500, "weight": 1.000},
+ "PUT{attendee-medium}" : {"mean": 1.000, "weight": 0.900},
+ "PUT{attendee-large}" : {"mean": 1.500, "weight": 0.750},
+ "PUT{attendee-huge}" : {"mean": 2.000, "weight": 0.500},
+ "PUT{organizer-small}" : {"mean": 0.250, "weight": 1.000},
+ "PUT{organizer-medium}" : {"mean": 0.500, "weight": 0.900},
+ "PUT{organizer-large}" : {"mean": 1.000, "weight": 0.750},
+ "PUT{organizer-huge}" : {"mean": 3.000, "weight": 0.500},
+
+ "DELETE{event}" : {"mean": 0.500, "weight": 0.750},
+
+ "POST{fb-small}" : {"mean": 0.500, "weight": 1.000},
+ "POST{fb-medium}" : {"mean": 0.750, "weight": 1.000},
+ "POST{fb-large}" : {"mean": 1.000, "weight": 0.800},
+ "POST{fb-huge}" : {"mean": 2.000, "weight": 0.700},
+
+ "PROPFIND{well-known}" : {"mean": 0.100, "weight": 0.100},
+ "PROPFIND{find-principal}" : {"mean": 0.250, "weight": 0.100},
+ "PROPFIND{principal}" : {"mean": 0.250, "weight": 0.100},
+ "PROPFIND{home}" : {"mean": 0.250, "weight": 1.000},
+ "PROPFIND{calendar}" : {"mean": 0.250, "weight": 1.000},
+ "PROPFIND{notification}" : {"mean": 0.100, "weight": 0.500},
+ "PROPFIND{notification-items}" : {"mean": 0.100, "weight": 0.500},
+
+ "PROPPATCH{calendar}" : {"mean": 0.250, "weight": 0.500},
+
+ "REPORT{pset}" : {"mean": 0.250, "weight": 0.100},
+ "REPORT{expand}" : {"mean": 0.250, "weight": 1.000},
+ "REPORT{psearch}" : {"mean": 0.250, "weight": 0.100},
+ "REPORT{sync-init}" : {"mean": 0.250, "weight": 0.500},
+ "REPORT{sync}" : {"mean": 0.250, "weight": 1.000},
+ "REPORT{vevent}" : {"mean": 0.250, "weight": 1.000},
+ "REPORT{vtodo}" : {"mean": 0.250, "weight": 1.000},
+ "REPORT{multiget-small}" : {"mean": 0.250, "weight": 1.000},
+ "REPORT{multiget-medium}" : {"mean": 0.500, "weight": 0.900},
+ "REPORT{multiget-large}" : {"mean": 1.000, "weight": 0.750},
+ "REPORT{multiget-huge}" : {"mean": 2.000, "weight": 0.500}
+}
Added: CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.txt
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.txt (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.txt 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,1288 @@
+
+
+
+Calendar Server Extension C. Daboo
+ E. York
+ Apple Inc.
+ August 8, 2011
+
+
+ Calendar Server Bulk Change Requests for *DAV Protocols
+
+Abstract
+
+ This specification defines an extension to CalDAV and CardDAV that
+ enables clients to do multiple changes on the server with a single
+ HTTP request.
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
+ 2. Conventions Used in This Document . . . . . . . . . . . . . . 2
+ 3. Simple Bulk Import Request . . . . . . . . . . . . . . . . . . 3
+ 3.1. Example: Calendar Resource Bulk Create . . . . . . . . . . 4
+ 3.2. Example: Address Book Resource Bulk Create . . . . . . . . 6
+ 4. Bulk CRUD Request . . . . . . . . . . . . . . . . . . . . . . 8
+ 4.1. Create request . . . . . . . . . . . . . . . . . . . . . . 9
+ 4.2. Update request . . . . . . . . . . . . . . . . . . . . . . 10
+ 4.3. Delete request . . . . . . . . . . . . . . . . . . . . . . 12
+ 4.4. Example: Calendar Resource Bulk CRUD . . . . . . . . . . . 12
+ 5. Size Limits for Bulk Requests . . . . . . . . . . . . . . . . 14
+ 5.1. MM:bulk-requests Property . . . . . . . . . . . . . . . . 15
+ 6. CTag Conditional Requests . . . . . . . . . . . . . . . . . . 16
+ 6.1. Example: CTag Pre-condition Failure . . . . . . . . . . . 17
+ 6.2. Example: CTag Pre-condition Success with Expect . . . . . 18
+ 6.3. Example: CTag Pre-condition on MKCALENDAR . . . . . . . . 18
+ 7. XML Element Definitions . . . . . . . . . . . . . . . . . . . 19
+ 7.1. MM:multiput XML Element . . . . . . . . . . . . . . . . . 19
+ 7.2. MM:resource XML Element . . . . . . . . . . . . . . . . . 19
+ 7.3. MM:if-match XML Element . . . . . . . . . . . . . . . . . 20
+ 7.4. MM:delete XML Element . . . . . . . . . . . . . . . . . . 20
+ 8. HTTP Header Definitions . . . . . . . . . . . . . . . . . . . 20
+ 8.1. X-MobileMe-DAV-Options Header . . . . . . . . . . . . . . 20
+ 8.2. CTag Header . . . . . . . . . . . . . . . . . . . . . . . 21
+ 8.3. Home-CTag Header . . . . . . . . . . . . . . . . . . . . . 21
+ 9. Security Considerations . . . . . . . . . . . . . . . . . . . 21
+ 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 22
+ 11. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 22
+ 12. Normative References . . . . . . . . . . . . . . . . . . . . . 22
+ Appendix A. Change History . . . . . . . . . . . . . . . . . . . 22
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 23
+
+
+
+Daboo & York [Page 1]
+
+ Calendar Server Bulk Changes August 2011
+
+
+1. Introduction
+
+ CalDAV [RFC4791] and CardDAV [I-D.ietf-vcarddav-carddav] provide a
+ way for calendar and contact data, respectively, to be stored on a
+ WebDAV [RFC4918] server. In a number of situations clients need to
+ make a lot of changes on the server all within a short period of
+ time. For example, a user could be trying to "import" a calendar or
+ address book from some other sources, requiring the client to create
+ a lot of calendar or address book resources. Or a client that is
+ able to operate in a disconnected mode could have a whole set of
+ changes queued up that need to be "replayed" to the server to
+ synchronize the changes. Currently, WebDAV operations such as PUT
+ and DELETE are limited to operating on a single resource at a time.
+ A more efficient approach would be to allow the client to send a
+ request that can change multiple resources in one go, thus cutting
+ down on round trips, authentication and authorization overhead on the
+ server.
+
+ This extension defines a way for clients to do two types of "bulk"
+ upload operations.
+
+ The first type covers the "import" use case, allowing a client to
+ submit a single request containing data for all the new resources to
+ be created. The server splits the data into separate components and
+ creates new resources as appropriate for each. The response from the
+ server allows the client to identify which new resources were
+ created, or which failed to be created.
+
+ The second type covers the "synchronize" use case, supporting CRUD
+ (Create, Update and Delete) operations in a single request. In this
+ case the client submits an XML request containing a list of CRUD
+ operations to execute. The server executes each one as appropriate
+ and returns a response indicating what was done.
+
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ When XML element types in the namespaces "DAV:",
+ "urn:ietf:params:xml:ns:caldav", "urn:ietf:params:xml:ns:carddav" and
+ "http://calendarserver.org/ns/" are referenced in this document
+ outside of the context of an XML fragment, the strings "DAV:",
+ "CALDAV:", "CARDDAV:" and "CS:" will be prefixed to the element type
+ names respectively.
+
+
+
+
+Daboo & York [Page 2]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ The namespace "http://me.com/_namespace/" is used for XML elements
+ defined in this specification. When XML element types in that
+ namespace are referenced in this document outside of the context of
+ an XML fragment, the string "MM:" will be prefixed to the element
+ type names.
+
+
+3. Simple Bulk Import Request
+
+ A bulk import is accomplished by the client issuing a POST request on
+ a calendar or address book collection resource, with the body of the
+ request containing an iCalendar object (a single "VCALENDAR"
+ component containing one or more child components) or a vCard stream
+ (multiple "VCARD" objects).
+
+ The data submitted in the request MUST conform to the following
+ requirements:
+
+ o For calendar data, the iCalendar object MUST conform to iCalendar
+ [RFC5545] semantics. Each component, other than "VTIMEZONE"s,
+ MUST contain a UID property. Components that represent a
+ recurring set (a master component and overridden instances) MUST
+ have the same UID property value. Components that represent
+ different recurring sets or non-recurring components MUST have
+ different UID property values. The UID property values in the
+ request MUST NOT match any of those in resources already stored in
+ the calendar collection targeted by the request.
+
+ o For address book data, the vCard stream MUST contain individual
+ vCards that conform to vCard [RFC2426] semantics. Each component
+ MUST have a UID property with a value that is unique. The UID
+ property values in the request SHOULD NOT match any of those in
+ resources already stored in the address book collection targeted
+ by the request.
+
+ When the server receives the request from the client it "breaks" up
+ the data into separate resources as follows:
+
+ o For calendar data, each resource is formed from each recurrence
+ set in the request data, together with the appropriate "VTIMEZONE"
+ component, to make a valid CalDAV calendar object resource.
+
+ o For address book data, each resource corresponds to a single
+ "VCARD" component in the request data, making a valid CardDAV
+ address book object resource.
+
+ The response from the server is a DAV:multistatus response. The
+ server MUST return one DAV:response for each calendar or address book
+
+
+
+Daboo & York [Page 3]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ object resource submitted in the request.
+
+ If creation of a resource succeeds, the DAV:response MUST include a
+ DAV:href containing the URI of the created resource, and MUST include
+ one of the following:
+
+ o If the server stores the calendar or address book object resource
+ data without modification to any properties, the server SHOULD
+ return a DAV:getetag WebDAV property in the DAV:response element,
+ with the strong ETag of the resource created. If the server does
+ not include a DAV:getetag or includes one with a weak ETag value,
+ then the client will need to reload the data to re-synchronize
+ with the server at some later point. The server MUST also return
+ a CS:uid pseudo-property containing the UID of the corresponding
+ calendar or address book object resource.
+
+ o If the server changes the calendar or address book data when
+ creating the resource, then it returns a response as follows:
+
+ * If the client did not include a X-MobileMe-DAV-Options header
+ with a "return-changed-data" option in the request, then the
+ server MUST NOT include a DAV:getetag property in the response
+ for the corresponding created resource. In this case the
+ client will be expected to reload the data to re-synchronize
+ with the server at some later point. The server MUST also
+ return a CS:uid pseudo-property containing the UID of the
+ corresponding calendar or address book object resource.
+
+ * If the client includes a X-MobileMe-DAV-Options header with a
+ "return-changed-data" option in the request, then the server
+ MUST include a DAV:getetag property in the response for the
+ corresponding created resource, as well as a CALDAV:calendar-
+ data or CARDDAV:address-data element containing the changed
+ data for CalDAV or CardDAV respectively.
+
+ If creation of a resource fails, then the server MUST return a DAV:
+ response element with an empty DAV:href, a DAV:status containing an
+ error code, and a DAV:error element containing the pre-condition
+ error element for the failure (where appropriate) and a CS:uid
+ element identifying the UID of the component in the request.
+
+ Clients MUST handle any partial failure - i.e. where some resources
+ are created and others not.
+
+3.1. Example: Calendar Resource Bulk Create
+
+ In this example the client is attempting to create two new calendar
+ object resources in a calendar. One resource is stored as-is, the
+
+
+
+Daboo & York [Page 4]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ other is rejected due to a UID uniqueness constraint violation.
+
+ >> Request <<
+
+ POST /home/cyrus/calendar/ HTTP/1.1
+ Host: cal.example.com
+ Content-Type: text/calendar; charset="utf-8"
+ Content-Length: xxx
+
+ BEGIN:VCALENDAR
+ VERSION:2.0
+ PRODID:-//Example Corp.//CalDAV Client//EN
+ BEGIN:VTIMEZONE
+ LAST-MODIFIED:20040110T032845Z
+ TZID:US/Eastern
+ BEGIN:DAYLIGHT
+ DTSTART:20000404T020000
+ RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+ TZNAME:EDT
+ TZOFFSETFROM:-0500
+ TZOFFSETTO:-0400
+ END:DAYLIGHT
+ BEGIN:STANDARD
+ DTSTART:20001026T020000
+ RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+ TZNAME:EST
+ TZOFFSETFROM:-0400
+ TZOFFSETTO:-0500
+ END:STANDARD
+ END:VTIMEZONE
+ BEGIN:VEVENT
+ DTSTART;TZID=US/Eastern:20101201T100000
+ DURATION:PT1H
+ SUMMARY:Event #1
+ UID:event1 at example.com
+ END:VEVENT
+ BEGIN:VEVENT
+ DTSTART;TZID=US/Eastern:20101202T100000
+ DURATION:PT1H
+ SUMMARY:Event #2
+ UID:event2 at example.com
+ END:VEVENT
+ END:VCALENDAR
+
+
+
+
+
+
+
+
+Daboo & York [Page 5]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ >> Response <<
+
+ HTTP/1.1 207 Multi-Status
+ Date: Sat, 27 Nov 2010 09:32:12 GMT
+ Content-Type: application/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:CS="http://calendarserver.org/ns/"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:response>
+ <D:href>/home/cyrus/calendar/abcd1.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ <CS:uid>event1 at example.com</CS:uid>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href/>
+ <D:status>HTTP/1.1 403 Forbidden</D:status>
+ <D:error>
+ <C:no-uid-conflict>/home/cyrus/calendar/abcd_other1.ics<
+ /C:no-uid-conflict>
+ <CS:uid>event2 at example.com</CS:uid>
+ </D:error>
+ </D:response>
+ </D:multistatus>
+
+3.2. Example: Address Book Resource Bulk Create
+
+ In this example the client is attempting to create two new address
+ book object resources in an address book. One resource is stored
+ as-is, the other is modified by the server as it is stored. The
+ client has requested the server to return the changed data.
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 6]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ >> Request <<
+
+ POST /home/cyrus/addressbook/ HTTP/1.1
+ Host: addressbook.example.com
+ X-MobileMe-DAV-Options:return-changed-data
+ Content-Type: text/vcard; charset="utf-8"
+ Content-Length: xxx
+
+ BEGIN:VCARD
+ VERSION:3.0
+ NICKNAME:me
+ UID:addr1 at example.com
+ FN:Cyrus Daboo
+ EMAIL:cdaboo at example.com
+ END:VCARD
+ BEGIN:VCARD
+ VERSION:3.0
+ NICKNAME:eric
+ UID:addr2 at example.com
+ FN:Eric York
+ END:VCARD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 7]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ >> Response <<
+
+ HTTP/1.1 207 Multi-Status
+ Date: Sat, 27 Nov 2010 09:32:12 GMT
+ Content-Type: application/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:CS="http://calendarserver.org/ns/"
+ xmlns:C="urn:ietf:params:xml:ns:carddav">
+ <D:response>
+ <D:href>/home/cyrus/addressbook/addr1.vcf</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ <CS:uid>addr1 at example.com</CS:uid>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/addressbook/addr2.vcf</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"A1FBCA60-1AF9-40D0-9406-B25EE3F33B80"</D:getetag>
+ <C:address-data>BEGIN:VCARD
+ VERSION:3.0
+ NICKNAME:eric
+ UID:addr2 at example.com
+ FN:Eric York
+ EMAIL:eyork at example.com
+ END:VCARD
+ </C:address-data>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ </D:multistatus>
+
+
+4. Bulk CRUD Request
+
+ A bulk CRUD operation is accomplished by the client issuing a POST
+ request on a calendar or address book collection resource, with the
+ body of the request containing an XML document with the MM:multiput
+ element as the root element. MM:resource elements appear within the
+
+
+
+Daboo & York [Page 8]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ MM:multiput element, with each one representing a CRUD operation on a
+ specific resource in the target collection.
+
+ For update and delete operations, the client MUST ensure that any
+ existing resource only appears once in the request.
+
+ The response from the server is a DAV:multistatus response. The
+ server MUST return one DAV:response for each MM:resource element
+ submitted in the request by the client. Each DAV:response MUST
+ include a DAV:href containing the URI of the created, updated or
+ deleted resource.
+
+ The precise details of each operation's request and response data
+ follows.
+
+4.1. Create request
+
+ A create operation is accomplished by the client specifying a DAV:set
+ element in the MM:resource element. The client does not specify a
+ DAV:href element, as it does for an update operation. The calendar
+ or address book data being stored is included in a CALDAV:calendar-
+ data or CARDDAV:address-data element, respectively, within the DAV:
+ set element. Additional WebDAV properties MAY be specified in the
+ DAV:set element with each being set on the new resource if creation
+ is successful. The server MUST fail the request if any one property
+ or data fails to be set according to the request. In this regard,
+ the resulting response should follow the behavior of the PROPPATCH
+ request in that each property or data that did not cause an error
+ should be returned using a 424 Failed Dependency status code, while
+ the properties or data element that failed, should use other failure
+ codes.
+
+ If creation succeeds, the server MUST include a DAV:response element
+ for the newly created resource. The DAV:response MUST include a DAV:
+ href containing the URI of the created resource, and MUST include one
+ of the following:
+
+ o If the server stores the calendar or address book object resource
+ data without modification to any properties, the server SHOULD
+ return a DAV:getetag WebDAV property in the DAV:response element,
+ with the strong ETag of the resource created. If the server does
+ not include a DAV:getetag or includes one with a weak ETag value,
+ then the client will need to reload the data to re-synchronize
+ with the server at some later point. The server MUST also return
+ a CS:uid pseudo-property containing the UID of the corresponding
+ calendar or address book object resource.
+
+
+
+
+
+Daboo & York [Page 9]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ o If the server changes the calendar or address book data when
+ creating the resource, then it returns a response as follows:
+
+ * If the client did not include a X-MobileMe-DAV-Options header
+ with a "return-changed-data" option in the request, then the
+ server MUST NOT include a DAV:getetag property in the response
+ for the corresponding created resource. In this case the
+ client will be expected to reload the data to re-synchronize
+ with the server at some later point. The server MUST return a
+ CS:uid pseudo-property containing the UID of the corresponding
+ calendar or address book object resource.
+
+ * If the client includes a X-MobileMe-DAV-Options header with a
+ "return-changed-data" option in the request, then the server
+ MUST include a DAV:getetag property in the response for the
+ corresponding created resource, as well as a CALDAV:calendar-
+ data or CARDDAV:address-data element containing the changed
+ data for CalDAV or CardDAV respectively.
+
+ If creation fails, then the server MUST return a DAV:response element
+ with an empty DAV:href, a DAV:status containing an error code, and a
+ DAV:error element containing the pre-condition error element for the
+ failure (where appropriate) and a CS:uid element identifying the UID
+ of the component in the request.
+
+ Clients MUST handle any partial failure - i.e. where some resources
+ are created and others not.
+
+4.2. Update request
+
+ An update operation is accomplished by the client specifying a DAV:
+ set element in the MM:resource element. The client MUST include a
+ DAV:href element whose value is the URI of the resource to be
+ updated. The client MAY update the actual CalDAV or CardDAV resource
+ data by including CALDAV:calendar-data or CARDDAV:address-data
+ elements, respectively, within the DAV:set element. Additional
+ WebDAV properties MAY be specified in the DAV:set element with each
+ being set on the resource if update is successful. Existing WebDAV
+ properties on the resource may be removed by including them in a DAV:
+ remove element. So the client can choose to update just the resource
+ data, just WebDAV properties or both. In all cases, the server MUST
+ fail the request if any one property or data fails to be set or
+ removed according to the request. In this regard, the resulting
+ response should follow the behavior of the PROPPATCH request in that
+ each property or data that did not cause an error should be returned
+ using a 424 Failed Dependency status code, while the properties or
+ data element that failed, should use other failure codes.
+
+
+
+
+Daboo & York [Page 10]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ The client MAY include an MM:if-match element with a DAV:getetag
+ element containing the the last ETag the client retrieved for the
+ resource being updated. This instructs the server to treat the
+ update as a conditional update. If the ETag of the resource on the
+ server being updated does not match the one in the MM:if-match
+ element, then the server MUST fail the update of the resource with a
+ 412 Precondition Failed error in the DAV:response element for the
+ resource.
+
+ If the update succeeds, the server MUST include a DAV:response
+ element for the updated resource. The DAV:response MUST include a
+ DAV:href containing the URI of the resource, and MUST include one of
+ the following:
+
+ o If the server stores the calendar or address book object resource
+ data without modification to any properties, the server SHOULD
+ return a DAV:getetag WebDAV property in the DAV:response element,
+ with the strong ETag of the resource created. If the server does
+ not include a DAV:getetag or includes one with a weak ETag value,
+ then the client will need to reload the data to re-synchronize
+ with the server at some later point.
+
+ o If the server changes the calendar or address book data when
+ updating the resource, then it returns a response as follows:
+
+ * If the client did not include a X-MobileMe-DAV-Options header
+ with a "return-changed-data" option in the request, then the
+ server MUST NOT include a DAV:getetag property in the response
+ for the corresponding updated resource. In this case the
+ client will be expected to reload the data to re-synchronize
+ with the server at some later point.
+
+ * If the client includes a X-MobileMe-DAV-Options header with a
+ "return-changed-data" option in the request, then the server
+ MUST include a DAV:getetag property in the response for the
+ corresponding created resource, as well as a CALDAV:calendar-
+ data or CARDDAV:address-data element containing the changed
+ data for CalDAV or CardDAV respectively.
+
+ If an update fails, then the server MUST return a DAV:response
+ element with a DAV:href containing the URI of the resource, a DAV:
+ status containing an error code, and a DAV:error element containing
+ the pre-condition error element for the failure (where appropriate).
+
+ Clients MUST handle any partial failure - i.e. where some resources
+ are updated and others not.
+
+
+
+
+
+Daboo & York [Page 11]
+
+ Calendar Server Bulk Changes August 2011
+
+
+4.3. Delete request
+
+ A delete operation is accomplished by the client specifying a MM:
+ delete element in the MM:resource element. The client MUST include a
+ DAV:href element whose value is the URI of the resource to be
+ updated.
+
+ The client MAY include an MM:if-match element with a DAV:getetag
+ element containing the the last ETag the client retrieved for the
+ resource being deleted. This instructs the server to treat the
+ delete as a conditional delete. If the ETag of the resource on the
+ server being deleted does not match the one in the MM:if-match
+ element, then the server MUST fail the delete of the resource with a
+ 412 Precondition Failed error in the DAV:response element for the
+ resource.
+
+ If the delete succeeds, the server MUST include a DAV:response
+ element for the deleted resource. The DAV:response MUST include a
+ DAV:href containing the URI of the resource and a success status.
+
+ If a delete fails, then the server MUST return a DAV:response element
+ with a DAV:href containing the URI of the resource, a DAV:status
+ containing an error code, and a DAV:error element containing the pre-
+ condition error element for the failure (where appropriate).
+
+ Clients MUST handle any partial failure - i.e. where some resources
+ are deleted and others not.
+
+4.4. Example: Calendar Resource Bulk CRUD
+
+ In this example the client is attempting to create, update and delete
+ calendar object resources in a calendar. In one case an update
+ fails.
+
+ >> Request <<
+
+ POST /home/cyrus/calendar/ HTTP/1.1
+ Host: cal.example.com
+ Content-Type: application/xml; charset="utf-8"
+ Content-Length: xxx
+
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ <MM:multiput xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <MM:resource>
+ <D:set>
+ <D:prop>
+
+
+
+Daboo & York [Page 12]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ <C:calendar-data>...some event...</C:calendar-data>
+ <D:displayname>event 1</D:displayname>
+ </D:prop>
+ </D:set>
+ </MM:resource>
+ <MM:resource>
+ <D:href>/home/cyrus/calendar/2.ics</D:href>
+ <MM:if-match>
+ <D:getetag>"12345"</D:getetag>
+ </MM:if-match>
+ <D:set>
+ <D:prop>
+ <C:calendar-data>...some event...</C:calendar-data>
+ <D:displayname>event 2</D:displayname>
+ </D:prop>
+ </D:set>
+ </MM:resource>
+ <MM:resource>
+ <D:href>/home/cyrus/calendar/3.ics</D:href>
+ <MM:if-match>
+ <D:getetag>"67890"</D:getetag>
+ </MM:if-match>
+ <MM:delete/>
+ </MM:resource>
+ <MM:resource>
+ <D:href>/home/cyrus/calendar/4.ics</D:href>
+ <MM:if-match>
+ <D:getetag>"12345"</D:getetag>
+ </MM:if-match>
+ <D:set>
+ <D:prop>
+ <C:calendar-data>...some event...</C:calendar-data>
+ <D:displayname>event 2</D:displayname>
+ </D:prop>
+ </D:set>
+ </MM:resource>
+ </MM:multiput>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 13]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ >> Response <<
+
+ HTTP/1.1 207 Multi-Status
+ Date: Sat, 27 Nov 2010 09:32:12 GMT
+ Content-Type: application/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:CS="http://calendarserver.org/ns/"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:response>
+ <D:href>/home/cyrus/calendar/1.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ <CS:uid>event1 at example.com</CS:uid>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/calendar/2.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/calendar/3.ics</D:href>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/calendar/4.ics</D:href>
+ <D:status>HTTP/1.1 403 FORBIDDEN</D:status>
+ <D:error><C:valid-calendar-data/><D:error>
+ </D:response>
+ </D:multistatus>
+
+
+5. Size Limits for Bulk Requests
+
+ Servers might want to limit the total number of resources or size of
+ request bodies for either the simple or CRUD bulk requests. In
+ addition, clients would want to discover the presence of bulk request
+
+
+
+Daboo & York [Page 14]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ support. To resolve both these issues, this specification defines a
+ new WebDAV property for use on calendar or address book collection
+ resources that advertises allowed limits and the presence of the bulk
+ requests.
+
+5.1. MM:bulk-requests Property
+
+ Name: bulk-requests
+
+ Namespace: http://me.com/_namespace/
+
+ Purpose: Describes limits for supported bulk request features.
+
+ Protected: This property MUST be protected and SHOULD NOT be
+ returned by a PROPFIND allprop request (as defined in Section 14.2
+ of [RFC4918]).
+
+ COPY/MOVE behavior: This property value SHOULD be kept during a MOVE
+ operation, and SHOULD be copied and preserved in a COPY.
+
+ Description: This property MUST be defined on all calendar or
+ address book collection resources that support the simple and CRUD
+ bulk requests defined by Section 3 and Section 4. If present, it
+ contains XML elements that indicate support for a particular bulk
+ request, and within those elements, server limits for number of
+ resources and request size are provided.
+
+ Clients MUST read this property from calendar or address book
+ collections in order to determine whether support for the bulk
+ requests is available. If the MM:simple element is present, then
+ the simple bulk request is supported. If the MM:crud element is
+ present, then the CRUD bulk request is present. Clients MUST
+ restrict their requests to lie within the limits advertised by the
+ server within each element.
+
+ Definition:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 15]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ <!ELEMENT bulk-requests (simple?, crud?) >
+
+ <!ELEMENT simple (max-resources, max-bytes)>
+ <!ELEMENT crud (max-resources, max-bytes)>
+
+ <!ELEMENT max-resources (CDATA)>
+ <!-- An integer value representing the maximum number
+ of resources that can be specified in the request
+ or empty if no limit -->
+
+ <!ELEMENT max-bytes (CDATA)>
+ <!-- An integer value representing the maximum size in
+ octets of the request body that the server will
+ accept or empty if no limit -->
+
+ Example:
+
+ <MM:bulk-requests
+ xmlns:MM="http://me.com/_namespace/">
+ <MM:simple>
+ <MM:max-resources/>
+ <MM:max-bytes>10485760</MM:max-bytes>
+ </MM:simple>
+ <MM:crud>
+ <MM:max-resources>50</MM:max-resources>
+ <MM:max-bytes>10485760</MM:max-bytes>
+ </MM:crud>
+ </MM:bulk-requests>
+
+
+6. CTag Conditional Requests
+
+ A CTag is a collection entity tag that changes when the contents of
+ the collection change. When doing bulk requests clients might need
+ to ensure that they are fully synchronized with the current state of
+ the target collection. To do that, clients carry out normal
+ synchronization operations and record the value of the CS:getctag
+ property on the collection. Then, when doing a bulk operation, the
+ client can use an If pre-condition header check to ensure that the
+ state of the collection has not changed. If it has changed, the
+ server MUST return a 412 pre-condition failed error.
+
+ The CTag property value used in an If header is appended to a uri of
+ the form "http://me.com/_namespace/ctag/", with appropriate URL
+ escaping applied.
+
+ Clients SHOULD use the CTag conditional requests when doing bulk
+ requests. When doing so, clients SHOULD use an "Expect: 100-
+
+
+
+Daboo & York [Page 16]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ Continue" request header and wait for the server response before
+ sending the request body.
+
+ When a CTag conditional request is used by the client, and the
+ request is successful, the server MUST return a CTag response header
+ (Section 8.2) with the new CS:getctag value for the collection after
+ changes have been applied.
+
+ For MKCALENDAR or extended MKCOL requests used to create calendar or
+ address book collections, the client might need to ensure that no
+ other calendars or address books have been created since its last
+ synchronization. To do that, clients record the DAV:getctag property
+ on the calendar or address book home collection and then use that
+ value in a CTag conditional request, where the condition is on the
+ home collection resource CTag value. When this occurs, and a
+ successful response is returned by the server, the server MUST also
+ include a Home-CTag response header containing the new value of the
+ DAV:getctag property value on the home collection. In addition, the
+ server MUST also include a CTag response header containing the
+ initial value of the DAV:getctag property on the newly created
+ calendar or address book collection.
+
+6.1. Example: CTag Pre-condition Failure
+
+ In this example the client is attempting to create two new address
+ book object resources in an address book, using CTag-based pre-
+ condition header. The pre-condition fails.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 17]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ >> Request <<
+
+ POST /home/cyrus/addressbook/ HTTP/1.1
+ Host: addressbook.example.com
+ X-MobileMe-DAV-Options:return-changed-data
+ If: (<http://me.com/_namespace/ctag/8054C6D4-C5C5-4ED9-A312-4E56753>)
+ Content-Type: text/vcard; charset="utf-8"
+ Content-Length: xxx
+
+ BEGIN:VCARD
+ VERSION:3.0
+ NICKNAME:me
+ UID:addr1 at example.com
+ FN:Cyrus Daboo
+ EMAIL:cdaboo at example.com
+ END:VCARD
+ BEGIN:VCARD
+ VERSION:3.0
+ NICKNAME:eric
+ UID:addr2 at example.com
+ FN:Eric York
+ END:VCARD
+
+ >> Response <<
+
+ HTTP/1.1 412 Precondition Failed
+ Date: Sat, 27 Nov 2010 09:32:12 GMT
+ Content-Type: application/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:error xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/">
+ <MM:ctag-ok/>
+ </D:error>
+
+6.2. Example: CTag Pre-condition Success with Expect
+
+ TBD
+
+6.3. Example: CTag Pre-condition on MKCALENDAR
+
+ In this example the client is attempting to create a new calendar
+ collection using a pre-condition based on the DAV:getctag value of
+ the parent home collection.
+
+
+
+
+
+
+Daboo & York [Page 18]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ >> Request <<
+
+ MKCALENDAR /home/cyrus/calendar/newcal/ HTTP/1.1
+ Host: calendar.example.com
+ If: </home/cyrus/calendar/>
+ (<http://me.com/_namespace/ctag/8054C6D4-C5C5-4ED9-A312-4E5675309>)
+
+ >> Response <<
+
+ HTTP/1.1 201 CREATED
+ Date: Sat, 27 Nov 2010 09:32:12 GMT
+ Home-CTag: D964B55F-3585-4234-B2B0-3C55E81083BC
+ CTag: A132E16A-4933-4E74-B914-6497CC0387BB
+
+
+7. XML Element Definitions
+
+7.1. MM:multiput XML Element
+
+ Name: multiput
+
+ Namespace: http://me.com/_namespace/
+
+ Purpose: The root element for a CRUD batch POST request.
+
+ Description: See Section 4.
+
+ Definition:
+
+ <!ELEMENT multiput (MM:resource)+>
+
+7.2. MM:resource XML Element
+
+ Name: resource
+
+ Namespace: http://me.com/_namespace/
+
+ Purpose: Identifies a resource for a CRUD operation
+
+ Description: See Section 4.
+
+ Definition:
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 19]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ <!ELEMENT resource (
+ (DAV:set) |
+ (DAV:href, MM:if-match?, DAV:set?, DAV:remove?) |
+ (DAV:href, MM:if-match?, MM:delete)
+ )+>
+
+ <!-- First combination is for create, second for update
+ and third for delete -->
+
+7.3. MM:if-match XML Element
+
+ Name: if-match
+
+ Namespace: http://me.com/_namespace/
+
+ Purpose: Used to specify ETag-based pre-condition option for a CRUD
+ update or delete operation.
+
+ Description: See Section 4.
+
+ Definition:
+
+ <!ELEMENT if-match (DAV:getetag)>
+
+7.4. MM:delete XML Element
+
+ Name: delete
+
+ Namespace: http://me.com/_namespace/
+
+ Purpose: Used to specify a CRUD delete operation.
+
+ Description: See Section 4.
+
+ Definition:
+
+ <!ELEMENT delete EMPTY>
+
+
+8. HTTP Header Definitions
+
+ This specification adds the following HTTP header definition.
+
+8.1. X-MobileMe-DAV-Options Header
+
+ The X-MobileMe-DAV-Options request header allows the client to
+ communicate options related to a request to the server. The header
+ contains a list of simple tokens or key/value pairs to control server
+
+
+
+Daboo & York [Page 20]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ behavior. The set of allowed tokens and keys are listed below.
+
+ X-MobileMe-DAV-Options = "X-MobileMe-DAV-Options" ":" 1#mme-options
+
+ mme-options = mme-option ["=" (token | quoted-string)]
+
+ mme-option = "return-changed-data" | token
+
+ Example:
+
+ X-MobileMe-DAV-Options: return-changed-data, Test=True
+
+8.2. CTag Header
+
+ The CTag response header allows the server to return the value of the
+ CS:getctag property on the collection targeted by the request after
+ all changes from the request have been applied. Clients can use this
+ to help with managing multiple batch requests making a set of changes
+ on the server.
+
+ CTag = "CTag" ":" token
+
+ Example:
+
+ CTag: 84C9ACB2-A7DE-4D5E-AA8A-2B1EA30F098A
+
+8.3. Home-CTag Header
+
+ The Home-CTag response header allows the server to return the value
+ of the CS:getctag property on the home collection targeted by the
+ request, or the home collection parent of the request target after
+ all changes from the request have been applied. Clients can use this
+ to help with managing multiple batch requests making a set of changes
+ on the server.
+
+ HomeCTag = "Home-CTag" ":" token
+
+ Example:
+
+ Home-CTag: 17883032-CBEF-4E21-BEE2-73D316063EDA
+
+
+9. Security Considerations
+
+ TBD
+
+
+
+
+
+
+Daboo & York [Page 21]
+
+ Calendar Server Bulk Changes August 2011
+
+
+10. IANA Considerations
+
+ This document does not require any actions on the part of IANA.
+
+
+11. Acknowledgments
+
+ This specification is the result of discussions between Apple server
+ and client teams.
+
+
+12. Normative References
+
+ [I-D.ietf-vcarddav-carddav]
+ Daboo, C., "vCard Extensions to WebDAV (CardDAV)",
+ draft-ietf-vcarddav-carddav-10 (work in progress),
+ November 2009.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory Profile",
+ RFC 2426, September 1998.
+
+ [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault,
+ "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791,
+ March 2007.
+
+ [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
+ Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
+
+ [RFC5545] Desruisseaux, B., "Internet Calendaring and Scheduling
+ Core Object Specification (iCalendar)", RFC 5545,
+ September 2009.
+
+
+Appendix A. Change History
+
+ Changes in -03:
+
+ 1. Changed MAY to SHOULD for Expect header.
+
+ 2. max-size -> max-bytes.
+
+ 3. Title changes.
+
+ Changes in -02:
+
+
+
+
+Daboo & York [Page 22]
+
+ Calendar Server Bulk Changes August 2011
+
+
+ 1. Home ctag behavior for MKCALENDAR/MKCOL.
+
+ 2. Fixed syntax of If: examples which was missing (...)
+
+ Changes in -01:
+
+ 1. Switched to calendar server namespace for UID property.
+
+ 2. Switched to http based namespace instead of urn.
+
+ 3. Now returns empty DAV:href when error occurs on resource create.
+
+ 4. Cleaned up error handling for both POST requests, including
+ stating possibility of partial failures.
+
+ 5. Indicated that clients can use Expect header.
+
+ 6. Defined Ctag header and server requirement to return it.
+
+
+Authors' Addresses
+
+ Cyrus Daboo
+ Apple Inc.
+ 1 Infinite Loop
+ Cupertino, CA 95014
+ USA
+
+ Email: cyrus at daboo.name
+ URI: http://www.apple.com/
+
+
+ Eric York
+ Apple Inc.
+ 1 Infinite Loop
+ Cupertino, CA 95014
+ USA
+
+ Email:
+ URI: http://www.apple.com/
+
+
+
+
+
+
+
+
+
+
+
+Daboo & York [Page 23]
+
Added: CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.xml
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.xml (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/doc/Extensions/calendarserver-bulk-change.xml 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,872 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="../rfc2629.xslt"?>
+<!DOCTYPE rfc SYSTEM 'rfc2629.dtd' [
+<!ENTITY rfc2119 PUBLIC '' 'bibxml/reference.RFC.2119.xml'>
+<!ENTITY rfc2426 PUBLIC '' 'bibxml/reference.RFC.2426.xml'>
+<!ENTITY rfc4791 PUBLIC '' 'bibxml/reference.RFC.4791.xml'>
+<!ENTITY rfc4918 PUBLIC '' 'bibxml/reference.RFC.4918.xml'>
+<!ENTITY rfc5545 PUBLIC '' 'bibxml/reference.RFC.5545.xml'>
+<!ENTITY idCardDAV PUBLIC '' 'bibxml3/reference.I-D.ietf-vcarddav-carddav.xml'>
+]>
+<?rfc toc="yes"?>
+<?rfc tocdepth="4"?>
+<?rfc strict="yes"?>
+<?rfc comments="yes"?>
+<?rfc inline="yes"?>
+<?rfc symrefs="yes"?>
+<?rfc sortrefs="yes"?>
+<?rfc compact="yes"?>
+<?rfc subcompact="no"?>
+<?rfc private="Calendar Server Extension"?>
+<rfc ipr="none" docName='calendarserver-bulk-change-02'>
+ <front>
+ <title abbrev="Calendar Server Bulk Changes">Calendar Server Bulk Change Requests for *DAV Protocols</title>
+ <author initials="C." surname="Daboo" fullname="Cyrus Daboo">
+ <organization abbrev="Apple Inc.">
+ Apple Inc.
+ </organization>
+ <address>
+ <postal>
+ <street>1 Infinite Loop</street>
+ <city>Cupertino</city>
+ <region>CA</region>
+ <code>95014</code>
+ <country>USA</country>
+ </postal>
+ <email>cyrus at daboo.name</email>
+ <uri>http://www.apple.com/</uri>
+ </address>
+ </author>
+ <author initials="E." surname="York" fullname="Eric York">
+ <organization abbrev="Apple Inc.">
+ Apple Inc.
+ </organization>
+ <address>
+ <postal>
+ <street>1 Infinite Loop</street>
+ <city>Cupertino</city>
+ <region>CA</region>
+ <code>95014</code>
+ <country>USA</country>
+ </postal>
+ <email></email>
+ <uri>http://www.apple.com/</uri>
+ </address>
+ </author>
+ <date/>
+ <abstract>
+ <t>
+ This specification defines an extension to CalDAV and CardDAV that enables clients to do multiple changes on the server with a single HTTP request.
+ </t>
+ </abstract>
+ </front>
+ <middle>
+ <section title='Introduction'>
+ <t>
+ <xref target="RFC4791">CalDAV</xref> and <xref target='I-D.ietf-vcarddav-carddav'>CardDAV</xref> provide a way for calendar and contact data, respectively, to be stored on a <xref target='RFC4918'>WebDAV</xref> server. In a number of situations clients need to make a lot of changes on the server all within a short period of time. For example, a user could be trying to "import" a calendar or address book from some other sources, requiring the client to create a lot of calendar or address book resources. Or a client that is able to operate in a disconnected mode could have a whole set of changes queued up that need to be "replayed" to the server to synchronize the changes. Currently, WebDAV operations such as PUT and DELETE are limited to operating on a single resource at a time. A more efficient approach would be to allow the client to send a request that can change multiple resources in one go, thus cutting down on round trips, authentication and authorization overhead on the server.
+ </t>
+ <t>
+ This extension defines a way for clients to do two types of "bulk" upload operations.
+ </t>
+ <t>
+ The first type covers the "import" use case, allowing a client to submit a single request containing data for all the new resources to be created. The server splits the data into separate components and creates new resources as appropriate for each. The response from the server allows the client to identify which new resources were created, or which failed to be created.
+ </t>
+ <t>
+ The second type covers the "synchronize" use case, supporting CRUD (Create, Update and Delete) operations in a single request. In this case the client submits an XML request containing a list of CRUD operations to execute. The server executes each one as appropriate and returns a response indicating what was done.
+ </t>
+ </section>
+
+ <!--<section title="Open Issues">
+ <t>
+ <list style="numbers">
+ <t>
+ </t>
+ </list>
+ </t>
+ </section>-->
+
+ <section title='Conventions Used in This Document'>
+ <t>
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in <xref target='RFC2119' />.
+ </t>
+ <t>
+ When XML element types in the namespaces "DAV:", "urn:ietf:params:xml:ns:caldav", "urn:ietf:params:xml:ns:carddav" and "http://calendarserver.org/ns/" are referenced in this document outside of the context of an XML fragment, the strings "DAV:", "CALDAV:", "CARDDAV:" and "CS:" will be prefixed to the element type names respectively.
+ </t>
+ <t>
+ The namespace "http://me.com/_namespace/" is used for XML elements defined in this specification. When XML element types in that namespace are referenced in this document outside of the context of an XML fragment, the string "MM:" will be prefixed to the element type names.
+ </t>
+ </section>
+
+ <section title="Simple Bulk Import Request" anchor="simple">
+ <t>
+ A bulk import is accomplished by the client issuing a POST request on a calendar or address book collection resource, with the body of the request containing an iCalendar object (a single "VCALENDAR" component containing one or more child components) or a vCard stream (multiple "VCARD" objects).
+ </t>
+ <t>
+ The data submitted in the request MUST conform to the following requirements:
+ <list style="symbols">
+ <t>
+ For calendar data, the iCalendar object MUST conform to <xref target="RFC5545">iCalendar</xref> semantics. Each component, other than "VTIMEZONE"s, MUST contain a UID property. Components that represent a recurring set (a master component and overridden instances) MUST have the same UID property value. Components that represent different recurring sets or non-recurring components MUST have different UID property values. The UID property values in the request MUST NOT match any of those in resources already stored in the calendar collection targeted by the request.
+ </t>
+ <t>
+ For address book data, the vCard stream MUST contain individual vCards that conform to <xref target="RFC2426">vCard</xref> semantics. Each component MUST have a UID property with a value that is unique. The UID property values in the request SHOULD NOT match any of those in resources already stored in the address book collection targeted by the request.
+ </t>
+ </list>
+ </t>
+ <t>
+ When the server receives the request from the client it "breaks" up the data into separate resources as follows:
+ <list style="symbols">
+ <t>
+ For calendar data, each resource is formed from each recurrence set in the request data, together with the appropriate "VTIMEZONE" component, to make a valid CalDAV calendar object resource.
+ </t>
+ <t>
+ For address book data, each resource corresponds to a single "VCARD" component in the request data, making a valid CardDAV address book object resource.
+ </t>
+ </list>
+ </t>
+ <t>
+ The response from the server is a DAV:multistatus response. The server MUST return one DAV:response for each calendar or address book object resource submitted in the request.
+ </t>
+ <t>
+ If creation of a resource succeeds, the DAV:response MUST include a DAV:href containing the URI of the created resource, and MUST include one of the following:
+ <list style="symbols">
+ <t>
+ If the server stores the calendar or address book object resource data without modification to any properties, the server SHOULD return a DAV:getetag WebDAV property in the DAV:response element, with the strong ETag of the resource created. If the server does not include a DAV:getetag or includes one with a weak ETag value, then the client will need to reload the data to re-synchronize with the server at some later point. The server MUST also return a CS:uid pseudo-property containing the UID of the corresponding calendar or address book object resource.
+ </t>
+ <t>
+ If the server changes the calendar or address book data when creating the resource, then it returns a response as follows:
+ <list style="symbols">
+ <t>
+ If the client did not include a X-MobileMe-DAV-Options header with a "return-changed-data" option in the request, then the server MUST NOT include a DAV:getetag property in the response for the corresponding created resource. In this case the client will be expected to reload the data to re-synchronize with the server at some later point. The server MUST also return a CS:uid pseudo-property containing the UID of the corresponding calendar or address book object resource.
+ </t>
+ <t>
+ If the client includes a X-MobileMe-DAV-Options header with a "return-changed-data" option in the request, then the server MUST include a DAV:getetag property in the response for the corresponding created resource, as well as a CALDAV:calendar-data or CARDDAV:address-data element containing the changed data for CalDAV or CardDAV respectively.
+ </t>
+ </list>
+ </t>
+ </list>
+ </t>
+ <t>
+ If creation of a resource fails, then the server MUST return a DAV:response element with an empty DAV:href, a DAV:status containing an error code, and a DAV:error element containing the pre-condition error element for the failure (where appropriate) and a CS:uid element identifying the UID of the component in the request.
+ </t>
+ <t>
+ Clients MUST handle any partial failure - i.e. where some resources are created and others not.
+ </t>
+ <section title="Example: Calendar Resource Bulk Create">
+ <t>
+ In this example the client is attempting to create two new calendar object resources in a calendar. One resource is stored as-is, the other is rejected due to a UID uniqueness constraint violation.
+ </t>
+
+ <figure>
+ <preamble>
+ >> Request <<
+ </preamble>
+ <artwork><![CDATA[
+POST /home/cyrus/calendar/ HTTP/1.1
+Host: cal.example.com
+Content-Type: text/calendar; charset="utf-8"
+Content-Length: xxx
+
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Example Corp.//CalDAV Client//EN
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=US/Eastern:20101201T100000
+DURATION:PT1H
+SUMMARY:Event #1
+UID:event1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=US/Eastern:20101202T100000
+DURATION:PT1H
+SUMMARY:Event #2
+UID:event2 at example.com
+END:VEVENT
+END:VCALENDAR
+]]></artwork>
+ </figure>
+ <figure>
+ <preamble>
+ >> Response <<
+ </preamble>
+ <artwork><![CDATA[
+HTTP/1.1 207 Multi-Status
+Date: Sat, 27 Nov 2010 09:32:12 GMT
+Content-Type: application/xml; charset="utf-8"
+Content-Length: xxxx
+
+<?xml version="1.0" encoding="utf-8" ?>
+<D:multistatus xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:CS="http://calendarserver.org/ns/"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:response>
+ <D:href>/home/cyrus/calendar/abcd1.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ <CS:uid>event1 at example.com</CS:uid>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href/>
+ <D:status>HTTP/1.1 403 Forbidden</D:status>
+ <D:error>
+ <C:no-uid-conflict>/home/cyrus/calendar/abcd_other1.ics<
+ /C:no-uid-conflict>
+ <CS:uid>event2 at example.com</CS:uid>
+ </D:error>
+ </D:response>
+</D:multistatus>
+]]></artwork>
+ </figure>
+ </section>
+ <section title="Example: Address Book Resource Bulk Create">
+ <t>
+ In this example the client is attempting to create two new address book object resources in an address book. One resource is stored as-is, the other is modified by the server as it is stored. The client has requested the server to return the changed data.
+ </t>
+
+ <figure>
+ <preamble>
+ >> Request <<
+ </preamble>
+ <artwork><![CDATA[
+POST /home/cyrus/addressbook/ HTTP/1.1
+Host: addressbook.example.com
+X-MobileMe-DAV-Options:return-changed-data
+Content-Type: text/vcard; charset="utf-8"
+Content-Length: xxx
+
+BEGIN:VCARD
+VERSION:3.0
+NICKNAME:me
+UID:addr1 at example.com
+FN:Cyrus Daboo
+EMAIL:cdaboo at example.com
+END:VCARD
+BEGIN:VCARD
+VERSION:3.0
+NICKNAME:eric
+UID:addr2 at example.com
+FN:Eric York
+END:VCARD
+]]></artwork>
+ </figure>
+ <figure>
+ <preamble>
+ >> Response <<
+ </preamble>
+ <artwork><![CDATA[
+HTTP/1.1 207 Multi-Status
+Date: Sat, 27 Nov 2010 09:32:12 GMT
+Content-Type: application/xml; charset="utf-8"
+Content-Length: xxxx
+
+<?xml version="1.0" encoding="utf-8" ?>
+<D:multistatus xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:CS="http://calendarserver.org/ns/"
+ xmlns:C="urn:ietf:params:xml:ns:carddav">
+ <D:response>
+ <D:href>/home/cyrus/addressbook/addr1.vcf</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ <CS:uid>addr1 at example.com</CS:uid>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/addressbook/addr2.vcf</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"A1FBCA60-1AF9-40D0-9406-B25EE3F33B80"</D:getetag>
+ <C:address-data>BEGIN:VCARD
+VERSION:3.0
+NICKNAME:eric
+UID:addr2 at example.com
+FN:Eric York
+EMAIL:eyork at example.com
+END:VCARD
+</C:address-data>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+</D:multistatus>
+]]></artwork>
+ </figure>
+ </section>
+ </section>
+
+ <section title="Bulk CRUD Request" anchor="crud">
+ <t>
+ A bulk CRUD operation is accomplished by the client issuing a POST request on a calendar or address book collection resource, with the body of the request containing an XML document with the MM:multiput element as the root element. MM:resource elements appear within the MM:multiput element, with each one representing a CRUD operation on a specific resource in the target collection.
+ </t>
+ <t>
+ For update and delete operations, the client MUST ensure that any existing resource only appears once in the request.
+ </t>
+ <t>
+ The response from the server is a DAV:multistatus response. The server MUST return one DAV:response for each MM:resource element submitted in the request by the client. Each DAV:response MUST include a DAV:href containing the URI of the created, updated or deleted resource.
+ </t>
+ <t>
+ The precise details of each operation's request and response data follows.
+ </t>
+ <section title="Create request">
+ <t>
+ A create operation is accomplished by the client specifying a DAV:set element in the MM:resource element. The client does not specify a DAV:href element, as it does for an update operation. The calendar or address book data being stored is included in a CALDAV:calendar-data or CARDDAV:address-data element, respectively, within the DAV:set element. Additional WebDAV properties MAY be specified in the DAV:set element with each being set on the new resource if creation is successful. The server MUST fail the request if any one property or data fails to be set according to the request. In this regard, the resulting response should follow the behavior of the PROPPATCH request in that each property or data that did not cause an error should be returned using a 424 Failed Dependency status code, while the properties or data element that failed, should use other failure codes.
+ </t>
+ <t>
+ If creation succeeds, the server MUST include a DAV:response element for the newly created resource. The DAV:response MUST include a DAV:href containing the URI of the created resource, and MUST include one of the following:
+ <list style="symbols">
+ <t>
+ If the server stores the calendar or address book object resource data without modification to any properties, the server SHOULD return a DAV:getetag WebDAV property in the DAV:response element, with the strong ETag of the resource created. If the server does not include a DAV:getetag or includes one with a weak ETag value, then the client will need to reload the data to re-synchronize with the server at some later point. The server MUST also return a CS:uid pseudo-property containing the UID of the corresponding calendar or address book object resource.
+ </t>
+ <t>
+ If the server changes the calendar or address book data when creating the resource, then it returns a response as follows:
+ <list style="symbols">
+ <t>
+ If the client did not include a X-MobileMe-DAV-Options header with a "return-changed-data" option in the request, then the server MUST NOT include a DAV:getetag property in the response for the corresponding created resource. In this case the client will be expected to reload the data to re-synchronize with the server at some later point. The server MUST return a CS:uid pseudo-property containing the UID of the corresponding calendar or address book object resource.
+ </t>
+ <t>
+ If the client includes a X-MobileMe-DAV-Options header with a "return-changed-data" option in the request, then the server MUST include a DAV:getetag property in the response for the corresponding created resource, as well as a CALDAV:calendar-data or CARDDAV:address-data element containing the changed data for CalDAV or CardDAV respectively.
+ </t>
+ </list>
+ </t>
+ </list>
+ </t>
+ <t>
+ If creation fails, then the server MUST return a DAV:response element with an empty DAV:href, a DAV:status containing an error code, and a DAV:error element containing the pre-condition error element for the failure (where appropriate) and a CS:uid element identifying the UID of the component in the request.
+ </t>
+ <t>
+ Clients MUST handle any partial failure - i.e. where some resources are created and others not.
+ </t>
+ </section>
+ <section title="Update request">
+ <t>
+ An update operation is accomplished by the client specifying a DAV:set element in the MM:resource element. The client MUST include a DAV:href element whose value is the URI of the resource to be updated. The client MAY update the actual CalDAV or CardDAV resource data by including CALDAV:calendar-data or CARDDAV:address-data elements, respectively, within the DAV:set element. Additional WebDAV properties MAY be specified in the DAV:set element with each being set on the resource if update is successful. Existing WebDAV properties on the resource may be removed by including them in a DAV:remove element. So the client can choose to update just the resource data, just WebDAV properties or both. In all cases, the server MUST fail the request if any one property or data fails to be set or removed according to the request. In this regard, the resulting response should follow the behavior of the PROPPATCH request in that each property or data that did not cause an error should be returned using a 424 Failed Dependency status code, while the properties or data element that failed, should use other failure codes.
+ </t>
+ <t>
+ The client MAY include an MM:if-match element with a DAV:getetag element containing the the last ETag the client retrieved for the resource being updated. This instructs the server to treat the update as a conditional update. If the ETag of the resource on the server being updated does not match the one in the MM:if-match element, then the server MUST fail the update of the resource with a 412 Precondition Failed error in the DAV:response element for the resource.
+ </t>
+ <t>
+ If the update succeeds, the server MUST include a DAV:response element for the updated resource. The DAV:response MUST include a DAV:href containing the URI of the resource, and MUST include one of the following:
+ <list style="symbols">
+ <t>
+ If the server stores the calendar or address book object resource data without modification to any properties, the server SHOULD return a DAV:getetag WebDAV property in the DAV:response element, with the strong ETag of the resource created. If the server does not include a DAV:getetag or includes one with a weak ETag value, then the client will need to reload the data to re-synchronize with the server at some later point.
+ </t>
+ <t>
+ If the server changes the calendar or address book data when updating the resource, then it returns a response as follows:
+ <list style="symbols">
+ <t>
+ If the client did not include a X-MobileMe-DAV-Options header with a "return-changed-data" option in the request, then the server MUST NOT include a DAV:getetag property in the response for the corresponding updated resource. In this case the client will be expected to reload the data to re-synchronize with the server at some later point.
+ </t>
+ <t>
+ If the client includes a X-MobileMe-DAV-Options header with a "return-changed-data" option in the request, then the server MUST include a DAV:getetag property in the response for the corresponding created resource, as well as a CALDAV:calendar-data or CARDDAV:address-data element containing the changed data for CalDAV or CardDAV respectively.
+ </t>
+ </list>
+ </t>
+ </list>
+ </t>
+ <t>
+ If an update fails, then the server MUST return a DAV:response element with a DAV:href containing the URI of the resource, a DAV:status containing an error code, and a DAV:error element containing the pre-condition error element for the failure (where appropriate).
+ </t>
+ <t>
+ Clients MUST handle any partial failure - i.e. where some resources are updated and others not.
+ </t>
+ </section>
+ <section title="Delete request">
+ <t>
+ A delete operation is accomplished by the client specifying a MM:delete element in the MM:resource element. The client MUST include a DAV:href element whose value is the URI of the resource to be updated.
+ </t>
+ <t>
+ The client MAY include an MM:if-match element with a DAV:getetag element containing the the last ETag the client retrieved for the resource being deleted. This instructs the server to treat the delete as a conditional delete. If the ETag of the resource on the server being deleted does not match the one in the MM:if-match element, then the server MUST fail the delete of the resource with a 412 Precondition Failed error in the DAV:response element for the resource.
+ </t>
+ <t>
+ If the delete succeeds, the server MUST include a DAV:response element for the deleted resource. The DAV:response MUST include a DAV:href containing the URI of the resource and a success status.
+ </t>
+ <t>
+ If a delete fails, then the server MUST return a DAV:response element with a DAV:href containing the URI of the resource, a DAV:status containing an error code, and a DAV:error element containing the pre-condition error element for the failure (where appropriate).
+ </t>
+ <t>
+ Clients MUST handle any partial failure - i.e. where some resources are deleted and others not.
+ </t>
+ </section>
+ <section title="Example: Calendar Resource Bulk CRUD">
+ <t>
+ In this example the client is attempting to create, update and delete calendar object resources in a calendar. In one case an update fails.
+ </t>
+
+ <figure>
+ <preamble>
+ >> Request <<
+ </preamble>
+ <artwork><![CDATA[
+POST /home/cyrus/calendar/ HTTP/1.1
+Host: cal.example.com
+Content-Type: application/xml; charset="utf-8"
+Content-Length: xxx
+
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<MM:multiput xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <MM:resource>
+ <D:set>
+ <D:prop>
+ <C:calendar-data>...some event...</C:calendar-data>
+ <D:displayname>event 1</D:displayname>
+ </D:prop>
+ </D:set>
+ </MM:resource>
+ <MM:resource>
+ <D:href>/home/cyrus/calendar/2.ics</D:href>
+ <MM:if-match>
+ <D:getetag>"12345"</D:getetag>
+ </MM:if-match>
+ <D:set>
+ <D:prop>
+ <C:calendar-data>...some event...</C:calendar-data>
+ <D:displayname>event 2</D:displayname>
+ </D:prop>
+ </D:set>
+ </MM:resource>
+ <MM:resource>
+ <D:href>/home/cyrus/calendar/3.ics</D:href>
+ <MM:if-match>
+ <D:getetag>"67890"</D:getetag>
+ </MM:if-match>
+ <MM:delete/>
+ </MM:resource>
+ <MM:resource>
+ <D:href>/home/cyrus/calendar/4.ics</D:href>
+ <MM:if-match>
+ <D:getetag>"12345"</D:getetag>
+ </MM:if-match>
+ <D:set>
+ <D:prop>
+ <C:calendar-data>...some event...</C:calendar-data>
+ <D:displayname>event 2</D:displayname>
+ </D:prop>
+ </D:set>
+ </MM:resource>
+</MM:multiput>
+]]></artwork>
+ </figure>
+ <figure>
+ <preamble>
+ >> Response <<
+ </preamble>
+ <artwork><![CDATA[
+HTTP/1.1 207 Multi-Status
+Date: Sat, 27 Nov 2010 09:32:12 GMT
+Content-Type: application/xml; charset="utf-8"
+Content-Length: xxxx
+
+<?xml version="1.0" encoding="utf-8" ?>
+<D:multistatus xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/"
+ xmlns:CS="http://calendarserver.org/ns/"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:response>
+ <D:href>/home/cyrus/calendar/1.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ <CS:uid>event1 at example.com</CS:uid>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/calendar/2.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"13FBAF6F-4FDE-4973-8A9A-0A2A3E6D4F3D"</D:getetag>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/calendar/3.ics</D:href>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:response>
+ <D:response>
+ <D:href>/home/cyrus/calendar/4.ics</D:href>
+ <D:status>HTTP/1.1 403 FORBIDDEN</D:status>
+ <D:error><C:valid-calendar-data/><D:error>
+ </D:response>
+</D:multistatus>
+]]></artwork>
+ </figure>
+ </section>
+ </section>
+
+ <section title="Size Limits for Bulk Requests">
+ <t>
+ Servers might want to limit the total number of resources or size of request bodies for either the simple or CRUD bulk requests. In addition, clients would want to discover the presence of bulk request support. To resolve both these issues, this specification defines a new WebDAV property for use on calendar or address book collection resources that advertises allowed limits and the presence of the bulk requests.
+ </t>
+ <section title="MM:bulk-requests Property">
+ <t>
+ <list style="hanging">
+ <t hangText="Name:">bulk-requests</t>
+ <t hangText="Namespace:">http://me.com/_namespace/</t>
+ <t hangText="Purpose:">Describes limits for supported bulk request features.</t>
+ <t hangText="Protected:">This property MUST be protected and SHOULD NOT be returned by a PROPFIND allprop request (as defined in Section 14.2 of <xref target="RFC4918"/>).</t>
+ <t hangText="COPY/MOVE behavior:">This property value SHOULD be kept during a MOVE operation, and SHOULD be copied and preserved in a COPY.</t>
+ <t hangText="Description:">This property MUST be defined on all calendar or address book collection resources that support the simple and CRUD bulk requests defined by <xref target="simple"/> and <xref target="crud"/>. If present, it contains XML elements that indicate support for a particular bulk request, and within those elements, server limits for number of resources and request size are provided.</t>
+ <t>Clients MUST read this property from calendar or address book collections in order to determine whether support for the bulk requests is available. If the MM:simple element is present, then the simple bulk request is supported. If the MM:crud element is present, then the CRUD bulk request is present. Clients MUST restrict their requests to lie within the limits advertised by the server within each element.</t>
+ <t hangText="Definition:">
+ <figure>
+ <artwork><![CDATA[
+ <!ELEMENT bulk-requests (simple?, crud?) >
+
+ <!ELEMENT simple (max-resources, max-bytes)>
+ <!ELEMENT crud (max-resources, max-bytes)>
+
+ <!ELEMENT max-resources (CDATA)>
+ <!-- An integer value representing the maximum number
+ of resources that can be specified in the request
+ or empty if no limit -->
+
+ <!ELEMENT max-bytes (CDATA)>
+ <!-- An integer value representing the maximum size in
+ octets of the request body that the server will
+ accept or empty if no limit -->
+]]></artwork>
+ </figure>
+ </t>
+ <t hangText="Example:">
+ <figure>
+ <artwork><![CDATA[
+<MM:bulk-requests
+ xmlns:MM="http://me.com/_namespace/">
+ <MM:simple>
+ <MM:max-resources/>
+ <MM:max-bytes>10485760</MM:max-bytes>
+ </MM:simple>
+ <MM:crud>
+ <MM:max-resources>50</MM:max-resources>
+ <MM:max-bytes>10485760</MM:max-bytes>
+ </MM:crud>
+</MM:bulk-requests>
+]]></artwork>
+ </figure>
+ </t>
+ </list>
+ </t>
+ </section>
+ </section>
+
+ <section title="CTag Conditional Requests">
+ <t>
+ A CTag is a collection entity tag that changes when the contents of the collection change. When doing bulk requests clients might need to ensure that they are fully synchronized with the current state of the target collection. To do that, clients carry out normal synchronization operations and record the value of the CS:getctag property on the collection. Then, when doing a bulk operation, the client can use an If pre-condition header check to ensure that the state of the collection has not changed. If it has changed, the server MUST return a 412 pre-condition failed error.
+ </t>
+ <t>
+ The CTag property value used in an If header is appended to a uri of the form "http://me.com/_namespace/ctag/", with appropriate URL escaping applied.
+ </t>
+ <t>
+ Clients SHOULD use the CTag conditional requests when doing bulk requests. When doing so, clients SHOULD use an "Expect: 100-Continue" request header and wait for the server response before sending the request body.
+ </t>
+ <t>
+ When a CTag conditional request is used by the client, and the request is successful, the server MUST return a <xref target='ctag-header'>CTag response header</xref> with the new CS:getctag value for the collection after changes have been applied.
+ </t>
+ <t>
+ For MKCALENDAR or extended MKCOL requests used to create calendar or address book collections, the client might need to ensure that no other calendars or address books have been created since its last synchronization. To do that, clients record the DAV:getctag property on the calendar or address book home collection and then use that value in a CTag conditional request, where the condition is on the home collection resource CTag value. When this occurs, and a successful response is returned by the server, the server MUST also include a Home-CTag response header containing the new value of the DAV:getctag property value on the home collection. In addition, the server MUST also include a CTag response header containing the initial value of the DAV:getctag property on the newly created calendar or address book collection.
+ </t>
+ <section title="Example: CTag Pre-condition Failure">
+ <t>
+ In this example the client is attempting to create two new address book object resources in an address book, using CTag-based pre-condition header. The pre-condition fails.
+ </t>
+
+ <figure>
+ <preamble>
+ >> Request <<
+ </preamble>
+ <artwork><![CDATA[
+POST /home/cyrus/addressbook/ HTTP/1.1
+Host: addressbook.example.com
+X-MobileMe-DAV-Options:return-changed-data
+If: (<http://me.com/_namespace/ctag/8054C6D4-C5C5-4ED9-A312-4E56753>)
+Content-Type: text/vcard; charset="utf-8"
+Content-Length: xxx
+
+BEGIN:VCARD
+VERSION:3.0
+NICKNAME:me
+UID:addr1 at example.com
+FN:Cyrus Daboo
+EMAIL:cdaboo at example.com
+END:VCARD
+BEGIN:VCARD
+VERSION:3.0
+NICKNAME:eric
+UID:addr2 at example.com
+FN:Eric York
+END:VCARD
+]]></artwork>
+ </figure>
+ <figure>
+ <preamble>
+ >> Response <<
+ </preamble>
+ <artwork><![CDATA[
+HTTP/1.1 412 Precondition Failed
+Date: Sat, 27 Nov 2010 09:32:12 GMT
+Content-Type: application/xml; charset="utf-8"
+Content-Length: xxxx
+
+<?xml version="1.0" encoding="utf-8" ?>
+<D:error xmlns:D="DAV:"
+ xmlns:MM="http://me.com/_namespace/">
+ <MM:ctag-ok/>
+</D:error>
+]]></artwork>
+ </figure>
+ </section>
+ <section title="Example: CTag Pre-condition Success with Expect">
+ <t>TBD</t>
+ </section>
+ <section title="Example: CTag Pre-condition on MKCALENDAR">
+ <t>
+ In this example the client is attempting to create a new calendar collection using a pre-condition based on the DAV:getctag value of the parent home collection.
+ </t>
+
+ <figure>
+ <preamble>
+ >> Request <<
+ </preamble>
+ <artwork><![CDATA[
+MKCALENDAR /home/cyrus/calendar/newcal/ HTTP/1.1
+Host: calendar.example.com
+If: </home/cyrus/calendar/>
+ (<http://me.com/_namespace/ctag/8054C6D4-C5C5-4ED9-A312-4E5675309>)
+]]></artwork>
+ </figure>
+ <figure>
+ <preamble>
+ >> Response <<
+ </preamble>
+ <artwork><![CDATA[
+HTTP/1.1 201 CREATED
+Date: Sat, 27 Nov 2010 09:32:12 GMT
+Home-CTag: D964B55F-3585-4234-B2B0-3C55E81083BC
+CTag: A132E16A-4933-4E74-B914-6497CC0387BB
+]]></artwork>
+ </figure>
+ </section>
+ </section>
+ <section title='XML Element Definitions'>
+ <section anchor="MM:multiput" title="MM:multiput XML Element">
+ <t>
+ <list style="hanging">
+ <t hangText="Name:">multiput</t>
+ <t hangText="Namespace:">http://me.com/_namespace/</t>
+ <t hangText="Purpose:">The root element for a CRUD batch POST request.</t>
+ <t hangText="Description:">See <xref target="crud"/>.</t>
+ <t hangText="Definition:">
+ <figure>
+ <artwork><![CDATA[
+<!ELEMENT multiput (MM:resource)+>
+]]></artwork>
+ </figure>
+ </t>
+ </list>
+ </t>
+ </section>
+ <section anchor="MM:resource" title="MM:resource XML Element">
+ <t>
+ <list style="hanging">
+ <t hangText="Name:">resource</t>
+ <t hangText="Namespace:">http://me.com/_namespace/</t>
+ <t hangText="Purpose:">Identifies a resource for a CRUD operation</t>
+ <t hangText="Description:">See <xref target="crud"/>.</t>
+ <t hangText="Definition:">
+ <figure>
+ <artwork><![CDATA[
+<!ELEMENT resource (
+ (DAV:set) |
+ (DAV:href, MM:if-match?, DAV:set?, DAV:remove?) |
+ (DAV:href, MM:if-match?, MM:delete)
+ )+>
+
+<!-- First combination is for create, second for update
+ and third for delete -->
+]]></artwork>
+ </figure>
+ </t>
+ </list>
+ </t>
+ </section>
+ <section anchor="MM:if-match" title="MM:if-match XML Element">
+ <t>
+ <list style="hanging">
+ <t hangText="Name:">if-match</t>
+ <t hangText="Namespace:">http://me.com/_namespace/</t>
+ <t hangText="Purpose:">Used to specify ETag-based pre-condition option for a CRUD update or delete operation.</t>
+ <t hangText="Description:">See <xref target="crud"/>.</t>
+ <t hangText="Definition:">
+ <figure>
+ <artwork><![CDATA[
+<!ELEMENT if-match (DAV:getetag)>
+]]></artwork>
+ </figure>
+ </t>
+ </list>
+ </t>
+ </section>
+ <section anchor="MM:delete" title="MM:delete XML Element">
+ <t>
+ <list style="hanging">
+ <t hangText="Name:">delete</t>
+ <t hangText="Namespace:">http://me.com/_namespace/</t>
+ <t hangText="Purpose:">Used to specify a CRUD delete operation.</t>
+ <t hangText="Description:">See <xref target="crud"/>.</t>
+ <t hangText="Definition:">
+ <figure>
+ <artwork><![CDATA[
+<!ELEMENT delete EMPTY>
+]]></artwork>
+ </figure>
+ </t>
+ </list>
+ </t>
+ </section>
+ </section>
+ <section title='HTTP Header Definitions'>
+ <t>
+ This specification adds the following HTTP header definition.
+ </t>
+ <section title="X-MobileMe-DAV-Options Header">
+ <t>
+ The X-MobileMe-DAV-Options request header allows the client to communicate options related to a request to the server. The header contains a list of simple tokens or key/value pairs to control server behavior. The set of allowed tokens and keys are listed below.
+ </t>
+ <figure>
+ <artwork name="abnf-rfc2616"><![CDATA[
+X-MobileMe-DAV-Options = "X-MobileMe-DAV-Options" ":" 1#mme-options
+
+mme-options = mme-option ["=" (token | quoted-string)]
+
+mme-option = "return-changed-data" | token
+]]></artwork>
+ </figure>
+ <t>Example:
+ <figure>
+ <artwork><![CDATA[
+X-MobileMe-DAV-Options: return-changed-data, Test=True
+]]></artwork>
+ </figure>
+ </t>
+ </section>
+ <section title="CTag Header" anchor="ctag-header">
+ <t>
+ The CTag response header allows the server to return the value of the CS:getctag property on the collection targeted by the request after all changes from the request have been applied. Clients can use this to help with managing multiple batch requests making a set of changes on the server.
+ </t>
+ <figure>
+ <artwork name="abnf-rfc2616"><![CDATA[
+CTag = "CTag" ":" token
+]]></artwork>
+ </figure>
+ <t>Example:
+ <figure>
+ <artwork><![CDATA[
+CTag: 84C9ACB2-A7DE-4D5E-AA8A-2B1EA30F098A
+]]></artwork>
+ </figure>
+ </t>
+ </section>
+ <section title="Home-CTag Header" anchor="home-ctag-header">
+ <t>
+ The Home-CTag response header allows the server to return the value of the CS:getctag property on the home collection targeted by the request, or the home collection parent of the request target after all changes from the request have been applied. Clients can use this to help with managing multiple batch requests making a set of changes on the server.
+ </t>
+ <figure>
+ <artwork name="abnf-rfc2616"><![CDATA[
+HomeCTag = "Home-CTag" ":" token
+]]></artwork>
+ </figure>
+ <t>Example:
+ <figure>
+ <artwork><![CDATA[
+Home-CTag: 17883032-CBEF-4E21-BEE2-73D316063EDA
+]]></artwork>
+ </figure>
+ </t>
+ </section>
+ </section>
+
+ <section title='Security Considerations'>
+ <t>
+ TBD
+ </t>
+ </section>
+ <section title='IANA Considerations'>
+ <t>
+ This document does not require any actions on the part of IANA.
+ </t>
+ </section>
+ <section title='Acknowledgments'>
+ <t>
+ This specification is the result of discussions between Apple server and client teams.
+ </t>
+ </section>
+ </middle>
+ <back>
+ <references title='Normative References'>
+ &rfc2119;
+ &rfc2426;
+ &rfc4791;
+ &rfc4918;
+ &rfc5545;
+ &idCardDAV;
+ </references>
+<!--
+<references title='Informative References'>
+</references>
+-->
+ <section title='Change History'>
+ <t>Changes in -03:
+ <list style='numbers'>
+ <t>Changed MAY to SHOULD for Expect header.</t>
+ <t>max-size -> max-bytes.</t>
+ <t>Title changes.</t>
+ </list>
+ </t>
+ <t>Changes in -02:
+ <list style='numbers'>
+ <t>Home ctag behavior for MKCALENDAR/MKCOL.</t>
+ <t>Fixed syntax of If: examples which was missing (...)</t>
+ </list>
+ </t>
+ <t>Changes in -01:
+ <list style='numbers'>
+ <t>Switched to calendar server namespace for UID property.</t>
+ <t>Switched to http based namespace instead of urn.</t>
+ <t>Now returns empty DAV:href when error occurs on resource create.</t>
+ <t>Cleaned up error handling for both POST requests, including stating possibility of partial failures.</t>
+ <t>Indicated that clients can use Expect header.</t>
+ <t>Defined Ctag header and server requirement to return it.</t>
+ </list>
+ </t>
+ </section>
+ </back>
+</rfc>
Added: CalendarServer/branches/users/gaya/directorybacker/doc/calendarserver_manage_timezones.8
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/doc/calendarserver_manage_timezones.8 (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/doc/calendarserver_manage_timezones.8 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,107 @@
+.\"
+.\" Copyright (c) 2006-2013 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 May 11, 2013
+.Dt CALENDARSERVER_MANAGE_TIMEZONES 8
+.Os
+.Sh NAME
+.Nm calendarserver_manage_timezones
+.Nd Calendar Server timezone database management utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar file
+.Op Fl h
+.Op Fl x Ar file
+.Op Fl z Ar directory
+.Op Fl -tzvers Ar tzdb-version
+.Op Fl -url Ar url
+.Op Fl -refresh
+.Op Fl -refreshpkg
+.Op Fl -create
+.Op Fl -update
+.Op Fl -list
+.Op Fl -changed Ar date
+.Op Fl -cache
+.Sh DESCRIPTION
+.Nm
+This utility will create, update, or list an XML timezone database
+summary XML file, or refresh timezone data from IANA, or refresh
+timezone data from another timezone server.
+.Pp
+It can also be used to update the server's own zoneinfo database
+from IANA.
+.Pp
+It should be run as a user with the same privileges as the Calendar
+Server itself, as it needs to read and write data that belongs to the
+server. If using the --refreshpkg option it will need to write to
+the actual python package data so will need to be run as root.
+.Pp
+Actions to perform are specified via the options below. Only one
+action is allowed.
+.Sh OPTIONS
+.Bl -tag -width flag
+.It Fl h
+Displays usage information
+.It Fl f Ar FILE
+Use the Calendar Server configuration specified in the given file.
+Defaults to /etc/caldavd/caldavd.plist.
+.It Fl x Ar FILE
+Update the timezone database XML file at the specified location.
+Defaults to timezones.xml in the zoneinfo directory.
+.It Fl z Ar DIRECTORY
+Path to a zoneinfo directory where timezone data is stored.
+Defaults to the configuration file's Data/zoneinfo directory.
+.It Fl -tzvers Ar version
+Name of IANA timezone data version to use (e.g., '2013a').
+.It Fl -url
+If the server is configured as a secondary timezone zone, use this URL
+as the URL of the secondary server to pull timezone data from.
+.El
+.Sh ACTIONS
+.Bl -tag -width flag
+.It Fl -refresh
+Update the zoneinfo data from the IANA registry.
+.It Fl -refreshpkg
+Update the server's zoneinfo package data from the IANA registry.
+This updates twistedcaldav.zoneinfo and should only be used by
+server developers wishing to update the server repository.
+.It Fl -create
+Create a new timezone database XML file based on the timezone data
+currently in the zoneinfo directory.
+.It Fl -update
+Update the timezone database XML file based on the timezone data
+currently in the zoneinfo directory.
+.It Fl -list
+List the timezones specified in the timezone database XML file.
+.It Fl -changed Ar date
+List the timezones in the timezone database XML file that have changed
+since the specified date value (YYYYMMHH).
+.It Fl -cache
+Update the server's timezone database by pulling data from a primary
+timezone server.
+.El
+.Sh EXAMPLES
+Update the server's timezone data from the latest IANA data:
+.Pp
+.Dl "calendarserver_manage_timezones --refesh"
+.Pp
+.Sh FILES
+.Bl -tag -width flag
+.It /etc/caldavd/caldavd.plist
+The Calendar Server configuration file.
+.El
+.Sh SEE ALSO
+.Xr caldavd 8
Modified: CalendarServer/branches/users/gaya/directorybacker/lib-patches/cx_Oracle/nclob-fixes-and-prefetch.patch
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/lib-patches/cx_Oracle/nclob-fixes-and-prefetch.patch 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/lib-patches/cx_Oracle/nclob-fixes-and-prefetch.patch 2013-10-03 22:51:14 UTC (rev 11787)
@@ -2,10 +2,11 @@
===================================================================
--- Connection.c 2011-03-19 16:05:30.000000000 -0700
+++ Connection.c 2012-08-01 09:22:17.000000000 -0700
-@@ -713,6 +713,19 @@
+@@ -713,6 +713,21 @@
if (newPasswordObj)
return Connection_ChangePassword(self, self->password, newPasswordObj);
++#ifdef OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE
+ /* set lob prefetch attribute to session */
+ ub4 default_lobprefetch_size = 4096; /* Set default size to 4K */
+ status = OCIAttrSet (self->sessionHandle, (ub4) OCI_HTYPE_SESSION,
@@ -19,6 +20,7 @@
+ return -1;
+ }
+
++#endif
// begin the session
Py_BEGIN_ALLOW_THREADS
status = OCISessionBegin(self->handle, self->environment->errorHandle,
Added: CalendarServer/branches/users/gaya/directorybacker/support/gendocs
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/support/gendocs (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/support/gendocs 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+##
+# Copyright (c) 2005-2013 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.
+##
+
+set -e
+set -u
+
+wd="$(cd "$(dirname "$0")/.." && pwd -P)";
+
+docroot="${wd}/data/doc";
+
+. "${wd}/support/build.sh";
+
+do_setup="false";
+ do_get="false";
+
+dependencies;
+
+##
+# Generate documentation
+##
+
+mkdir -p "${docroot}";
+
+set - -; shift; # Not sure how else to clear "$@"...
+
+for package in twisted pycalendar; do
+ package_dir="$(dirname "$(python -c "import ${package}; print ${package}.__file__")")";
+ set - "$@" "--add-package=${package_dir}";
+done;
+
+for package in twext txdav twistedcaldav calendarserver; do
+ set - "$@" "${package}";
+done;
+
+echo "Generating TwistedCalDAV documentation...";
+pydoctor \
+ --project-name="CalendarServer" \
+ --project-url="http://www.calendarserver.org/" \
+ --resolve-aliases \
+ --make-html --html-output="${docroot}/apidocs" \
+ "$@";
+
+# --html-viewsource-base="http://trac.calendarserver.org/browser/CalendarServer/trunk" \
+# --html-use-splitlinks \
+# --html-use-sorttable \
+# --html-shorten-lists \
Added: CalendarServer/branches/users/gaya/directorybacker/twext/python/launchd.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twext/python/launchd.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/twext/python/launchd.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,294 @@
+# -*- test-case-name: twext.python.test.test_launchd -*-
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Bindings for launchd check-in API.
+
+ at see: U{SampleD.c
+ <http://developer.apple.com/library/mac/#samplecode/SampleD/>}
+
+ at var ffi: a L{cffi.FFI} instance wrapping the functions exposed by C{launch.h}.
+
+ at var lib: a L{cffi} "U{dynamic library object
+ <http://cffi.readthedocs.org/en/release-0.6/#the-verification-step>}"
+ wrapping the functions exposed by C{launch.h}.
+
+ at var constants: Select C{LAUNCH_*} constants from C{launch.h}, exposed as plain
+ Python values. Note that this is not a complete wrapping, but as the
+ header file suggests, these APIs are only for use during check-in.
+"""
+
+from __future__ import print_function
+
+from cffi import FFI, VerificationError
+
+ffi = FFI()
+
+ffi.cdef("""
+
+static const char* LAUNCH_KEY_CHECKIN;
+static const char* LAUNCH_JOBKEY_LABEL;
+static const char* LAUNCH_JOBKEY_SOCKETS;
+
+typedef enum {
+ LAUNCH_DATA_DICTIONARY = 1,
+ LAUNCH_DATA_ARRAY,
+ LAUNCH_DATA_FD,
+ LAUNCH_DATA_INTEGER,
+ LAUNCH_DATA_REAL,
+ LAUNCH_DATA_BOOL,
+ LAUNCH_DATA_STRING,
+ LAUNCH_DATA_OPAQUE,
+ LAUNCH_DATA_ERRNO,
+ LAUNCH_DATA_MACHPORT,
+} launch_data_type_t;
+
+typedef struct _launch_data *launch_data_t;
+
+bool launch_data_dict_insert(launch_data_t, const launch_data_t, const char *);
+
+launch_data_t launch_data_alloc(launch_data_type_t);
+launch_data_t launch_data_new_string(const char *);
+launch_data_t launch_data_new_integer(long long);
+launch_data_t launch_data_new_fd(int);
+launch_data_t launch_data_new_bool(bool);
+launch_data_t launch_data_new_real(double);
+launch_data_t launch_msg(const launch_data_t);
+
+launch_data_type_t launch_data_get_type(const launch_data_t);
+
+launch_data_t launch_data_dict_lookup(const launch_data_t, const char *);
+size_t launch_data_dict_get_count(const launch_data_t);
+long long launch_data_get_integer(const launch_data_t);
+void launch_data_dict_iterate(
+ const launch_data_t, void (*)(const launch_data_t, const char *, void *),
+ void *);
+
+int launch_data_get_fd(const launch_data_t);
+bool launch_data_get_bool(const launch_data_t);
+const char * launch_data_get_string(const launch_data_t);
+double launch_data_get_real(const launch_data_t);
+
+size_t launch_data_array_get_count(const launch_data_t);
+launch_data_t launch_data_array_get_index(const launch_data_t, size_t);
+bool launch_data_array_set_index(launch_data_t, const launch_data_t, size_t);
+
+void launch_data_free(launch_data_t);
+""")
+
+try:
+ lib = ffi.verify("""
+ #include <launch.h>
+ """,
+ tag=__name__.replace(".", "_"))
+except VerificationError as ve:
+ raise ImportError(ve)
+
+
+
+class _LaunchArray(object):
+ def __init__(self, launchdata):
+ self.launchdata = launchdata
+
+
+ def __len__(self):
+ return lib.launch_data_array_get_count(self.launchdata)
+
+
+ def __getitem__(self, index):
+ if index >= len(self):
+ raise IndexError(index)
+ return _launchify(
+ lib.launch_data_array_get_index(self.launchdata, index)
+ )
+
+
+
+class _LaunchDictionary(object):
+ def __init__(self, launchdata):
+ self.launchdata = launchdata
+
+
+ def keys(self):
+ """
+ Return keys in the dictionary.
+ """
+ keys = []
+ @ffi.callback("void (*)(const launch_data_t, const char *, void *)")
+ def icb(v, k, n):
+ keys.append(ffi.string(k))
+ lib.launch_data_dict_iterate(self.launchdata, icb, ffi.NULL)
+ return keys
+
+
+ def values(self):
+ """
+ Return values in the dictionary.
+ """
+ values = []
+ @ffi.callback("void (*)(const launch_data_t, const char *, void *)")
+ def icb(v, k, n):
+ values.append(_launchify(v))
+ lib.launch_data_dict_iterate(self.launchdata, icb, ffi.NULL)
+ return values
+
+
+ def items(self):
+ """
+ Return items in the dictionary.
+ """
+ values = []
+ @ffi.callback("void (*)(const launch_data_t, const char *, void *)")
+ def icb(v, k, n):
+ values.append((ffi.string(k), _launchify(v)))
+ lib.launch_data_dict_iterate(self.launchdata, icb, ffi.NULL)
+ return values
+
+
+ def __getitem__(self, key):
+ launchvalue = lib.launch_data_dict_lookup(self.launchdata, key)
+ try:
+ return _launchify(launchvalue)
+ except LaunchErrno:
+ raise KeyError(key)
+
+
+ def __len__(self):
+ return lib.launch_data_dict_get_count(self.launchdata)
+
+
+
+def plainPython(x):
+ """
+ Convert a launchd python-like data structure into regular Python
+ dictionaries and lists.
+ """
+ if isinstance(x, _LaunchDictionary):
+ result = {}
+ for k, v in x.items():
+ result[k] = plainPython(v)
+ return result
+ elif isinstance(x, _LaunchArray):
+ return map(plainPython, x)
+ else:
+ return x
+
+
+
+
+class LaunchErrno(Exception):
+ """
+ Error from launchd.
+ """
+
+
+
+def _launchify(launchvalue):
+ """
+ Convert a ctypes value wrapping a C{_launch_data} structure into the
+ relevant Python object (integer, bytes, L{_LaunchDictionary},
+ L{_LaunchArray}).
+ """
+ if launchvalue == ffi.NULL:
+ return None
+ dtype = lib.launch_data_get_type(launchvalue)
+
+ if dtype == lib.LAUNCH_DATA_DICTIONARY:
+ return _LaunchDictionary(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_ARRAY:
+ return _LaunchArray(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_FD:
+ return lib.launch_data_get_fd(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_INTEGER:
+ return lib.launch_data_get_integer(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_REAL:
+ return lib.launch_data_get_real(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_BOOL:
+ return lib.launch_data_get_bool(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_STRING:
+ cvalue = lib.launch_data_get_string(launchvalue)
+ if cvalue == ffi.NULL:
+ return None
+ return ffi.string(cvalue)
+ elif dtype == lib.LAUNCH_DATA_OPAQUE:
+ return launchvalue
+ elif dtype == lib.LAUNCH_DATA_ERRNO:
+ raise LaunchErrno(launchvalue)
+ elif dtype == lib.LAUNCH_DATA_MACHPORT:
+ return lib.launch_data_get_machport(launchvalue)
+ else:
+ raise TypeError("Unknown Launch Data Type", dtype)
+
+
+
+def checkin():
+ """
+ Perform a launchd checkin, returning a Pythonic wrapped data structure
+ representing the retrieved check-in plist.
+
+ @return: a C{dict}-like object.
+ """
+ lkey = lib.launch_data_new_string(lib.LAUNCH_KEY_CHECKIN)
+ msgr = lib.launch_msg(lkey)
+ return _launchify(msgr)
+
+
+
+def _managed(obj):
+ """
+ Automatically free an object that was allocated with a launch_data_*
+ function, or raise L{MemoryError} if it's C{NULL}.
+ """
+ if obj == ffi.NULL:
+ raise MemoryError()
+ else:
+ return ffi.gc(obj, lib.launch_data_free)
+
+
+
+class _Strings(object):
+ """
+ Expose constants as Python-readable values rather than wrapped ctypes
+ pointers.
+ """
+ def __getattribute__(self, name):
+ value = getattr(lib, name)
+ if isinstance(value, int):
+ return value
+ if ffi.typeof(value) != ffi.typeof("char *"):
+ raise AttributeError("no such constant", name)
+ return ffi.string(value)
+
+constants = _Strings()
+
+
+
+def getLaunchDSocketFDs():
+ """
+ Perform checkin via L{checkin} and return just a dictionary mapping the
+ sockets to file descriptors.
+ """
+ return plainPython(checkin()[constants.LAUNCH_JOBKEY_SOCKETS])
+
+
+
+__all__ = [
+ 'checkin',
+ 'lib',
+ 'ffi',
+ 'plainPython',
+]
Added: CalendarServer/branches/users/gaya/directorybacker/twext/python/test/test_launchd.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twext/python/test/test_launchd.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/twext/python/test/test_launchd.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,397 @@
+##
+# Copyright (c) 2013 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 L{twext.python.launchd}.
+"""
+
+import sys, os, plistlib, socket, json
+
+if __name__ == '__main__':
+ # This module is loaded as a launchd job by test-cases below; the following
+ # code looks up an appropriate function to run.
+ testID = sys.argv[1]
+ a, b = testID.rsplit(".", 1)
+ from twisted.python.reflect import namedAny
+ try:
+ namedAny(".".join([a, b.replace("test_", "job_")]))()
+ finally:
+ sys.stdout.flush()
+ sys.stderr.flush()
+ skt = socket.socket()
+ skt.connect(("127.0.0.1", int(os.environ["TESTING_PORT"])))
+ sys.exit(0)
+
+
+
+try:
+ from twext.python.launchd import (
+ lib, ffi, _LaunchDictionary, _LaunchArray, _managed, constants,
+ plainPython, checkin, _launchify, getLaunchDSocketFDs
+ )
+except ImportError:
+ skip = "LaunchD not available."
+else:
+ skip = False
+
+from twisted.trial.unittest import TestCase
+from twisted.python.filepath import FilePath
+
+
+class LaunchDataStructures(TestCase):
+ """
+ Tests for L{_launchify} converting data structures from launchd's internals
+ to Python objects.
+ """
+
+ def test_fd(self):
+ """
+ Test converting a launchd FD to an integer.
+ """
+ fd = _managed(lib.launch_data_new_fd(2))
+ self.assertEquals(_launchify(fd), 2)
+
+
+ def test_bool(self):
+ """
+ Test converting a launchd bool to a Python bool.
+ """
+ t = _managed(lib.launch_data_new_bool(True))
+ f = _managed(lib.launch_data_new_bool(False))
+ self.assertEqual(_launchify(t), True)
+ self.assertEqual(_launchify(f), False)
+
+
+ def test_real(self):
+ """
+ Test converting a launchd real to a Python float.
+ """
+ notQuitePi = _managed(lib.launch_data_new_real(3.14158))
+ self.assertEqual(_launchify(notQuitePi), 3.14158)
+
+
+
+class DictionaryTests(TestCase):
+ """
+ Tests for L{_LaunchDictionary}
+ """
+
+ def setUp(self):
+ """
+ Assemble a test dictionary.
+ """
+ self.testDict = _managed(
+ lib.launch_data_alloc(lib.LAUNCH_DATA_DICTIONARY)
+ )
+ key1 = ffi.new("char[]", "alpha")
+ val1 = lib.launch_data_new_string("alpha-value")
+ key2 = ffi.new("char[]", "beta")
+ val2 = lib.launch_data_new_string("beta-value")
+ key3 = ffi.new("char[]", "gamma")
+ val3 = lib.launch_data_new_integer(3)
+ lib.launch_data_dict_insert(self.testDict, val1, key1)
+ lib.launch_data_dict_insert(self.testDict, val2, key2)
+ lib.launch_data_dict_insert(self.testDict, val3, key3)
+ self.assertEquals(lib.launch_data_dict_get_count(self.testDict), 3)
+
+
+ def test_len(self):
+ """
+ C{len(_LaunchDictionary())} returns the number of keys in the
+ dictionary.
+ """
+ self.assertEquals(len(_LaunchDictionary(self.testDict)), 3)
+
+
+ def test_keys(self):
+ """
+ L{_LaunchDictionary.keys} returns keys present in a C{launch_data_dict}.
+ """
+ dictionary = _LaunchDictionary(self.testDict)
+ self.assertEquals(set(dictionary.keys()),
+ set([b"alpha", b"beta", b"gamma"]))
+
+
+ def test_values(self):
+ """
+ L{_LaunchDictionary.values} returns keys present in a
+ C{launch_data_dict}.
+ """
+ dictionary = _LaunchDictionary(self.testDict)
+ self.assertEquals(set(dictionary.values()),
+ set([b"alpha-value", b"beta-value", 3]))
+
+
+ def test_items(self):
+ """
+ L{_LaunchDictionary.items} returns all (key, value) tuples present in a
+ C{launch_data_dict}.
+ """
+ dictionary = _LaunchDictionary(self.testDict)
+ self.assertEquals(set(dictionary.items()),
+ set([(b"alpha", b"alpha-value"),
+ (b"beta", b"beta-value"), (b"gamma", 3)]))
+
+
+ def test_plainPython(self):
+ """
+ L{plainPython} will convert a L{_LaunchDictionary} into a Python
+ dictionary.
+ """
+ self.assertEquals({b"alpha": b"alpha-value", b"beta": b"beta-value",
+ b"gamma": 3},
+ plainPython(_LaunchDictionary(self.testDict)))
+
+
+ def test_plainPythonNested(self):
+ """
+ L{plainPython} will convert a L{_LaunchDictionary} containing another
+ L{_LaunchDictionary} into a nested Python dictionary.
+ """
+ otherDict = lib.launch_data_alloc(lib.LAUNCH_DATA_DICTIONARY)
+ lib.launch_data_dict_insert(otherDict,
+ lib.launch_data_new_string("bar"), "foo")
+ lib.launch_data_dict_insert(self.testDict, otherDict, "delta")
+ self.assertEquals({b"alpha": b"alpha-value", b"beta": b"beta-value",
+ b"gamma": 3, b"delta": {b"foo": b"bar"}},
+ plainPython(_LaunchDictionary(self.testDict)))
+
+
+class ArrayTests(TestCase):
+ """
+ Tests for L{_LaunchArray}
+ """
+
+ def setUp(self):
+ """
+ Assemble a test array.
+ """
+ self.testArray = ffi.gc(
+ lib.launch_data_alloc(lib.LAUNCH_DATA_ARRAY),
+ lib.launch_data_free
+ )
+
+ lib.launch_data_array_set_index(
+ self.testArray, lib.launch_data_new_string("test-string-1"), 0
+ )
+ lib.launch_data_array_set_index(
+ self.testArray, lib.launch_data_new_string("another string."), 1
+ )
+ lib.launch_data_array_set_index(
+ self.testArray, lib.launch_data_new_integer(4321), 2
+ )
+
+
+ def test_length(self):
+ """
+ C{len(_LaunchArray(...))} returns the number of elements in the array.
+ """
+ self.assertEquals(len(_LaunchArray(self.testArray)), 3)
+
+
+ def test_indexing(self):
+ """
+ C{_LaunchArray(...)[n]} returns the n'th element in the array.
+ """
+ array = _LaunchArray(self.testArray)
+ self.assertEquals(array[0], b"test-string-1")
+ self.assertEquals(array[1], b"another string.")
+ self.assertEquals(array[2], 4321)
+
+
+ def test_indexTooBig(self):
+ """
+ C{_LaunchArray(...)[n]}, where C{n} is greater than the length of the
+ array, raises an L{IndexError}.
+ """
+ array = _LaunchArray(self.testArray)
+ self.assertRaises(IndexError, lambda: array[3])
+
+
+ def test_iterating(self):
+ """
+ Iterating over a C{_LaunchArray} returns each item in sequence.
+ """
+ array = _LaunchArray(self.testArray)
+ i = iter(array)
+ self.assertEquals(i.next(), b"test-string-1")
+ self.assertEquals(i.next(), b"another string.")
+ self.assertEquals(i.next(), 4321)
+ self.assertRaises(StopIteration, i.next)
+
+
+ def test_plainPython(self):
+ """
+ L{plainPython} converts a L{_LaunchArray} into a Python list.
+ """
+ array = _LaunchArray(self.testArray)
+ self.assertEquals(plainPython(array),
+ [b"test-string-1", b"another string.", 4321])
+
+
+ def test_plainPythonNested(self):
+ """
+ L{plainPython} converts a L{_LaunchArray} containing another
+ L{_LaunchArray} into a Python list.
+ """
+ sub = lib.launch_data_alloc(lib.LAUNCH_DATA_ARRAY)
+ lib.launch_data_array_set_index(sub, lib.launch_data_new_integer(7), 0)
+ lib.launch_data_array_set_index(self.testArray, sub, 3)
+ array = _LaunchArray(self.testArray)
+ self.assertEqual(plainPython(array), [b"test-string-1",
+ b"another string.", 4321, [7]])
+
+
+
+class SimpleStringConstants(TestCase):
+ """
+ Tests for bytestring-constants wrapping.
+ """
+
+ def test_constant(self):
+ """
+ C{launchd.constants.LAUNCH_*} will return a bytes object corresponding
+ to a constant.
+ """
+ self.assertEqual(constants.LAUNCH_JOBKEY_SOCKETS,
+ b"Sockets")
+ self.assertRaises(AttributeError, getattr, constants,
+ "launch_data_alloc")
+ self.assertEquals(constants.LAUNCH_DATA_ARRAY, 2)
+
+
+
+class CheckInTests(TestCase):
+ """
+ Integration tests making sure that actual checkin with launchd results in
+ the expected values.
+ """
+
+ def setUp(self):
+ fp = FilePath(self.mktemp())
+ fp.makedirs()
+ from twisted.internet.protocol import Protocol, Factory
+ from twisted.internet import reactor, defer
+ d = defer.Deferred()
+ class JustLetMeMoveOn(Protocol):
+ def connectionMade(self):
+ d.callback(None)
+ self.transport.abortConnection()
+ f = Factory()
+ f.protocol = JustLetMeMoveOn
+ port = reactor.listenTCP(0, f, interface="127.0.0.1")
+ @self.addCleanup
+ def goodbyePort():
+ return port.stopListening()
+ env = dict(os.environ)
+ env["TESTING_PORT"] = repr(port.getHost().port)
+ self.stdout = fp.child("stdout.txt")
+ self.stderr = fp.child("stderr.txt")
+ self.launchLabel = ("org.calendarserver.UNIT-TESTS." +
+ str(os.getpid()) + "." + self.id())
+ plist = {
+ "Label": self.launchLabel,
+ "ProgramArguments": [sys.executable, "-m", __name__, self.id()],
+ "EnvironmentVariables": env,
+ "KeepAlive": False,
+ "StandardOutPath": self.stdout.path,
+ "StandardErrorPath": self.stderr.path,
+ "Sockets": {
+ "Awesome": [{"SecureSocketWithKey": "GeneratedSocket"}]
+ },
+ "RunAtLoad": True,
+ }
+ self.job = fp.child("job.plist")
+ self.job.setContent(plistlib.writePlistToString(plist))
+ os.spawnlp(os.P_WAIT, "launchctl", "launchctl", "load", self.job.path)
+ return d
+
+
+ @staticmethod
+ def job_test():
+ """
+ Do something observable in a subprocess.
+ """
+ sys.stdout.write("Sample Value.")
+ sys.stdout.flush()
+
+
+ def test_test(self):
+ """
+ Since this test framework is somewhat finicky, let's just make sure
+ that a test can complete.
+ """
+ self.assertEquals("Sample Value.", self.stdout.getContent())
+
+
+ @staticmethod
+ def job_checkin():
+ """
+ Check in in the subprocess.
+ """
+ sys.stdout.write(json.dumps(plainPython(checkin())))
+
+
+ def test_checkin(self):
+ """
+ L{checkin} performs launchd checkin and returns a launchd data
+ structure.
+ """
+ d = json.loads(self.stdout.getContent())
+ self.assertEqual(d[constants.LAUNCH_JOBKEY_LABEL], self.launchLabel)
+ self.assertIsInstance(d, dict)
+ sockets = d[constants.LAUNCH_JOBKEY_SOCKETS]
+ self.assertEquals(len(sockets), 1)
+ self.assertEqual(['Awesome'], sockets.keys())
+ awesomeSocket = sockets['Awesome']
+ self.assertEqual(len(awesomeSocket), 1)
+ self.assertIsInstance(awesomeSocket[0], int)
+
+
+ @staticmethod
+ def job_getFDs():
+ """
+ Check-in via the high-level C{getLaunchDSocketFDs} API, that just gives
+ us listening FDs.
+ """
+ sys.stdout.write(json.dumps(getLaunchDSocketFDs()))
+
+
+ def test_getFDs(self):
+ """
+ L{getLaunchDSocketFDs} returns a Python dictionary mapping the names of
+ sockets specified in the property list to lists of integers
+ representing FDs.
+ """
+ sockets = json.loads(self.stdout.getContent())
+ self.assertEquals(len(sockets), 1)
+ self.assertEqual(['Awesome'], sockets.keys())
+ awesomeSocket = sockets['Awesome']
+ self.assertEqual(len(awesomeSocket), 1)
+ self.assertIsInstance(awesomeSocket[0], int)
+
+
+ def tearDown(self):
+ """
+ Un-load the launchd job and report any errors it encountered.
+ """
+ os.spawnlp(os.P_WAIT, "launchctl",
+ "launchctl", "unload", self.job.path)
+ err = self.stderr.getContent()
+ if 'Traceback' in err:
+ self.fail(err)
+
+
Added: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/directory/test/augments-normalization.xml
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/directory/test/augments-normalization.xml (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/directory/test/augments-normalization.xml 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2013 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>
+ <uid>aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa</uid>
+ <enable>true</enable>
+ </record>
+</augments>
Modified: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/scheduling_store/caldav/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/scheduling_store/caldav/resource.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/scheduling_store/caldav/resource.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -411,6 +411,17 @@
originator = (yield self.loadOriginatorFromRequestDetails(request))
recipients = self.loadRecipientsFromCalendarData(calendar)
+ # storeComponent needs to know who the auth'd user is for access control
+ # TODO: this needs to be done in a better way - ideally when the txn is created for the request,
+ # we should set a txn.authzid attribute.
+ authz = None
+ authz_principal = self.parent.currentPrincipal(request).children[0]
+ if isinstance(authz_principal, davxml.HRef):
+ principalURL = str(authz_principal)
+ if principalURL:
+ authz = (yield request.locateResource(principalURL))
+ self._associatedTransaction._authz_uid = authz.record.guid
+
# This is a local CALDAV scheduling operation.
scheduler = CalDAVScheduler(self._associatedTransaction, self.parent._newStoreHome.uid())
Modified: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Africa/Casablanca.ics
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Africa/Casablanca.ics 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Africa/Casablanca.ics 2013-10-03 22:51:14 UTC (rev 11787)
@@ -24,6 +24,7 @@
RDATE:20090601T000000
RDATE:20100502T000000
RDATE:20110403T000000
+RDATE:20120820T020000
RDATE:20130810T020000
RDATE:20140729T020000
RDATE:20150718T020000
@@ -52,6 +53,7 @@
RDATE:20090821T000000
RDATE:20100808T000000
RDATE:20110731T000000
+RDATE:20120720T030000
RDATE:20130707T030000
RDATE:20140629T030000
RDATE:20150618T030000
Added: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Khandyga.ics
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Khandyga.ics (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Khandyga.ics 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,136 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+BEGIN:VTIMEZONE
+TZID:Asia/Khandyga
+X-LIC-LOCATION:Asia/Khandyga
+BEGIN:STANDARD
+DTSTART:19191215T000000
+RDATE:19191215T000000
+TZNAME:YAKT
+TZOFFSETFROM:+090213
+TZOFFSETTO:+0800
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19300621T000000
+RDATE:19300621T000000
+RDATE:19920119T020000
+TZNAME:YAKST
+TZOFFSETFROM:+0800
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19810401T000000
+RRULE:FREQ=YEARLY;UNTIL=19840331T150000Z;BYMONTH=4
+TZNAME:YAKST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+1000
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19811001T000000
+RRULE:FREQ=YEARLY;UNTIL=19830930T140000Z;BYMONTH=10
+TZNAME:YAKT
+TZOFFSETFROM:+1000
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19840930T030000
+RRULE:FREQ=YEARLY;UNTIL=19900929T170000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:YAKT
+TZOFFSETFROM:+1000
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19850331T020000
+RRULE:FREQ=YEARLY;UNTIL=19900324T170000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:YAKST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+1000
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19910331T020000
+RDATE:19910331T020000
+TZNAME:YAKST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19910929T030000
+RDATE:19910929T030000
+TZNAME:YAKT
+TZOFFSETFROM:+0900
+TZOFFSETTO:+0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19920328T230000
+RDATE:19920328T230000
+TZNAME:YAKST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+1000
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19920926T230000
+RDATE:19920926T230000
+TZNAME:YAKT
+TZOFFSETFROM:+1000
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19930328T020000
+RRULE:FREQ=YEARLY;UNTIL=20030329T170000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:YAKST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+1000
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19930926T030000
+RRULE:FREQ=YEARLY;UNTIL=19950923T170000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:YAKT
+TZOFFSETFROM:+1000
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;UNTIL=20031025T170000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:YAKT
+TZOFFSETFROM:+1000
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:20040101T000000
+RDATE:20040101T000000
+TZNAME:VLAST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+1000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20040328T020000
+RRULE:FREQ=YEARLY;UNTIL=20100327T160000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:VLAST
+TZOFFSETFROM:+1000
+TZOFFSETTO:+1100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20041031T030000
+RRULE:FREQ=YEARLY;UNTIL=20101030T160000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:VLAT
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1000
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:20110327T020000
+RDATE:20110327T020000
+TZNAME:VLAT
+TZOFFSETFROM:+1000
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:20110913T000000
+RDATE:20110913T000000
+TZNAME:YAKT
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1000
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
Added: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Ust-Nera.ics
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Ust-Nera.ics (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Asia/Ust-Nera.ics 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,128 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+BEGIN:VTIMEZONE
+TZID:Asia/Ust-Nera
+X-LIC-LOCATION:Asia/Ust-Nera
+BEGIN:STANDARD
+DTSTART:19191215T000000
+RDATE:19191215T000000
+TZNAME:YAKT
+TZOFFSETFROM:+093254
+TZOFFSETTO:+0800
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19300621T000000
+RDATE:19300621T000000
+TZNAME:YAKT
+TZOFFSETFROM:+0800
+TZOFFSETTO:+0900
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19810401T000000
+RDATE:19810401T000000
+TZNAME:MAGST
+TZOFFSETFROM:+0900
+TZOFFSETTO:+1200
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19811001T000000
+RRULE:FREQ=YEARLY;UNTIL=19830930T120000Z;BYMONTH=10
+TZNAME:MAGT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19820401T000000
+RRULE:FREQ=YEARLY;UNTIL=19840331T130000Z;BYMONTH=4
+TZNAME:MAGST
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19840930T030000
+RRULE:FREQ=YEARLY;UNTIL=19900929T150000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:MAGT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19850331T020000
+RRULE:FREQ=YEARLY;UNTIL=19900324T150000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:MAGST
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19910331T020000
+RDATE:19910331T020000
+TZNAME:MAGST
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19910929T030000
+RDATE:19910929T030000
+TZNAME:MAGT
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1000
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19920119T020000
+RDATE:19920119T020000
+TZNAME:MAGST
+TZOFFSETFROM:+1000
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19920328T230000
+RDATE:19920328T230000
+TZNAME:MAGST
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19920926T230000
+RDATE:19920926T230000
+TZNAME:MAGT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19930328T020000
+RRULE:FREQ=YEARLY;UNTIL=20100327T150000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:MAGST
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19930926T030000
+RRULE:FREQ=YEARLY;UNTIL=19950923T150000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:MAGT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;UNTIL=20101030T150000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:MAGT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1100
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:20110327T020000
+RDATE:20110327T020000
+TZNAME:MAGT
+TZOFFSETFROM:+1100
+TZOFFSETTO:+1200
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:20110913T000000
+RDATE:20110913T000000
+TZNAME:VLAT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1100
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
Added: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Europe/Busingen.ics
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Europe/Busingen.ics (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Europe/Busingen.ics 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,65 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+BEGIN:VTIMEZONE
+TZID:Europe/Busingen
+X-LIC-LOCATION:Europe/Busingen
+BEGIN:STANDARD
+DTSTART:18530716T000000
+RDATE:18530716T000000
+TZNAME:BMT
+TZOFFSETFROM:+003408
+TZOFFSETTO:+002946
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:18940601T000000
+RDATE:18940601T000000
+TZNAME:CEST
+TZOFFSETFROM:+002946
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19410505T010000
+RRULE:FREQ=YEARLY;UNTIL=19420504T000000Z;BYDAY=1MO;BYMONTH=5
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19411006T020000
+RRULE:FREQ=YEARLY;UNTIL=19421005T000000Z;BYDAY=1MO;BYMONTH=10
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19810101T000000
+RDATE:19810101T000000
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19810329T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19810927T030000
+RRULE:FREQ=YEARLY;UNTIL=19950924T010000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
Modified: CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Pacific/Fakaofo.ics
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Pacific/Fakaofo.ics 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/twistedcaldav/zoneinfo/Pacific/Fakaofo.ics 2013-10-03 22:51:14 UTC (rev 11787)
@@ -10,14 +10,14 @@
RDATE:19010101T000000
TZNAME:TKT
TZOFFSETFROM:-112456
-TZOFFSETTO:-1000
+TZOFFSETTO:-1100
END:STANDARD
BEGIN:STANDARD
DTSTART:20111230T000000
RDATE:20111230T000000
TZNAME:TKT
-TZOFFSETFROM:-1000
-TZOFFSETTO:+1400
+TZOFFSETFROM:-1100
+TZOFFSETTO:+1300
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/schedule.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/schedule.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/schedule.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,218 @@
+# -*- test-case-name: txdav.caldav.datastore.test.test_scheduling -*-
+##
+# Copyright (c) 2010-2013 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 zope.interface.declarations import implements
+from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject, \
+ ICalendarTransaction, ICalendarStore
+
+from twisted.python.util import FancyEqMixin
+from twisted.python.components import proxyForInterface
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+
+
+class ImplicitTransaction(
+ proxyForInterface(ICalendarTransaction,
+ originalAttribute="_transaction")):
+ """
+ Wrapper around an L{ICalendarStoreTransaction}.
+ """
+
+ def __init__(self, transaction):
+ """
+ Initialize an L{ImplicitTransaction}.
+
+ @type transaction: L{ICalendarStoreTransaction}
+ """
+ self._transaction = transaction
+
+
+ @inlineCallbacks
+ def calendarHomeWithUID(self, uid, create=False):
+ # FIXME: 'create' flag
+ newHome = yield super(ImplicitTransaction, self
+ ).calendarHomeWithUID(uid, create)
+# return ImplicitCalendarHome(newHome, self)
+ if newHome is None:
+ returnValue(None)
+ else:
+ # FIXME: relay transaction
+ returnValue(ImplicitCalendarHome(newHome, None))
+
+
+
+class ImplicitCalendarHome(
+ proxyForInterface(ICalendarHome, "_calendarHome")
+ ):
+
+ implements(ICalendarHome)
+
+ def __init__(self, calendarHome, transaction):
+ """
+ Initialize L{ImplicitCalendarHome} with an underlying
+ calendar home and L{ImplicitTransaction}.
+ """
+ self._calendarHome = calendarHome
+ self._transaction = transaction
+
+
+# def properties(self):
+# # FIXME: wrap?
+# return self._calendarHome.properties()
+
+ @inlineCallbacks
+ def calendars(self):
+ superCalendars = (yield super(ImplicitCalendarHome, self).calendars())
+ wrapped = []
+ for calendar in superCalendars:
+ wrapped.append(ImplicitCalendar(self, calendar))
+ returnValue(wrapped)
+
+
+ @inlineCallbacks
+ def loadCalendars(self):
+ superCalendars = (yield super(ImplicitCalendarHome, self).loadCalendars())
+ wrapped = []
+ for calendar in superCalendars:
+ wrapped.append(ImplicitCalendar(self, calendar))
+ returnValue(wrapped)
+
+
+ def createCalendarWithName(self, name):
+ self._calendarHome.createCalendarWithName(name)
+
+
+ def removeCalendarWithName(self, name):
+ self._calendarHome.removeCalendarWithName(name)
+
+
+ @inlineCallbacks
+ def calendarWithName(self, name):
+ calendar = yield self._calendarHome.calendarWithName(name)
+ if calendar is not None:
+ returnValue(ImplicitCalendar(self, calendar))
+ else:
+ returnValue(None)
+
+
+ def hasCalendarResourceUIDSomewhereElse(self, uid, ok_object, type):
+ return self._calendarHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object, type)
+
+
+ def getCalendarResourcesForUID(self, uid, allow_shared=False):
+ return self._calendarHome.getCalendarResourcesForUID(uid, allow_shared)
+
+
+
+class ImplicitCalendarObject(object):
+ implements(ICalendarObject)
+
+ def setComponent(self, component):
+ pass
+
+
+ def component(self):
+ pass
+
+
+ def uid(self):
+ pass
+
+
+ def componentType(self):
+ pass
+
+
+ def organizer(self):
+ pass
+
+
+ def properties(self):
+ pass
+
+
+
+class ImplicitCalendar(FancyEqMixin,
+ proxyForInterface(ICalendar, "_subCalendar")):
+
+ compareAttributes = (
+ "_subCalendar",
+ "_parentHome",
+ )
+
+ def __init__(self, parentHome, subCalendar):
+ self._parentHome = parentHome
+ self._subCalendar = subCalendar
+ self._supportedComponents = None
+
+# def ownerCalendarHome(self):
+# return self._parentHome
+# def calendarObjects(self):
+# # FIXME: wrap
+# return self._subCalendar.calendarObjects()
+# def calendarObjectWithUID(self, uid): ""
+# def createCalendarObjectWithName(self, name, component):
+# # FIXME: implement most of StoreCalendarObjectResource here!
+# self._subCalendar.createCalendarObjectWithName(name, component)
+# def syncToken(self): ""
+# def calendarObjectsInTimeRange(self, start, end, timeZone): ""
+# def calendarObjectsSinceToken(self, token): ""
+# def properties(self):
+# # FIXME: probably need to wrap this as well
+# return self._subCalendar.properties()
+#
+# def calendarObjectWithName(self, name):
+# #FIXME: wrap
+# return self._subCalendar.calendarObjectWithName(name)
+
+
+ def _createCalendarObjectWithNameInternal(self, name, component, internal_state, options=None):
+ return self.createCalendarObjectWithName(name, component, options)
+
+
+ def setSupportedComponents(self, supported_components):
+ """
+ Update the database column with the supported components. Technically this should only happen once
+ on collection creation, but for migration we may need to change after the fact - hence a separate api.
+ """
+ self._supportedComponents = supported_components
+
+
+ def getSupportedComponents(self):
+ return self._supportedComponents
+
+
+
+class ImplicitStore(proxyForInterface(ICalendarStore, "_calendarStore")):
+ """
+ This is a wrapper around an L{ICalendarStore} that implements implicit
+ scheduling.
+ """
+
+ def __init__(self, calendarStore):
+ """
+ Create an L{ImplicitStore} wrapped around another
+ L{ICalendarStore} provider.
+ """
+ self._calendarStore = calendarStore
+
+
+ def newTransaction(self, label="unlabeled"):
+ """
+ Wrap an underlying L{ITransaction}.
+ """
+ return ImplicitTransaction(
+ self._calendarStore.newTransaction(label))
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/delivery.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/delivery.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -25,18 +25,19 @@
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
+from txdav.base.propertystore.base import PropertyName
from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, \
PartitionedCalendarUser, OtherServerCalendarUser
from txdav.caldav.datastore.scheduling.delivery import DeliveryService
+from txdav.caldav.datastore.scheduling.freebusy import processAvailabilityFreeBusy, \
+ generateFreeBusyInfo, buildFreeBusyResult
from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
from txdav.caldav.datastore.scheduling.processing import ImplicitProcessor, ImplicitProcessorException
+from txdav.caldav.datastore.scheduling.utils import extractEmailDomain
+from txdav.caldav.icalendarstore import ComponentUpdateState
import hashlib
import uuid
-from txdav.base.propertystore.base import PropertyName
-from txdav.caldav.icalendarstore import ComponentUpdateState
-from txdav.caldav.datastore.scheduling.freebusy import processAvailabilityFreeBusy, \
- generateFreeBusyInfo, buildFreeBusyResult
"""
@@ -71,9 +72,8 @@
# Check for local address matches first
if cuaddr.startswith("mailto:") and config.Scheduling[cls.serviceType()]["EmailDomain"]:
- addr = cuaddr[7:].split("?")[0]
+ addrDomain = extractEmailDomain(cuaddr)
domain = config.Scheduling[cls.serviceType()]["EmailDomain"]
- _ignore_account, addrDomain = addr.split("@")
if addrDomain == domain:
return succeed(True)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/test/test_delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/test/test_delivery.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/caldav/test/test_delivery.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -39,3 +39,7 @@
self.assertFalse(result)
result = yield ScheduleViaCalDAV.matchCalendarUserAddress("mailto:user at xyzexample.com")
self.assertFalse(result)
+ result = yield ScheduleViaCalDAV.matchCalendarUserAddress("mailto:user at xyzexample.com?subject=foobar")
+ self.assertFalse(result)
+ result = yield ScheduleViaCalDAV.matchCalendarUserAddress("mailto:user")
+ self.assertFalse(result)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/cuaddress.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/cuaddress.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/cuaddress.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -17,6 +17,7 @@
from twext.python.log import Logger
from txdav.caldav.datastore.scheduling.delivery import DeliveryService
+from txdav.caldav.datastore.scheduling.utils import extractEmailDomain
__all__ = [
"LocalCalendarUser",
@@ -92,8 +93,7 @@
def extractDomain(self):
if self.cuaddr.startswith("mailto:"):
- splits = self.cuaddr[7:].split("?")
- self.domain = splits[0].split("@")[1]
+ self.domain = extractEmailDomain(self.cuaddr)
elif self.cuaddr.startswith("http://") or self.cuaddr.startswith("https://"):
splits = self.cuaddr.split(":")[1][2:].split("/")
self.domain = splits[0]
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/freebusy.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/freebusy.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/freebusy.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -136,8 +136,8 @@
organizer_uid = organizer_record.uid if organizer_record else ""
# Free busy is per-user
- useruid = calresource.viewerHome().uid()
- user_record = calresource.directoryService().recordWithUID(useruid)
+ attendee_uid = calresource.viewerHome().uid()
+ attendee_record = calresource.directoryService().recordWithUID(attendee_uid)
# Get the timezone property from the collection.
tz = calresource.getTimezone()
@@ -149,26 +149,34 @@
"resource": False,
}
do_event_details = False
- if event_details is not None and organizer_record is not None and user_record is not None:
+ if event_details is not None and organizer_record is not None and attendee_record is not None:
- # Check if organizer is attendee
- if organizer_uid == useruid:
+ # Get the principal of the authorized user which may be different from the organizer if a delegate of
+ # the organizer is making the request
+ authz_uid = organizer_uid
+ authz_record = organizer_record
+ if hasattr(calresource._txn, "_authz_uid") and calresource._txn._authz_uid != organizer_uid:
+ authz_uid = calresource._txn._authz_uid
+ authz_record = calresource.directoryService().recordWithUID(authz_uid)
+
+ # Check if attendee is also the organizer or the delegate doing the request
+ if attendee_uid in (organizer_uid, authz_uid):
do_event_details = True
rich_options["organizer"] = True
- # Check if organizer is a delegate of attendee
- proxy = (yield organizer_record.isProxyFor(user_record))
+ # Check if authorized user is a delegate of attendee
+ proxy = (yield authz_record.isProxyFor(attendee_record))
if config.Scheduling.Options.DelegeteRichFreeBusy and proxy:
do_event_details = True
rich_options["delegate"] = True
# Check if attendee is room or resource
- if config.Scheduling.Options.RoomResourceRichFreeBusy and user_record.getCUType() in ("RESOURCE", "ROOM",):
+ if config.Scheduling.Options.RoomResourceRichFreeBusy and attendee_record.getCUType() in ("RESOURCE", "ROOM",):
do_event_details = True
rich_options["resource"] = True
# Try cache
- resources = (yield FBCacheEntry.getCacheEntry(calresource, useruid, timerange)) if config.EnableFreeBusyCache else None
+ resources = (yield FBCacheEntry.getCacheEntry(calresource, attendee_uid, timerange)) if config.EnableFreeBusyCache else None
if resources is None:
@@ -207,9 +215,9 @@
tzinfo = filter.settimezone(tz)
try:
- resources = yield calresource._index.indexedSearch(filter, useruid=useruid, fbtype=True)
+ resources = yield calresource._index.indexedSearch(filter, useruid=attendee_uid, fbtype=True)
if caching:
- yield FBCacheEntry.makeCacheEntry(calresource, useruid, cache_timerange, resources)
+ yield FBCacheEntry.makeCacheEntry(calresource, attendee_uid, cache_timerange, resources)
except IndexedSearchException:
resources = yield calresource._index.bruteForceSearch()
@@ -286,8 +294,10 @@
# Add extended details
if do_event_details:
child = (yield calresource.calendarObjectWithName(name))
- calendar = (yield child.componentForUser())
- _addEventDetails(calendar, event_details, rich_options, timerange, tzinfo)
+ # Only add fully public events
+ if not child.accessMode or child.accessMode == Component.ACCESS_PUBLIC:
+ calendar = (yield child.componentForUser())
+ _addEventDetails(calendar, event_details, rich_options, timerange, tzinfo)
else:
child = (yield calresource.calendarObjectWithName(name))
@@ -333,8 +343,10 @@
# Add extended details
if calendar.mainType() == "VEVENT" and do_event_details:
child = (yield calresource.calendarObjectWithName(name))
- calendar = (yield child.componentForUser())
- _addEventDetails(calendar, event_details, rich_options, timerange, tzinfo)
+ # Only add fully public events
+ if not child.accessMode or child.accessMode == Component.ACCESS_PUBLIC:
+ calendar = (yield child.componentForUser())
+ _addEventDetails(calendar, event_details, rich_options, timerange, tzinfo)
returnValue(matchtotal)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icaldiff.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icaldiff.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -124,7 +124,7 @@
# Determine whether the instance is still valid in the new calendar
new_component = self.newcalendar.deriveInstance(rid)
- if new_component:
+ if new_component is not None:
# Derive a new instance from the new calendar and transfer attendee status
self.newcalendar.addComponent(new_component)
self._tryComponentMerge(old_component, new_component, ignore_attendee, is_organizer)
@@ -153,7 +153,7 @@
# Try to derive a new instance in the client and transfer attendee status
old_component = self.oldcalendar.deriveInstance(rid)
- if old_component:
+ if old_component is not None:
self.oldcalendar.addComponent(old_component)
self._tryComponentMerge(old_component, new_component, ignore_attendee, is_organizer)
else:
@@ -424,7 +424,7 @@
overridden = returnCalendar.overriddenComponent(decline)
if not overridden:
overridden = returnCalendar.deriveInstance(decline)
- if overridden:
+ if overridden is not None:
if self._attendeeDecline(overridden):
changeCausesReply = True
changedRids.append(decline.getText() if decline else "")
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icalsplitter.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icalsplitter.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/icalsplitter.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -113,7 +113,7 @@
return rid
- def split(self, ical, rid=None, newUID=None):
+ def split(self, ical, rid=None, olderUID=None):
"""
Split the specified iCalendar object. This assumes that L{willSplit} has already
been called and returned C{True}. Splitting is done by carving out old instances
@@ -126,11 +126,11 @@
@param rid: recurrence-id where the split should occur, or C{None} to determine it here
@type rid: L{PyCalendarDateTime} or C{None}
- @param newUID: UID to use for the split off component, or C{None} to generate one here
- @type newUID: C{str} or C{None}
+ @param olderUID: UID to use for the split off component, or C{None} to generate one here
+ @type olderUID: C{str} or C{None}
- @return: iCalendar object for the old "carved out" instances
- @rtype: L{Component}
+ @return: iCalendar objects for the old and new "carved out" instances
+ @rtype: C{tuple} of two L{Component}'s
"""
# Find the instance RECURRENCE-ID where a split is going to happen
@@ -138,16 +138,17 @@
# Create the old one with a new UID value (or the one passed in)
icalOld = ical.duplicate()
- oldUID = icalOld.newUID(newUID=newUID)
+ oldUID = icalOld.newUID(newUID=olderUID)
icalOld.onlyPastInstances(rid)
# Adjust the current one
- ical.onlyFutureInstances(rid)
+ icalNew = ical.duplicate()
+ icalNew.onlyFutureInstances(rid)
# Relate them - add RELATED-TO;RELTYPE=RECURRENCE-SET if not already present
if not icalOld.hasPropertyWithParameterMatch("RELATED-TO", "RELTYPE", "X-CALENDARSERVER-RECURRENCE-SET"):
property = Property("RELATED-TO", oldUID, params={"RELTYPE": "X-CALENDARSERVER-RECURRENCE-SET"})
icalOld.addPropertyToAllComponents(property)
- ical.addPropertyToAllComponents(property)
+ icalNew.addPropertyToAllComponents(property)
- return icalOld
+ return (icalOld, icalNew,)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/implicit.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/implicit.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -63,6 +63,8 @@
self.allowed_to_schedule = True
self.suppress_refresh = False
+ self.split_details = None
+
NotAllowedExceptionDetails = collections.namedtuple("NotAllowedExceptionDetails", ("type", "args", "kwargs",))
def setSchedulingNotAllowed(self, ex, *ex_args, **ex_kwargs):
@@ -301,7 +303,7 @@
@inlineCallbacks
- def doImplicitScheduling(self, do_smart_merge=False):
+ def doImplicitScheduling(self, do_smart_merge=False, split_details=None):
"""
Do implicit scheduling operation based on the data already set by call to checkImplicitScheduling.
@@ -315,6 +317,7 @@
self.do_smart_merge = do_smart_merge
self.except_attendees = ()
self.only_refresh_attendees = None
+ self.split_details = split_details
# Determine what type of scheduling this is: Organizer triggered or Attendee triggered
if self.state == "organizer":
@@ -561,25 +564,30 @@
log.debug("Implicit - organizer '%s' is modifying UID: '%s' but change is not significant" % (self.organizer, self.uid))
returnValue(None)
else:
- log.debug("Implicit - organizer '%s' is modifying UID: '%s'" % (self.organizer, self.uid))
+ # Do not change PARTSTATs for a split operation
+ if self.split_details is None:
+ log.debug("Implicit - organizer '%s' is modifying UID: '%s'" % (self.organizer, self.uid))
- for rid in self.needs_action_rids:
- comp = self.calendar.overriddenComponent(rid)
- if comp is None:
- comp = self.calendar.deriveInstance(rid)
- self.calendar.addComponent(comp)
+ for rid in self.needs_action_rids:
+ comp = self.calendar.overriddenComponent(rid)
+ if comp is None:
+ comp = self.calendar.deriveInstance(rid)
+ if comp is not None:
+ self.calendar.addComponent(comp)
- for attendee in comp.getAllAttendeeProperties():
- if attendee.hasParameter("PARTSTAT"):
- cuaddr = attendee.value()
+ for attendee in comp.getAllAttendeeProperties():
+ if attendee.hasParameter("PARTSTAT"):
+ cuaddr = attendee.value()
- if cuaddr in self.organizerPrincipal.calendarUserAddresses:
- # If the attendee is the organizer then do not update
- # the PARTSTAT to NEEDS-ACTION.
- # The organizer is automatically ACCEPTED to the event.
- continue
+ if cuaddr in self.organizerPrincipal.calendarUserAddresses:
+ # If the attendee is the organizer then do not update
+ # the PARTSTAT to NEEDS-ACTION.
+ # The organizer is automatically ACCEPTED to the event.
+ continue
- attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
+ attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
+ else:
+ log.debug("Implicit - organizer '%s' is splitting UID: '%s'" % (self.organizer, self.uid))
# Check for removed attendees
if not recurrence_reschedule:
@@ -592,8 +600,12 @@
self.needs_sequence_change = self.calendar.needsiTIPSequenceChange(self.oldcalendar)
elif self.action == "create":
- log.debug("Implicit - organizer '%s' is creating UID: '%s'" % (self.organizer, self.uid))
- self.coerceAttendeesPartstatOnCreate()
+ if self.split_details is None:
+ log.debug("Implicit - organizer '%s' is creating UID: '%s'" % (self.organizer, self.uid))
+ self.coerceAttendeesPartstatOnCreate()
+ else:
+ log.debug("Implicit - organizer '%s' is creating a split UID: '%s'" % (self.organizer, self.uid))
+ self.needs_sequence_change = False
# Always set RSVP=TRUE for any NEEDS-ACTION
for attendee in self.calendar.getAllAttendeeProperties():
@@ -845,6 +857,8 @@
# Compare the old one to a derived instance, and if there is a change
# add the derived instance to the new data
newcomp = self.calendar.deriveInstance(rid)
+ if newcomp is None:
+ continue
changed = self.compareAttendeePartstats(
self.oldcalendar.overriddenComponent(rid),
newcomp,
@@ -932,6 +946,13 @@
if attendee in self.organizerPrincipal.calendarUserAddresses:
continue
+ # Handle split by not scheduling local attendees
+ if self.split_details is not None:
+ attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
+ attendeeAddress = (yield addressmapping.mapper.getCalendarUser(attendee, attendeePrincipal))
+ if type(attendeeAddress) is LocalCalendarUser:
+ continue
+
# Generate an iTIP CANCEL message for this attendee, cancelling
# each instance or the whole
@@ -944,6 +965,13 @@
# Send scheduling message
if itipmsg:
+
+ # Add split details if needed
+ if self.split_details is not None:
+ rid, uid, newer_piece = self.split_details
+ itipmsg.addProperty(Property("X-CALENDARSERVER-SPLIT-RID", rid))
+ itipmsg.addProperty(Property("X-CALENDARSERVER-SPLIT-OLDER-UID" if newer_piece else "X-CALENDARSERVER-SPLIT-NEWER-UID", uid))
+
# This is a local CALDAV scheduling operation.
scheduler = self.makeScheduler()
@@ -983,10 +1011,24 @@
if self.reinvites and attendee not in self.reinvites:
continue
+ # Handle split by not scheduling local attendees
+ if self.split_details is not None:
+ attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
+ attendeeAddress = (yield addressmapping.mapper.getCalendarUser(attendee, attendeePrincipal))
+ if type(attendeeAddress) is LocalCalendarUser:
+ continue
+
itipmsg = iTipGenerator.generateAttendeeRequest(self.calendar, (attendee,), self.changed_rids)
# Send scheduling message
if itipmsg is not None:
+
+ # Add split details if needed
+ if self.split_details is not None:
+ rid, uid, newer_piece = self.split_details
+ itipmsg.addProperty(Property("X-CALENDARSERVER-SPLIT-RID", rid))
+ itipmsg.addProperty(Property("X-CALENDARSERVER-SPLIT-OLDER-UID" if newer_piece else "X-CALENDARSERVER-SPLIT-NEWER-UID", uid))
+
# This is a local CALDAV scheduling operation.
scheduler = self.makeScheduler()
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/delivery.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/delivery.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -38,6 +38,8 @@
from twistedcaldav.client.pool import _configuredClientContextFactory
from twistedcaldav.config import config
from twistedcaldav.ical import normalizeCUAddress, Component
+from twistedcaldav.util import utf8String
+
from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser, RemoteCalendarUser, \
OtherServerCalendarUser
from txdav.caldav.datastore.scheduling.delivery import DeliveryService
@@ -49,7 +51,8 @@
RequestStatus, Recipient, ischedule_namespace, CalendarData, \
ResponseDescription, Error
from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
-from twistedcaldav.util import utf8String, normalizationLookup
+from txdav.caldav.datastore.scheduling.utils import extractEmailDomain
+from txdav.caldav.datastore.util import normalizationLookup
from urlparse import urlsplit
@@ -83,7 +86,7 @@
# Only handle mailtos:
if cuaddr.lower().startswith("mailto:"):
- _ignore_local, domain = cuaddr[7:].split("@", 1)
+ domain = extractEmailDomain(cuaddr)
server = (yield cls.serverForDomain(domain))
returnValue(server is not None)
@@ -226,11 +229,10 @@
# Generate an HTTP client request
try:
- if not hasattr(self.scheduler.request, "extendedLogItems"):
- self.scheduler.request.extendedLogItems = {}
- if "itip.ischedule" not in self.scheduler.request.extendedLogItems:
- self.scheduler.request.extendedLogItems["itip.ischedule"] = 0
- self.scheduler.request.extendedLogItems["itip.ischedule"] += 1
+ if self.scheduler.logItems is not None:
+ if "itip.ischedule" not in self.scheduler.logItems:
+ self.scheduler.logItems["itip.ischedule"] = 0
+ self.scheduler.logItems["itip.ischedule"] += 1
# Loop over at most 3 redirects
ssl, host, port, path = self.server.details()
@@ -363,7 +365,7 @@
# The Originator must be the ORGANIZER (for a request) or ATTENDEE (for a reply)
originator = self.scheduler.organizer.cuaddr if self.scheduler.isiTIPRequest else self.scheduler.attendee
- originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.resource.principalForCalendarUserAddress, toUUID=False)
+ originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
self.headers.addRawHeader("Originator", utf8String(originator))
self.sign_headers.append("Originator")
@@ -417,7 +419,7 @@
normalizedCalendar = self.scheduler.calendar.duplicate()
normalizedCalendar.normalizeCalendarUserAddresses(
normalizationLookup,
- self.scheduler.resource.principalForCalendarUserAddress,
+ self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
toUUID=False)
else:
normalizedCalendar = self.scheduler.calendar
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/dkim.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/dkim.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/dkim.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -513,12 +513,15 @@
Class used to verify an DKIM-signed HTTP request.
"""
- def __init__(self, request, key_lookup=None, protocol_debug=False):
+ def __init__(self, headers, body, key_lookup=None, protocol_debug=False):
"""
- @param request: The HTTP request to process
- @type request: L{twext.server.Request}
+ @param headers: The HTTP request headers to process
+ @type headers: L{twext.web2.http_headers.Headers}
+ @param body: The HTTP request body to process
+ @type body: C{str}
"""
- self.request = request
+ self.headers = headers
+ self.body = body
self._debug = protocol_debug
self.dkim_tags = {}
@@ -563,17 +566,14 @@
Public key used:
%s
-""" % (self.request.headers.getRawHeaders(DKIM_SIGNATURE)[0], headers, pubkey._original_data,)
+""" % (self.headers.getRawHeaders(DKIM_SIGNATURE)[0], headers, pubkey._original_data,)
log.debug("DKIM: %s:%s" % (msg, _debug_msg,))
if self._debug:
msg = "%s:%s" % (msg, _debug_msg,)
raise DKIMVerificationError(msg)
# Do body validation
- data = (yield allDataFromStream(self.request.stream))
- self.request.stream = MemoryStream(data if data is not None else "")
- self.request.stream.doStartReading = None
- body = DKIMUtils.canonicalizeBody(data)
+ body = DKIMUtils.canonicalizeBody(self.body)
bh = base64.b64encode(self.hash_method(body).digest())
if bh != self.dkim_tags["_bh"]:
msg = "Could not verify the DKIM body hash"
@@ -584,7 +584,7 @@
Base64 encoded body:
%s
-""" % (self.request.headers.getRawHeaders(DKIM_SIGNATURE), self.hash_method.__name__, base64.b64encode(body),)
+""" % (self.headers.getRawHeaders(DKIM_SIGNATURE), self.hash_method.__name__, base64.b64encode(body),)
log.debug("DKIM: %s:%s" % (msg, _debug_msg,))
if self._debug:
msg = "%s:%s" % (msg, _debug_msg,)
@@ -599,7 +599,7 @@
"""
# Check presence of header
- dkim = self.request.headers.getRawHeaders(DKIM_SIGNATURE)
+ dkim = self.headers.getRawHeaders(DKIM_SIGNATURE)
if dkim is None:
msg = "No DKIM-Signature header present in the request"
log.debug("DKIM: " + msg)
@@ -683,12 +683,12 @@
headers = []
for header in header_list:
- actual_headers = self.request.headers.getRawHeaders(header)
+ actual_headers = self.headers.getRawHeaders(header)
if actual_headers:
headers.append((header, ",".join(actual_headers),))
# DKIM-Signature is always included at the end
- headers.append((DKIM_SIGNATURE, self.request.headers.getRawHeaders(DKIM_SIGNATURE)[0],))
+ headers.append((DKIM_SIGNATURE, self.headers.getRawHeaders(DKIM_SIGNATURE)[0],))
# Now canonicalize the values
return "".join([DKIMUtils.canonicalizeHeader(name, value, dkim_tags=self.dkim_tags) for name, value in headers])
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/localservers.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/localservers.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/localservers.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -193,10 +193,10 @@
return ip in self.allowed_from_ips
- def checkSharedSecret(self, request):
+ def checkSharedSecret(self, headers):
# Get header from the request
- request_secret = request.headers.getRawHeaders(SERVER_SECRET_HEADER)
+ request_secret = headers.getRawHeaders(SERVER_SECRET_HEADER)
if request_secret is not None and self.shared_secret is None:
log.error("iSchedule request included unexpected %s header" % (SERVER_SECRET_HEADER,))
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/resource.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/resource.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -20,6 +20,7 @@
from twext.web2 import responsecode
from twext.web2.dav.http import ErrorResponse
from twext.web2.dav.noneprops import NonePropertyStore
+from twext.web2.dav.util import allDataFromStream
from twext.web2.http import Response, HTTPError, StatusResponse, XMLResponse
from twext.web2.http_headers import MimeType
@@ -223,11 +224,11 @@
originator = self.loadOriginatorFromRequestHeaders(request)
recipients = self.loadRecipientsFromRequestHeaders(request)
- calendar = (yield self.loadCalendarFromRequest(request))
+ body = (yield allDataFromStream(request.stream))
# Do the POST processing treating this as a non-local schedule
try:
- result = (yield scheduler.doSchedulingViaPOST(request, originator, recipients, calendar))
+ result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, originator, recipients))
except Exception:
ex = Failure()
yield txn.abort()
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/scheduler.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/scheduler.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/scheduler.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -23,7 +23,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav.config import config
-from twistedcaldav.ical import normalizeCUAddress
+from twistedcaldav.ical import normalizeCUAddress, Component
from txdav.caldav.datastore.scheduling import addressmapping
from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser
@@ -139,15 +139,17 @@
}
@inlineCallbacks
- def doSchedulingViaPOST(self, request, originator, recipients, calendar):
+ def doSchedulingViaPOST(self, remoteAddr, headers, body, originator, recipients):
"""
Carry out iSchedule specific processing.
"""
- self.request = request
+ self.remoteAddr = remoteAddr
+ self.headers = headers
self.verified = False
+
if config.Scheduling.iSchedule.DKIM.Enabled:
- verifier = DKIMVerifier(self.request, protocol_debug=config.Scheduling.iSchedule.DKIM.ProtocolDebug)
+ verifier = DKIMVerifier(self.headers, body, protocol_debug=config.Scheduling.iSchedule.DKIM.ProtocolDebug)
try:
yield verifier.verify()
self.verified = True
@@ -170,7 +172,9 @@
msg,
))
- if self.request.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
+ calendar = Component.fromString(body)
+
+ if self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
self.txn.doing_attendee_refresh = 1
# Normalize recipient addresses
@@ -193,15 +197,6 @@
self.calendar.normalizeCalendarUserAddresses(normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
- def loadRecipientsFromRequestHeaders(self):
- """
- Need to normalize the calendar data and recipient values to keep those in sync,
- as we might later try to match them
- """
- super(IScheduleScheduler, self).loadRecipientsFromRequestHeaders()
- self.recipients = [normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress) for recipient in self.recipients]
-
-
def checkAuthorization(self):
# Must have an unauthenticated user
if self.originator_uid is not None:
@@ -259,7 +254,7 @@
))
else:
# Get the request IP and map to hostname.
- clientip = self.request.remoteAddr.host
+ clientip = self.remoteAddr.host
# First compare as dotted IP
matched = False
@@ -312,7 +307,7 @@
expected_uri = urlparse.urlparse(expected_uri)
# Get the request IP and map to hostname.
- clientip = self.request.remoteAddr.host
+ clientip = self.remoteAddr.host
# Check against this server (or any of its partitions). We need this because an external iTIP message
# may be addressed to users on different partitions, and the node receiving the iTIP message will need to
@@ -345,7 +340,7 @@
log.debug("iSchedule cannot lookup client ip '%s': %s" % (clientip, str(e),))
# Check possible shared secret
- if matched and not Servers.getThisServer().checkSharedSecret(self.request):
+ if matched and not Servers.getThisServer().checkSharedSecret(self.headers):
log.error("Invalid iSchedule shared secret")
matched = False
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -60,3 +60,7 @@
self.assertTrue(result)
result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user at example.org")
self.assertFalse(result)
+ result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user at example.org?subject=foobar")
+ self.assertFalse(result)
+ result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user")
+ self.assertFalse(result)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_dkim.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_dkim.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_dkim.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -16,6 +16,7 @@
from Crypto.PublicKey import RSA
+from twext.web2.dav.util import allDataFromStream
from twext.web2.http_headers import Headers, MimeType
from twext.web2.stream import MemoryStream
@@ -244,6 +245,13 @@
self.stream = MemoryStream(body)
+ def _makeHeaders(self, headers_pairs):
+ headers = Headers()
+ for name, value in headers_pairs:
+ headers.addRawHeader(name, value)
+ return headers
+
+
def test_valid_dkim_headers(self):
"""
L{DKIMVerifier.processDKIMHeader} correctly validates DKIM-Signature headers.
@@ -276,8 +284,7 @@
)
for headers, result in data:
- request = self.StubRequest("POST", "/", headers, "")
- verifier = DKIMVerifier(request)
+ verifier = DKIMVerifier(self._makeHeaders(headers), "")
if result:
verifier.processDKIMHeader()
else:
@@ -307,8 +314,7 @@
)
for name, value, result in data:
- request = self.StubRequest("POST", "/", ((name, value,),), "")
- verifier = DKIMVerifier(request)
+ verifier = DKIMVerifier(self._makeHeaders(((name, value,),)), "")
if name == "DKIM-Signature":
verifier.processDKIMHeader()
canonicalized = DKIMUtils.canonicalizeHeader(name, value, verifier.dkim_tags if name == "DKIM-Signature" else None)
@@ -374,8 +380,7 @@
for hdrs, result in data:
headers = [hdr.split(":", 1) for hdr in hdrs.splitlines()]
- request = self.StubRequest("POST", "/", headers, "")
- verifier = DKIMVerifier(request)
+ verifier = DKIMVerifier(self._makeHeaders(headers), "")
verifier.processDKIMHeader()
extracted = verifier.extractSignedHeaders()
self.assertEqual(extracted, result.replace("\n", "\r\n"))
@@ -427,9 +432,8 @@
for hdrs, keys, result in data:
headers = [hdr.split(":", 1) for hdr in hdrs.splitlines()]
- request = self.StubRequest("POST", "/", headers, "")
TestPublicKeyLookup.PublicKeyLookup_Testing.keys = keys
- verifier = DKIMVerifier(request, key_lookup=(TestPublicKeyLookup.PublicKeyLookup_Testing,))
+ verifier = DKIMVerifier(self._makeHeaders(headers), "", key_lookup=(TestPublicKeyLookup.PublicKeyLookup_Testing,))
verifier.processDKIMHeader()
pkey = (yield verifier.locatePublicKey())
if result:
@@ -461,7 +465,8 @@
# Verify signature
TestPublicKeyLookup.PublicKeyLookup_Testing.keys = keys
- verifier = DKIMVerifier(request, key_lookup=(TestPublicKeyLookup.PublicKeyLookup_Testing,))
+ data = (yield allDataFromStream(request.stream))
+ verifier = DKIMVerifier(request.headers, data, key_lookup=(TestPublicKeyLookup.PublicKeyLookup_Testing,))
TestPublicKeyLookup.PublicKeyLookup_Testing({}).flushCache()
try:
yield verifier.verify()
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -167,22 +167,22 @@
request = SimpleRequest(None, "POST", "/ischedule")
request.headers.addRawHeader(SERVER_SECRET_HEADER, "foobar")
- self.assertTrue(servers.getServerById("00001").checkSharedSecret(request))
+ self.assertTrue(servers.getServerById("00001").checkSharedSecret(request.headers))
request = SimpleRequest(None, "POST", "/ischedule")
request.headers.addRawHeader(SERVER_SECRET_HEADER, "foobar1")
- self.assertFalse(servers.getServerById("00001").checkSharedSecret(request))
+ self.assertFalse(servers.getServerById("00001").checkSharedSecret(request.headers))
request = SimpleRequest(None, "POST", "/ischedule")
- self.assertFalse(servers.getServerById("00001").checkSharedSecret(request))
+ self.assertFalse(servers.getServerById("00001").checkSharedSecret(request.headers))
request = SimpleRequest(None, "POST", "/ischedule")
request.headers.addRawHeader(SERVER_SECRET_HEADER, "foobar")
- self.assertFalse(servers.getServerById("00002").checkSharedSecret(request))
+ self.assertFalse(servers.getServerById("00002").checkSharedSecret(request.headers))
request = SimpleRequest(None, "POST", "/ischedule")
request.headers.addRawHeader(SERVER_SECRET_HEADER, "foobar1")
- self.assertFalse(servers.getServerById("00002").checkSharedSecret(request))
+ self.assertFalse(servers.getServerById("00002").checkSharedSecret(request.headers))
request = SimpleRequest(None, "POST", "/ischedule")
- self.assertTrue(servers.getServerById("00002").checkSharedSecret(request))
+ self.assertTrue(servers.getServerById("00002").checkSharedSecret(request.headers))
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/itip.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/itip.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -179,7 +179,7 @@
allowCancelled = component.propertyValue("STATUS") == "CANCELLED"
hidden = component.hasProperty(Component.HIDDEN_INSTANCE_PROPERTY)
new_component = new_calendar.deriveInstance(rid, allowCancelled=allowCancelled and not hidden)
- if new_component:
+ if new_component is not None:
new_calendar.addComponent(new_component)
iTipProcessing.transferItems(calendar, new_component, master_valarms, private_comments, transps, completeds, organizer_schedule_status, attendee_dtstamp, other_props, recipient)
if hidden:
@@ -298,7 +298,7 @@
else:
# Derive a new component and cancel it.
overridden = calendar.deriveInstance(rid)
- if overridden:
+ if overridden is not None:
overridden.replaceProperty(Property("STATUS", "CANCELLED"))
calendar.addComponent(overridden)
newseq = component.propertyValue("SEQUENCE")
@@ -376,7 +376,7 @@
# Attendee is overriding an instance themselves - we need to create a derived one
# for the Organizer
match_component = calendar.deriveInstance(rid)
- if match_component:
+ if match_component is not None:
calendar.addComponent(match_component)
else:
log.error("Ignoring instance: %s in iTIP REPLY for: %s" % (rid, itip_message.resourceUID()))
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/processing.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/processing.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -234,7 +234,9 @@
# and only if the request does not indicate we should skip attendee refresh
# (e.g. inbox item processing during migration from non-implicit server)
if partstatChanged and not self.noAttendeeRefresh:
- yield self.queueAttendeeUpdate((attendeeReplying, organizer,))
+ # Check limit of attendees
+ if config.Scheduling.Options.AttendeeRefreshCountLimit == 0 or len(self.recipient_calendar.getAllUniqueAttendees()) <= config.Scheduling.Options.AttendeeRefreshCountLimit:
+ yield self.queueAttendeeUpdate((attendeeReplying, organizer,))
result = (True, False, True, changes,)
@@ -460,6 +462,26 @@
log.debug("ImplicitProcessing - originator '%s' to recipient '%s' ignoring UID: '%s' - organizer has no copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
raise ImplicitProcessorException("5.3;Organizer change not allowed")
+ # Handle splitting of data early so we can preserve per-attendee data
+ if self.message.hasProperty("X-CALENDARSERVER-SPLIT-OLDER-UID"):
+ if config.Scheduling.Options.Splitting.Enabled:
+ # Tell the existing resource to split
+ log.debug("ImplicitProcessing - originator '%s' to recipient '%s' splitting UID: '%s'" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+ split = (yield self.doImplicitAttendeeSplit())
+ if split:
+ returnValue((True, False, False, None,))
+ else:
+ self.message.removeProperty("X-CALENDARSERVER-SPLIT-OLDER-UID")
+ self.message.removeProperty("X-CALENDARSERVER-SPLIT-RID")
+
+ elif self.message.hasProperty("X-CALENDARSERVER-SPLIT-NEWER-UID"):
+ if config.Scheduling.Options.Splitting.Enabled:
+ log.debug("ImplicitProcessing - originator '%s' to recipient '%s' ignoring UID: '%s' - split already done" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+ returnValue((True, False, False, None,))
+ else:
+ self.message.removeProperty("X-CALENDARSERVER-SPLIT-OLDER-UID")
+ self.message.removeProperty("X-CALENDARSERVER-SPLIT-RID")
+
# Different based on method
if self.method == "REQUEST":
result = (yield self.doImplicitAttendeeRequest())
@@ -476,6 +498,22 @@
@inlineCallbacks
+ def doImplicitAttendeeSplit(self):
+ """
+ Handle splitting of the existing calendar data.
+ """
+ olderUID = self.message.propertyValue("X-CALENDARSERVER-SPLIT-OLDER-UID")
+ split_rid = self.message.propertyValue("X-CALENDARSERVER-SPLIT-RID")
+ if olderUID is None or split_rid is None:
+ returnValue(False)
+
+ # Split the resource
+ yield self.recipient_calendar_resource.splitForAttendee(rid=split_rid, olderUID=olderUID)
+
+ returnValue(True)
+
+
+ @inlineCallbacks
def doImplicitAttendeeRequest(self):
"""
@return: C{tuple} of (processed, auto-processed, store inbox item, changes)
@@ -868,7 +906,7 @@
partstat = "MIXED RESPONSE"
# Default state is whichever of free or busy has most instances
- defaultPartStat = max(partstat_counts.items(), key=lambda x: x[1])[0]
+ defaultPartStat = max(sorted(partstat_counts.items()), key=lambda x: x[1])[0]
# See if there is a master component first
hadMasterRsvp = False
@@ -898,7 +936,7 @@
# Derive a new overridden component and change partstat. We also need to make sure we restore any RSVP
# value that may have been overwritten by any change to the master itself.
derived = calendar.deriveInstance(instance.rid)
- if derived:
+ if derived is not None:
attendee = derived.getAttendeeProperty(cuas)
if attendee:
if instance.partstat == "NEEDS-ACTION" and instance.active:
@@ -1028,7 +1066,7 @@
# We only need to fix data that already exists
if recipient_resource is not None:
if originator_calendar.mainType() != None:
- yield self.writeCalendarResource(recipient_resource, originator_calendar)
+ yield self.writeCalendarResource(None, recipient_resource, originator_calendar)
else:
yield self.deleteCalendarResource(recipient_resource)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -2009,12 +2009,12 @@
splitter = iCalSplitter(1024, 14)
if title[0] == "1":
self.assertTrue(splitter.willSplit(ical), "Failed will split: %s" % (title,))
- icalOld = splitter.split(ical)
+ icalOld, icalNew = splitter.split(ical)
relsubs = dict(self.subs)
relsubs["relID"] = icalOld.resourceUID()
- self.assertEqual(str(ical).replace("\r\n ", ""), split_future.replace("\n", "\r\n") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(icalNew).replace("\r\n ", ""), split_future.replace("\n", "\r\n") % relsubs, "Failed future: %s" % (title,))
self.assertEqual(str(icalOld).replace("\r\n ", ""), split_past.replace("\n", "\r\n") % relsubs, "Failed past: %s" % (title,))
# Make sure new items won't split again
- self.assertFalse(splitter.willSplit(ical), "Failed future will split: %s" % (title,))
+ self.assertFalse(splitter.willSplit(icalNew), "Failed future will split: %s" % (title,))
self.assertFalse(splitter.willSplit(icalOld), "Failed past will split: %s" % (title,))
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_pocessing.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_pocessing.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_pocessing.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -14,7 +14,7 @@
# limitations under the License.
##
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, succeed
from twisted.trial import unittest
from twistedcaldav import memcacher
@@ -22,6 +22,7 @@
from twistedcaldav.stdconfig import config
from txdav.caldav.datastore.scheduling.processing import ImplicitProcessor
+from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser
class FakeImplicitProcessor(ImplicitProcessor):
@@ -37,7 +38,11 @@
self.batches += 1
+ def writeCalendarResource(self, collection, resource, calendar):
+ return succeed(FakeResource())
+
+
class FakePrincipal(object):
def __init__(self, cuaddr):
@@ -49,6 +54,25 @@
+class FakeResource(object):
+
+ def parentCollection(self):
+ return self
+
+
+ def ownerHome(self):
+ return self
+
+
+ def uid(self):
+ return None
+
+
+ def id(self):
+ return None
+
+
+
class BatchRefresh (unittest.TestCase):
"""
iCalendar support tests
@@ -113,3 +137,96 @@
processor.recipient_calendar = calendar
yield processor.queueAttendeeUpdate(("urn:uuid:user02", "urn:uuid:user01",))
self.assertEqual(processor.batches, 1)
+
+
+ @inlineCallbacks
+ def test_queueAttendeeUpdate_count_suppressed(self):
+
+ self.patch(config.Scheduling.Options, "AttendeeRefreshCountLimit", 5)
+ self.patch(config.Scheduling.Options, "AttendeeRefreshBatch", 5)
+
+ calendar_small = Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER:urn:uuid:user01
+ATTENDEE:urn:uuid:user01
+ATTENDEE:urn:uuid:user02
+ATTENDEE:urn:uuid:user03
+ATTENDEE:urn:uuid:user04
+END:VEVENT
+END:VCALENDAR
+""")
+ itip_small = Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER:urn:uuid:user01
+ATTENDEE;PARTSTAT="ACCEPTED":urn:uuid:user02
+END:VEVENT
+END:VCALENDAR
+""")
+ calendar_large = Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER:urn:uuid:user01
+ATTENDEE:urn:uuid:user01
+ATTENDEE:urn:uuid:user02
+ATTENDEE:urn:uuid:user03
+ATTENDEE:urn:uuid:user04
+ATTENDEE:urn:uuid:user05
+ATTENDEE:urn:uuid:user06
+ATTENDEE:urn:uuid:user07
+ATTENDEE:urn:uuid:user08
+ATTENDEE:urn:uuid:user09
+ATTENDEE:urn:uuid:user10
+END:VEVENT
+END:VCALENDAR
+""")
+ itip_large = Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER:urn:uuid:user01
+ATTENDEE;PARTSTAT="ACCEPTED":urn:uuid:user02
+END:VEVENT
+END:VCALENDAR
+""")
+
+ for count, calendar, itip, result, msg in (
+ (5, calendar_small, itip_small, 1, "Small, count=5"),
+ (5, calendar_large, itip_large, 0, "Large, count=5"),
+ (0, calendar_small, itip_small, 1, "Small, count=0"),
+ (0, calendar_large, itip_large, 1, "Large, count=0"),
+ ):
+ config.Scheduling.Options.AttendeeRefreshCountLimit = count
+ processor = FakeImplicitProcessor()
+ processor.txn = ""
+ processor.recipient_calendar = calendar.duplicate()
+ processor.uid = processor.recipient_calendar.newUID()
+ processor.recipient_calendar_resource = None
+ processor.message = itip.duplicate()
+ processor.message.newUID(processor.uid)
+ processor.originator = LocalCalendarUser(None, None)
+ processor.recipient = LocalCalendarUser(None, None)
+ processor.uid = calendar.resourceUID()
+ processor.noAttendeeRefresh = False
+
+ processed = yield processor.doImplicitOrganizerUpdate()
+ self.assertTrue(processed[3] is not None, msg=msg)
+ self.assertEqual(processor.batches, result, msg=msg)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_utils.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_utils.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/test/test_utils.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -23,7 +23,8 @@
from twisted.internet.defer import inlineCallbacks
from twisted.trial import unittest
-from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord
+from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord, \
+ extractEmailDomain
from txdav.caldav.datastore.test.util import buildCalendarStore, \
buildDirectoryRecord
from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
@@ -180,3 +181,20 @@
resource3 = (yield self.calendarObjectUnderTest(txn, name="3.ics", calendar_name="calendar3", home="user02"))
self.assertTrue((resource2 is not None) ^ (resource3 is not None))
yield self.commit()
+
+
+ def test_extractEmailDomain(self):
+ """
+ Test that L{extractEmailDomain} returns the expected results.
+ """
+
+ data = (
+ ("mailto:foo at example.com", "example.com"),
+ ("mailto:foo at example.com?subject=bar", "example.com"),
+ ("mailto:foo", ""),
+ ("mailto:foo@", ""),
+ ("http://foobar.com", ""),
+ )
+
+ for mailto, domain in data:
+ self.assertEqual(extractEmailDomain(mailto), domain)
Modified: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/utils.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/utils.py 2013-10-03 21:53:22 UTC (rev 11786)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/scheduling/utils.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -46,3 +46,13 @@
returnValue(objectResources[0] if len(objectResources) == 1 else None)
else:
returnValue(None)
+
+
+
+def extractEmailDomain(mailtoURI):
+ try:
+ addr = mailtoURI[7:].split("?")[0]
+ _ignore_account, addrDomain = addr.split("@")
+ except ValueError:
+ addrDomain = ""
+ return addrDomain
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_implicit.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_implicit.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,1045 @@
+##
+# Copyright (c) 2005-2013 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 twisted.internet.defer import inlineCallbacks
+
+from twistedcaldav.ical import Component
+
+from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
+from twisted.trial.unittest import TestCase
+from twext.python.clsprop import classproperty
+from twistedcaldav.config import config
+from txdav.common.icommondatastore import ObjectResourceTooBigError, \
+ InvalidObjectResourceError, InvalidComponentForStoreError, InvalidUIDError, \
+ UIDExistsError, UIDExistsElsewhereError
+from txdav.caldav.icalendarstore import InvalidComponentTypeError, \
+ TooManyAttendeesError, InvalidCalendarAccessError, ComponentUpdateState
+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
+from txdav.caldav.datastore.test.util import buildCalendarStore
+
+class ImplicitRequests (CommonCommonTests, TestCase):
+ """
+ Test twistedcaldav.scheduyling.implicit with a Request object.
+ """
+
+ @inlineCallbacks
+ def setUp(self):
+ yield super(ImplicitRequests, self).setUp()
+ self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+ yield self.populate()
+
+
+ @inlineCallbacks
+ def populate(self):
+ yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
+ self.notifierFactory.reset()
+
+
+ @classproperty(cache=False)
+ def requirements(cls): #@NoSelf
+ return {
+ "user01": {
+ "calendar_1": {
+ },
+ "inbox": {
+ },
+ },
+ "user02": {
+ "calendar_1": {
+ },
+ "inbox": {
+ },
+ },
+ }
+
+
+ def storeUnderTest(self):
+ """
+ Create and return a L{CalendarStore} for testing.
+ """
+ return self._sqlCalendarStore
+
+
+ @inlineCallbacks
+ def test_doCreateResource(self):
+ """
+ Test that resource creation works.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar1 = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar1)
+ yield self.commit()
+
+ calendar_resource1 = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource1.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("urn:uuid:user01" in calendar1)
+ self.assertTrue("urn:uuid:user02" in calendar1)
+ self.assertTrue("CN=" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_maxResourceSize(self):
+ """
+ Test that various types of invalid calendar data are rejected when creating a resource.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ self.patch(config, "MaxResourceSize", 100)
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar1 = Component.fromString(data1)
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar1), ObjectResourceTooBigError)
+ yield self.commit()
+
+ self.patch(config, "MaxResourceSize", 10000)
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar1 = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar1)
+ yield self.commit()
+
+ self.patch(config, "MaxResourceSize", 100)
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar2 = Component.fromString(data2)
+ yield self.failUnlessFailure(calendar_resource.setComponent(calendar2), ObjectResourceTooBigError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_validCalendarDataCheck(self):
+ """
+ Test that various types of invalid calendar data are rejected when creating a resource.
+ """
+
+ data = (
+ "xyz",
+ Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:1
+SUMMARY:2
+END:VEVENT
+END:VCALENDAR
+"""),
+
+ Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:PUBLISH
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""),
+ )
+
+ for item in data:
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = item
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidObjectResourceError, InvalidComponentForStoreError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_validSupportedComponentType(self):
+ """
+ Test that resources are restricted by component type.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar_collection.setSupportedComponents("VTODO")
+ calendar = Component.fromString(data1)
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidComponentTypeError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_validAttendeeListSizeCheck(self):
+ """
+ Test that resource with too many attendees are rejected.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
+ATTENDEE:mailto:user04 at example.com
+ATTENDEE:mailto:user05 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ self.patch(config, "MaxAttendeesPerInstance", 2)
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), TooManyAttendeesError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_validAccess_invalidValue(self):
+ """
+ Test that resource access mode changes are rejected.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:BOGUS
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ self.patch(config, "EnablePrivateEvents", True)
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidCalendarAccessError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_validAccess_authzChangeNotAllowed(self):
+ """
+ Test that resource access mode changes are rejected.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:PRIVATE
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ self.patch(config, "EnablePrivateEvents", True)
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user02"
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidCalendarAccessError)
+ yield self.commit()
+
+ # This one should be OK
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ # This one should re-insert access mode
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("X-CALENDARSERVER-ACCESS:PRIVATE" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_overwriteUID(self):
+ """
+ Test that a change to a resource UID is not allowed.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ # This one should fail
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply-1
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ yield self.failUnlessFailure(calendar_resource.setComponent(calendar), InvalidUIDError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_duplicateUIDSameCalendar(self):
+ """
+ Test that a resource with a duplicate UID in the same calendar is not allowed.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ # This one should fail
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data2)
+ yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test2.ics", calendar), UIDExistsError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_duplicateUIDDifferentCalendar(self):
+ """
+ Test that a resource with a duplicate UID in a different calendar is not allowed.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ # This one should fail
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ home_collection = (yield self.homeUnderTest(name="user01"))
+ calendar_collection_2 = (yield home_collection.createCalendarWithName("calendar_2"))
+ calendar = Component.fromString(data2)
+ yield self.failUnlessFailure(calendar_collection_2.createCalendarObjectWithName("test2.ics", calendar), UIDExistsElsewhereError)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_preservePrivateComments(self):
+ """
+ Test that resource private comments are restored.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+X-CALENDARSERVER-PRIVATE-COMMENT:My Comment
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_replaceMissingToDoProperties_OrganizerAttendee(self):
+ """
+ Test that missing scheduling properties in VTODOs are recovered.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTODO
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+END:VTODO
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTODO
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VTODO
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("ORGANIZER" in calendar1)
+ self.assertTrue("ATTENDEE" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_replaceMissingToDoProperties_Completed(self):
+ """
+ Test that VTODO completed status is fixed.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTODO
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+END:VTODO
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTODO
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+COMPLETED:20080601T140000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+END:VTODO
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("ORGANIZER" in calendar1)
+ self.assertTrue("ATTENDEE" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ self.assertTrue("PARTSTAT=COMPLETED" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_dropboxPathNormalization(self):
+ """
+ Test that dropbox paths are normalized.
+ """
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ sharee_home = (yield self.homeUnderTest(name="user02"))
+ shared_name = (yield calendar_collection.shareWith(sharee_home, _BIND_MODE_WRITE,))
+ yield self.commit()
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTODO
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+X-APPLE-DROPBOX:https://example.com/calendars/users/user02/dropbox/123.dropbox
+ATTACH;VALUE=URI:https://example.com/calendars/users/user02/dropbox/123.dropbox/1.txt
+ATTACH;VALUE=URI:https://example.org/attachments/2.txt
+END:VTODO
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(name=shared_name, home="user02"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", calendar_name=shared_name, home="user02",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("X-APPLE-DROPBOX:https://example.com/calendars/__uids__/user01/dropbox/123.dropbox" in calendar1)
+ self.assertTrue("ATTACH:https://example.com/calendars/__uids__/user01/dropbox/123.dropbox/1.txt" in calendar1)
+ self.assertTrue("ATTACH:https://example.org/attachments/2.txt" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_processAlarms_DuplicateRemoval(self):
+ """
+ Test that duplicate alarms are removed.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+BEGIN:VALARM
+X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+DESCRIPTION:Event reminder
+TRIGGER:-PT8M
+ACTION:DISPLAY
+END:VALARM
+BEGIN:VALARM
+X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+DESCRIPTION:Event reminder
+TRIGGER:-PT8M
+ACTION:DISPLAY
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ result = (yield calendar_resource.setComponent(calendar))
+ yield self.commit()
+ self.assertTrue(result)
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_processAlarms_AddDefault(self):
+ """
+ Test that default alarms are added.
+ """
+
+ alarm = """BEGIN:VALARM
+X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+DESCRIPTION:Event reminder
+TRIGGER:-PT8M
+ACTION:DISPLAY
+END:VALARM
+"""
+
+ home = (yield self.homeUnderTest(name="user01"))
+ yield home.setDefaultAlarm(alarm, True, True)
+ yield self.commit()
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_processAlarms_NoDefaultShared(self):
+ """
+ Test that default alarms are not added to shared resources.
+ """
+
+ # Set default alarm for user02
+ alarm = """BEGIN:VALARM
+X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+DESCRIPTION:Event reminder
+TRIGGER:-PT8M
+ACTION:DISPLAY
+END:VALARM
+"""
+
+ home = (yield self.homeUnderTest(name="user02"))
+ yield home.setDefaultAlarm(alarm, True, True)
+ yield self.commit()
+
+ # user01 shares calendar with user02
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ sharee_home = (yield self.homeUnderTest(name="user02"))
+ shared_name = (yield calendar_collection.shareWith(sharee_home, _BIND_MODE_WRITE,))
+ yield self.commit()
+
+ # user02 writes event to shared calendar
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(name=shared_name, home="user02"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", calendar_name=shared_name, home="user02",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 0)
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_mergePerUserData(self):
+ """
+ Test that per-user data is correctly stored and retrieved.
+ """
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ sharee_home = (yield self.homeUnderTest(name="user02"))
+ shared_name = (yield calendar_collection.shareWith(sharee_home, _BIND_MODE_WRITE,))
+ yield self.commit()
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+BEGIN:VALARM
+X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+DESCRIPTION:Event reminder
+TRIGGER:-PT5M
+ACTION:DISPLAY
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+BEGIN:VALARM
+X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
+DESCRIPTION:Event reminder
+TRIGGER:-PT10M
+ACTION:DISPLAY
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", calendar_name=shared_name, home="user02",))
+ calendar = Component.fromString(data2)
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+
+ # Unfiltered view of event
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("TRIGGER:-PT5M" in calendar1)
+ self.assertTrue("TRIGGER:-PT10M" in calendar1)
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 2)
+
+ # user01 view of event
+ calendar1 = (yield calendar_resource.componentForUser("user01"))
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("TRIGGER:-PT5M" in calendar1)
+ self.assertFalse("TRIGGER:-PT10M" in calendar1)
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
+
+ # user02 view of event
+ calendar1 = (yield calendar_resource.componentForUser("user02"))
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertFalse("TRIGGER:-PT5M" in calendar1)
+ self.assertTrue("TRIGGER:-PT10M" in calendar1)
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
+
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", calendar_name=shared_name, home="user02",))
+
+ # Unfiltered view of event
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("TRIGGER:-PT5M" in calendar1)
+ self.assertTrue("TRIGGER:-PT10M" in calendar1)
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 2)
+
+ # user01 view of event
+ calendar1 = (yield calendar_resource.componentForUser("user01"))
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("TRIGGER:-PT5M" in calendar1)
+ self.assertFalse("TRIGGER:-PT10M" in calendar1)
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
+
+ # user02 view of event
+ calendar1 = (yield calendar_resource.componentForUser("user02"))
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertFalse("TRIGGER:-PT5M" in calendar1)
+ self.assertTrue("TRIGGER:-PT10M" in calendar1)
+ self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
+
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_validation_processScheduleTags(self):
+ """
+ Test that schedule tags are correctly updated.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+SUMMARY:Changed #1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ yield calendar_resource.setComponent(calendar)
+ schedule_tag = calendar_resource.scheduleTag
+ yield self.commit()
+
+ data3 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+SUMMARY:Changed #2
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data3)
+ yield calendar_resource.setComponent(calendar)
+ self.assertNotEqual(calendar_resource.scheduleTag, schedule_tag)
+ schedule_tag = calendar_resource.scheduleTag
+ yield self.commit()
+
+ data4 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+SUMMARY:Changed #2
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data4)
+ yield calendar_resource._setComponentInternal(calendar, internal_state=ComponentUpdateState.ORGANIZER_ITIP_UPDATE)
+ self.assertEqual(calendar_resource.scheduleTag, schedule_tag)
+ yield self.commit()
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_schedule.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_schedule.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/test_schedule.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,70 @@
+##
+# Copyright (c) 2010-2013 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 L{txdav.caldav.datastore.scheduling}.
+
+The aforementioned module is intended to eventually support implicit
+scheduling; however, it does not currently. The interim purpose of this module
+and accompanying tests is to effectively test the interface specifications to
+make sure that the common tests don't require anything I{not} specified in the
+interface, so that dynamic proxies specified with a tool like
+C{proxyForInterface} can be used to implement features such as implicit
+scheduling or data caching as middleware in the data-store layer.
+"""
+
+from twisted.trial.unittest import TestCase, SkipTest
+from txdav.caldav.datastore.test.test_file import FileStorageTests
+from txdav.caldav.datastore.schedule import ImplicitStore
+
+simpleEvent = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+class ImplicitStoreTests(FileStorageTests, TestCase):
+ """
+ Tests for L{ImplicitSchedulingStore}.
+ """
+
+ implicitStore = None
+
+ def storeUnderTest(self):
+ if self.implicitStore is None:
+ sut = super(ImplicitStoreTests, self).storeUnderTest()
+ self.implicitStore = ImplicitStore(sut)
+ return self.implicitStore
+
+
+ def skipit(self):
+ raise SkipTest("No private attribute tests.")
+
+ test_calendarObjectsWithDotFile = skipit
+ test_countComponentTypes = skipit
+ test_init = skipit
+ test_calendarObjectsWithDirectory = skipit
+ test_hasCalendarResourceUIDSomewhereElse = skipit
+
+del FileStorageTests
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/util.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/datastore/test/util.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,240 @@
+# -*- test-case-name: txdav.carddav.datastore.test -*-
+##
+# Copyright (c) 2010-2013 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 twisted.trial.unittest import TestCase
+from twext.python.clsprop import classproperty
+from twisted.internet.defer import inlineCallbacks
+
+"""
+Store test utility functions
+"""
+
+from twistedcaldav.config import config
+from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryService, \
+ ICalendarStoreDirectoryRecord
+from txdav.common.datastore.test.util import TestStoreDirectoryService, \
+ TestStoreDirectoryRecord, theStoreBuilder, CommonCommonTests, \
+ populateCalendarsFrom
+from zope.interface.declarations import implements
+
+class TestCalendarStoreDirectoryService(TestStoreDirectoryService):
+
+ implements(ICalendarStoreDirectoryService)
+
+ def __init__(self):
+ super(TestCalendarStoreDirectoryService, self).__init__()
+ self.recordsByCUA = {}
+
+
+ def recordWithCalendarUserAddress(self, cuaddr):
+ return self.recordsByCUA.get(cuaddr)
+
+
+ def addRecord(self, record):
+ super(TestCalendarStoreDirectoryService, self).addRecord(record)
+ for cuaddr in record.calendarUserAddresses:
+ self.recordsByCUA[cuaddr] = record
+
+
+
+class TestCalendarStoreDirectoryRecord(TestStoreDirectoryRecord):
+
+ implements(ICalendarStoreDirectoryRecord)
+
+ def __init__(
+ self,
+ uid,
+ shortNames,
+ fullName,
+ calendarUserAddresses,
+ cutype="INDIVIDUAL",
+ locallyHosted=True,
+ thisServer=True,
+ ):
+
+ super(TestCalendarStoreDirectoryRecord, self).__init__(uid, shortNames, fullName)
+ self.uid = uid
+ self.shortNames = shortNames
+ self.fullName = fullName
+ self.displayName = self.fullName if self.fullName else self.shortNames[0]
+ self.calendarUserAddresses = calendarUserAddresses
+ self.cutype = cutype
+ self._locallyHosted = locallyHosted
+ self._thisServer = thisServer
+
+
+ def canonicalCalendarUserAddress(self):
+ cua = ""
+ for candidate in self.calendarUserAddresses:
+ # Pick the first one, but urn:uuid: and mailto: can override
+ if not cua:
+ cua = candidate
+ # But always immediately choose the urn:uuid: form
+ if candidate.startswith("urn:uuid:"):
+ cua = candidate
+ break
+ # Prefer mailto: if no urn:uuid:
+ elif candidate.startswith("mailto:"):
+ cua = candidate
+ return cua
+
+
+ def locallyHosted(self):
+ return self._locallyHosted
+
+
+ def thisServer(self):
+ return self._thisServer
+
+
+ def calendarsEnabled(self):
+ return True
+
+
+ def enabledAsOrganizer(self):
+ if self.cutype == "INDIVIDUAL":
+ return True
+ elif self.recordType == "GROUP":
+ return config.Scheduling.Options.AllowGroupAsOrganizer
+ elif self.recordType == "ROOM":
+ return config.Scheduling.Options.AllowLocationAsOrganizer
+ elif self.recordType == "RESOURCE":
+ return config.Scheduling.Options.AllowResourceAsOrganizer
+ else:
+ return False
+
+
+ def getCUType(self):
+ return self.cutype
+
+
+ def canAutoSchedule(self, organizer):
+ return False
+
+
+ def getAutoScheduleMode(self, organizer):
+ return "automatic"
+
+
+ def isProxyFor(self, other):
+ return False
+
+
+
+def buildDirectory(homes=None):
+
+ directory = TestCalendarStoreDirectoryService()
+
+ # User accounts
+ for ctr in range(1, 100):
+ directory.addRecord(TestCalendarStoreDirectoryRecord(
+ "user%02d" % (ctr,),
+ ("user%02d" % (ctr,),),
+ "User %02d" % (ctr,),
+ frozenset((
+ "urn:uuid:user%02d" % (ctr,),
+ "mailto:user%02d at example.com" % (ctr,),
+ )),
+ ))
+
+ homes = set(homes) if homes is not None else set()
+ homes.update((
+ "home1",
+ "home2",
+ "Home_attachments",
+ "home_bad",
+ "home_defaults",
+ "home_no_splits",
+ "home_splits",
+ "home_splits_shared",
+ ))
+ for uid in homes:
+ directory.addRecord(buildDirectoryRecord(uid))
+
+ return directory
+
+
+
+def buildDirectoryRecord(uid):
+ return TestCalendarStoreDirectoryRecord(
+ uid,
+ (uid,),
+ uid.capitalize(),
+ frozenset((
+ "urn:uuid:%s" % (uid,),
+ "mailto:%s at example.com" % (uid,),
+ )),
+ )
+
+
+
+def buildCalendarStore(testCase, notifierFactory, directoryService=None, homes=None):
+ if directoryService is None:
+ directoryService = buildDirectory(homes=homes)
+ return theStoreBuilder.buildStore(testCase, notifierFactory, directoryService)
+
+
+
+class CommonStoreTests(CommonCommonTests, TestCase):
+
+ @inlineCallbacks
+ def setUp(self):
+ yield super(CommonStoreTests, self).setUp()
+ self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+ yield self.populate()
+
+
+ @inlineCallbacks
+ def populate(self):
+ yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
+ self.notifierFactory.reset()
+
+
+ @classproperty(cache=False)
+ def requirements(cls): #@NoSelf
+ return {
+ "user01": {
+ "calendar_1": {
+ },
+ "inbox": {
+ },
+ },
+ "user02": {
+ "calendar_1": {
+ },
+ "inbox": {
+ },
+ },
+ "user03": {
+ "calendar_1": {
+ },
+ "inbox": {
+ },
+ },
+ "user04": {
+ "calendar_1": {
+ },
+ "inbox": {
+ },
+ },
+ }
+
+
+ def storeUnderTest(self):
+ """
+ Create and return a L{CalendarStore} for testing.
+ """
+ return self._sqlCalendarStore
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/icalendardirectoryservice.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/icalendardirectoryservice.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/caldav/icalendardirectoryservice.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,135 @@
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Common directory service interfaces for calendaring
+"""
+
+from txdav.common.idirectoryservice import IStoreDirectoryService, \
+ IStoreDirectoryRecord
+from zope.interface.interface import Attribute
+
+
+__all__ = [
+ "ICalendarStoreDirectoryService",
+ "ICalendarStoreDirectoryRecord",
+]
+
+class ICalendarStoreDirectoryService(IStoreDirectoryService):
+ """
+ Directory Service for looking up users.
+ """
+
+ def recordWithCalendarUserAddress(cuaddr): #@NoSelf
+ """
+ Return the record for the specified calendar user address.
+
+ @return: the record.
+ @rtype: L{ICalendarStoreDirectoryRecord}
+ """
+
+
+
+class ICalendarStoreDirectoryRecord(IStoreDirectoryRecord):
+ """
+ Record object for calendar users.
+
+ A record identifies a "user" in the system.
+ """
+
+ calendarUserAddresses = Attribute("Calendar users address for entity associated with the record: C{frozenset}")
+
+ def canonicalCalendarUserAddress(): #@NoSelf
+ """
+ The canonical calendar user address to use for this record.
+
+ @return: the canonical calendar user address.
+ @rtype: C{str}
+ """
+
+ def locallyHosted(): #@NoSelf
+ """
+ Indicates whether the record is host on this specific server "pod".
+
+ @return: C{True} if locally hosted.
+ @rtype: C{bool}
+ """
+
+ def thisServer(): #@NoSelf
+ """
+ Indicates whether the record is hosted on this server or another "pod"
+ that hosts the same directory service.
+
+ @return: C{True} if hosted by this service.
+ @rtype: C{bool}
+ """
+
+ def calendarsEnabled(): #@NoSelf
+ """
+ Indicates whether the record enabled for using the calendar service.
+
+ @return: C{True} if enabled for this service.
+ @rtype: C{bool}
+ """
+
+ def enabledAsOrganizer(): #@NoSelf
+ """
+ Indicates that the record is allowed to be the Organizer in calendar data.
+
+ @return: C{True} if allowed to be the Organizer.
+ @rtype: C{bool}
+ """
+
+ def getCUType(): #@NoSelf
+ """
+ Indicates the calendar user type for this record. It is the RFC 5545 CUTYPE value.
+
+ @return: the calendar user type.
+ @rtype: C{str}
+ """
+
+ def canAutoSchedule(organizer): #@NoSelf
+ """
+ Indicates that calendar data for this record can be automatically scheduled.
+
+ @param organizer: the organizer of the scheduling message being processed
+ @type organizer: C{str}
+
+ @return: C{True} if automatically scheduled.
+ @rtype: C{bool}
+ """
+
+ def getAutoScheduleMode(organizer): #@NoSelf
+ """
+ Indicates the mode of automatic scheduling used for this record.
+
+ @param organizer: the organizer of the scheduling message being processed
+ @type organizer: C{str}
+
+ @return: C{True} if automatically scheduled.
+ @rtype: C{bool}
+ """
+
+ def isProxyFor(other): #@NoSelf
+ """
+ Test whether the record is a calendar user proxy for the specified record.
+
+ @param other: record to test
+ @type other: L{IDirectoryRecord}
+
+ @return: C{True} if it is a proxy.
+ @rtype: C{bool}
+ """
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_dump.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_dump.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_dump.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,64 @@
+# -*- test-case-name: txdav.common.datastore.test.test_sql_tables -*-
+##
+# Copyright (c) 2010-2013 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 twisted.internet.defer import inlineCallbacks, returnValue
+from twext.enterprise.dal.model import Schema, Table, Column, Sequence
+from twext.enterprise.dal.parseschema import addSQLToSchema
+
+"""
+Dump a postgres DB into an L{Schema} model object.
+"""
+
+ at inlineCallbacks
+def dumpSchema(txn, title, schemaname="public"):
+ """
+ Generate the L{Schema}.
+ """
+
+ schemaname = schemaname.lower()
+
+ schema = Schema(title)
+ tables = {}
+
+ # Tables
+ rows = yield txn.execSQL("select table_name from information_schema.tables where table_schema = '%s';" % (schemaname,))
+ for row in rows:
+ name = row[0]
+ table = Table(schema, name)
+ tables[name] = table
+
+ # Columns
+ rows = yield txn.execSQL("select column_name from information_schema.columns where table_schema = '%s' and table_name = '%s';" % (schemaname, name,))
+ for row in rows:
+ name = row[0]
+ # TODO: figure out the type
+ column = Column(table, name, None)
+ table.columns.append(column)
+
+ # Indexes
+ # TODO: handle implicit indexes created via primary key() and unique() statements within CREATE TABLE
+ rows = yield txn.execSQL("select indexdef from pg_indexes where schemaname = '%s';" % (schemaname,))
+ for indexdef in rows:
+ addSQLToSchema(schema, indexdef[0].replace("%s." % (schemaname,), ""))
+
+ # Sequences
+ rows = yield txn.execSQL("select sequence_name from information_schema.sequences where sequence_schema = '%s';" % (schemaname,))
+ for row in rows:
+ name = row[0]
+ Sequence(schema, name)
+
+ returnValue(schema)
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v17.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v17.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v17.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,399 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "MESSAGE" nclob,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK (
+ "RESOURCE_ID" integer primary key
+);
+
+create table ADDRESSBOOK_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK on delete cascade,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_RESOURCE_ID", "VCARD_UID")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer references ADDRESSBOOK,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '17');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_BIND_RESO_205aa75c on ADDRESSBOOK_BIND (
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_f460d62d on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_9a848f39 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_cb101e6b on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v18.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v18.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v18.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,404 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "MESSAGE" nclob,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK (
+ "RESOURCE_ID" integer primary key
+);
+
+create table ADDRESSBOOK_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK on delete cascade,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_RESOURCE_ID", "VCARD_UID")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer references ADDRESSBOOK,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '18');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_BIND_RESO_205aa75c on ADDRESSBOOK_BIND (
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_f460d62d on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_9a848f39 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_cb101e6b on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,424 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK (
+ "RESOURCE_ID" integer primary key
+);
+
+create table ADDRESSBOOK_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK on delete cascade,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_RESOURCE_ID", "VCARD_UID")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer references ADDRESSBOOK,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '19');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '4');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_BIND_RESO_205aa75c on ADDRESSBOOK_BIND (
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_f460d62d on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_9a848f39 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_cb101e6b on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,455 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '20');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '4');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,455 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '21');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '4');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,457 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '22');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v17.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v17.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v17.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,572 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ MESSAGE text,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+-----------------
+-- AddressBook --
+-----------------
+
+create table ADDRESSBOOK (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+--------------------------
+-- AddressBook Metadata --
+--------------------------
+
+create table ADDRESSBOOK_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK on delete cascade, -- implicit index
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+----------------------
+-- AddressBook Bind --
+----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK
+
+create table ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index ADDRESSBOOK_BIND_RESOURCE_ID on
+ ADDRESSBOOK_BIND(ADDRESSBOOK_RESOURCE_ID);
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+---------------
+-- Revisions --
+---------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+-------------------------------
+-- AddressBook Object Revisions --
+-------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer references ADDRESSBOOK,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_ADDRESSBOOK_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, REVISION);
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '17');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '1');
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v18.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v18.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v18.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,581 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ MESSAGE text,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+-----------------
+-- AddressBook --
+-----------------
+
+create table ADDRESSBOOK (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+--------------------------
+-- AddressBook Metadata --
+--------------------------
+
+create table ADDRESSBOOK_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK on delete cascade, -- implicit index
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+----------------------
+-- AddressBook Bind --
+----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK
+
+create table ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index ADDRESSBOOK_BIND_RESOURCE_ID on
+ ADDRESSBOOK_BIND(ADDRESSBOOK_RESOURCE_ID);
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+---------------
+-- Revisions --
+---------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+-------------------------------
+-- AddressBook Object Revisions --
+-------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer references ADDRESSBOOK,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_ADDRESSBOOK_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, REVISION);
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '18');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '1');
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,605 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+-----------------
+-- AddressBook --
+-----------------
+
+create table ADDRESSBOOK (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+--------------------------
+-- AddressBook Metadata --
+--------------------------
+
+create table ADDRESSBOOK_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK on delete cascade, -- implicit index
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+----------------------
+-- AddressBook Bind --
+----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK
+
+create table ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index ADDRESSBOOK_BIND_RESOURCE_ID on
+ ADDRESSBOOK_BIND(ADDRESSBOOK_RESOURCE_ID);
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+---------------
+-- Revisions --
+---------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+-------------------------------
+-- AddressBook Object Revisions --
+-------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer references ADDRESSBOOK,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_ADDRESSBOOK_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, REVISION);
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '19');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '4');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '1');
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v20.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v20.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v20.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,665 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '20');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '4');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v21.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v21.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v21.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,665 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '21');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '4');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,667 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '22');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_17_to_18.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_17_to_18.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_17_to_18.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,35 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 17 to 18 --
+---------------------------------------------------
+
+
+-----------------
+-- GroupCacher --
+-----------------
+
+
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '18' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,59 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 18 to 19 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add ("DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null);
+
+
+-- Calendar bind related updates
+
+alter table CALENDAR_BIND
+ add ("BIND_REVISION" integer default 0 not null,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null);
+
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+
+
+-- Addressbook bind related updates
+
+alter table ADDRESSBOOK_BIND
+ add ("BIND_REVISION" integer default 0 not null);
+
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '19' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,274 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 19 to 20 --
+---------------------------------------------------
+
+----------------
+-- New Tables --
+----------------
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+
+
+-----------------------------
+-- Alter ADDRESSBOOK_HOME --
+-----------------------------
+
+alter table ADDRESSBOOK_HOME
+ add ("ADDRESSBOOK_PROPERTY_STORE_ID" integer not null);
+
+update ADDRESSBOOK_HOME
+ set ADDRESSBOOK_PROPERTY_STORE_ID = (
+ select ADDRESSBOOK_RESOURCE_ID
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_HOME_RESOURCE_ID = ADDRESSBOOK_HOME.RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ )
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_HOME_RESOURCE_ID = ADDRESSBOOK_HOME.RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ );
+
+
+--------------------------------
+-- change ADDRESSBOOK_OBJECT --
+--------------------------------
+
+alter table ADDRESSBOOK_OBJECT
+ add ("KIND" integer) -- enum ADDRESSBOOK_OBJECT_KIND
+ add ("ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME on delete cascade);
+
+update ADDRESSBOOK_OBJECT
+ set ADDRESSBOOK_HOME_RESOURCE_ID = (
+ select ADDRESSBOOK_HOME_RESOURCE_ID
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ ), KIND = 0 -- ADDRESSBOOK_OBJECT_KIND 'person'
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ );
+
+-- delete rows for shared and non-default address books
+delete
+ from ADDRESSBOOK_OBJECT
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT.ADDRESSBOOK_RESOURCE_ID and (
+ ADDRESSBOOK_BIND.BIND_MODE != 0 or -- not CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME != 'addressbook'
+ )
+ );
+
+-- add non null constraints after update and delete are complete
+alter table ADDRESSBOOK_OBJECT
+ modify ("KIND" not null)
+ modify ("ADDRESSBOOK_HOME_RESOURCE_ID" not null);
+
+alter table ADDRESSBOOK_OBJECT
+ drop column ADDRESSBOOK_RESOURCE_ID cascade constraints;
+
+alter table ADDRESSBOOK_OBJECT
+ add unique ("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME")
+ add unique ("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID");
+
+------------------------------------------
+-- change ADDRESSBOOK_OBJECT_REVISIONS --
+------------------------------------------
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ add ("OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME);
+
+update ADDRESSBOOK_OBJECT_REVISIONS
+ set OWNER_HOME_RESOURCE_ID = (
+ select ADDRESSBOOK_HOME_RESOURCE_ID
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ )
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ );
+
+-- delete rows for shared and non-default address books
+delete
+ from ADDRESSBOOK_OBJECT_REVISIONS
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_RESOURCE_ID and (
+ ADDRESSBOOK_BIND.BIND_MODE != 0 or -- not CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME != 'addressbook'
+ )
+ );
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ drop ("ADDRESSBOOK_RESOURCE_ID");
+
+-- New indexes
+create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+
+-------------------------------
+-- change RESOURCE_PROPERTY --
+-------------------------------
+
+-- delete rows for shared and non-default address books
+delete
+ from RESOURCE_PROPERTY
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = RESOURCE_PROPERTY.RESOURCE_ID and (
+ ADDRESSBOOK_BIND.BIND_MODE != 0 or -- not CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME != 'addressbook'
+ )
+ );
+
+
+-------------------------------------
+-- Drop ADDRESSBOOK related tables --
+-------------------------------------
+
+drop table ADDRESSBOOK_METADATA;
+drop table ADDRESSBOOK_BIND;
+drop table ADDRESSBOOK;
+
+-- update schema version
+update CALENDARSERVER set VALUE = '20' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_20_to_21.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_20_to_21.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_20_to_21.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,29 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 20 to 21 --
+---------------------------------------------------
+
+--------------------------
+-- Update CALENDAR_BIND --
+--------------------------
+
+update CALENDAR_BIND set MESSAGE = 'shared' where BIND_MODE = 0 and CALENDAR_RESOURCE_ID in (select CALENDAR_RESOURCE_ID from CALENDAR_BIND group by CALENDAR_RESOURCE_ID having count(CALENDAR_RESOURCE_ID) > 1);
+
+-- update schema version
+update CALENDARSERVER set VALUE = '21' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_21_to_22.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_21_to_22.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_21_to_22.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,35 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 21 to 22 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add ("AVAILABILITY" nclob default null);
+
+
+-- Calendar related updates
+
+alter table CALENDAR_BIND
+ add ("TIMEZONE" nclob default null);
+
+
+-- update schema version
+update CALENDARSERVER set VALUE = '22' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,32 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 22 to 23 --
+---------------------------------------------------
+
+-- Object Splitter Work --
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+
+-- update schema version
+update CALENDARSERVER set VALUE = '23' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_17_to_18.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_17_to_18.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_17_to_18.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,31 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 17 to 18 --
+---------------------------------------------------
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '18' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,60 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 18 to 19 --
+---------------------------------------------------
+
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add column DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ add column DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ add column ALARM_VEVENT_TIMED text default null,
+ add column ALARM_VEVENT_ALLDAY text default null,
+ add column ALARM_VTODO_TIMED text default null,
+ add column ALARM_VTODO_ALLDAY text default null;
+
+
+-- Calendar bind related updates
+
+alter table CALENDAR_BIND
+ add column BIND_REVISION integer default 0 not null,
+ add column TRANSP integer default 0 not null,
+ add column ALARM_VEVENT_TIMED text default null,
+ add column ALARM_VEVENT_ALLDAY text default null,
+ add column ALARM_VTODO_TIMED text default null,
+ add column ALARM_VTODO_ALLDAY text default null;
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+-- Addressbook bind related updates
+
+alter table ADDRESSBOOK_BIND
+ add column BIND_REVISION integer default 0 not null;
+
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '19' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_19_to_20.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_19_to_20.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_19_to_20.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,264 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 19 to 20 --
+---------------------------------------------------
+
+----------------
+-- New Tables --
+----------------
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+
+
+-----------------------------
+-- Alter ADDRESSBOOK_HOME --
+-----------------------------
+
+alter table ADDRESSBOOK_HOME
+ add column ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null;
+
+update ADDRESSBOOK_HOME
+ set ADDRESSBOOK_PROPERTY_STORE_ID = (
+ select ADDRESSBOOK_RESOURCE_ID
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_HOME_RESOURCE_ID = ADDRESSBOOK_HOME.RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ )
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_HOME_RESOURCE_ID = ADDRESSBOOK_HOME.RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ );
+
+
+--------------------------------
+-- change ADDRESSBOOK_OBJECT --
+--------------------------------
+
+alter table ADDRESSBOOK_OBJECT
+ add column KIND integer, -- enum ADDRESSBOOK_OBJECT_KIND
+ add column ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME on delete cascade;
+
+update ADDRESSBOOK_OBJECT
+ set ADDRESSBOOK_HOME_RESOURCE_ID = (
+ select ADDRESSBOOK_HOME_RESOURCE_ID
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ ), KIND = 0 -- ADDRESSBOOK_OBJECT_KIND 'person'
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ );
+
+-- delete rows for shared and non-default address books
+delete
+ from ADDRESSBOOK_OBJECT
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT.ADDRESSBOOK_RESOURCE_ID and (
+ ADDRESSBOOK_BIND.BIND_MODE != 0 or -- not CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME != 'addressbook'
+ )
+ );
+
+-- add non null constraints after update and delete are complete
+alter table ADDRESSBOOK_OBJECT
+ alter column KIND set not null,
+ alter column ADDRESSBOOK_HOME_RESOURCE_ID set not null,
+ drop column ADDRESSBOOK_RESOURCE_ID,
+ add unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME),
+ add unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID);
+
+
+------------------------------------------
+-- change ADDRESSBOOK_OBJECT_REVISIONS --
+------------------------------------------
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ add column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME;
+
+update ADDRESSBOOK_OBJECT_REVISIONS
+ set OWNER_ADDRESSBOOK_HOME_RESOURCE_ID = (
+ select ADDRESSBOOK_HOME_RESOURCE_ID
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ )
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_RESOURCE_ID and
+ ADDRESSBOOK_BIND.BIND_MODE = 0 and -- CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
+ );
+
+-- delete rows for shared and non-default address books
+delete
+ from ADDRESSBOOK_OBJECT_REVISIONS
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_RESOURCE_ID and (
+ ADDRESSBOOK_BIND.BIND_MODE != 0 or -- not CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME != 'addressbook'
+ )
+ );
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ drop column ADDRESSBOOK_RESOURCE_ID;
+
+-- New indexes
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------
+-- change RESOURCE_PROPERTY --
+-------------------------------
+
+-- delete rows for shared and non-default address books
+delete
+ from RESOURCE_PROPERTY
+ where exists (
+ select *
+ from ADDRESSBOOK_BIND
+ where
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_ID = RESOURCE_PROPERTY.RESOURCE_ID and (
+ ADDRESSBOOK_BIND.BIND_MODE != 0 or -- not CALENDAR_BIND_MODE 'own'
+ ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME != 'addressbook'
+ )
+ );
+
+
+-------------------------------------
+-- Drop ADDRESSBOOK related tables --
+-------------------------------------
+
+drop table ADDRESSBOOK_METADATA;
+drop table ADDRESSBOOK_BIND;
+drop table ADDRESSBOOK;
+
+-- update schema version
+update CALENDARSERVER set VALUE = '20' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_20_to_21.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_20_to_21.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_20_to_21.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,30 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 20 to 21 --
+---------------------------------------------------
+
+
+--------------------------
+-- Update CALENDAR_BIND --
+--------------------------
+
+update CALENDAR_BIND set MESSAGE = 'shared' where BIND_MODE = 0 and CALENDAR_RESOURCE_ID in (select CALENDAR_RESOURCE_ID from CALENDAR_BIND group by CALENDAR_RESOURCE_ID having count(CALENDAR_RESOURCE_ID) > 1);
+
+-- update schema version
+update CALENDARSERVER set VALUE = '21' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_21_to_22.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_21_to_22.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_21_to_22.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,35 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 21 to 22 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add column AVAILABILITY text default null;
+
+
+-- Calendar bind related updates
+
+alter table CALENDAR_BIND
+ add column TIMEZONE text default null;
+
+
+ -- update schema version
+update CALENDARSERVER set VALUE = '22' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,32 @@
+----
+-- Copyright (c) 2011-2013 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.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 22 to 23 --
+---------------------------------------------------
+
+-- Object Splitter Work --
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+
+ -- update schema version
+update CALENDARSERVER set VALUE = '23' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,86 @@
+# -*- test-case-name: txdav.common.datastore.upgrade.sql.test -*-
+# #
+# Copyright (c) 2011-2013 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 twext.enterprise.dal.syntax import Update
+
+from twisted.internet.defer import inlineCallbacks
+
+from txdav.base.propertystore.base import PropertyName
+from txdav.common.datastore.sql_tables import _ABO_KIND_GROUP, schema
+from txdav.common.datastore.upgrade.sql.upgrades.util import updateAddressBookDataVersion, \
+ doToEachHomeNotAtVersion, removeProperty, cleanPropertyStore
+from txdav.xml import element
+
+"""
+AddressBook Data upgrade from database version 1 to 2
+"""
+
+UPGRADE_TO_VERSION = 2
+
+ at inlineCallbacks
+def doUpgrade(sqlStore):
+ """
+ fill in members tables and increment data version
+ """
+ yield populateMemberTables(sqlStore)
+ yield removeResourceType(sqlStore)
+
+ # bump data version
+ yield updateAddressBookDataVersion(sqlStore, UPGRADE_TO_VERSION)
+
+
+
+ at inlineCallbacks
+def populateMemberTables(sqlStore):
+ """
+ Set the group kind and and members tables
+ """
+ @inlineCallbacks
+ def doIt(txn, homeResourceID):
+ """
+ KIND is set to person by schema upgrade.
+ To upgrade MEMBERS and FOREIGN_MEMBERS:
+ 1. Set group KIND (avoids assert)
+ 2. Write groups. Write logic will fill in MEMBERS and FOREIGN_MEMBERS
+ (Remember that all members resource IDs must already be in the address book).
+ """
+ home = yield txn.addressbookHomeWithResourceID(homeResourceID)
+ abObjectResources = yield home.addressbook().objectResources()
+ for abObject in abObjectResources:
+ component = yield abObject.component()
+ lcResourceKind = component.resourceKind().lower() if component.resourceKind() else component.resourceKind()
+ if lcResourceKind == "group":
+ # update kind
+ abo = schema.ADDRESSBOOK_OBJECT
+ yield Update({abo.KIND: _ABO_KIND_GROUP},
+ Where=abo.RESOURCE_ID == abObject._resourceID,
+ ).on(txn)
+ abObject._kind = _ABO_KIND_GROUP
+ #update rest
+ yield abObject.setComponent(component)
+
+ # Do this to each calendar home not already at version 2
+ yield doToEachHomeNotAtVersion(sqlStore, schema.ADDRESSBOOK_HOME, UPGRADE_TO_VERSION, doIt)
+
+
+
+ at inlineCallbacks
+def removeResourceType(sqlStore):
+ sqlTxn = sqlStore.newTransaction()
+ yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceType))
+ yield sqlTxn.commit()
+ yield cleanPropertyStore()
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,90 @@
+# -*- test-case-name: txdav.common.datastore.upgrade.sql.test -*-
+##
+# Copyright (c) 2011-2013 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 twext.enterprise.dal.syntax import Update
+from txdav.xml.parser import WebDAVDocument
+from twisted.internet.defer import inlineCallbacks
+from twistedcaldav import caldavxml
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty,\
+ removeProperty, updateCalendarDataVersion, doToEachHomeNotAtVersion
+
+"""
+Calendar data upgrade from database version 1 to 2
+"""
+
+UPGRADE_TO_VERSION = 2
+
+ at inlineCallbacks
+def doUpgrade(sqlStore):
+ """
+ Do the required upgrade steps.
+ """
+ yield moveSupportedComponentSetProperties(sqlStore)
+ yield splitCalendars(sqlStore)
+
+ # Always bump the DB value
+ yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
+
+
+
+ at inlineCallbacks
+def moveSupportedComponentSetProperties(sqlStore):
+ """
+ Need to move all the CalDAV:supported-component-set properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_METADATA table column,
+ extracting the new format value from the XML property.
+ """
+
+ sqlTxn = sqlStore.newTransaction()
+ try:
+ rows = (yield rowsForProperty(sqlTxn, caldavxml.SupportedCalendarComponentSet))
+ for calendar_rid, value in rows:
+ prop = WebDAVDocument.fromString(value).root_element
+ supported_components = ",".join(sorted([comp.attributes["name"].upper() for comp in prop.children]))
+ meta = schema.CALENDAR_METADATA
+ yield Update(
+ {
+ meta.SUPPORTED_COMPONENTS : supported_components
+ },
+ Where=(meta.RESOURCE_ID == calendar_rid)
+ ).on(sqlTxn)
+
+ yield removeProperty(sqlTxn, caldavxml.SupportedCalendarComponentSet)
+ yield sqlTxn.commit()
+ except RuntimeError:
+ yield sqlTxn.abort()
+ raise
+
+
+
+ at inlineCallbacks
+def splitCalendars(sqlStore):
+ """
+ Split all calendars by component type.
+ """
+
+ @inlineCallbacks
+ def doIt(txn, homeResourceID):
+ """
+ Split each regular calendar in the home.
+ """
+ home = yield txn.calendarHomeWithResourceID(homeResourceID)
+ yield home.splitCalendars()
+
+ # Do this to each calendar home not already at version 2
+ yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, doIt)
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_2_to_3.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_2_to_3.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_2_to_3.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,41 @@
+# -*- test-case-name: txdav.common.datastore.upgrade.sql.test -*-
+##
+# Copyright (c) 2011-2013 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.
+##
+
+"""
+Upgrade to deal with normalization of UUIDs in
+CALENDAR_HOME/ADDRESSBOOK_HOME/NOTIFICATION/APN_SUBSCRIPTIONS tables, as well
+as in calendar data and properties.
+"""
+
+from txdav.common.datastore.sql import fixUUIDNormalization
+from twisted.internet.defer import inlineCallbacks
+from txdav.common.datastore.upgrade.sql.upgrades.util import updateCalendarDataVersion
+
+UPGRADE_TO_VERSION = 3
+
+ at inlineCallbacks
+def doUpgrade(sqlStore):
+ """
+ Do the UUID-normalization upgrade if necessary and then bump the data
+ version to indicate that it's been done.
+ """
+ yield fixUUIDNormalization(sqlStore)
+
+ # Always bump the DB value
+ yield updateCalendarDataVersion(
+ sqlStore, UPGRADE_TO_VERSION
+ )
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,323 @@
+# -*- test-case-name: txdav.common.datastore.upgrade.sql.test -*-
+##
+# Copyright (c) 2011-2013 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 twext.enterprise.dal.syntax import Select, Delete, Parameter
+
+from twisted.internet.defer import inlineCallbacks
+
+from twistedcaldav import caldavxml, customxml
+
+from txdav.base.propertystore.base import PropertyName
+from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
+from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty, updateCalendarDataVersion, \
+ updateAllCalendarHomeDataVersions, removeProperty, cleanPropertyStore
+from txdav.xml.parser import WebDAVDocument
+from txdav.xml import element
+from twisted.python.failure import Failure
+
+"""
+Data upgrade from database version 3 to 4
+"""
+
+UPGRADE_TO_VERSION = 4
+BATCH_SIZE = 100
+
+ at inlineCallbacks
+def doUpgrade(sqlStore):
+ """
+ Do the required upgrade steps.
+ """
+ yield moveDefaultCalendarProperties(sqlStore)
+ yield moveCalendarTranspProperties(sqlStore)
+ yield moveDefaultAlarmProperties(sqlStore)
+ yield removeResourceType(sqlStore)
+
+ # Always bump the DB value
+ yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
+ yield updateAllCalendarHomeDataVersions(sqlStore, UPGRADE_TO_VERSION)
+
+
+
+ at inlineCallbacks
+def moveDefaultCalendarProperties(sqlStore):
+ """
+ Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
+ the new value from the XML property.
+ """
+
+ meta = schema.CALENDAR_HOME_METADATA
+ yield _processDefaultCalendarProperty(sqlStore, caldavxml.ScheduleDefaultCalendarURL, meta.DEFAULT_EVENTS)
+ yield _processDefaultCalendarProperty(sqlStore, customxml.ScheduleDefaultTasksURL, meta.DEFAULT_TASKS)
+
+
+
+ at inlineCallbacks
+def _processDefaultCalendarProperty(sqlStore, propname, colname):
+ """
+ Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
+
+ Since the number of calendar homes may well be large, we need to do this in batches.
+ """
+
+ cb = schema.CALENDAR_BIND
+ rp = schema.RESOURCE_PROPERTY
+
+ try:
+ while True:
+ sqlTxn = sqlStore.newTransaction()
+ rows = (yield rowsForProperty(sqlTxn, propname, batch=BATCH_SIZE))
+ if len(rows) == 0:
+ yield sqlTxn.commit()
+ break
+ delete_ids = []
+ for inbox_rid, value in rows:
+ delete_ids.append(inbox_rid)
+ ids = yield Select(
+ [cb.CALENDAR_HOME_RESOURCE_ID, ],
+ From=cb,
+ Where=cb.CALENDAR_RESOURCE_ID == inbox_rid,
+ ).on(sqlTxn)
+ if len(ids) > 0:
+
+ calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
+ if calendarHome is not None:
+
+ prop = WebDAVDocument.fromString(value).root_element
+ defaultCalendar = str(prop.children[0])
+ parts = defaultCalendar.split("/")
+ if len(parts) == 5:
+
+ calendarName = parts[-1]
+ calendarHomeUID = parts[-2]
+ expectedHome = (yield sqlTxn.calendarHomeWithUID(calendarHomeUID))
+ if expectedHome is not None and expectedHome.id() == calendarHome.id():
+
+ calendar = (yield calendarHome.calendarWithName(calendarName))
+ if calendar is not None:
+ yield calendarHome.setDefaultCalendar(
+ calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
+ )
+
+ # Always delete the rows so that batch processing works correctly
+ yield Delete(
+ From=rp,
+ Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
+ (rp.NAME == PropertyName.fromElement(propname).toString()),
+ ).on(sqlTxn, ids=delete_ids)
+
+ yield sqlTxn.commit()
+
+ yield cleanPropertyStore()
+
+ except RuntimeError:
+ f = Failure()
+ yield sqlTxn.abort()
+ f.raiseException()
+
+
+
+ at inlineCallbacks
+def moveCalendarTranspProperties(sqlStore):
+ """
+ Need to move all the CalDAV:schedule-calendar-transp properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
+ the new value from the XML property.
+ """
+
+ cb = schema.CALENDAR_BIND
+ rp = schema.RESOURCE_PROPERTY
+
+ try:
+ calendars_for_id = {}
+ while True:
+ sqlTxn = sqlStore.newTransaction()
+ rows = (yield rowsForProperty(sqlTxn, caldavxml.ScheduleCalendarTransp, with_uid=True, batch=BATCH_SIZE))
+ if len(rows) == 0:
+ yield sqlTxn.commit()
+ break
+ delete_ids = []
+ for calendar_rid, value, viewer in rows:
+ delete_ids.append(calendar_rid)
+ if calendar_rid not in calendars_for_id:
+ ids = yield Select(
+ [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
+ From=cb,
+ Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
+ ).on(sqlTxn)
+ calendars_for_id[calendar_rid] = ids
+
+ if viewer:
+ calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
+ else:
+ calendarHome = None
+ for row in calendars_for_id[calendar_rid]:
+ home_id, bind_mode = row
+ if bind_mode == _BIND_MODE_OWN:
+ calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
+ break
+
+ if calendarHome is not None:
+ prop = WebDAVDocument.fromString(value).root_element
+ calendar = (yield calendarHome.childWithID(calendar_rid))
+ if calendar is not None:
+ yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
+
+ # Always delete the rows so that batch processing works correctly
+ yield Delete(
+ From=rp,
+ Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
+ (rp.NAME == PropertyName.fromElement(caldavxml.ScheduleCalendarTransp).toString()),
+ ).on(sqlTxn, ids=delete_ids)
+
+ yield sqlTxn.commit()
+
+ sqlTxn = sqlStore.newTransaction()
+ yield removeProperty(sqlTxn, PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
+ yield sqlTxn.commit()
+ yield cleanPropertyStore()
+
+ except RuntimeError:
+ f = Failure()
+ yield sqlTxn.abort()
+ f.raiseException()
+
+
+
+ at inlineCallbacks
+def moveDefaultAlarmProperties(sqlStore):
+ """
+ Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
+ the new value from the XML property.
+ """
+
+ yield _processDefaultAlarmProperty(
+ sqlStore,
+ caldavxml.DefaultAlarmVEventDateTime,
+ True,
+ True,
+ )
+ yield _processDefaultAlarmProperty(
+ sqlStore,
+ caldavxml.DefaultAlarmVEventDate,
+ True,
+ False,
+ )
+ yield _processDefaultAlarmProperty(
+ sqlStore,
+ caldavxml.DefaultAlarmVToDoDateTime,
+ False,
+ True,
+ )
+ yield _processDefaultAlarmProperty(
+ sqlStore,
+ caldavxml.DefaultAlarmVToDoDate,
+ False,
+ False,
+ )
+
+
+
+ at inlineCallbacks
+def _processDefaultAlarmProperty(sqlStore, propname, vevent, timed):
+ """
+ Move the specified property value to the matching CALENDAR_HOME_METADATA or CALENDAR_BIND table column.
+
+ Since the number of properties may well be large, we need to do this in batches.
+ """
+
+ hm = schema.CALENDAR_HOME_METADATA
+ cb = schema.CALENDAR_BIND
+ rp = schema.RESOURCE_PROPERTY
+
+ try:
+ calendars_for_id = {}
+ while True:
+ sqlTxn = sqlStore.newTransaction()
+ rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
+ if len(rows) == 0:
+ yield sqlTxn.commit()
+ break
+ delete_ids = []
+ for rid, value, viewer in rows:
+ delete_ids.append(rid)
+
+ prop = WebDAVDocument.fromString(value).root_element
+ alarm = str(prop.children[0]) if prop.children else None
+
+ # First check if the rid is a home - this is the most common case
+ ids = yield Select(
+ [hm.RESOURCE_ID, ],
+ From=hm,
+ Where=hm.RESOURCE_ID == rid,
+ ).on(sqlTxn)
+
+ if len(ids) > 0:
+ # Home object
+ calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
+ if calendarHome is not None:
+ yield calendarHome.setDefaultAlarm(alarm, vevent, timed)
+ else:
+ # rid is a calendar - we need to find the per-user calendar for the resource viewer
+ if rid not in calendars_for_id:
+ ids = yield Select(
+ [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
+ From=cb,
+ Where=cb.CALENDAR_RESOURCE_ID == rid,
+ ).on(sqlTxn)
+ calendars_for_id[rid] = ids
+
+ if viewer:
+ calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
+ else:
+ calendarHome = None
+ for row in calendars_for_id[rid]:
+ home_id, bind_mode = row
+ if bind_mode == _BIND_MODE_OWN:
+ calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
+ break
+
+ if calendarHome is not None:
+ calendar = yield calendarHome.childWithID(rid)
+ if calendar is not None:
+ yield calendar.setDefaultAlarm(alarm, vevent, timed)
+
+ # Always delete the rows so that batch processing works correctly
+ yield Delete(
+ From=rp,
+ Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
+ (rp.NAME == PropertyName.fromElement(propname).toString()),
+ ).on(sqlTxn, ids=delete_ids)
+
+ yield sqlTxn.commit()
+
+ yield cleanPropertyStore()
+
+ except RuntimeError:
+ f = Failure()
+ yield sqlTxn.abort()
+ f.raiseException()
+
+
+
+ at inlineCallbacks
+def removeResourceType(sqlStore):
+ sqlTxn = sqlStore.newTransaction()
+ yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceType))
+ yield sqlTxn.commit()
+ yield cleanPropertyStore()
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,207 @@
+# -*- test-case-name: txdav.common.datastore.upgrade.sql.test -*-
+##
+# Copyright (c) 2011-2013 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 twext.enterprise.dal.syntax import Select, Delete, Parameter
+
+from twisted.internet.defer import inlineCallbacks
+from twisted.python.failure import Failure
+
+from twistedcaldav import caldavxml, customxml
+
+from txdav.base.propertystore.base import PropertyName
+from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
+from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty, updateCalendarDataVersion, \
+ updateAllCalendarHomeDataVersions, removeProperty, cleanPropertyStore
+from txdav.xml import element
+from txdav.xml.parser import WebDAVDocument
+from twext.web2.dav.resource import TwistedQuotaUsedProperty, \
+ TwistedGETContentMD5
+
+"""
+Data upgrade from database version 4 to 5
+"""
+
+UPGRADE_TO_VERSION = 5
+BATCH_SIZE = 100
+
+ at inlineCallbacks
+def doUpgrade(sqlStore):
+ """
+ Do the required upgrade steps.
+ """
+ yield moveCalendarTimezoneProperties(sqlStore)
+ yield moveCalendarAvailabilityProperties(sqlStore)
+ yield removeOtherProperties(sqlStore)
+
+ # Always bump the DB value
+ yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
+ yield updateAllCalendarHomeDataVersions(sqlStore, UPGRADE_TO_VERSION)
+
+
+
+ at inlineCallbacks
+def moveCalendarTimezoneProperties(sqlStore):
+ """
+ Need to move all the CalDAV:calendar-timezone properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
+ the new value from the XML property.
+ """
+
+ cb = schema.CALENDAR_BIND
+ rp = schema.RESOURCE_PROPERTY
+
+ try:
+ calendars_for_id = {}
+ while True:
+ sqlTxn = sqlStore.newTransaction()
+ rows = (yield rowsForProperty(sqlTxn, caldavxml.CalendarTimeZone, with_uid=True, batch=BATCH_SIZE))
+ if len(rows) == 0:
+ yield sqlTxn.commit()
+ break
+ delete_ids = []
+ for calendar_rid, value, viewer in rows:
+ delete_ids.append(calendar_rid)
+ if calendar_rid not in calendars_for_id:
+ ids = yield Select(
+ [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
+ From=cb,
+ Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
+ ).on(sqlTxn)
+ calendars_for_id[calendar_rid] = ids
+
+ if viewer:
+ calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
+ else:
+ calendarHome = None
+ for row in calendars_for_id[calendar_rid]:
+ home_id, bind_mode = row
+ if bind_mode == _BIND_MODE_OWN:
+ calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
+ break
+
+ if calendarHome is not None:
+ prop = WebDAVDocument.fromString(value).root_element
+ calendar = (yield calendarHome.childWithID(calendar_rid))
+ if calendar is not None:
+ yield calendar.setTimezone(prop.calendar())
+
+ # Always delete the rows so that batch processing works correctly
+ yield Delete(
+ From=rp,
+ Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
+ (rp.NAME == PropertyName.fromElement(caldavxml.CalendarTimeZone).toString()),
+ ).on(sqlTxn, ids=delete_ids)
+
+ yield sqlTxn.commit()
+
+ yield cleanPropertyStore()
+
+ except RuntimeError:
+ f = Failure()
+ yield sqlTxn.abort()
+ f.raiseException()
+
+
+
+ at inlineCallbacks
+def moveCalendarAvailabilityProperties(sqlStore):
+ """
+ Need to move all the CS:calendar-availability properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
+ the new value from the XML property.
+ """
+
+ cb = schema.CALENDAR_BIND
+ rp = schema.RESOURCE_PROPERTY
+
+ try:
+ while True:
+ sqlTxn = sqlStore.newTransaction()
+ rows = (yield rowsForProperty(sqlTxn, customxml.CalendarAvailability, batch=BATCH_SIZE))
+ if len(rows) == 0:
+ yield sqlTxn.commit()
+ break
+
+ # Map each calendar to a home id using a single query for efficiency
+ calendar_ids = [row[0] for row in rows]
+
+ home_map = yield Select(
+ [cb.CALENDAR_RESOURCE_ID, cb.CALENDAR_HOME_RESOURCE_ID, ],
+ From=cb,
+ Where=(cb.CALENDAR_RESOURCE_ID.In(Parameter("ids", len(calendar_ids)))).And(cb.BIND_MODE == _BIND_MODE_OWN),
+ ).on(sqlTxn, ids=calendar_ids)
+ calendar_to_home = dict(home_map)
+
+ # Move property to each home
+ for calendar_rid, value in rows:
+ if calendar_rid in calendar_to_home:
+ calendarHome = (yield sqlTxn.calendarHomeWithResourceID(calendar_to_home[calendar_rid]))
+
+ if calendarHome is not None:
+ prop = WebDAVDocument.fromString(value).root_element
+ yield calendarHome.setAvailability(prop.calendar())
+
+ # Always delete the rows so that batch processing works correctly
+ yield Delete(
+ From=rp,
+ Where=(rp.RESOURCE_ID.In(Parameter("ids", len(calendar_ids)))).And
+ (rp.NAME == PropertyName.fromElement(customxml.CalendarAvailability).toString()),
+ ).on(sqlTxn, ids=calendar_ids)
+
+ yield sqlTxn.commit()
+
+ yield cleanPropertyStore()
+
+ except RuntimeError:
+ f = Failure()
+ yield sqlTxn.abort()
+ f.raiseException()
+
+
+
+ at inlineCallbacks
+def removeOtherProperties(sqlStore):
+ """
+ Remove the following properties:
+
+ DAV:acl
+ DAV:getcontenttype
+ DAV:resource-id
+ {urn:ietf:params:xml:ns:caldav}originator
+ {urn:ietf:params:xml:ns:caldav}recipient
+ {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
+ {http://calendarserver.org/ns/}getctag
+ {http://twistedmatrix.com/xml_namespace/dav/private/}quota-used
+ {http://twistedmatrix.com/xml_namespace/dav/}getcontentmd5
+ {http://twistedmatrix.com/xml_namespace/dav/}schedule-auto-respond
+
+ """
+ sqlTxn = sqlStore.newTransaction()
+
+ yield removeProperty(sqlTxn, PropertyName.fromElement(element.ACL))
+ yield removeProperty(sqlTxn, PropertyName.fromElement(element.GETContentType))
+ yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceID))
+ yield removeProperty(sqlTxn, PropertyName(caldavxml.caldav_namespace, "originator"))
+ yield removeProperty(sqlTxn, PropertyName(caldavxml.caldav_namespace, "recipient"))
+ yield removeProperty(sqlTxn, PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet))
+ yield removeProperty(sqlTxn, PropertyName.fromElement(customxml.GETCTag))
+ yield removeProperty(sqlTxn, PropertyName.fromElement(TwistedQuotaUsedProperty))
+ yield removeProperty(sqlTxn, PropertyName.fromElement(TwistedGETContentMD5))
+ yield removeProperty(sqlTxn, PropertyName(element.twisted_dav_namespace, "schedule-auto-respond"))
+
+ yield sqlTxn.commit()
+ yield cleanPropertyStore()
Added: CalendarServer/branches/users/gaya/directorybacker/txdav/common/idirectoryservice.py
===================================================================
--- CalendarServer/branches/users/gaya/directorybacker/txdav/common/idirectoryservice.py (rev 0)
+++ CalendarServer/branches/users/gaya/directorybacker/txdav/common/idirectoryservice.py 2013-10-03 22:51:14 UTC (rev 11787)
@@ -0,0 +1,57 @@
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Common directory service interfaces
+"""
+
+from zope.interface.interface import Interface, Attribute
+
+
+__all__ = [
+ "IStoreDirectoryService",
+ "IStoreDirectoryRecord",
+]
+
+class IStoreDirectoryService(Interface):
+ """
+ Directory Service for looking up users.
+ """
+
+ def recordWithUID(uid): #@NoSelf
+ """
+ Return the record for the specified store uid.
+
+ @return: the record.
+ @rtype: L{IStoreDirectoryRecord}
+ """
+
+
+
+class IStoreDirectoryRecord(Interface):
+ """
+ Directory record object
+
+ A record identifies a "user" in the system.
+ """
+
+ uid = Attribute("The record UID: C{str}")
+
+ shortNames = Attribute("Short names of the record: C{tuple}")
+
+ fullName = Attribute("Full name for the entity associated with the record: C{str}")
+
+ displayName = Attribute("Display name for entity associated with the record: C{str}")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20131003/81909596/attachment-0001.html>
More information about the calendarserver-changes
mailing list