[CalendarServer-changes] [10936] CalendarServer/branches/users/gaya/sharedgroups
source_changes at macosforge.org
source_changes at macosforge.org
Fri Mar 15 14:46:49 PDT 2013
Revision: 10936
http://trac.calendarserver.org//changeset/10936
Author: gaya at apple.com
Date: 2013-03-15 14:46:49 -0700 (Fri, 15 Mar 2013)
Log Message:
-----------
merge from trunk
Modified Paths:
--------------
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/amppush.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/applepush.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/notifier.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_amppush.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_applepush.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_notifier.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/config.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/gateway.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/push.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/gateway/caldavd.plist
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_gateway.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/upgrade.py
CalendarServer/branches/users/gaya/sharedgroups/conf/auth/resources-test.xml
CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-apple.plist
CalendarServer/branches/users/gaya/sharedgroups/run
CalendarServer/branches/users/gaya/sharedgroups/setup.py
CalendarServer/branches/users/gaya/sharedgroups/support/Makefile.Apple
CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/queue.py
CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/test/test_queue.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/config.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/appleopendirectory.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/calendaruserproxy.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts-modified.xml
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts.xml
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/augments.xml
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/proxies.xml
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_directory.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_opendirectory.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_proxyprincipalmembers.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/scheduling/imip/inbound.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/test/test_config.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_12_to_13.sql
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql
Removed Paths:
-------------
CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/__init__.py
CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarcommonextra.py
CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendardemotion.py
CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarmigrator.py
CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarpromotion.py
CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/test/
CalendarServer/branches/users/gaya/sharedgroups/doc/RFC/draft-daboo-srv-caldav.txt
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/amppush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/amppush.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/amppush.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -71,7 +71,7 @@
controlSocket.addFactory(PUSH_ROUTE, AMPPushForwardingFactory(self))
@inlineCallbacks
- def enqueue(self, id, dataChangedTimestamp=None):
+ def enqueue(self, transaction, id, dataChangedTimestamp=None):
if dataChangedTimestamp is None:
dataChangedTimestamp = int(time.time())
for protocol in self.protocols:
@@ -92,7 +92,7 @@
def enqueueFromWorker(self, id, dataChangedTimestamp=None):
if dataChangedTimestamp is None:
dataChangedTimestamp = int(time.time())
- self.master.enqueue(id, dataChangedTimestamp=dataChangedTimestamp)
+ self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp)
return {"status" : "OK"}
@@ -145,7 +145,7 @@
self.log_debug("Removed subscriber")
self.subscribers.remove(p)
- def enqueue(self, pushKey, dataChangedTimestamp=None):
+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
"""
Sends an AMP push notification to any clients subscribing to this pushKey.
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/applepush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/applepush.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/applepush.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -176,7 +176,7 @@
@inlineCallbacks
- def enqueue(self, pushKey, dataChangedTimestamp=None):
+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
"""
Sends an Apple Push Notification to any device token subscribed to
this pushKey.
@@ -207,9 +207,7 @@
if provider is not None:
# Look up subscriptions for this key
- txn = self.store.newTransaction()
- subscriptions = (yield txn.apnSubscriptionsByKey(pushKey))
- yield txn.commit()
+ subscriptions = (yield transaction.apnSubscriptionsByKey(pushKey))
numSubscriptions = len(subscriptions)
if numSubscriptions > 0:
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/notifier.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/notifier.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/notifier.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -32,7 +32,7 @@
class PushNotificationWork(WorkItem, fromTable(schema.PUSH_NOTIFICATION_WORK)):
- group = "PUSH_ID"
+ group = property(lambda self: self.pushID)
@inlineCallbacks
def doWork(self):
@@ -44,7 +44,7 @@
pushDistributor = self.transaction._pushDistributor
if pushDistributor is not None:
- yield pushDistributor.enqueue(self.pushID)
+ yield pushDistributor.enqueue(self.transaction, self.pushID)
@@ -204,12 +204,15 @@
self.observers = observers
@inlineCallbacks
- def enqueue(self, pushKey):
+ def enqueue(self, transaction, pushKey):
"""
Pass along enqueued pushKey to any observers
+ @param transaction: a transaction to use, if needed
+ @type transaction: L{CommonStoreTransaction}
+
@param pushKey: the push key to distribute to the observers
@type pushKey: C{str}
"""
for observer in self.observers:
- yield observer.enqueue(pushKey)
+ yield observer.enqueue(transaction, pushKey)
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_amppush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_amppush.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_amppush.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -57,7 +57,7 @@
self.assertTrue(client3.subscribedToID("/CalDAV/localhost/user03/"))
dataChangedTimestamp = 1354815999
- service.enqueue("/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
self.assertEquals(len(client1.history), 0)
self.assertEquals(len(client2.history), 0)
self.assertEquals(len(client3.history), 0)
@@ -74,7 +74,7 @@
client1.reset()
client2.reset()
client2.unsubscribe("token2", "/CalDAV/localhost/user01/")
- service.enqueue("/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
self.assertEquals(len(client1.history), 0)
clock.advance(1)
self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
@@ -87,7 +87,7 @@
client1.reset()
client2.reset()
client2.subscribe("token2", "/CalDAV/localhost/user01/")
- service.enqueue("/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_applepush.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_applepush.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -125,8 +125,10 @@
# Notification arrives from calendar server
dataChangedTimestamp = 1354815999
- yield service.enqueue("/CalDAV/calendars.example.com/user01/calendar/",
+ txn = self.store.newTransaction()
+ yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
dataChangedTimestamp=dataChangedTimestamp)
+ yield txn.commit()
# The notifications should be in the queue
self.assertTrue(((token, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
@@ -165,7 +167,9 @@
# Reset sent data
providerConnector.transport.data = None
# Send notification while service is connected
- yield service.enqueue("/CalDAV/calendars.example.com/user01/calendar/")
+ txn = self.store.newTransaction()
+ yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/")
+ yield txn.commit()
clock.advance(1) # so that first push is sent
self.assertEquals(len(providerConnector.transport.data), 183)
# Reset sent data
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_notifier.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_notifier.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/push/test/test_notifier.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -30,7 +30,7 @@
def reset(self):
self.history = []
- def enqueue(self, id):
+ def enqueue(self, transaction, id):
self.history.append(id)
return(succeed(None))
@@ -40,7 +40,7 @@
def test_enqueue(self):
stub = StubService()
dist = PushDistributor([stub])
- yield dist.enqueue("testing")
+ yield dist.enqueue(None, "testing")
self.assertEquals(stub.history, ["testing"])
def test_getPubSubAPSConfiguration(self):
@@ -82,7 +82,7 @@
def reset(self):
self.history = []
- def enqueue(self, pushID):
+ def enqueue(self, transaction, pushID):
self.history.append(pushID)
class PushNotificationWorkTests(TestCase):
@@ -117,6 +117,13 @@
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
))
+ # Enqueue a different pushID to ensure those are not grouped with
+ # the others:
+ wp = (yield txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/baz/",
+ ))
+
yield txn.commit()
yield wp.whenExecuted()
- self.assertEquals(pushDistributor.history, ["/CalDAV/localhost/bar/"])
+ self.assertEquals(pushDistributor.history,
+ ["/CalDAV/localhost/bar/", "/CalDAV/localhost/baz/"])
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/config.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/config.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/config.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -18,18 +18,56 @@
from __future__ import print_function
"""
-This tool reads the Calendar Server configuration file and emits the
-requested value.
+This tool gets and sets Calendar Server configuration keys
"""
-import os, sys
from getopt import getopt, GetoptError
+import os
+import plistlib
+import signal
+import sys
+import xml
-from twistedcaldav.config import ConfigurationError
+from twext.python.plistlib import readPlistFromString, writePlistToString
+from twistedcaldav.config import config, ConfigDict, ConfigurationError, mergeData
from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
+WRITABLE_CONFIG_KEYS = [
+ "EnableSSL",
+ "RedirectHTTPToHTTPS",
+ "EnableCalDAV",
+ "EnableCardDAV",
+ "DataRoot",
+ "SSLCertificate",
+ "SSLPrivateKey",
+ "SSLAuthorityChain",
+ "EnableSearchAddressBook",
+ "Authentication.Basic.Enabled",
+ "Authentication.Basic.AllowedOverWireUnencrypted",
+ "Authentication.Digest.Enabled",
+ "Authentication.Digest.AllowedOverWireUnencrypted",
+ "Authentication.Kerberos.Enabled",
+ "Authentication.Kerberos.AllowedOverWireUnencrypted",
+ "Authentication.Wiki.Enabled",
+ "Scheduling.iMIP.Enabled",
+ "Scheduling.iMIP.Receiving.Username",
+ "Scheduling.iMIP.Receiving.Server",
+ "Scheduling.iMIP.Receiving.Port",
+ "Scheduling.iMIP.Receiving.Type",
+ "Scheduling.iMIP.Receiving.UseSSL",
+ "Scheduling.iMIP.Sending.Username",
+ "Scheduling.iMIP.Sending.Server",
+ "Scheduling.iMIP.Sending.Port",
+ "Scheduling.iMIP.Sending.UseSSL",
+ "Scheduling.iMIP.Sending.Address",
+ "Notifications.Services.APNS.Enabled",
+ "Notifications.Services.APNS.CalDAV.CertificatePath",
+ "Notifications.Services.APNS.CalDAV.AuthorityChainPath",
+ "Notifications.Services.APNS.CalDAV.PrivateKeyPath",
+ "Notifications.Services.APNS.CardDAV.CertificatePath",
+ "Notifications.Services.APNS.CardDAV.AuthorityChainPath",
+ "Notifications.Services.APNS.CardDAV.PrivateKeyPath",
+]
-from calendarserver.tools.util import loadConfig
-
def usage(e=None):
if e:
print(e)
@@ -42,6 +80,7 @@
print("options:")
print(" -h --help: print this help and exit")
print(" -f --config: Specify caldavd.plist configuration path")
+ print(" -w --writeconfig: Specify caldavd.plist configuration path for writing")
if e:
sys.exit(64)
@@ -51,15 +90,17 @@
def main():
try:
(optargs, args) = getopt(
- sys.argv[1:], "hf:", [
+ sys.argv[1:], "hf:w:", [
"help",
"config=",
+ "writeconfig=",
],
)
except GetoptError, e:
usage(e)
configFileName = DEFAULT_CONFIG_FILE
+ writeConfigFileName = ""
for opt, arg in optargs:
if opt in ("-h", "--help"):
@@ -68,18 +109,332 @@
elif opt in ("-f", "--config"):
configFileName = arg
+ elif opt in ("-w", "--writeconfig"):
+ writeConfigFileName = arg
+
try:
- config = loadConfig(configFileName)
+ config.load(configFileName)
except ConfigurationError, e:
sys.stdout.write("%s\n" % (e,))
sys.exit(1)
- for configKey in args:
- c = config
- for subKey in configKey.split("."):
- c = c.get(subKey, None)
- if c is None:
- sys.stderr.write("No such config key: %s\n" % configKey)
- break
+ if not writeConfigFileName:
+ # If --writeconfig was not passed, use WritableConfigFile from
+ # main plist. If that's an empty string, writes will happen to
+ # the main file.
+ writeConfigFileName = config.WritableConfigFile
+
+ if not writeConfigFileName:
+ writeConfigFileName = configFileName
+
+ writable = WritableConfig(config, writeConfigFileName)
+ writable.read()
+
+ if args:
+ for configKey in args:
+
+ if "=" in configKey:
+ # This is an assignment
+ configKey, stringValue = configKey.split("=")
+ value = writable.convertToValue(stringValue)
+ writable.set({configKey:value})
+ else:
+ # This is a read
+ c = config
+ for subKey in configKey.split("."):
+ c = c.get(subKey, None)
+ if c is None:
+ sys.stderr.write("No such config key: %s\n" % configKey)
+ break
+ sys.stdout.write("%s=%s\n" % (configKey, c))
+
+ writable.save(restart=True)
+
+ else:
+ # Read plist commands from stdin
+ rawInput = sys.stdin.read()
+ try:
+ plist = readPlistFromString(rawInput)
+ except xml.parsers.expat.ExpatError, e:
+ respondWithError(str(e))
+ return
+
+ # If the plist is an array, each element of the array is a separate
+ # command dictionary.
+ if isinstance(plist, list):
+ commands = plist
else:
- sys.stdout.write("%s\n" % c)
+ commands = [plist]
+
+ runner = Runner(commands)
+ runner.run()
+
+
+
+class Runner(object):
+
+ """
+ A class which carries out commands, which are plist strings containing
+ dictionaries with a "command" key, plus command-specific data.
+ """
+
+ def __init__(self, commands):
+ """
+ @param commands: the commands to run
+ @type commands: list of plist strings
+ """
+ self.commands = commands
+
+ def validate(self):
+ """
+ Validate all the commands by making sure this class implements
+ all the command keys.
+ @return: True if all commands are valid, False otherwise
+ """
+ # Make sure commands are valid
+ for command in self.commands:
+ if 'command' not in command:
+ respondWithError("'command' missing from plist")
+ return False
+ commandName = command['command']
+ methodName = "command_%s" % (commandName,)
+ if not hasattr(self, methodName):
+ respondWithError("Unknown command '%s'" % (commandName,))
+ return False
+ return True
+
+ def run(self):
+ """
+ Find the appropriate method for each command and call them.
+ """
+ try:
+ for command in self.commands:
+ commandName = command['command']
+ methodName = "command_%s" % (commandName,)
+ if hasattr(self, methodName):
+ getattr(self, methodName)(command)
+ else:
+ respondWithError("Unknown command '%s'" % (commandName,))
+
+ except Exception, e:
+ respondWithError("Command failed: '%s'" % (str(e),))
+ raise
+
+ def command_readConfig(self, command):
+ """
+ Return current configuration
+
+ @param command: the dictionary parsed from the plist read from stdin
+ @type command: C{dict}
+ """
+ result = {}
+ for keyPath in WRITABLE_CONFIG_KEYS:
+ value = getKeyPath(config, keyPath)
+ if value is not None:
+ setKeyPath(result, keyPath, value)
+ respond(command, result)
+
+ def command_writeConfig(self, command):
+ """
+ Write config to secondary, writable plist
+
+ @param command: the dictionary parsed from the plist read from stdin
+ @type command: C{dict}
+ """
+ writable = WritableConfig(config, config.WritableConfigFile)
+ writable.read()
+ valuesToWrite = command.get("Values", {})
+ for keyPath, value in flattenDictionary(valuesToWrite):
+ if keyPath in WRITABLE_CONFIG_KEYS:
+ writable.set(setKeyPath(ConfigDict(), keyPath, value))
+ try:
+ writable.save(restart=False)
+ except Exception, e:
+ respond(command, {"error": str(e)})
+ else:
+ config.reload()
+ self.command_readConfig(command)
+
+
+def setKeyPath(parent, keyPath, value):
+ """
+ Allows the setting of arbitrary nested dictionary keys via a single
+ dot-separated string. For example, setKeyPath(parent, "foo.bar.baz",
+ "xyzzy") would create any intermediate missing directories (or whatever
+ class parent is, such as ConfigDict) so that the following structure
+ results: parent = { "foo" : { "bar" : { "baz" : "xyzzy } } }
+
+ @param parent: the object to modify
+ @type parent: any dict-like object
+ @param keyPath: a dot-delimited string specifying the path of keys to
+ traverse
+ @type keyPath: C{str}
+ @param value: the value to set
+ @type value: c{object}
+ @return: parent
+ """
+ original = parent
+ parts = keyPath.split(".")
+ for part in parts[:-1]:
+ child = parent.get(part, None)
+ if child is None:
+ parent[part] = child = parent.__class__()
+ parent = child
+ parent[parts[-1]] = value
+ return original
+
+def getKeyPath(parent, keyPath):
+ """
+ Allows the getting of arbitrary nested dictionary keys via a single
+ dot-separated string. For example, getKeyPath(parent, "foo.bar.baz")
+ would fetch parent["foo"]["bar"]["baz"]. If any of the keys don't
+ exist, None is returned instead.
+
+ @param parent: the object to traverse
+ @type parent: any dict-like object
+ @param keyPath: a dot-delimited string specifying the path of keys to
+ traverse
+ @type keyPath: C{str}
+ @return: the value at keyPath
+ """
+ parts = keyPath.split(".")
+ for part in parts[:-1]:
+ child = parent.get(part, None)
+ if child is None:
+ return None
+ parent = child
+ return parent.get(parts[-1], None)
+
+def flattenDictionary(dictionary, current=""):
+ """
+ Returns a generator of (keyPath, value) tuples for the given dictionary,
+ where each keyPath is a dot-separated string representing the complete
+ path to a nested key.
+
+ @param dictionary: the dict object to traverse
+ @type dictionary: C{dict}
+ @param current: do not use; used internally for recursion
+ @type current: C{str}
+ @return: generator of (keyPath, value) tuples
+ """
+ for key, value in dictionary.iteritems():
+ if isinstance(value, dict):
+ for result in flattenDictionary(value, current + key + "."):
+ yield result
+ else:
+ yield (current + key, value)
+
+
+def restartService(pidFilename):
+ """
+ Given the path to a PID file, sends a HUP signal to the contained pid
+ in order to cause calendar server to restart.
+
+ @param pidFilename: an absolute path to a PID file
+ @type pidFilename: C{str}
+ """
+ if os.path.exists(pidFilename):
+ pidFile = open(pidFilename, "r")
+ pid = pidFile.read().strip()
+ pidFile.close()
+ try:
+ pid = int(pid)
+ except ValueError:
+ return
+ try:
+ os.kill(pid, signal.SIGHUP)
+ except OSError:
+ pass
+
+
+class WritableConfig(object):
+ """
+ A wrapper around a Config object which allows writing of values. The idea
+ is a deployment could have a master plist which doesn't change, and have
+ it include a plist file which does. This class facilitates writing to that
+ included plist.
+ """
+
+ def __init__(self, wrappedConfig, fileName):
+ """
+ @param wrappedConfig: the Config object to read from
+ @type wrappedConfig: C{Config}
+ @param fileName: the full path to the modifiable plist
+ @type fileName: C{str}
+ """
+ self.config = wrappedConfig
+ self.fileName = fileName
+ self.changes = None
+ self.currentConfigSubset = ConfigDict()
+ self.dirty = False
+
+ def set(self, data):
+ """
+ Merges data into a ConfigDict of changes intended to be saved to disk
+ when save( ) is called.
+
+ @param data: a dict containing new values
+ @type data: C{dict}
+ """
+ if not isinstance(data, ConfigDict):
+ data = ConfigDict(mapping=data)
+ mergeData(self.currentConfigSubset, data)
+ self.dirty = True
+
+ def read(self):
+ """
+ Reads in the data contained in the writable plist file.
+
+ @return: C{ConfigDict}
+ """
+ if os.path.exists(self.fileName):
+ self.currentConfigSubset = ConfigDict(mapping=plistlib.readPlist(self.fileName))
+ else:
+ self.currentConfigSubset = ConfigDict()
+
+ def toString(self):
+ return plistlib.writePlistToString(self.currentConfigSubset)
+
+ def save(self, restart=False):
+ """
+ Writes any outstanding changes to the writable plist file. Optionally
+ restart calendar server.
+
+ @param restart: whether to restart the calendar server.
+ @type restart: C{bool}
+ """
+ if self.dirty:
+ plistlib.writePlist(self.currentConfigSubset, self.fileName)
+ self.dirty = False
+ if restart:
+ restartService(self.config.PIDFile)
+
+ @classmethod
+ def convertToValue(cls, string):
+ """
+ Inspect string and convert the value into an appropriate Python data type
+ TODO: change this to look at actual types definied within stdconfig
+ """
+ if "." in string:
+ try:
+ value = float(string)
+ except ValueError:
+ value = string
+ else:
+ try:
+ value = int(string)
+ except ValueError:
+ if string == "True":
+ value = True
+ elif string == "False":
+ value = False
+ else:
+ value = string
+ return value
+
+
+def respond(command, result):
+ sys.stdout.write(writePlistToString({'command' : command['command'], 'result' : result}))
+
+def respondWithError(msg, status=1):
+ sys.stdout.write(writePlistToString({'error' : msg, }))
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/gateway.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/gateway.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -465,7 +465,6 @@
respond(command, {'EventsRemoved' : eventCount, "RetainDays" : retainDays})
-
@inlineCallbacks
def respondWithProxies(directory, command, principal, proxyType):
proxies = []
@@ -508,21 +507,13 @@
respond(command, result)
-
def respond(command, result):
sys.stdout.write(writePlistToString({'command' : command['command'], 'result' : result}))
-
def respondWithError(msg, status=1):
sys.stdout.write(writePlistToString({'error' : msg, }))
- """
- try:
- reactor.stop()
- except RuntimeError:
- pass
- sys.exit(status)
- """
+
if __name__ == "__main__":
main()
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -792,10 +792,14 @@
# FIXME: probably want a more elegant way to accomplish this,
# since it requires the aggregate directory to examine these first:
record = DirectoryRecord(self.directory, "users", uid, shortNames=(uid,), enabledForCalendaring=True)
- record.enabled = True
self.directory._tmpRecords["shortNames"][uid] = record
self.directory._tmpRecords["uids"][uid] = record
+ # Override augments settings for this record
+ record.enabled = True
+ record.enabledForCalendaring = True
+ record.enabledForAddressBooks = True
+
cua = "urn:uuid:%s" % (uid,)
principalCollection = self.directory.principalCollection
@@ -872,76 +876,98 @@
for childName in childNames:
- childResource = (yield collection.getChild(childName))
- # Allways delete inbox items
- if self.completely or collName == "inbox":
- action = self.CANCELEVENT_SHOULD_DELETE
- else:
- event = (yield childResource.iCalendar())
- event = perUserFilter.filter(event)
- action = self._cancelEvent(event, self.when, cua)
-
- uri = "/calendars/__uids__/%s/%s/%s" % (uid, collName, childName)
- request.path = uri
- if action == self.CANCELEVENT_MODIFIED:
- count += 1
- request._rememberResource(childResource, uri)
- storer = StoreCalendarObjectResource(
- request=request,
- destination=childResource,
- destination_uri=uri,
- destinationcal=True,
- destinationparent=collection,
- calendar=str(event),
+ try:
+ perresource_request = FakeRequest(self.root, None, None)
+ perresource_request.checkedSACL = True
+ perresource_request.authnUser = perresource_request.authzUser = davxml.Principal(
+ davxml.HRef.fromString("/principals/__uids__/%s/" % (uid,))
)
- if self.verbose:
- if self.dryrun:
- print("Would modify: %s" % (uri,))
- else:
- print("Modifying: %s" % (uri,))
- if not self.dryrun:
- result = (yield storer.run())
+ perresource_request._rememberResource(collection,
+ "/calendars/__uids__/%s/%s/" % (uid, collName))
- elif action == self.CANCELEVENT_SHOULD_DELETE:
- incrementCount = self.dryrun
- request._rememberResource(childResource, uri)
- if self.verbose:
- if self.dryrun:
- print("Would delete: %s" % (uri,))
- else:
- print("Deleting: %s" % (uri,))
- if not self.dryrun:
- retry = False
- try:
- result = (yield childResource.storeRemove(request, self.doimplicit, uri))
- if result != NO_CONTENT:
- print("Error deleting %s/%s/%s: %s" % (uid,
- collName, childName, result))
- retry = True
+ childResource = (yield collection.getChild(childName))
+
+ # Allways delete inbox items
+ if self.completely or collName == "inbox":
+ action = self.CANCELEVENT_SHOULD_DELETE
+ else:
+ event = (yield childResource.iCalendar())
+ event = perUserFilter.filter(event)
+ action = self._cancelEvent(event, self.when, cua)
+
+ uri = "/calendars/__uids__/%s/%s/%s" % (uid, collName, childName)
+ perresource_request.path = uri
+ if action == self.CANCELEVENT_MODIFIED:
+ perresource_request._rememberResource(childResource, uri)
+ storer = StoreCalendarObjectResource(
+ request=perresource_request,
+ destination=childResource,
+ destination_uri=uri,
+ destinationcal=True,
+ destinationparent=collection,
+ calendar=str(event),
+ )
+ if self.verbose:
+ if self.dryrun:
+ print("Would modify: %s" % (uri,))
else:
- incrementCount = True
+ print("Modifying: %s" % (uri,))
+ if not self.dryrun:
+ result = (yield storer.run())
+ count += 1
- except Exception, e:
- print("Exception deleting %s/%s/%s: %s" % (uid,
- collName, childName, str(e)))
- retry = True
-
- if retry and self.doimplicit:
- # Try again with implicit scheduling off
- print("Retrying deletion of %s/%s/%s with implicit scheduling turned off" % (uid, collName, childName))
+ elif action == self.CANCELEVENT_SHOULD_DELETE:
+ incrementCount = self.dryrun
+ perresource_request._rememberResource(childResource, uri)
+ if self.verbose:
+ if self.dryrun:
+ print("Would delete: %s" % (uri,))
+ else:
+ print("Deleting: %s" % (uri,))
+ if not self.dryrun:
+ retry = False
try:
- result = (yield childResource.storeRemove(request, False, uri))
+ result = (yield childResource.storeRemove(perresource_request, self.doimplicit, uri))
if result != NO_CONTENT:
print("Error deleting %s/%s/%s: %s" % (uid,
collName, childName, result))
+ retry = True
else:
incrementCount = True
+
except Exception, e:
- print("Still couldn't delete %s/%s/%s even with implicit scheduling turned off: %s" % (uid, collName, childName, str(e)))
+ print("Exception deleting %s/%s/%s: %s" % (uid,
+ collName, childName, str(e)))
+ retry = True
- if incrementCount:
- count += 1
+ if retry and self.doimplicit:
+ # Try again with implicit scheduling off
+ print("Retrying deletion of %s/%s/%s with implicit scheduling turned off" % (uid, collName, childName))
+ try:
+ result = (yield childResource.storeRemove(perresource_request, False, uri))
+ if result != NO_CONTENT:
+ print("Error deleting %s/%s/%s: %s" % (uid,
+ collName, childName, result))
+ else:
+ incrementCount = True
+ except Exception, e:
+ print("Still couldn't delete %s/%s/%s even with implicit scheduling turned off: %s" % (uid, collName, childName, str(e)))
+ if incrementCount:
+ count += 1
+ txn = getattr(perresource_request, "_newStoreTransaction", None)
+ # Commit
+ if txn is not None:
+ (yield txn.commit())
+
+ except Exception, e:
+ # Abort
+ txn = getattr(perresource_request, "_newStoreTransaction", None)
+ if txn is not None:
+ (yield txn.abort())
+ raise e
+
+
txn = getattr(request, "_newStoreTransaction", None)
# Commit
if txn is not None:
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/push.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/push.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/push.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -19,39 +19,18 @@
from calendarserver.tap.util import getRootResource
from calendarserver.tools.cmdline import utilityMain
from errno import ENOENT, EACCES
-from getopt import getopt, GetoptError
+from argparse import ArgumentParser
from twext.python.log import Logger
from twisted.application.service import Service
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from twistedcaldav.config import config, ConfigurationError
-import os
import sys
import time
log = Logger()
-def usage(e=None):
- name = os.path.basename(sys.argv[0])
- print("usage: %s [options] [user ...]" % (name,))
- print("")
- print(" Display Apple Push Notification subscriptions")
- print("")
- print("options:")
- print(" -h --help: print this help and exit")
- print(" -f --config <path>: Specify caldavd.plist configuration path")
- print(" -D --debug: debug logging")
- print("")
-
- if e:
- sys.stderr.write("%s\n" % (e,))
- sys.exit(64)
- else:
- sys.exit(0)
-
-
-
class WorkerService(Service):
def __init__(self, store):
@@ -109,45 +88,18 @@
def main():
- try:
- (optargs, args) = getopt(
- sys.argv[1:], "Df:h", [
- "config=",
- "help",
- "debug",
- ],
- )
- except GetoptError, e:
- usage(e)
+ parser = ArgumentParser(description='Display Apple Push Notification subscriptions')
+ parser.add_argument('-f', '--config', dest='configFileName', metavar='CONFIGFILE', help='caldavd.plist configuration file path')
+ parser.add_argument('-d', '--debug', action='store_true', help='show debug logging')
+ parser.add_argument('user', help='one or more users to display', nargs='+') # Required
+ args = parser.parse_args()
- #
- # Get configuration
- #
- configFileName = None
- debug = False
+ DisplayAPNSubscriptions.users = args.user
- for opt, arg in optargs:
- if opt in ("-h", "--help"):
- usage()
-
- elif opt in ("-f", "--config"):
- configFileName = arg
-
- if opt in ("-d", "--debug"):
- debug = True
-
- else:
- raise NotImplementedError(opt)
-
- if not args:
- usage("Not enough arguments")
-
- DisplayAPNSubscriptions.users = args
-
utilityMain(
- configFileName,
+ args.configFileName,
DisplayAPNSubscriptions,
- verbose=debug,
+ verbose=args.debug,
)
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/gateway/caldavd.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/gateway/caldavd.plist 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/gateway/caldavd.plist 2013-03-15 21:46:49 UTC (rev 10936)
@@ -34,6 +34,14 @@
<key>ServerHostName</key>
<string></string> <!-- The hostname clients use when connecting -->
+ <!-- Enable Calendars -->
+ <key>EnableCalDAV</key>
+ <true/>
+
+ <!-- Enable AddressBooks -->
+ <key>EnableCardDAV</key>
+ <true/>
+
<!-- HTTP port [0 = disable HTTP] -->
<key>HTTPPort</key>
<integer>8008</integer>
@@ -87,7 +95,7 @@
<!-- Configuration root -->
<key>ConfigRoot</key>
- <string>/etc/caldavd</string>
+ <string>config</string>
<!-- Log root -->
<key>LogRoot</key>
@@ -496,65 +504,31 @@
<key>Services</key>
<dict>
- <key>SimpleLineNotifier</key>
- <dict>
- <!-- Simple line notification service (for testing) -->
- <key>Service</key>
- <string>twistedcaldav.notify.SimpleLineNotifierService</string>
- <key>Enabled</key>
- <false/>
- <key>Port</key>
- <integer>62308</integer>
- </dict>
- <key>XMPPNotifier</key>
+ <key>APNS</key>
<dict>
- <!-- XMPP notification service -->
- <key>Service</key>
- <string>twistedcaldav.notify.XMPPNotifierService</string>
<key>Enabled</key>
<false/>
-
- <!-- XMPP host and port to contact -->
- <key>Host</key>
- <string>xmpp.host.name</string>
- <key>Port</key>
- <integer>5222</integer>
-
- <!-- Jabber ID and password for the server -->
- <key>JID</key>
- <string>jid at xmpp.host.name/resource</string>
- <key>Password</key>
- <string>password_goes_here</string>
-
- <!-- PubSub service address -->
- <key>ServiceAddress</key>
- <string>pubsub.xmpp.host.name</string>
-
- <key>NodeConfiguration</key>
+ <key>EnableStaggering</key>
+ <true/>
+ <key>StaggerSeconds</key>
+ <integer>5</integer>
+ <key>CalDAV</key>
<dict>
- <key>pubsub#deliver_payloads</key>
- <string>1</string>
- <key>pubsub#persist_items</key>
- <string>1</string>
+ <key>CertificatePath</key>
+ <string>/example/calendar.cer</string>
+ <key>PrivateKeyPath</key>
+ <string>/example/calendar.pem</string>
</dict>
-
- <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
- <key>KeepAliveSeconds</key>
- <integer>120</integer>
-
- <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
- <key>HeartbeatMinutes</key>
- <integer>30</integer>
-
- <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
- <key>AllowedJIDs</key>
- <array>
- <!--
- <string>*.example.com</string>
- -->
- </array>
+ <key>CardDAV</key>
+ <dict>
+ <key>CertificatePath</key>
+ <string>/example/contacts.cer</string>
+ <key>PrivateKeyPath</key>
+ <string>/example/contacts.pem</string>
+ </dict>
</dict>
+
</dict>
</dict>
@@ -764,5 +738,12 @@
</dict>
+ <key>Includes</key>
+ <array>
+ <string>%(WritablePlist)s</string>
+ </array>
+ <key>WritableConfigFile</key>
+ <string>%(WritablePlist)s</string>
+
</dict>
</plist>
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_gateway.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_gateway.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -29,10 +29,10 @@
from calendarserver.tools.util import getDirectory
-class GatewayTestCase(TestCase):
+class RunCommandTestCase(TestCase):
def setUp(self):
- super(GatewayTestCase, self).setUp()
+ super(RunCommandTestCase, self).setUp()
testRoot = os.path.join(os.path.dirname(__file__), "gateway")
templateName = os.path.join(testRoot, "caldavd.plist")
@@ -42,6 +42,7 @@
newConfig = template % {
"ServerRoot" : os.path.abspath(config.ServerRoot),
+ "WritablePlist" : os.path.join(os.path.abspath(config.ConfigRoot), "caldavd-writable.plist"),
}
configFilePath = FilePath(os.path.join(config.ConfigRoot, "caldavd.plist"))
configFilePath.setContent(newConfig)
@@ -71,7 +72,8 @@
return d
@inlineCallbacks
- def runCommand(self, command, error=False):
+ def runCommand(self, command, error=False,
+ script="calendarserver_command_gateway"):
"""
Run the given command by feeding it as standard input to
calendarserver_command_gateway in a subprocess.
@@ -82,9 +84,9 @@
sourceRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
python = sys.executable
- gateway = os.path.join(sourceRoot, "bin", "calendarserver_command_gateway")
+ script = os.path.join(sourceRoot, "bin", script)
- args = [python, gateway, "-f", self.configFileName]
+ args = [python, script, "-f", self.configFileName]
if error:
args.append("--error")
@@ -101,6 +103,9 @@
returnValue(plist)
+
+class GatewayTestCase(RunCommandTestCase):
+
@inlineCallbacks
def test_getLocationList(self):
results = yield self.runCommand(command_getLocationList)
@@ -287,6 +292,7 @@
self.assertEquals(results["result"]["RetainDays"], 365)
+
command_addReadProxy = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/upgrade.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/upgrade.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/upgrade.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -15,12 +15,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from __future__ import print_function
"""
This tool allows any necessary upgrade to complete, then exits.
"""
+from __future__ import print_function
import os
import sys
import time
Modified: CalendarServer/branches/users/gaya/sharedgroups/conf/auth/resources-test.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/conf/auth/resources-test.xml 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/conf/auth/resources-test.xml 2013-03-15 21:46:49 UTC (rev 10936)
@@ -35,7 +35,7 @@
<uid>mercury</uid>
<guid>mercury</guid>
<password>test</password>
- <name>Mecury Conference Room, Building 1, 2nd Floor</name>
+ <name>Mercury Conference Room, Building 1, 2nd Floor</name>
</location>
<location>
<uid>venus</uid>
Modified: CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-apple.plist 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-apple.plist 2013-03-15 21:46:49 UTC (rev 10936)
@@ -522,5 +522,13 @@
<key>OpenDirectoryModule</key>
<string>calendarserver.platform.darwin.od.opendirectory</string>
+ <key>Includes</key>
+ <array>
+ <string>/Library/Server/Calendar and Contacts/Config/caldavd-system.plist</string>
+ <string>/Library/Server/Calendar and Contacts/Config/caldavd-user.plist</string>
+ </array>
+ <key>WritableConfigFile</key>
+ <string>/Library/Server/Calendar and Contacts/Config/caldavd-system.plist</string>
+
</dict>
</plist>
Deleted: CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/__init__.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/__init__.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/__init__.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1,15 +0,0 @@
-##
-# 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.
-##
Deleted: CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarcommonextra.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarcommonextra.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarcommonextra.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1,187 +0,0 @@
-#!/usr/bin/env python
-#
-# CommonExtra script for calendar server.
-#
-# Copyright (c) 2012-2013 Apple Inc. All Rights Reserved.
-#
-# IMPORTANT NOTE: This file is licensed only for use on Apple-labeled
-# computers and is subject to the terms and conditions of the Apple
-# Software License Agreement accompanying the package this file is a
-# part of. You may not port this file to another platform without
-# Apple's written consent.
-from __future__ import print_function
-
-import datetime
-import subprocess
-from plistlib import readPlist, writePlist
-
-LOG = "/Library/Logs/Migration/calendarmigrator.log"
-SERVER_APP_ROOT = "/Applications/Server.app/Contents/ServerRoot"
-CALENDAR_SERVER_ROOT = "/Library/Server/Calendar and Contacts"
-CALDAVD_PLIST = "%s/Config/caldavd.plist" % (CALENDAR_SERVER_ROOT,)
-SERVER_ADMIN = "%s/usr/sbin/serveradmin" % (SERVER_APP_ROOT,)
-CERT_ADMIN = "/Applications/Server.app/Contents/ServerRoot/usr/sbin/certadmin"
-PGDUMP = "%s/usr/bin/pg_dump" % (SERVER_APP_ROOT,)
-DROPDB = "%s/usr/bin/dropdb" % (SERVER_APP_ROOT,)
-POSTGRES_SERVICE_NAME = "postgres_server"
-PGSOCKETDIR = "/Library/Server/PostgreSQL For Server Services/Socket"
-USERNAME = "caldav"
-DATABASENAME = "caldav"
-DATADUMPFILENAME = "%s/DataDump.sql" % (CALENDAR_SERVER_ROOT,)
-
-def log(msg):
- try:
- timestamp = datetime.datetime.now().strftime("%b %d %H:%M:%S")
- msg = "calendarcommonextra: %s %s" % (timestamp, msg)
- print(msg) # so it appears in Setup.log
- with open(LOG, 'a') as output:
- output.write("%s\n" % (msg,)) # so it appears in our log
- except IOError:
- # Could not write to log
- pass
-
-
-def startPostgres():
- """
- Start postgres via serveradmin
-
- This will block until postgres is up and running
- """
- log("Starting %s via %s" % (POSTGRES_SERVICE_NAME, SERVER_ADMIN))
- ret = subprocess.call([SERVER_ADMIN, "start", POSTGRES_SERVICE_NAME])
- log("serveradmin exited with %d" % (ret,))
-
-def stopPostgres():
- """
- Stop postgres via serveradmin
- """
- log("Stopping %s via %s" % (POSTGRES_SERVICE_NAME, SERVER_ADMIN))
- ret = subprocess.call([SERVER_ADMIN, "stop", POSTGRES_SERVICE_NAME])
- log("serveradmin exited with %d" % (ret,))
-
-
-def dumpOldDatabase(dumpFile):
- """
- Use pg_dump to dump data to dumpFile
- """
-
- cmdArgs = [
- PGDUMP,
- "-h", PGSOCKETDIR,
- "--username=%s" % (USERNAME,),
- "--inserts",
- "--no-privileges",
- "--file=%s" % (dumpFile,),
- DATABASENAME
- ]
- try:
- log("Dumping data to %s" % (dumpFile,))
- log("Executing: %s" % (" ".join(cmdArgs)))
- out = subprocess.check_output(cmdArgs, stderr=subprocess.STDOUT)
- log(out)
- return True
- except subprocess.CalledProcessError, e:
- log(e.output)
- return False
-
-
-def dropOldDatabase():
- """
- Use dropdb to delete the caldav database from the shared postgres server
- """
-
- cmdArgs = [
- DROPDB,
- "-h", PGSOCKETDIR,
- "--username=%s" % (USERNAME,),
- DATABASENAME
- ]
- try:
- log("\nDropping %s database" % (DATABASENAME,))
- log("Executing: %s" % (" ".join(cmdArgs)))
- out = subprocess.check_output(cmdArgs, stderr=subprocess.STDOUT)
- log(out)
- return True
- except subprocess.CalledProcessError, e:
- log(e.output)
- return False
-
-
-def getDefaultCert():
- """
- Ask certadmin for default cert
- @returns: path to default certificate, or empty string if no default
- @rtype: C{str}
- """
- child = subprocess.Popen(
- args=[CERT_ADMIN, "--default-certificate-path"],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
- output, error = child.communicate()
- if child.returncode:
- log("Error looking up default certificate (%d): %s" % (child.returncode, error))
- return ""
- else:
- certPath = output.strip()
- log("Default certificate is: %s" % (certPath,))
- return certPath
-
-def updateSettings(settings, otherCert):
- """
- Replace SSL settings based on otherCert path
- """
- basePath = otherCert[:-len("cert.pem")]
- log("Base path is %s" % (basePath,))
-
- log("Setting SSLCertificate to %s" % (otherCert,))
- settings["SSLCertificate"] = otherCert
-
- otherChain = basePath + "chain.pem"
- log("Setting SSLAuthorityChain to %s" % (otherChain,))
- settings["SSLAuthorityChain"] = otherChain
-
- otherKey = basePath + "key.pem"
- log("Setting SSLPrivateKey to %s" % (otherKey,))
- settings["SSLPrivateKey"] = otherKey
-
- settings["EnableSSL"] = True
- settings["RedirectHTTPToHTTPS"] = True
- settings.setdefault("Authentication", {}).setdefault("Basic", {})["Enabled"] = True
-
-def setCert(plistPath, otherCert):
- """
- Replace SSL settings in plist at plistPath based on otherCert path
- """
- log("Reading plist %s" % (plistPath,))
- plist = readPlist(plistPath)
- log("Read in plist %s" % (plistPath,))
-
- updateSettings(plist, otherCert)
-
- log("Writing plist %s" % (plistPath,))
- writePlist(plist, plistPath)
-
-def isSSLEnabled(plistPath):
- """
- Examine plist for EnableSSL
- """
- log("Reading plist %s" % (plistPath,))
- plist = readPlist(plistPath)
- return plist.get("EnableSSL", False)
-
-def main():
- startPostgres()
- if dumpOldDatabase(DATADUMPFILENAME):
- dropOldDatabase()
- stopPostgres()
-
- if not isSSLEnabled(CALDAVD_PLIST):
- defaultCertPath = getDefaultCert()
- log("Default cert path: %s" % (defaultCertPath,))
- if defaultCertPath:
- setCert(CALDAVD_PLIST, defaultCertPath)
-
-
-if __name__ == "__main__":
- main()
Deleted: CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendardemotion.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendardemotion.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendardemotion.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-#
-# UninstallExtra script for calendar server.
-#
-# Copyright (c) 2011-2013 Apple Inc. All Rights Reserved.
-#
-# IMPORTANT NOTE: This file is licensed only for use on Apple-labeled
-# computers and is subject to the terms and conditions of the Apple
-# Software License Agreement accompanying the package this file is a
-# part of. You may not port this file to another platform without
-# Apple's written consent.
-from __future__ import print_function
-
-import os
-from plistlib import readPlist, writePlist
-
-CALENDAR_SERVER_ROOT = "/Library/Server/Calendar and Contacts"
-DEST_CONFIG_DIR = "%s/Config" % (CALENDAR_SERVER_ROOT,)
-CALDAVD_PLIST = "caldavd.plist"
-
-def main():
-
- plistPath = os.path.join(DEST_CONFIG_DIR, CALDAVD_PLIST)
-
- if os.path.exists(plistPath):
- try:
- # Turn off services
- plistData = readPlist(plistPath)
- plistData["EnableCalDAV"] = False
- plistData["EnableCardDAV"] = False
- writePlist(plistData, plistPath)
-
- except Exception, e:
- print("Unable to disable services in %s: %s" % (plistPath, e))
-
-
-if __name__ == '__main__':
- main()
Deleted: CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarmigrator.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarmigrator.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarmigrator.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1,900 +0,0 @@
-#!/usr/bin/env python
-#
-# MigrationExtra script to maintain the enabled/disabled state of the
-# calendar server.
-#
-# This script examines the launchd preferences from the previous system
-# (also taking into account the overrides.plist) and then invokes serveradmin
-# to start/stop calendar server.
-#
-# The only argument this script currently cares about is --sourceRoot, which
-# should point to the root of the previous system.
-#
-# Copyright (c) 2005-2013 Apple Inc. All Rights Reserved.
-#
-# IMPORTANT NOTE: This file is licensed only for use on Apple-labeled
-# computers and is subject to the terms and conditions of the Apple
-# Software License Agreement accompanying the package this file is a
-# part of. You may not port this file to another platform without
-# Apple's written consent.
-from __future__ import print_function
-from __future__ import with_statement
-
-import datetime
-import grp
-import optparse
-import os
-import pwd
-import shutil
-import subprocess
-import sys
-
-from plistlib import readPlist, readPlistFromString, writePlist
-
-SERVER_APP_ROOT = "/Applications/Server.app/Contents/ServerRoot"
-LOG = "/Library/Logs/Migration/calendarmigrator.log"
-CALDAVD_CONFIG_DIR = "private/etc/caldavd"
-CARDDAVD_CONFIG_DIR = "private/etc/carddavd"
-CALDAVD_PLIST = "caldavd.plist"
-CARDDAVD_PLIST = "carddavd.plist"
-NEW_SERVER_DIR = "Calendar and Contacts"
-NEW_SERVER_ROOT = "/Library/Server/" + NEW_SERVER_DIR
-NEW_CONFIG_DIR = "Library/Server/" + NEW_SERVER_DIR + "/Config"
-LOG_DIR = "var/log/caldavd"
-DITTO = "/usr/bin/ditto"
-RESOURCE_MIGRATION_TRIGGER = "trigger_resource_migration"
-
-# For looking up previous run state
-CALDAV_LAUNCHD_KEY = "org.calendarserver.calendarserver"
-CARDDAV_LAUNCHD_KEY = "org.addressbookserver.addressbookserver"
-LAUNCHD_OVERRIDES = "var/db/launchd.db/com.apple.launchd/overrides.plist"
-LAUNCHD_PREFS_DIR = "System/Library/LaunchDaemons"
-SERVER_ADMIN = "%s/usr/sbin/serveradmin" % (SERVER_APP_ROOT,)
-
-# Processed by mergePlist
-specialKeys = """
-Authentication
-BindHTTPPorts
-BindSSLPorts
-DataRoot
-DirectoryService
-DocumentRoot
-EnableSSL
-HTTPPort
-RedirectHTTPToHTTPS
-SSLAuthorityChain
-SSLCertificate
-SSLPort
-SSLPrivateKey
-""".split()
-
-# Ignored by mergePlist
-ignoredKeys = """
-EnableFindSharedReport
-EnableNotifications
-MaxAddressBookMultigetHrefs
-MaxAddressBookQueryResults
-PythonDirector
-Verbose
-""".split()
-
-
-def main():
-
- optionParser = optparse.OptionParser()
-
- optionParser.add_option('--purge', choices=('0', '1'),
- metavar='[0|1]',
- help='remove old files after migration (IGNORED)')
-
- optionParser.add_option('--sourceRoot', type='string',
- metavar='DIR',
- help='path to the root of the system to migrate')
-
- optionParser.add_option('--sourceType', type='string',
- metavar='[System|TimeMachine]',
- help='migration source type (IGNORED)')
-
- optionParser.add_option('--sourceVersion', type='string',
- metavar='10.X.X',
- help='version number of previous system')
-
- optionParser.add_option('--targetRoot', type='string',
- metavar='DIR',
- help='path to the root of the new system',
- default='/')
-
- optionParser.add_option('--language',
- help='language identifier (IGNORED)',
- default="en")
-
- (options, args) = optionParser.parse_args()
- log("Options: %s" % (options,))
-
- if options.sourceRoot and options.sourceVersion:
-
- if os.path.exists(options.sourceRoot):
-
- enableCalDAV, enableCardDAV = examineRunState(options)
-
- # Pull values out of previous plists
- (
- oldServerRootValue,
- oldCalDocumentRootValue,
- oldCalDataRootValue,
- oldABDocumentRootValue,
- uid,
- gid
- ) = examinePreviousSystem(
- options.sourceRoot,
- options.targetRoot
- )
-
- # Copy data as needed
- (
- newServerRoot,
- newServerRootValue,
- newDataRootValue
- ) = relocateData(
- options.sourceRoot,
- options.targetRoot,
- options.sourceVersion,
- oldServerRootValue,
- oldCalDocumentRootValue,
- oldCalDataRootValue,
- oldABDocumentRootValue,
- uid,
- gid
- )
-
- # Combine old and new plists
- migrateConfiguration(
- options,
- newServerRootValue,
- newDataRootValue,
- enableCalDAV,
- enableCardDAV
- )
-
- # Create log directory
- try:
- logDir = os.path.join(options.targetRoot, LOG_DIR)
- os.mkdir(logDir, 0755)
- except OSError:
- # Already exists
- pass
- # Set ownership
- os.chown(logDir, uid, gid)
-
- # Trigger migration of locations and resources from OD
- triggerResourceMigration(newServerRoot)
-
- # TODO: instead of starting now, leave breadcrumbs for
- # the commonextra to start the service, so that data can
- # be dumped from the old Postgres to a file which will
- # be executed by calendar server when it next starts up.
-
- # setRunState(options, enableCalDAV, enableCardDAV)
-
- else:
- log("ERROR: --sourceRoot and --sourceVersion must be specified")
- sys.exit(1)
-
-
-def examineRunState(options):
- """
- Try to determine whether the CalDAV and CardDAV services were running in
- previous system.
-
- @return: a tuple of booleans: whether CalDAV was enabled, and whether
- CardDAV was enabled
- """
-
- enableCalDAV = None
- enableCardDAV = None
-
- try:
- disabled = isServiceDisabled(options.sourceRoot, CALDAV_LAUNCHD_KEY)
- enableCalDAV = not disabled
- log("Calendar service '%s' was previously %s" %
- (CALDAV_LAUNCHD_KEY, "disabled" if disabled else "enabled"))
- except ServiceStateError, e:
- log("Couldn't determine previous state of calendar service '%s': %s" %
- (CALDAV_LAUNCHD_KEY, e))
-
- try:
- disabled = isServiceDisabled(options.sourceRoot, CARDDAV_LAUNCHD_KEY)
- enableCardDAV = not disabled
- log("Addressbook service '%s' was previously %s" %
- (CARDDAV_LAUNCHD_KEY, "disabled" if disabled else "enabled"))
- except ServiceStateError, e:
- log("Couldn't determine previous state of addressbook service '%s': %s" %
- (CARDDAV_LAUNCHD_KEY, e))
-
- if enableCalDAV:
- # Check previous plist in case previous system was Lion, since there
- # is now only one launchd key for both services
- oldCalDAVPlistPath = os.path.join(options.sourceRoot,
- CALDAVD_CONFIG_DIR, CALDAVD_PLIST)
- if os.path.exists(oldCalDAVPlistPath):
- log("Examining previous caldavd.plist for EnableCalDAV and EnableCardDAV: %s" % (oldCalDAVPlistPath,))
- oldCalDAVDPlist = readPlist(oldCalDAVPlistPath)
- if "EnableCalDAV" in oldCalDAVDPlist:
- enableCalDAV = oldCalDAVDPlist["EnableCalDAV"]
- log("Based on caldavd.plist, setting EnableCalDAV to %s" % (enableCalDAV,))
- if "EnableCardDAV" in oldCalDAVDPlist:
- enableCardDAV = oldCalDAVDPlist["EnableCardDAV"]
- log("Based on caldavd.plist, setting EnableCardDAV to %s" % (enableCardDAV,))
-
- # A value of None means we weren't able to determine, so default to off
- if enableCalDAV is None:
- enableCalDAV = False
- if enableCardDAV is None:
- enableCardDAV = False
-
- return (enableCalDAV, enableCardDAV)
-
-
-def setRunState(options, enableCalDAV, enableCardDAV):
- """
- Use serveradmin to launch the service if needed.
- """
-
- if enableCalDAV or enableCardDAV:
- serviceName = "calendar" if enableCalDAV else "addressbook"
- log("Starting service via serveradmin start %s" % (serviceName,))
- ret = subprocess.call([SERVER_ADMIN, "start", serviceName])
- log("serveradmin exited with %d" % (ret,))
-
-
-def isServiceDisabled(source, service, launchdOverrides=LAUNCHD_OVERRIDES,
- launchdPrefsDir=LAUNCHD_PREFS_DIR):
- """
- Returns whether or not a service is disabled
-
- @param source: System root to examine
- @param service: launchd key representing service
- @return: True if service is disabled, False if enabled
- """
-
- overridesPath = os.path.join(source, launchdOverrides)
- if os.path.isfile(overridesPath):
- try:
- overrides = readPlist(overridesPath)
- except Exception, e:
- raise ServiceStateError("Could not parse %s : %s" %
- (overridesPath, str(e)))
-
- try:
- return overrides[service]['Disabled']
- except KeyError:
- # Key is not in the overrides.plist, continue on
- pass
-
- prefsPath = os.path.join(source, launchdPrefsDir, "%s.plist" % service)
- if os.path.isfile(prefsPath):
- try:
- prefs = readPlist(prefsPath)
- except Exception, e:
- raise ServiceStateError("Could not parse %s : %s" %
- (prefsPath, str(e)))
- try:
- return prefs['Disabled']
- except KeyError:
- return False
-
- raise ServiceStateError("Neither %s nor %s exist" %
- (overridesPath, prefsPath))
-
-
-class ServiceStateError(Exception):
- """
- Could not determine service state
- """
-
-
-
-def migrateConfiguration(options, newServerRootValue, newDataRootValue, enableCalDAV, enableCardDAV):
- """
- Copy files/directories/symlinks from previous system's /etc/caldavd
- and /etc/carddavd
-
- Skips anything ending in ".default".
- Regular files overwrite copies in new system.
- Directories and symlinks only copied over if they don't overwrite anything.
- """
-
- newConfigDir = os.path.join(options.targetRoot, NEW_CONFIG_DIR)
- newConfigFile = os.path.join(newConfigDir, CALDAVD_PLIST)
-
- # Create config directory if it doesn't exist
- if not os.path.exists(newConfigDir):
- os.mkdir(newConfigDir)
-
- defaultConfig = os.path.join(SERVER_APP_ROOT, CALDAVD_CONFIG_DIR, CALDAVD_PLIST)
- if os.path.exists(defaultConfig) and not os.path.exists(newConfigFile):
- log("Copying default config file %s to %s" % (defaultConfig, newConfigFile))
- shutil.copy2(defaultConfig, newConfigFile)
-
- for configDir in (NEW_CONFIG_DIR, CALDAVD_CONFIG_DIR, CARDDAVD_CONFIG_DIR):
-
- oldConfigDir = os.path.join(options.sourceRoot, configDir)
- if not os.path.exists(oldConfigDir):
- log("Old configuration directory does not exist: %s" % (oldConfigDir,))
- continue
-
- log("Copying configuration files from %s to %s" % (oldConfigDir, newConfigDir))
-
- for name in os.listdir(oldConfigDir):
-
- if not (name.endswith(".default") or name in (CALDAVD_PLIST, CARDDAVD_PLIST)):
-
- oldPath = os.path.join(oldConfigDir, name)
- newPath = os.path.join(newConfigDir, name)
-
- if os.path.islink(oldPath) and not os.path.exists(newPath):
- # Recreate the symlink if it won't overwrite an existing file
- link = os.readlink(oldPath)
- log("Symlinking %s to %s" % (newPath, link))
- os.symlink(link, newPath)
-
- elif os.path.isfile(oldPath):
- # Copy the file over, overwriting copy in newConfigDir
- log("Copying file %s to %s" % (oldPath, newConfigDir))
- shutil.copy2(oldPath, newConfigDir)
-
- elif os.path.isdir(oldPath) and not os.path.exists(newPath):
- # Copy the dir over, but only if new one doesn't exist
- log("Copying directory %s to %s" % (oldPath, newPath))
- shutil.copytree(oldPath, newPath, symlinks=True)
-
-
- # Migrate certain settings from the old plists to new:
-
- oldCalDAVPlistPath = os.path.join(options.sourceRoot, CALDAVD_CONFIG_DIR,
- CALDAVD_PLIST)
- if os.path.exists(oldCalDAVPlistPath):
- oldCalDAVDPlist = readPlist(oldCalDAVPlistPath)
- else:
- oldCalDAVDPlist = { }
-
- oldCardDAVDPlistPath = os.path.join(options.sourceRoot, CARDDAVD_CONFIG_DIR,
- CARDDAVD_PLIST)
- if os.path.exists(oldCardDAVDPlistPath):
- oldCardDAVDPlist = readPlist(oldCardDAVDPlistPath)
- else:
- oldCardDAVDPlist = { }
-
- if os.path.exists(newConfigFile):
- newCalDAVDPlist = readPlist(newConfigFile)
- else:
- newCalDAVDPlist = { }
-
- log("Processing %s and %s" % (oldCalDAVPlistPath, oldCardDAVDPlistPath))
- adminChanges = mergePlist(oldCalDAVDPlist, oldCardDAVDPlist, newCalDAVDPlist)
-
- newCalDAVDPlist["ServerRoot"] = newServerRootValue
- newCalDAVDPlist["DocumentRoot"] = "Documents"
- newCalDAVDPlist["DataRoot"] = newDataRootValue
-
- newCalDAVDPlist["EnableCalDAV"] = enableCalDAV
- newCalDAVDPlist["EnableCardDAV"] = enableCardDAV
-
- log("Writing %s" % (newConfigFile,))
- writePlist(newCalDAVDPlist, newConfigFile)
-
- for key, value in adminChanges:
- log("Setting %s to %s via serveradmin..." % (key, value))
- ret = subprocess.call([SERVER_ADMIN, "settings", "calendar:%s=%s" % (key, value)])
- log("serveradmin exited with %d" % (ret,))
-
-
-
-def mergePlist(caldav, carddav, combined):
-
- adminChanges = []
-
- # Copy all non-ignored keys
- for key in carddav:
- if key not in ignoredKeys and key not in specialKeys:
- combined[key] = carddav[key]
- for key in caldav:
- if key not in ignoredKeys and key not in specialKeys:
- combined[key] = caldav[key]
-
- # Copy all "Authentication" sub-keys
- if "Authentication" in caldav:
- if "Authentication" not in combined:
- combined["Authentication"] = { }
- for key in caldav["Authentication"]:
- combined["Authentication"][key] = caldav["Authentication"][key]
-
- # Reset the wiki settings since URL is only used wieh LionCompatibility
- combined["Authentication"]["Wiki"] = { "Enabled" : True }
-
- # Strip out any unknown params from the DirectoryService:
- if "DirectoryService" in caldav:
- combined["DirectoryService"] = caldav["DirectoryService"]
- for key in combined["DirectoryService"]["params"].keys():
- if key in ("requireComputerRecord",):
- del combined["DirectoryService"]["params"][key]
-
- # Disable XMPPNotifier now that we're directly talking to APNS
- try:
- XMPPNotifier = caldav["Notifications"]["Services"]["XMPPNotifier"]
- if XMPPNotifier["Enabled"]:
- XMPPNotifier["Enabled"] = False
- except KeyError:
- pass
-
- # If XMPP was also previously being routed to APNS, enable APNS
- EnableAPNS = False
- try:
- if caldav["Notifications"]["Services"]["XMPPNotifier"]["CalDAV"]["APSBundleID"]:
- EnableAPNS = True
- except KeyError:
- pass
- try:
- if caldav["Notifications"]["Services"]["XMPPNotifier"]["CardDAV"]["APSBundleID"]:
- EnableAPNS = True
- except KeyError:
- pass
- if EnableAPNS:
- adminChanges.append(["EnableAPNS", "yes"])
-
- # Merge ports
- if not caldav.get("HTTPPort", 0):
- caldav["HTTPPort"] = 8008
- if not carddav.get("HTTPPort", 0):
- carddav["HTTPPort"] = 8800
- if not caldav.get("SSLPort", 0):
- caldav["SSLPort"] = 8443
- if not carddav.get("SSLPort", 0):
- carddav["SSLPort"] = 8843
-
- for portType in ["HTTPPort", "SSLPort"]:
- bindPorts = list(set(caldav.get("Bind%ss" % (portType,), [])).union(set(carddav.get("Bind%ss" % (portType,), []))))
- for prev in (carddav, caldav):
- port = prev.get(portType, 0)
- if port and port not in bindPorts:
- bindPorts.append(port)
- bindPorts.sort()
- combined["Bind%ss" % (portType,)] = bindPorts
-
- combined["HTTPPort"] = caldav["HTTPPort"]
- combined["SSLPort"] = caldav["SSLPort"]
-
- # Was SSL enabled?
- sslAuthorityChain = ""
- sslCertificate = ""
- sslPrivateKey = ""
- enableSSL = False
- for prev in (carddav, caldav):
- if (prev["SSLPort"] and prev.get("SSLCertificate", "")):
- sslAuthorityChain = prev.get("SSLAuthorityChain", "")
- sslCertificate = prev.get("SSLCertificate", "")
- sslPrivateKey = prev.get("SSLPrivateKey", "")
- enableSSL = True
-
- combined["SSLAuthorityChain"] = sslAuthorityChain
- combined["SSLCertificate"] = sslCertificate
- combined["SSLPrivateKey"] = sslPrivateKey
- combined["EnableSSL"] = enableSSL
-
- # If SSL is enabled, redirect HTTP to HTTPS.
- combined["RedirectHTTPToHTTPS"] = enableSSL
-
- # New DBType value indicating we launch our own Postgres
- combined["DBType"] = ""
-
- # No DSN value since we launch our own Postgres
- combined["DSN"] = ""
-
- # Path to SQL file to import previous data from
- combined["DBImportFile"] = "/Library/Server/Calendar and Contacts/DataDump.sql"
-
- # ConfigRoot is now always "Config"
- combined["ConfigRoot"] = "Config"
-
- # Remove RunRoot and PIDFile keys so they use the new defaults
- try:
- del combined["RunRoot"]
- except:
- pass
- try:
- del combined["PIDFile"]
- except:
- pass
-
- return adminChanges
-
-
-def log(msg):
- try:
- timestamp = datetime.datetime.now().strftime("%b %d %H:%M:%S")
- msg = "calendarmigrator: %s %s" % (timestamp, msg)
- print(msg) # so it appears in Setup.log
- with open(LOG, 'a') as output:
- output.write("%s\n" % (msg,)) # so it appears in our log
- except IOError:
- # Could not write to log
- pass
-
-def examinePreviousSystem(sourceRoot, targetRoot, diskAccessor=None):
- """
- Examines the old caldavd.plist and carddavd.plist to see where data
- lives in the previous system.
- """
-
- if diskAccessor is None:
- diskAccessor = DiskAccessor()
-
- oldServerRootValue = None
- oldCalDocumentRootValue = None
- oldCalDataRootValue = None
- oldABDocumentRootValue = None
-
- uid = pwd.getpwnam("calendar").pw_uid
- gid = grp.getgrnam("calendar").gr_gid
-
- # Try and read old caldavd.plist
- oldCalConfigDir = os.path.join(sourceRoot, CALDAVD_CONFIG_DIR)
- oldCalPlistPath = os.path.join(oldCalConfigDir, CALDAVD_PLIST)
- if diskAccessor.exists(oldCalPlistPath):
- contents = diskAccessor.readFile(oldCalPlistPath)
- oldCalPlist = readPlistFromString(contents)
- log("Found previous caldavd plist at %s" % (oldCalPlistPath,))
-
- oldServerRootValue = oldCalPlist.get("ServerRoot", None)
- oldCalDocumentRootValue = oldCalPlist.get("DocumentRoot", None)
- oldCalDataRootValue = oldCalPlist.get("DataRoot", None)
-
- else:
- log("Can't find previous calendar plist at %s" % (oldCalPlistPath,))
- oldCalPlist = None
-
- # Try and read old carddavd.plist
- oldABConfigDir = os.path.join(sourceRoot, CARDDAVD_CONFIG_DIR)
- oldABPlistPath = os.path.join(oldABConfigDir, CARDDAVD_PLIST)
- if diskAccessor.exists(oldABPlistPath):
- contents = diskAccessor.readFile(oldABPlistPath)
- oldABPlist = readPlistFromString(contents)
- log("Found previous carddavd plist at %s" % (oldABPlistPath,))
-
- oldABDocumentRootValue = oldABPlist.get("DocumentRoot", None)
- else:
- log("Can't find previous carddavd plist at %s" % (oldABPlistPath,))
- oldABPlist = None
-
- return (
- oldServerRootValue,
- oldCalDocumentRootValue,
- oldCalDataRootValue,
- oldABDocumentRootValue,
- uid,
- gid
- )
-
-
-def relocateData(sourceRoot, targetRoot, sourceVersion, oldServerRootValue,
- oldCalDocumentRootValue, oldCalDataRootValue, oldABDocumentRootValue,
- uid, gid, diskAccessor=None):
- """
- Copy data from sourceRoot to targetRoot, except when data is on another
- volume in which case we just refer to it there.
- """
-
- if diskAccessor is None:
- diskAccessor = DiskAccessor()
-
- log("RelocateData: sourceRoot=%s, targetRoot=%s, oldServerRootValue=%s, oldCalDocumentRootValue=%s, oldCalDataRootValue=%s, oldABDocumentRootValue=%s, uid=%d, gid=%d" % (sourceRoot, targetRoot, oldServerRootValue, oldCalDocumentRootValue, oldCalDataRootValue, oldABDocumentRootValue, uid, gid))
-
- newServerRootValue = "/Library/Server/Calendar and Contacts"
- newServerRoot = absolutePathWithRoot(targetRoot, newServerRootValue)
-
- if sourceVersion < "10.7":
- oldCalDocumentRootValueProcessed = oldCalDocumentRootValue
- oldCalDataRootValueProcessed = oldCalDataRootValue
-
- else:
- # If there was an old ServerRoot value, process DocumentRoot and
- # DataRoot because those could be relative to ServerRoot
-
- if sourceVersion < "10.8":
- # DocumentRoot and DataRoot are both relative to ServerRoot
- oldCalDocumentRootValueProcessed = os.path.join(oldServerRootValue,
- oldCalDocumentRootValue)
- oldCalDataRootValueProcessed = os.path.join(oldServerRootValue,
- oldCalDataRootValue)
- else:
- # DocumentRoot is relative to DataRoot, DataRoot is relative to ServerRoot
- oldCalDataRootValueProcessed = os.path.join(oldServerRootValue,
- oldCalDataRootValue)
- oldCalDocumentRootValueProcessed = os.path.join(oldCalDataRootValueProcessed,
- oldCalDocumentRootValue)
-
-
- # Set default values for these, possibly overridden below:
- newDataRootValue = "Data"
- newDataRoot = absolutePathWithRoot(
- targetRoot,
- os.path.join(newServerRootValue, newDataRootValue)
- )
- newDocumentRootValue = "Documents"
- newDocumentRoot = os.path.join(newDataRoot, newDocumentRootValue)
-
- if sourceVersion < "10.7":
- # Before 10.7 there was no ServerRoot; DocumentRoot and DataRoot were separate.
- # Reconfigure so DocumentRoot is under DataRoot is under ServerRoot. DataRoot
- # will be /Library/Server/Calendar and Contacts/Data unless old DocumentRoot was on
- # an external volume, in which case that becomes the new DataRoot and DocumentRoot
- # moves under DataRoot.
- # /Library/Server/Calendar and Contacts will be new ServerRoot no matter what.
-
- if oldCalDocumentRootValueProcessed:
- if oldCalDocumentRootValueProcessed.startswith("/Volumes/"): # external volume
- # The old external calendar DocumentRoot becomes the new DataRoot
- newDataRoot = newDataRootValue = os.path.join(os.path.dirname(oldCalDocumentRootValue.rstrip("/")), "Calendar and Contacts Data")
- newDocumentRoot = os.path.join(newDataRoot, newDocumentRootValue)
- # Move aside whatever is there
- if diskAccessor.exists(newDataRoot):
- renameTo = nextAvailable(newDataRoot, "bak", diskAccessor=diskAccessor)
- diskAccessor.rename(newDataRoot, renameTo)
-
- if diskAccessor.exists(absolutePathWithRoot(sourceRoot, oldCalDataRootValueProcessed)):
- diskAccessor.ditto(
- absolutePathWithRoot(sourceRoot, oldCalDataRootValueProcessed),
- newDataRoot
- )
- else:
- diskAccessor.mkdir(newDataRoot)
-
- # Move old DocumentRoot under new DataRoot
- diskAccessor.rename(oldCalDocumentRootValue, newDocumentRoot)
- diskAccessor.chown(newDataRoot, uid, gid, recursive=True)
-
- else: # The old calendar DocumentRoot is not external
- if oldCalDataRootValueProcessed:
- if diskAccessor.exists(absolutePathWithRoot(sourceRoot,
- oldCalDataRootValueProcessed)):
- diskAccessor.ditto(
- absolutePathWithRoot(sourceRoot, oldCalDataRootValueProcessed),
- newDataRoot
- )
- if diskAccessor.exists(absolutePathWithRoot(sourceRoot,
- oldCalDocumentRootValueProcessed)):
- diskAccessor.ditto(
- absolutePathWithRoot(sourceRoot, oldCalDocumentRootValueProcessed),
- newDocumentRoot
- )
-
- # Old AddressBook DocumentRoot
- if oldABDocumentRootValue:
- newAddressBooks = os.path.join(newDocumentRoot, "addressbooks")
- if oldABDocumentRootValue.startswith("/Volumes/"): # external volume
- diskAccessor.ditto(
- os.path.join(oldABDocumentRootValue, "addressbooks"),
- newAddressBooks
- )
- elif diskAccessor.exists(
- absolutePathWithRoot(sourceRoot, oldABDocumentRootValue)
- ):
- diskAccessor.ditto(
- absolutePathWithRoot(
- sourceRoot,
- os.path.join(oldABDocumentRootValue, "addressbooks")
- ),
- os.path.join(newDocumentRoot, "addressbooks")
- )
-
-
- elif sourceVersion < "10.8":
- # Before 10.8, DocumentRoot and DataRoot were relative to ServerRoot
-
- if oldServerRootValue:
- if oldServerRootValue.rstrip("/").startswith("/Volumes/"): # external volume
- log("Using external calendar server root: %s" % (oldServerRootValue,))
- # ServerRoot needs to be /Library/Server/Calendar and Contacts
- # Since DocumentRoot is now relative to DataRoot, move DocumentRoot into DataRoot
- newDataRoot = newDataRootValue = os.path.join(oldServerRootValue, "Data")
- if not diskAccessor.exists(newDataRoot):
- diskAccessor.mkdir(newDataRoot)
- newDocumentRoot = os.path.join(newDataRootValue, "Documents")
- if not diskAccessor.exists(newDocumentRoot):
- if diskAccessor.exists(os.path.join(oldServerRootValue, "Documents")):
- diskAccessor.rename(os.path.join(oldServerRootValue, "Documents"),
- newDocumentRoot)
- else:
- diskAccessor.mkdir(newDocumentRoot)
- elif diskAccessor.exists(absolutePathWithRoot(sourceRoot, oldServerRootValue)):
- log("Copying calendar server root: %s" % (newServerRoot,))
- diskAccessor.ditto(
- absolutePathWithRoot(sourceRoot, oldServerRootValue),
- newServerRoot
- )
- newDataRoot = os.path.join(newServerRoot, "Data")
- if not diskAccessor.exists(newDataRoot):
- diskAccessor.mkdir(newDataRoot)
- newDocumentRoot = os.path.join(newDataRoot, "Documents")
- if not diskAccessor.exists(newDocumentRoot):
- if diskAccessor.exists(os.path.join(newServerRoot, "Documents")):
- log("Moving Documents into Data root: %s" % (newDataRoot,))
- diskAccessor.rename(os.path.join(newServerRoot, "Documents"),
- newDocumentRoot)
- else:
- diskAccessor.mkdir(newDocumentRoot)
- else:
- if not diskAccessor.exists(newServerRoot):
- log("Creating new calendar server root: %s" % (newServerRoot,))
- diskAccessor.mkdir(newServerRoot)
- newDataRoot = os.path.join(newServerRoot, "Data")
- if not diskAccessor.exists(newDataRoot):
- log("Creating new data root: %s" % (newDataRoot,))
- diskAccessor.mkdir(newDataRoot)
- newDocumentRoot = os.path.join(newDataRoot, "Documents")
- if not diskAccessor.exists(newDocumentRoot):
- log("Creating new document root: %s" % (newDocumentRoot,))
- diskAccessor.mkdir(newDocumentRoot)
-
-
- else: # 10.8 -> 10.8
-
- if oldServerRootValue:
- if oldServerRootValue.rstrip("/").startswith("/Volumes/"): # external volume
- log("Using external calendar server root: %s" % (oldServerRootValue,))
- elif diskAccessor.exists(absolutePathWithRoot(sourceRoot, oldServerRootValue)):
- log("Copying calendar server root: %s" % (newServerRoot,))
- diskAccessor.ditto(
- absolutePathWithRoot(sourceRoot, oldServerRootValue),
- newServerRoot
- )
- else:
- log("Creating new calendar server root: %s" % (newServerRoot,))
- diskAccessor.mkdir(newServerRoot)
- newDataRoot = os.path.join(newServerRoot, "Data")
- diskAccessor.mkdir(newDataRoot)
- newDocumentRoot = os.path.join(newDataRoot, "Documents")
- diskAccessor.mkdir(newDocumentRoot)
-
- if not diskAccessor.exists(newServerRoot):
- diskAccessor.mkdir(newServerRoot)
- diskAccessor.chown(newServerRoot, uid, gid, recursive=True)
-
- newServerRootValue, newDataRootValue = relativize(newServerRootValue,
- newDataRootValue)
- newDataRootValue, newDocumentRootValue = relativize(newDataRootValue,
- newDocumentRootValue)
-
-
- return (
- newServerRoot,
- newServerRootValue,
- newDataRootValue
- )
-
-
-def triggerResourceMigration(newServerRoot):
- """
- Leave a file in the server root to act as a signal that the server
- should migrate locations and resources from OD when it starts up.
- """
- triggerPath = os.path.join(newServerRoot, RESOURCE_MIGRATION_TRIGGER)
- if not os.path.exists(newServerRoot):
- log("New server root directory doesn't exist: %s" % (newServerRoot,))
- return
-
- if not os.path.exists(triggerPath):
- # Create an empty trigger file
- log("Creating resource migration trigger file: %s" % (triggerPath,))
- open(triggerPath, "w").close()
-
-
-def relativize(parent, child):
- """
- If child is really a child of parent, make child relative to parent.
- """
- if child.startswith(parent):
- parent = parent.rstrip("/")
- child = child[len(parent):].strip("/")
- return parent.rstrip("/"), child.rstrip("/")
-
-
-def absolutePathWithRoot(root, path):
- """
- Combine root and path as long as path does not start with /Volumes/
- """
- if path.startswith("/Volumes/"):
- return path
- else:
- path = path.strip("/")
- return os.path.join(root, path)
-
-
-def nextAvailable(path, ext, diskAccessor=None):
- """
- If path.ext doesn't exist, return path.ext. Otherwise return the first path name
- following the path.N.ext pattern that doesn't exist, where N starts at 1
- and increments until a non-existent path name is determined.
-
- @param path: path to examine
- @type path: C{str}
- @param ext: filename extension to append (don't include ".")
- @type ext: C{str}
- @returns: non-existent path name
- @rtype: C{str}
- """
-
- if diskAccessor is None:
- diskAccessor = DiskAccessor()
-
- newPath = "%s.%s" % (path, ext)
- if not diskAccessor.exists(newPath):
- return newPath
-
- i = 1
- while(True):
- newPath = "%s.%d.%s" % (path, i, ext)
- if not diskAccessor.exists(newPath):
- return newPath
- i += 1
-
-
-class DiskAccessor(object):
- """
- A wrapper around various disk access methods so that unit tests can easily
- replace these with a stub that doesn't actually require disk access.
- """
-
- def exists(self, path):
- return os.path.exists(path)
-
- def readFile(self, path):
- input = file(path)
- contents = input.read()
- input.close()
- return contents
-
- def mkdir(self, path):
- if not self.exists(path):
- return os.mkdir(path)
- else:
- return
-
- def rename(self, before, after):
- log("Renaming: %s to %s" % (before, after))
- try:
- return os.rename(before, after)
- except OSError:
- # Can't rename because it's cross-volume; must copy/delete
- self.ditto(before, after)
- return os.remove(before)
-
- def isfile(self, path):
- return os.path.isfile(path)
-
- def symlink(self, orig, link):
- return os.symlink(orig, link)
-
- def chown(self, path, uid, gid, recursive=False):
- os.chown(path, uid, gid)
- if recursive:
- for root, dirs, files in os.walk(path, followlinks=True):
- for name in dirs:
- os.chown(os.path.join(root, name), uid, gid)
- for name in files:
- os.chown(os.path.join(root, name), uid, gid)
-
-
- def walk(self, path, followlinks=True):
- return os.walk(path, followlinks=followlinks)
-
- def listdir(self, path):
- return list(os.listdir(path))
-
- def ditto(self, src, dest):
- log("Copying with ditto: %s to %s" % (src, dest))
- return subprocess.call([DITTO, src, dest])
-
-
-if __name__ == '__main__':
- main()
Deleted: CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarpromotion.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarpromotion.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/contrib/migration/calendarpromotion.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-#
-# PromotionExtra script for calendar server.
-#
-# Copyright (c) 2011-2013 Apple Inc. All Rights Reserved.
-#
-# IMPORTANT NOTE: This file is licensed only for use on Apple-labeled
-# computers and is subject to the terms and conditions of the Apple
-# Software License Agreement accompanying the package this file is a
-# part of. You may not port this file to another platform without
-# Apple's written consent.
-from __future__ import print_function
-
-import os
-import shutil
-from pwd import getpwnam
-from grp import getgrnam
-from plistlib import readPlist, writePlist
-
-SRC_CONFIG_DIR = "/Applications/Server.app/Contents/ServerRoot/private/etc/caldavd"
-CALENDAR_SERVER_ROOT = "/Library/Server/Calendar and Contacts"
-DEST_CONFIG_DIR = "%s/Config" % (CALENDAR_SERVER_ROOT,)
-DEST_DATA_DIR = "%s/Data" % (CALENDAR_SERVER_ROOT,)
-CALDAVD_PLIST = "caldavd.plist"
-USER_NAME = "calendar"
-GROUP_NAME = "calendar"
-LOG_DIR = "/var/log/caldavd"
-RUN_DIR = "/var/run/caldavd"
-
-
-def updatePlist(plistData):
- """
- Update the passed-in plist data with new values for disabling the XMPPNotifier,
- to set DBType to empty string indicating we'll be starting our own Postgres server,
- and to specify the new location for ConfigRoot ("Config" directory beneath ServerRoot).
-
- @param plistData: the plist data to update in place
- @type plistData: C{dict}
- """
- try:
- if plistData["Notifications"]["Services"]["XMPPNotifier"]["Enabled"]:
- plistData["Notifications"]["Services"]["XMPPNotifier"]["Enabled"] = False
- except KeyError:
- pass
- plistData["DBType"] = ""
- plistData["DSN"] = ""
- plistData["ConfigRoot"] = "Config"
- plistData["DBImportFile"] = "/Library/Server/Calendar and Contacts/DataDump.sql"
- # Remove RunRoot and PIDFile keys so they use the new defaults
- try:
- del plistData["RunRoot"]
- except:
- pass
- try:
- del plistData["PIDFile"]
- except:
- pass
-
-
-
-def main():
-
- for dirName in (
- CALENDAR_SERVER_ROOT,
- DEST_CONFIG_DIR,
- DEST_DATA_DIR,
- LOG_DIR,
- RUN_DIR
- ):
- try:
- os.mkdir(dirName)
- except OSError:
- # Already exists
- pass
-
- try:
- uid = getpwnam(USER_NAME).pw_uid
- gid = getgrnam(GROUP_NAME).gr_gid
- os.chown(dirName, uid, gid)
- except Exception, e:
- print("Unable to chown %s: %s" % (dirName, e))
-
-
- plistPath = os.path.join(DEST_CONFIG_DIR, CALDAVD_PLIST)
-
- if os.path.exists(plistPath):
- try:
- plistData = readPlist(plistPath)
- updatePlist(plistData)
- writePlist(plistData, plistPath)
-
- except Exception, e:
- print("Unable to disable update values in %s: %s" % (plistPath, e))
-
- else:
- # Copy configuration
- srcPlistPath = os.path.join(SRC_CONFIG_DIR, CALDAVD_PLIST)
- shutil.copy(srcPlistPath, DEST_CONFIG_DIR)
-
-
-if __name__ == '__main__':
- main()
Deleted: CalendarServer/branches/users/gaya/sharedgroups/doc/RFC/draft-daboo-srv-caldav.txt
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/doc/RFC/draft-daboo-srv-caldav.txt 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/doc/RFC/draft-daboo-srv-caldav.txt 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1,840 +0,0 @@
-
-
-
-Network Working Group C. Daboo
-Internet-Draft Apple Inc.
-Updates: 4791,CardDAV-RFC-to-be September 16, 2010
-(if approved)
-Intended status: Standards Track
-Expires: March 20, 2011
-
-
- Locating CalDAV and CardDAV services
- draft-daboo-srv-caldav-10
-
-Abstract
-
- This specification describes how DNS SRV records, DNS TXT records and
- well-known URIs can be used together or separately to locate
- Calendaring Extensions to WebDAV (CalDAV) or vCard Extensions to
- WebDAV (CardDAV) services.
-
-Status of This Memo
-
- This Internet-Draft is submitted in full conformance with the
- provisions of BCP 78 and BCP 79.
-
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF). Note that other groups may also distribute
- working documents as Internet-Drafts. The list of current Internet-
- Drafts is at http://datatracker.ietf.org/drafts/current/.
-
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet-Drafts as reference
- material or to cite them other than as "work in progress."
-
- This Internet-Draft will expire on March 20, 2011.
-
-Copyright Notice
-
- Copyright (c) 2010 IETF Trust and the persons identified as the
- document authors. All rights reserved.
-
- This document is subject to BCP 78 and the IETF Trust's Legal
- Provisions Relating to IETF Documents
- (http://trustee.ietf.org/license-info) in effect on the date of
- publication of this document. Please review these documents
- carefully, as they describe your rights and restrictions with respect
- to this document. Code Components extracted from this document must
- include Simplified BSD License text as described in Section 4.e of
- the Trust Legal Provisions and are provided without warranty as
-
-
-
-Daboo Expires March 20, 2011 [Page 1]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- described in the Simplified BSD License.
-
-Table of Contents
-
- 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
- 2. Conventions Used in This Document . . . . . . . . . . . . . . 4
- 3. CalDAV SRV Service Labels . . . . . . . . . . . . . . . . . . 4
- 4. CalDAV and CardDAV Service TXT Records . . . . . . . . . . . . 4
- 5. CalDAV and CardDAV Service Well-Known URI . . . . . . . . . . 5
- 5.1. Example: well-known URI redirects to actual context
- path . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
- 6. Client "Bootstrapping" Procedures . . . . . . . . . . . . . . 5
- 7. Guidance for Service Providers . . . . . . . . . . . . . . . . 8
- 8. Security Considerations . . . . . . . . . . . . . . . . . . . 9
- 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 9
- 9.1. caldav Well-Known URI Registration . . . . . . . . . . . . 10
- 9.2. carddav Well-Known URI Registration . . . . . . . . . . . 10
- 9.3. SRV Service Label Registration . . . . . . . . . . . . . . 10
- 10. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 10
- 11. References . . . . . . . . . . . . . . . . . . . . . . . . . . 10
- 11.1. Normative References . . . . . . . . . . . . . . . . . . . 10
- 11.2. Informative References . . . . . . . . . . . . . . . . . . 12
- Appendix A. Change History (to be removed prior to
- publication as an RFC) . . . . . . . . . . . . . . . 13
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Daboo Expires March 20, 2011 [Page 2]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
-1. Introduction
-
- [RFC4791] defines the CalDAV calendar access protocol, based on HTTP
- [RFC2616], for accessing calendar data stored on a server. CalDAV
- clients need to be able to discover appropriate CalDAV servers within
- their local area network and at other domains, e.g., to minimize the
- need for end users to know specific details such as the fully
- qualified domain name (FQDN) and port number for their servers.
-
- [I-D.ietf-vcarddav-carddav] defines the CardDAV address book access
- protocol based on HTTP [RFC2616], for accessing contact data stored
- on a server. As with CalDAV, clients also need to be able to
- discover CardDAV servers.
-
- [RFC2782] defines a DNS-based service discovery protocol that has
- been widely adopted as a means of locating particular services within
- a local area network and beyond, using DNS SRV Resource Records
- (RRs). This has been enhanced to provide additional service meta-
- data by use of DNS TXT Resource Records as per
- [I-D.cheshire-dnsext-dns-sd].
-
- This specification defines new SRV service types for the CalDAV
- protocol, and gives an example of how clients can use this together
- with other protocol features to enable simple client configuration.
- SRV service types for CardDAV are already defined in Section 11 of
- [I-D.ietf-vcarddav-carddav].
-
- Another issue with CalDAV or CardDAV service discovery is that the
- service might not be located at the "root" URI of the HTTP server
- hosting it. Thus a client needs to be able to determine the complete
- path component of the Request-URI to use in HTTP requests: the
- "context path". For example, if CalDAV is implemented as a "servlet"
- in a web server "container", the servlet "context path" might be
- "/caldav/". So the URI for the CalDAV service would be, e.g.,
- "http://caldav.example.com/caldav/" rather than
- "http://caldav.example.com/". SRV RRs by themselves only provide a
- FQDN and port number for the service, not a path. Since the client
- "bootstrapping" process requires initial access to the "context path"
- of the service, there needs to be a simple way for clients to also
- discover what that path is.
-
- This specification makes use of the "well known URI" feature
- [RFC5785] of HTTP servers to provide a well known URI for CalDAV or
- CardDAV services that clients can make use of. The well known URI
- will point to a resource on the server that is simply a "stub"
- resource that provides a redirect to the actual "context path"
- resource representing the service endpoint.
-
-
-
-
-Daboo Expires March 20, 2011 [Page 3]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
-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].
-
-3. CalDAV SRV Service Labels
-
- This specification adds two SRV service labels for use with CalDAV:
-
- _caldav: Identifies a CalDAV server that uses HTTP without transport
- layer security ([RFC2818]).
-
- _caldavs: Identifies a CalDAV server that uses HTTP with transport
- layer security ([RFC2818]).
-
- Clients MUST honor "Priority" and "Weight" values in the SRV RRs, as
- described by [RFC2782].
-
- Example: service record for server without transport layer security
-
- _caldav._tcp SRV 0 1 80 calendar.example.com.
-
- Example: service record for server with transport layer security
-
- _caldavs._tcp SRV 0 1 443 calendar.example.com.
-
-4. CalDAV and CardDAV Service TXT Records
-
- When SRV RRs are used to advertise CalDAV and CardDAV services, it is
- also convenient to be able to specify a "context path" in the DNS to
- be retrieved at the same time. To enable that, this specification
- uses a TXT RR that follows the syntax defined in Section 6 of
- [I-D.cheshire-dnsext-dns-sd] and defines a "path" key for use in that
- record. The value of the key MUST be the actual "context path" to
- the corresponding service on the server.
-
- A site might provide TXT records in addition to SRV records for each
- service. When present, clients MUST use the "path" value as the
- "context path" for the service in HTTP requests. When not present,
- clients use the ".well-known" URI approach described next.
-
- Example: text record for service with transport layer security
-
- _caldavs._tcp TXT path=/caldav
-
-
-
-
-
-
-Daboo Expires March 20, 2011 [Page 4]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
-5. CalDAV and CardDAV Service Well-Known URI
-
- Two ".well-known" URIs are registered by this specification for
- CalDAV and CardDAV services, "caldav" and "carddav" respectively (see
- Section 9). These URIs point to a resource that the client can use
- as the initial "context path" for the service they are trying to
- connect to. The server MUST redirect HTTP requests for that resource
- to the actual "context path" using one of the available mechanisms
- provided by HTTP (e.g., using a 301, 303, 307 response). Clients
- MUST handle HTTP redirects on the ".well-known" URI. Servers MUST
- NOT locate the actual CalDAV or CardDAV service endpoint at the
- ".well-known" URI as per Section 1.1 of [RFC5785].
-
- Servers SHOULD set an appropriate Cache-Control header value (as per
- Section 14.9 of [RFC2616]) in the redirect response to ensure caching
- occurs or does not occur as needed, or as required by the type of
- response generated. For example, if it is anticipated that the
- location of the redirect might change over time, then a "no-cache"
- value would be used.
-
- To facilitate "context path's" that might differ from user to user,
- the server MAY require authentication when a client tries to access
- the ".well-known" URI (i.e., the server would return a 401 status
- response to the unauthenticated request from the client, then return
- the redirect response only after a successful authentication by the
- client).
-
-5.1. Example: well-known URI redirects to actual context path
-
- A CalDAV server has a "context path" that is "/servlet/caldav". The
- client will use "/.well-known/caldav" as the path for its
- "bootstrapping" process after it has first found the FQDN and port
- number via an SRV lookup or via manual entry of information by the
- user which the client can parse suitable information from. When the
- client makes an HTTP request against "/.well-known/caldav", the
- server would issue an HTTP redirect response with a Location response
- header using the path "/servlet/caldav". The client would then
- "follow" this redirect to the new resource and continue making HTTP
- requests there to complete its "bootstrapping" process.
-
-6. Client "Bootstrapping" Procedures
-
- This section describes a procedure that CalDAV or CardDAV clients
- SHOULD use to do their initial configuration based on minimal user
- input. The goal is to determine an http: or https: URI that
- describes the full path to the user's principal-URL [RFC3744].
-
-
-
-
-
-Daboo Expires March 20, 2011 [Page 5]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- 1. Processing user input:
-
- * For a CalDAV server:
-
- + Minimal input from a user would consist of a calendar user
- address and a password. A calendar user address is defined
- by iCalendar [RFC5545] to be a URI [RFC3986]. Provided a
- user identifier and a domain name can be extracted from the
- URI, this simple "bootstrap" configuration can be done.
-
- + If the calendar user address is a "mailto:" [RFC2368] URI,
- the "mailbox" portion of the URI is examined and the
- "local-part" and "domain" portions extracted.
-
- + If the calendar user address is an "http:" [RFC2616] or
- "https:" [RFC2818] URI, the "userinfo" and "host" portion
- of the URI [RFC3986] is extracted.
-
- * For a CardDAV server:
-
- + Minimal input from a user would consist of their email
- address [RFC5322] for the domain where the CardDAV service
- is hosted, and a password. The "mailbox" portion of the
- email address is examined and the "local-part" and "domain"
- portions extracted.
-
- 2. Determination of service FQDN and port number:
-
- * An SRV lookup for _caldavs._tcp (for CalDAV) or _carddavs._tcp
- (for CardDAV) is done with the extracted "domain" as the
- service domain.
-
- * If no result is found, the client can try _caldav._tcp (for
- CalDAV) or _carddav._tcp (for CardDAV) provided non-SSL
- connections are appropriate.
-
- * If an SRV record is returned, the client extracts the target
- FQDN and port number. In the case of multiple SRV records
- returned, the client MUST use the priority and weight fields
- in the record to determine which one to pick (as per
- [RFC2782]).
-
- * If an SRV record is not found, the client will need to prompt
- the user to enter the FQDN and port number information
- directly, or use some other heuristic, for example using the
- extracted "domain" as the FQDN and default HTTPS or HTTP port
- numbers. In this situation clients MUST first attempt an HTTP
- connection with transport layer security.
-
-
-
-Daboo Expires March 20, 2011 [Page 6]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- 3. Determination of initial "context path":
-
- * When an SRV lookup is done and a valid SRV record returned,
- the client MUST also query for a corresponding TXT record and
- check for the presence of a "path" key in its response. If
- present, the value of the "path" key is used for the initial
- "context path".
-
- * When an initial "context path" has not been determined from a
- TXT record, the initial "context path" is taken to be "/.well-
- known/caldav" (for CalDAV) or "/.well-known/carddav" (for
- CardDAV).
-
- * If the initial "context path" derived from a TXT record
- generates HTTP errors when targeted by requests, the client
- SHOULD repeat its bootstrap procedure using the appropriate
- ".well-known" URI instead.
-
- 4. Determination of user identifier:
-
- * The client will need to make authenticated HTTP requests to
- the service. Typically a "user identifier" is required for
- some form of user/password authentication. When a user
- identifier is required, clients MUST first use the "mailbox"
- portion of the calendar user address provided by the user in
- the case of a "mailto:" address, and if that results in an
- authentication failure, SHOULD fall back to using the "local-
- part" extracted from the "mailto:" address. For an "http:" or
- "https:" calendar user address, the "userinfo" portion is used
- as the user identifier for authentication. This is in line
- with the guidance outlined in Section 7. If these user
- identifiers result in authentication failure, the client
- SHOULD prompt the user for a valid identifier.
-
- 5. Connecting to the service:
-
- * Subsequent to configuration, the client will make HTTP
- requests to the service. When using "_caldavs" or "_carddavs"
- services, a transport layer security negotiation is done
- immediately upon connection. The client MUST do certificate
- verification using the procedure outlined in Section 4 of
- [I-D.saintandre-tls-server-id-check] in regard to verification
- with an SRV RR as the starting point.
-
- * The client does a "PROPFIND" [RFC4918] request with the
- request URI set to the initial "context path". The body of
- the request SHOULD include the DAV:current-user-principal
- [RFC5397] property as one of the properties to return. Note
-
-
-
-Daboo Expires March 20, 2011 [Page 7]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- that clients MUST properly handle HTTP redirect responses for
- the request. The server will use the HTTP authentication
- procedure outlined in [RFC2617] or use some other appropriate
- authentication schemes to authenticate the user.
-
- * If the server returns a 404 Not Found HTTP status response to
- the request on the initial "context path", clients MAY try
- repeating the request on the "root" URI "/" or prompt the user
- for a suitable path.
-
- * If the DAV:current-user-principal property is returned on the
- request, the client uses that value for the principal-URL of
- the authenticated user. With that, it can execute a
- "PROPFIND" request on the principal-URL and discover
- additional properties for configuration (e.g., calendar or
- address book "home" collections).
-
- * If the DAV:current-user-principal property is not returned,
- then the client will need to request the principal-URL path
- from the user in order to continue with configuration.
-
- Once a successful account discovery step has been done, clients
- SHOULD cache the service details that were successfully used (user
- identity, principal-URL with full scheme/host/port details), and re-
- use those when connecting again at a later time.
-
- If a subsequent connection attempt fails, or authentication fails
- persistently, clients SHOULD re-try the SRV lookup and account
- discovery to "refresh" the cached data.
-
-7. Guidance for Service Providers
-
- Service providers wanting to offer CalDAV or CardDAV services that
- can be configured by clients using SRV records need to follow certain
- procedures to ensure proper operation.
-
- o CalDAV or CardDAV servers SHOULD be configured to allow
- authentication with calendar user addresses (just taking the
- "mailbox" portion of any "mailto:" URI) or email addresses
- respectively, or "user identifiers" extracted from them. In the
- former case, the addresses MUST NOT conflict with other forms of
- permitted user login name. In the latter case, the extracted
- "user identifiers" need to be unique across the server and MUST
- NOT conflict with any login name on the server.
-
- o Servers MUST force authentication for "PROPFIND" requests that
- retrieve the DAV:current-user-principal property to ensure that
- the value of the DAV:current-user-principal property returned
-
-
-
-Daboo Expires March 20, 2011 [Page 8]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- corresponds to the principal-URL of the user making the request.
-
- o If the service provider uses transport layer security, the service
- provider MUST ensure a certificate is installed that can be
- verified by clients using the procedure outlined in Section 4 of
- [I-D.saintandre-tls-server-id-check] in regard to verification
- with an SRV RR as the starting point.
-
- o Install the appropriate SRV records for the offered services.
- Optionally include TXT records.
-
-8. Security Considerations
-
- Clients that support transport layer security as defined by [RFC2818]
- SHOULD try the "_caldavs" or "_carddavs" services first before trying
- the "_caldav" or "_carddav" services respectively. If a user has
- explicitly requested a connection with transport layer security, the
- client MUST NOT use any service information returned for the
- "_caldav" or "_carddav" services. Clients MUST follow the
- certificate verification process specified in
- [I-D.saintandre-tls-server-id-check].
-
- A malicious attacker with access to the DNS server data, or able to
- get spoofed answers cached in a recursive resolver, can potentially
- cause clients to connect to any server chosen by the attacker. In
- the absence of a secure DNS option, clients SHOULD check that the
- target FQDN returned in the SRV record matches the original service
- domain that was queried. If the target FQDN is not in the queried
- domain, clients SHOULD verify with the user that the SRV target FQDN
- is suitable for use before executing any connections to the host.
- Alternatively, if transport layer security is being used for the
- service, clients MUST use the procedure outlined in Section 4 of
- [I-D.saintandre-tls-server-id-check] to verify the service.
-
- Implementations of TLS [RFC5246], used as the basis for transport
- layer security ([RFC2818]), typically support multiple versions of
- the protocol as well as the older Secure Sockets Layer (SSL)
- protocol. Because of known security vulnerabilities, clients and
- servers MUST NOT request, offer, or use SSL 2.0. See Appendix E.2 of
- [RFC5246] for further details.
-
-9. IANA Considerations
-
- This document defines two ".well-known" URIs using the registration
- procedure and template from Section 5.1 of [RFC5785].
-
-
-
-
-
-
-Daboo Expires March 20, 2011 [Page 9]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
-9.1. caldav Well-Known URI Registration
-
- URI suffix: caldav
-
- Change controller: IETF.
-
- Specification document(s): This RFC.
-
- Related information: See also [RFC4791].
-
-9.2. carddav Well-Known URI Registration
-
- URI suffix: carddav
-
- Change controller: IETF.
-
- Specification document(s): This RFC.
-
- Related information: See also [I-D.ietf-vcarddav-carddav].
-
-9.3. SRV Service Label Registration
-
- Service labels have been registered according to
- <http://www.dns-sd.org/ServiceTypes.html> [1] and will be
- incorporated into IANA once a new registry is available there.
-
-10. Acknowledgments
-
- This specification was suggested by discussion that took place within
- the Calendaring and Scheduling Consortium's CalDAV Technical
- Committee. The author thanks the following for their contributions:
- Stuart Cheshire, Bernard Desruisseaux, Eran Hammer-Lahav, Helge Hess,
- Arnaud Quillaud, Wilfredo Sanchez, and Joe Touch.
-
-11. References
-
-11.1. Normative References
-
- [I-D.cheshire-dnsext-dns-sd] Cheshire, S. and M. Krochmal,
- "DNS-Based Service Discovery",
- draft-cheshire-dnsext-dns-sd-06
- (work in progress), March 2010.
-
- [I-D.ietf-vcarddav-carddav] Daboo, C., "vCard Extensions to
- WebDAV (CardDAV)",
- draft-ietf-vcarddav-carddav-10
- (work in progress),
- November 2009.
-
-
-
-Daboo Expires March 20, 2011 [Page 10]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- [I-D.saintandre-tls-server-id-check] Saint-Andre, P. and J. Hodges,
- "Representation and
- Verification of Domain-Based
- Application Service Identity in
- Certificates Used with
- Transport Layer Security", draf
- t-saintandre-tls-server-id-
- check-09 (work in progress),
- August 2010.
-
- [RFC2119] Bradner, S., "Key words for use
- in RFCs to Indicate Requirement
- Levels", BCP 14, RFC 2119,
- March 1997.
-
- [RFC2368] Hoffman, P., Masinter, L., and
- J. Zawinski, "The mailto URL
- scheme", RFC 2368, July 1998.
-
- [RFC2616] Fielding, R., Gettys, J.,
- Mogul, J., Frystyk, H.,
- Masinter, L., Leach, P., and T.
- Berners-Lee, "Hypertext
- Transfer Protocol -- HTTP/1.1",
- RFC 2616, June 1999.
-
- [RFC2617] Franks, J., Hallam-Baker, P.,
- Hostetler, J., Lawrence, S.,
- Leach, P., Luotonen, A., and L.
- Stewart, "HTTP Authentication:
- Basic and Digest Access
- Authentication", RFC 2617,
- June 1999.
-
- [RFC2782] Gulbrandsen, A., Vixie, P., and
- L. Esibov, "A DNS RR for
- specifying the location of
- services (DNS SRV)", RFC 2782,
- February 2000.
-
- [RFC2818] Rescorla, E., "HTTP Over TLS",
- RFC 2818, May 2000.
-
- [RFC3744] Clemm, G., Reschke, J., Sedlar,
- E., and J. Whitehead, "Web
- Distributed Authoring and
- Versioning (WebDAV)
- Access Control Protocol",
-
-
-
-Daboo Expires March 20, 2011 [Page 11]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- RFC 3744, May 2004.
-
- [RFC3986] Berners-Lee, T., Fielding, R.,
- and L. Masinter, "Uniform
- Resource Identifier (URI):
- Generic Syntax", STD 66,
- RFC 3986, January 2005.
-
- [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.
-
- [RFC5246] Dierks, T. and E. Rescorla,
- "The Transport Layer Security
- (TLS) Protocol Version 1.2",
- RFC 5246, August 2008.
-
- [RFC5322] Resnick, P., Ed., "Internet
- Message Format", RFC 5322,
- October 2008.
-
- [RFC5397] Sanchez, W. and C. Daboo,
- "WebDAV Current Principal
- Extension", RFC 5397,
- December 2008.
-
- [RFC5785] Nottingham, M. and E. Hammer-
- Lahav, "Defining Well-Known
- Uniform Resource Identifiers
- (URIs)", RFC 5785, April 2010.
-
-11.2. Informative References
-
- [RFC5545] Desruisseaux, B., "Internet
- Calendaring and Scheduling Core
- Object Specification
- (iCalendar)", RFC 5545,
- September 2009.
-
-URIs
-
- [1] <http://www.dns-sd.org/ServiceTypes.html>
-
-
-
-Daboo Expires March 20, 2011 [Page 12]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
-Appendix A. Change History (to be removed prior to publication as an
- RFC)
-
- Changes in -09:
-
- 1. IESG Review: minor editorial changes.
-
- 2. GenART Review: minor editorial changes.
-
- 3. GenART Review: "guideline" -> "procedure".
-
- 4. GenART Review: "port" -> "port number".
-
- 5. GenART Review: added definition of "context path".
-
- 6. GenART Review: clarified OPTIONAL nature of suggested client
- procedure.
-
- 7. GenART Review: clarified that TXT lookup is an additional query.
-
- 8. IESG Review: now allow any HTTP redirect response, not just 301.
-
- 9. IESG Review: added text on cache interaction with redirect.
-
- Changes in -10:
-
- 1. AD Review: make client procedure a SHOULD.
-
- Changes in -08:
-
- 1. Clarify that email address is a valid input in Section 7 for
- CardDAV.
-
- 2. Clarified aspects of DAV:current-user-principal handling for
- servers.
-
- 3. Added additional text to indicate TXT being used in abstract and
- introduction.
-
- Changes in -07:
-
- 1. Add password to required minimal user input
-
- 2. Section 3 -> Section 4 of server-id check draft.
-
- Changes in -06:
-
-
-
-
-
-Daboo Expires March 20, 2011 [Page 13]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- 1. Last call comments: Revised title, abstract and text to indicate
- that SRV and .well-known can be done separately.
-
- 2. Revised IANA section to use dns-sd registry for now.
-
- 3. Added optional TXT RR with path key for service context path in
- the DNS
-
- 4. Re-organized client bootstrap to take account of TXT and to call-
- out the different "phases" involved via a numbered list.
-
- Changes in -05:
-
- 1. AD Review: Added "Updates" for 4791 and CardDAV.
-
- 2. AD Review: Changed SHOULD to MUST for honoring priority and
- weight.
-
- 3. AD Review: Added additional reference to 3986 when talking about
- userinfo/host portions of the URI.
-
- 4. AD Review: Changed section reference for tls-server-id-check
- draft.
-
- 5. AD Review: Changed should to SHOULD when describing PROPFIND
- request and made 5397 normative.
-
- 6. AD Review: Made 3744 and 5322 normative references.
-
- 7. AD Review: Added IANA SRV registration request.
-
- Changes in -04:
-
- 1. Added addition text to client guidelines indicating that clients
- cache the discovery details and can re-do discovery if
- connections later fail.
-
- 2. Changed principal-URI to principal-URL.
-
- Changes in -03:
-
- 1. Updated to RFC 5785 reference.
-
- 2. Added SSL v2 restriction from srv-email document added after IESG
- review.
-
- 3. Tweaked client/server guidelines to better match HTTP challenge/
- response authentication mechanism.
-
-
-
-Daboo Expires March 20, 2011 [Page 14]
-
-Internet-Draft SRV for CalDAV & CardDAV September 2010
-
-
- Changes in -02:
-
- 1. Re-organized introduction.
-
- 2. Brought terminology into line with srv-email document which has
- been through last call.
-
- 3. Brought security section into line with srv-email document which
- has been through last call.
-
- Changes in -01:
-
- 1. Added discovery of CardDAV service.
-
- 2. Now makes use of well-known URIs for the service "context path".
-
- 3. Updated to RFC 5545 reference.
-
- 4. Added reference to certificate verification spec.
-
-Author's Address
-
- Cyrus Daboo
- Apple Inc.
- 1 Infinite Loop
- Cupertino, CA 95014
- USA
-
- EMail: cyrus at daboo.name
- URI: http://www.apple.com/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Daboo Expires March 20, 2011 [Page 15]
-
Modified: CalendarServer/branches/users/gaya/sharedgroups/run
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/run 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/run 2013-03-15 21:46:49 UTC (rev 10936)
@@ -190,20 +190,15 @@
# If we've been asked to read a configuration key, just read it and exit.
if [ -n "${read_key}" ]; then
- "${caldav}/bin/calendarserver_config" "${read_key}";
+ value="$("${caldav}/bin/calendarserver_config" "${read_key}")";
+ IFS="="; set ${value}; echo "$2"; unset IFS;
exit $?;
fi;
if "${kill}" || "${restart}"; then
- # mimic logic of 'fullServerPath' from twistedcaldav/config.py to find the pid file
pidfile="$("${caldav}/bin/calendarserver_config" "PIDFile")";
- serverroot="$("${caldav}/bin/calendarserver_config" "ServerRoot")";
- runroot="$("${caldav}/bin/calendarserver_config" "RunRoot")";
- # examine first character of $pidfile
- if ( [ "${pidfile:0:1}" == "/" ] || [ "${pidfile:0:1}" == "." ]; ) then
- pidfile=$pidfile;
- else pidfile=${serverroot}/${runroot}/${pidfile};
- fi
+ # Split key and value on "=" and just grab the value
+ IFS="="; set ${pidfile}; pidfile="$2"; unset IFS;
if [ ! -r "${pidfile}" ]; then
echo "Unreadable PID file: ${pidfile}";
exit 1
Modified: CalendarServer/branches/users/gaya/sharedgroups/setup.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/setup.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/setup.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -132,7 +132,7 @@
"bin/calendarserver_backup",
"bin/calendarserver_bootstrap_database",
"bin/calendarserver_command_gateway",
- #"bin/calendarserver_config", # Used by run script.
+ "bin/calendarserver_config",
#"bin/calendarserver_dbinspect",
#"bin/calendarserver_dkimtool",
"bin/calendarserver_export",
Modified: CalendarServer/branches/users/gaya/sharedgroups/support/Makefile.Apple
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/support/Makefile.Apple 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/support/Makefile.Apple 2013-03-15 21:46:49 UTC (rev 10936)
@@ -85,7 +85,7 @@
$(_v) cd $(BuildDirectory)/pycrypto-2.5 && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
$(_v) for so in $$(find "$(DSTROOT)$(PY_HOME)/lib" -type f -name '*.so'); do $(STRIP) -Sx "$${so}"; done
$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)"
- $(_v) $(INSTALL_FILE) "$(Sources)/conf/caldavd-apple.plist" "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)/caldavd.plist"
+ $(_v) $(INSTALL_FILE) "$(Sources)/conf/caldavd-apple.plist" "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)/caldavd-apple.plist"
$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(ETCDIR)$(WEBAPPSSUBDIR)"
$(_v) $(INSTALL_FILE) "$(Sources)/contrib/migration/com.apple.webapp.contacts.plist" "$(DSTROOT)$(SIPP)$(ETCDIR)$(WEBAPPSSUBDIR)/com.apple.webapp.contacts.plist"
$(_v) $(INSTALL_FILE) "$(Sources)/contrib/migration/com.apple.webapp.contactsssl.plist" "$(DSTROOT)$(SIPP)$(ETCDIR)$(WEBAPPSSUBDIR)/com.apple.webapp.contactsssl.plist"
@@ -112,22 +112,6 @@
$(_v) $(INSTALL_DIRECTORY) -o "$(CS_USER)" -g "$(CS_GROUP)" -m 0755 "$(DSTROOT)$(VARDIR)/log$(CALDAVDSUBDIR)"
$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(NSLIBRARYDIR)/LaunchDaemons"
$(_v) $(INSTALL_FILE) "$(Sources)/contrib/launchd/calendarserver.plist" "$(DSTROOT)$(SIPP)$(NSLIBRARYDIR)/LaunchDaemons/org.calendarserver.calendarserver.plist"
- @echo "Installing migration extras script..."
- $(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SERVERSETUP)/MigrationExtras"
- $(_v) $(INSTALL_FILE) "$(Sources)/contrib/migration/calendarmigrator.py" "$(DSTROOT)$(SERVERSETUP)/MigrationExtras/70_calendarmigrator.py"
- $(_v) chmod ugo+x "$(DSTROOT)$(SERVERSETUP)/MigrationExtras/70_calendarmigrator.py"
- @echo "Installing common extras script..."
- $(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SERVERSETUP)/CommonExtras"
- $(_v) $(INSTALL_FILE) "$(Sources)/contrib/migration/calendarcommonextra.py" "$(DSTROOT)$(SERVERSETUP)/CommonExtras/70_calendarcommonextra.py"
- $(_v) chmod ugo+x "$(DSTROOT)$(SERVERSETUP)/CommonExtras/70_calendarcommonextra.py"
- @echo "Installing server promotion extras script..."
- $(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SERVERSETUP)/PromotionExtras"
- $(_v) $(INSTALL_FILE) "$(Sources)/contrib/migration/calendarpromotion.py" "$(DSTROOT)$(SERVERSETUP)/PromotionExtras/59_calendarpromotion.py"
- $(_v) chmod ugo+x "$(DSTROOT)$(SERVERSETUP)/PromotionExtras/59_calendarpromotion.py"
- @echo "Installing server uninstall extras script..."
- $(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SERVERSETUP)/UninstallExtras"
- $(_v) $(INSTALL_FILE) "$(Sources)/contrib/migration/calendardemotion.py" "$(DSTROOT)$(SERVERSETUP)/UninstallExtras/59_calendardemotion.py"
- $(_v) chmod ugo+x "$(DSTROOT)$(SERVERSETUP)/UninstallExtras/59_calendardemotion.py"
@echo "Installing changeip script..."
$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip"
$(_v) $(INSTALL_FILE) "$(Sources)/calendarserver/tools/changeip_calendar.py" "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip/changeip_calendar.py"
Modified: CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/queue.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/queue.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/queue.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1197,8 +1197,6 @@
return self.choosePerformer(onlyLocally=True).performWork(table, workID)
-
-
def allWorkItemTypes(self):
"""
Load all the L{WorkItem} types that this node can process and return
@@ -1208,7 +1206,14 @@
"""
# TODO: For completeness, this may need to involve a plugin query to
# make sure that all WorkItem subclasses are imported first.
- return WorkItem.__subclasses__()
+ for workItemSubclass in WorkItem.__subclasses__():
+ # TODO: It might be a good idea to offload this table-filtering to
+ # SchemaSyntax.__contains__, adding in some more structure-
+ # comparison of similarly-named tables. For now a name check is
+ # sufficient.
+ if workItemSubclass.table.model.name in set([x.model.name for x in
+ self.schema]):
+ yield workItemSubclass
def totalNumberOfNodes(self):
Modified: CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/test/test_queue.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/test/test_queue.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/test/test_queue.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -337,7 +337,7 @@
@inlineCallbacks
- def FIXME_test_notBeforeWhenCheckingForLostWork(self):
+ def test_notBeforeWhenCheckingForLostWork(self):
"""
L{PeerConnectionPool._periodicLostWorkCheck} should execute any
outstanding work items, but only those that are expired.
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/config.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/config.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/config.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -33,7 +33,7 @@
class ConfigDict(dict):
"""
Dictionary which can be accessed using attribute syntax, because
- that reads an writes nicer in code. For example:
+ that reads and writes nicer in code. For example:
C{config.Thingo.Tiny.Tweak}
instead of:
C{config["Thingo"]["Tiny"]["Tweak"]}
@@ -211,7 +211,7 @@
self.reset()
def updateDefaults(self, items):
- _mergeData(self._provider.getDefaults(), items)
+ mergeData(self._provider.getDefaults(), items)
self.update(items)
def update(self, items=None, reloading=False):
@@ -225,7 +225,7 @@
# Call hooks
for hook in self._preUpdateHooks:
hook(self._data, items, reloading=reloading)
- _mergeData(self._data, items)
+ mergeData(self._data, items)
for hook in self._postUpdateHooks:
hook(self._data, reloading=reloading)
@@ -234,7 +234,7 @@
def load(self, configFile):
self._provider.setConfigFileName(configFile)
- configDict = ConfigDict(self._provider.loadConfig())
+ configDict = self._provider.loadConfig()
if not self._provider.hasErrors():
self.update(configDict)
else:
@@ -242,7 +242,7 @@
% (self._provider.getConfigFileName(),))
def reload(self):
- configDict = ConfigDict(self._provider.loadConfig())
+ configDict = self._provider.loadConfig()
if not self._provider.hasErrors():
if self._beforeResetHook:
# Give the beforeResetHook a chance to stash away values we want
@@ -263,7 +263,15 @@
self._data = ConfigDict(copy.deepcopy(self._provider.getDefaults()))
self._dirty = True
-def _mergeData(oldData, newData):
+def mergeData(oldData, newData):
+ """
+ Merge two ConfigDict objects; oldData will be updated with all the keys
+ and values from newData
+ @param oldData: the object to modify
+ @type oldData: ConfigDict
+ @param newData: the object to copy data from
+ @type newData: ConfigDict
+ """
for key, value in newData.iteritems():
if isinstance(value, (dict,)):
if key in oldData:
@@ -271,7 +279,7 @@
"%r in %r is not a ConfigDict" % (oldData[key], oldData)
else:
oldData[key] = {}
- _mergeData(oldData[key], value)
+ mergeData(oldData[key], value)
else:
oldData[key] = value
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/appleopendirectory.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/appleopendirectory.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -56,17 +56,22 @@
return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.node)
- def __init__(self, params):
+ def __init__(self, params, odModule=None):
"""
@param params: a dictionary containing the following keys:
- node: an OpenDirectory node name to bind to.
- restrictEnabledRecords: C{True} if a group in the
- directory is to be used to determine which calendar
- users are enabled.
- restrictToGroup: C{str} guid or name of group used to
- restrict enabled users.
- cacheTimeout: C{int} number of minutes before cache is invalidated.
- negativeCache: C{False} cache the fact that a record wasn't found
+
+ - node: an OpenDirectory node name to bind to.
+
+ - restrictEnabledRecords: C{True} if a group in the directory is to
+ be used to determine which calendar users are enabled.
+
+ - restrictToGroup: C{str} guid or name of group used to restrict
+ enabled users.
+
+ - cacheTimeout: C{int} number of minutes before cache is
+ invalidated.
+
+ - negativeCache: C{False} cache the fact that a record wasn't found
"""
defaults = {
'node' : '/Search',
@@ -90,7 +95,9 @@
super(OpenDirectoryService, self).__init__(params['cacheTimeout'],
params['negativeCaching'])
- self.odModule = namedModule(config.OpenDirectoryModule)
+ if odModule is None:
+ odModule = namedModule(config.OpenDirectoryModule)
+ self.odModule = odModule
try:
directory = self.odModule.odInit(params['node'])
@@ -1447,7 +1454,7 @@
self.shortNames[0],
challenge,
response,
- credentials.originalMethod if credentials.originalMethod else credentials.method
+ credentials.method
):
try:
cache = self.digestcache
@@ -1465,7 +1472,8 @@
Challenge: %s
Response: %s
Method: %s
-""" % (self.nodeName, self.shortNames[0], challenge, response, credentials.originalMethod if credentials.originalMethod else credentials.method))
+""" % (self.nodeName, self.shortNames[0], challenge, response,
+ credentials.method))
except self.service.odModule.ODError, e:
self.log_error(
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/calendaruserproxy.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/calendaruserproxy.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -375,7 +375,9 @@
p = self.pcollection.principalForUID(uid)
if p:
# Only principals enabledForLogin can be a delegate
- if p.record.enabledForLogin:
+ # (and groups as well)
+ if (p.record.enabledForLogin or
+ p.record.recordType == p.record.service.recordType_groups):
found.append(p)
# Make sure any outstanding deletion timer entries for
# existing principals are removed
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts-modified.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts-modified.xml 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts-modified.xml 2013-03-15 21:46:49 UTC (rev 10936)
@@ -115,6 +115,35 @@
<name>佐藤佐藤佐藤</name>
<email-address>nonascii at example.com</email-address>
</user>
+ <user>
+ <uid>delegator</uid>
+ <guid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</guid>
+ <password>a</password>
+ <name>Calendar Delegator</name>
+ <email-address>calendardelegator at example.com</email-address>
+ </user>
+ <user>
+ <uid>occasionaldelegate</uid>
+ <guid>EC465590-E9E9-4746-ACE8-6C756A49FE4D</guid>
+ <password>a</password>
+ <name>Occasional Delegate</name>
+ <email-address>occasional at example.com</email-address>
+ </user>
+ <user>
+ <uid>delegateviagroup</uid>
+ <guid>46D9D716-CBEE-490F-907A-66FA6C3767FF</guid>
+ <password>a</password>
+ <name>Delegate Via Group</name>
+ <email-address>delegateviagroup at example.com</email-address>
+ </user>
+ <group>
+ <uid>delegategroup</uid>
+ <guid>00599DAF-3E75-42DD-9DB7-52617E79943F</guid>
+ <name>Delegate Group</name>
+ <members>
+ <member type="users">delegateviagroup</member>
+ </members>
+ </group>
<user repeat="2">
<uid>user%02d</uid>
<guid>user%02d</guid>
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts.xml 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/accounts.xml 2013-03-15 21:46:49 UTC (rev 10936)
@@ -124,6 +124,21 @@
<name>Occasional Delegate</name>
<email-address>occasional at example.com</email-address>
</user>
+ <user>
+ <uid>delegateviagroup</uid>
+ <guid>46D9D716-CBEE-490F-907A-66FA6C3767FF</guid>
+ <password>a</password>
+ <name>Delegate Via Group</name>
+ <email-address>delegateviagroup at example.com</email-address>
+ </user>
+ <group>
+ <uid>delegategroup</uid>
+ <guid>00599DAF-3E75-42DD-9DB7-52617E79943F</guid>
+ <name>Delegate Group</name>
+ <members>
+ <member type="users">delegateviagroup</member>
+ </members>
+ </group>
<user repeat="2">
<uid>user%02d</uid>
<guid>user%02d</guid>
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/augments.xml 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/augments.xml 2013-03-15 21:46:49 UTC (rev 10936)
@@ -186,4 +186,10 @@
<enable-calendar>true</enable-calendar>
<enable-login>true</enable-login>
</record>
+ <record>
+ <uid>00599DAF-3E75-42DD-9DB7-52617E79943F</uid>
+ <enable>true</enable>
+ <enable-calendar>false</enable-calendar>
+ <enable-login>false</enable-login>
+ </record>
</augments>
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/proxies.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/proxies.xml 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/proxies.xml 2013-03-15 21:46:49 UTC (rev 10936)
@@ -63,6 +63,7 @@
<guid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</guid>
<proxies>
<member>EC465590-E9E9-4746-ACE8-6C756A49FE4D</member>
+ <member>00599DAF-3E75-42DD-9DB7-52617E79943F</member>
</proxies>
</record>
</proxies>
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_directory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_directory.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_directory.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -220,6 +220,8 @@
self.assertEquals(
groups,
{
+ '00599DAF-3E75-42DD-9DB7-52617E79943F':
+ set(['46D9D716-CBEE-490F-907A-66FA6C3767FF']),
'9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1':
set(['8B4288F6-CC82-491D-8EF9-642EF4F3E7D0']),
'admin':
@@ -250,6 +252,8 @@
self.assertEquals(
aliases,
{
+ '00599DAF-3E75-42DD-9DB7-52617E79943F':
+ '00599DAF-3E75-42DD-9DB7-52617E79943F',
'9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1':
'9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1',
'admin': 'admin',
@@ -286,7 +290,7 @@
# Allow an update by unlocking the cache
yield cache.releaseLock()
- self.assertEquals((False, 8, 8), (yield updater.updateCache()))
+ self.assertEquals((False, 9, 9), (yield updater.updateCache()))
# Verify cache is populated:
self.assertTrue((yield cache.isPopulated()))
@@ -382,7 +386,7 @@
# that wsanchez is only a proxy for gemini (since that assignment does not involve groups)
self.directoryService.xmlFile = dirTest.child("accounts-modified.xml")
self.directoryService._alwaysStat = True
- self.assertEquals((False, 7, 1), (yield updater.updateCache()))
+ self.assertEquals((False, 8, 1), (yield updater.updateCache()))
delegate = self._getPrincipalByShortName(DirectoryService.recordType_users, "wsanchez")
proxyFor = (yield delegate.proxyFor(True))
self.assertEquals(
@@ -689,8 +693,8 @@
self.assertFalse((yield cache.isPopulated()))
fast, numMembers, numChanged = (yield updater.updateCache(fast=True))
self.assertEquals(fast, False)
- self.assertEquals(numMembers, 8)
- self.assertEquals(numChanged, 8)
+ self.assertEquals(numMembers, 9)
+ self.assertEquals(numChanged, 9)
self.assertTrue(snapshotFile.exists())
self.assertTrue((yield cache.isPopulated()))
@@ -708,7 +712,7 @@
# Try an update which faults in from the directory (fast=False)
fast, numMembers, numChanged = (yield updater.updateCache(fast=False))
self.assertEquals(fast, False)
- self.assertEquals(numMembers, 8)
+ self.assertEquals(numMembers, 9)
self.assertEquals(numChanged, 0)
# Verify the snapshot contains the pickled dictionary we expect
@@ -716,6 +720,10 @@
self.assertEquals(
members,
{
+ "46D9D716-CBEE-490F-907A-66FA6C3767FF":
+ set([
+ u"00599DAF-3E75-42DD-9DB7-52617E79943F",
+ ]),
"5A985493-EE2C-4665-94CF-4DFEA3A89500":
set([
u"non_calendar_group",
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_opendirectory.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_opendirectory.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -19,6 +19,7 @@
except ImportError:
pass
else:
+ from collections import defaultdict
from twisted.trial.unittest import SkipTest
from twisted.internet.defer import inlineCallbacks
from twisted.python.runtime import platform
@@ -28,7 +29,24 @@
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.directory.appleopendirectory import OpenDirectoryRecord
from calendarserver.platform.darwin.od import dsattributes
+ from txdav.common.datastore.test.util import deriveValue, withSpecialValue
+ class DigestAuthModule(object):
+ """
+ Stand-in for either configurable OD module, that verifies the response
+ according to its '.response' attribute, set by the test.
+ """
+ class ODError(Exception):
+ pass
+
+ def odInit(self, node):
+ return self
+
+ def authenticateUserDigest(self, directory, node, user, challenge,
+ response, method):
+ val = (response == self.response)
+ return val
+
# Wonky hack to prevent unclean reactor shutdowns
class DummyReactor(object):
@staticmethod
@@ -60,7 +78,8 @@
{
"node" : "/Search",
"augmentService": augment.AugmentXMLDB(xmlFiles=()),
- }
+ },
+ odModule=deriveValue(self, "odModule", lambda self: None)
)
except ImportError, e:
raise SkipTest("OpenDirectory module is not available: %s" % (e,))
@@ -87,6 +106,8 @@
)
self.assertEquals(record.fullName, "")
+
+ @withSpecialValue("odModule", DigestAuthModule())
def test_invalidODDigest(self):
record = OpenDirectoryRecord(
service = self.service(),
@@ -105,11 +126,16 @@
extReadOnlyProxies = [],
)
- digestFields = {}
- digested = DigestedCredentials("user", "GET", "example.com", digestFields, None)
+ digestFields = defaultdict(lambda: "...")
+ digested = DigestedCredentials("user", "GET", "example.com",
+ digestFields)
+ od = deriveValue(self, "odModule", lambda x: None)
+ od.response = "invalid"
self.assertFalse(record.verifyCredentials(digested))
+
+ @withSpecialValue("odModule", DigestAuthModule())
def test_validODDigest(self):
record = OpenDirectoryRecord(
service = self.service(),
@@ -136,8 +162,8 @@
"response":"123",
"algorithm":"md5",
}
-
- response = (
+ od = deriveValue(self, "odModule", lambda self: None)
+ od.response = (
'Digest username="%(username)s", '
'realm="%(realm)s", '
'nonce="%(nonce)s", '
@@ -146,9 +172,8 @@
'algorithm=%(algorithm)s'
) % digestFields
- record.digestcache = {}
- record.digestcache["/"] = response
- digested = DigestedCredentials("user", "GET", "example.com", digestFields, None)
+ digested = DigestedCredentials("user", "GET", "example.com",
+ digestFields)
self.assertTrue(record.verifyCredentials(digested))
@@ -469,5 +494,6 @@
"node" : "/Search",
"recordTypes" : (DirectoryService.recordType_users, DirectoryService.recordType_groups),
"augmentService" : augment.AugmentXMLDB(xmlFiles=()),
- }
+ },
+ odModule=deriveValue(self, "odModule", lambda x: None)
)
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_proxyprincipalmembers.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/directory/test/test_proxyprincipalmembers.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -462,7 +462,17 @@
"""
self.assertEquals(
set((yield calendaruserproxy.ProxyDBService.getAllMembers())), #@UndefinedVariable
- set([u'6423F94A-6B76-4A3A-815B-D52CFD77935D', u'8A985493-EE2C-4665-94CF-4DFEA3A89500', u'9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2', u'both_coasts', u'left_coast', u'non_calendar_group', u'recursive1_coasts', u'recursive2_coasts', u'EC465590-E9E9-4746-ACE8-6C756A49FE4D'])
+ set([
+ u'00599DAF-3E75-42DD-9DB7-52617E79943F',
+ u'6423F94A-6B76-4A3A-815B-D52CFD77935D',
+ u'8A985493-EE2C-4665-94CF-4DFEA3A89500',
+ u'9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2',
+ u'both_coasts',
+ u'left_coast',
+ u'non_calendar_group',
+ u'recursive1_coasts',
+ u'recursive2_coasts',
+ u'EC465590-E9E9-4746-ACE8-6C756A49FE4D'])
)
@@ -470,6 +480,7 @@
def test_hideDisabledDelegates(self):
"""
Delegates who are not enabledForLogin are "hidden" from the delegate lists
+ (but groups *are* allowed)
"""
record = self.directoryService.recordWithGUID("EC465590-E9E9-4746-ACE8-6C756A49FE4D")
@@ -477,19 +488,19 @@
record.enabledForLogin = True
yield self._groupMembersTest(
DirectoryService.recordType_users, "delegator", "calendar-proxy-write",
- ("Occasional Delegate",),
+ ("Occasional Delegate", "Delegate Via Group", "Delegate Group"),
)
# Login disabled -- no longer shown as a delegate
record.enabledForLogin = False
yield self._groupMembersTest(
DirectoryService.recordType_users, "delegator", "calendar-proxy-write",
- [],
+ ("Delegate Via Group", "Delegate Group"),
)
# Login re-enabled -- once again a delegate (it wasn't not removed from proxydb)
record.enabledForLogin = True
yield self._groupMembersTest(
DirectoryService.recordType_users, "delegator", "calendar-proxy-write",
- ("Occasional Delegate",),
+ ("Occasional Delegate", "Delegate Via Group", "Delegate Group"),
)
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/scheduling/imip/inbound.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/scheduling/imip/inbound.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/scheduling/imip/inbound.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -21,6 +21,7 @@
from calendarserver.tap.util import FakeRequest
import email.utils
from twext.enterprise.dal.record import fromTable
+from twext.enterprise.dal.syntax import Delete
from twext.enterprise.queue import WorkItem
from twext.python.log import Logger, LoggingMixIn
from twisted.application import service
@@ -78,11 +79,15 @@
class IMIPPollingWork(WorkItem, fromTable(schema.IMIP_POLLING_WORK)):
- # FIXME: delete all other polling work items
# FIXME: purge all old tokens here
+ group = "imip_polling"
@inlineCallbacks
def doWork(self):
+
+ # Delete all other work items
+ yield Delete(From=self.table, Where=None).on(self.transaction)
+
mailRetriever = self.transaction._mailRetriever
if mailRetriever is not None:
try:
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -29,8 +29,8 @@
from twext.python.log import clearLogLevels, setLogLevelForNamespace
from twistedcaldav import caldavxml, customxml, carddavxml, mkcolxml
-from twistedcaldav.config import ConfigProvider, ConfigurationError
-from twistedcaldav.config import config, _mergeData, fullServerPath
+from twistedcaldav.config import ConfigProvider, ConfigurationError, ConfigDict
+from twistedcaldav.config import config, mergeData, fullServerPath
from twistedcaldav.util import getPasswordFromKeychain
from twistedcaldav.util import KeychainAccessError, KeychainPasswordNotFound
@@ -41,7 +41,7 @@
log = Logger()
if platform.isMacOSX():
- DEFAULT_CONFIG_FILE = "/Library/Server/Calendar and Contacts/Config/caldavd.plist"
+ DEFAULT_CONFIG_FILE = "/Applications/Server.app/Contents/ServerRoot/private/etc/caldavd/caldavd-apple.plist"
else:
DEFAULT_CONFIG_FILE = "/etc/caldavd/caldavd.plist"
@@ -153,6 +153,7 @@
},
"resourceSchema": {
"resourceInfoAttr": None, # contains location/resource info
+ "autoAcceptGroupAttr": None, # auto accept group
},
"partitionSchema": {
"serverIdAttr": None, # maps to augments server-id
@@ -974,7 +975,10 @@
# America/Los_Angeles.
"DefaultTimezone" : "",
+ # These two aren't relative to ConfigRoot:
"Includes": [], # Other plists to parse after this one
+ "WritableConfigFile" : "", # which config file calendarserver_config should
+ # write to for changes; empty string means the main config file.
}
@@ -1002,15 +1006,19 @@
configDict = {}
if self._configFileName:
configDict = self._parseConfigFromFile(self._configFileName)
+ configDict = ConfigDict(configDict)
# Now check for Includes and parse and add each of those
if "Includes" in configDict:
- configRoot = os.path.join(configDict.ServerRoot, configDict.ConfigRoot)
for include in configDict.Includes:
- path = _expandPath(fullServerPath(configRoot, include))
- additionalDict = self._parseConfigFromFile(path)
- if additionalDict:
- log.info("Adding configuration from file: '%s'" % (path,))
- configDict.update(additionalDict)
+ # Includes are not relative to ConfigRoot
+ path = _expandPath(include)
+ if os.path.exists(path):
+ additionalDict = ConfigDict(self._parseConfigFromFile(path))
+ if additionalDict:
+ log.info("Adding configuration from file: '%s'" % (path,))
+ mergeData(configDict, additionalDict)
+ else:
+ log.warn("Missing configuration file: '%s'" % (path,))
return configDict
@@ -1027,7 +1035,6 @@
return configDict
-
def _expandPath(path):
if '$' in path:
return path.replace('$', getfqdn())
@@ -1066,7 +1073,6 @@
Post-update configuration hook for making all configured paths relative to
their respective root directories rather than the current working directory.
"""
-
# Remove possible trailing slash from ServerRoot
try:
configDict["ServerRoot"] = configDict["ServerRoot"].rstrip("/")
@@ -1129,7 +1135,7 @@
if dsType == configDict.DirectoryService.type:
oldParams = configDict.DirectoryService.params
newParams = items.DirectoryService.get("params", {})
- _mergeData(oldParams, newParams)
+ mergeData(oldParams, newParams)
else:
if dsType in DEFAULT_SERVICE_PARAMS:
configDict.DirectoryService.params = copy.deepcopy(DEFAULT_SERVICE_PARAMS[dsType])
@@ -1159,7 +1165,7 @@
if dsType == configDict.ResourceService.type:
oldParams = configDict.ResourceService.params
newParams = items.ResourceService.get("params", {})
- _mergeData(oldParams, newParams)
+ mergeData(oldParams, newParams)
else:
if dsType in DEFAULT_RESOURCE_PARAMS:
configDict.ResourceService.params = copy.deepcopy(DEFAULT_RESOURCE_PARAMS[dsType])
@@ -1191,7 +1197,7 @@
if dsType == configDict.DirectoryAddressBook.type:
oldParams = configDict.DirectoryAddressBook.params
newParams = items["DirectoryAddressBook"].get("params", {})
- _mergeData(oldParams, newParams)
+ mergeData(oldParams, newParams)
else:
if dsType in directoryAddressBookBackingServiceDefaultParams:
configDict.DirectoryAddressBook.params = copy.deepcopy(directoryAddressBookBackingServiceDefaultParams[dsType])
@@ -1202,7 +1208,7 @@
if param not in directoryAddressBookBackingServiceDefaultParams[dsType]:
raise ConfigurationError("Parameter %s is not supported by service %s" % (param, dsType))
- _mergeData(configDict, items)
+ mergeData(configDict, items)
for param in tuple(configDict.DirectoryAddressBook.params):
if param not in directoryAddressBookBackingServiceDefaultParams[configDict.DirectoryAddressBook.type]:
@@ -1365,10 +1371,20 @@
):
if not service[protocol]["Topic"]:
certPath = service[protocol]["CertificatePath"]
- if certPath and os.path.exists(certPath):
- topic = getAPNTopicFromCertificate(certPath)
- service[protocol]["Topic"] = topic
+ if certPath:
+ if os.path.exists(certPath):
+ topic = getAPNTopicFromCertificate(certPath)
+ service[protocol]["Topic"] = topic
+ else:
+ log.error("APNS certificate not found: %s" %
+ (certPath,))
+ else:
+ log.error("APNS certificate path not specified")
+ if not service[protocol]["Topic"]:
+ log.error("APNS cannot proceed; disabling APNS")
+ service["Enabled"] = False
+
# If we already have the cert passphrase, don't fetch it again
if service[protocol]["Passphrase"]:
continue
@@ -1378,13 +1394,13 @@
try:
passphrase = getPasswordFromKeychain(accountName)
service[protocol]["Passphrase"] = passphrase
- log.info("%s APN certificate passphrase retreived from keychain" % (protocol,))
+ log.info("%s APNS certificate passphrase retreived from keychain" % (protocol,))
except KeychainAccessError:
# The system doesn't support keychain
pass
except KeychainPasswordNotFound:
# The password doesn't exist in the keychain.
- log.info("%s APN certificate passphrase not found in keychain" % (protocol,))
+ log.info("%s APNS certificate passphrase not found in keychain" % (protocol,))
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/test/test_config.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/test/test_config.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/test/test_config.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -17,7 +17,7 @@
from twext.python.plistlib import writePlist #@UnresolvedImport
from twext.python.log import logLevelForNamespace
-from twistedcaldav.config import config, ConfigDict
+from twistedcaldav.config import config, ConfigDict, mergeData
from twistedcaldav.resource import CalDAVResource
from twistedcaldav.stdconfig import DEFAULT_CONFIG, PListConfigProvider,\
RELATIVE_PATHS
@@ -371,6 +371,40 @@
configDict._x = "X"
self.assertEquals(configDict._x, "X")
+ def test_mergeData(self):
+ """
+ Verify we don't lose keys which are present in the old but not
+ replaced in the new.
+ """
+ old = ConfigDict({
+ "Scheduling" : ConfigDict({
+ "iMIP" : ConfigDict({
+ "Enabled" : True,
+ "Receiving" : ConfigDict({
+ "Username" : "xyzzy",
+ "Server" : "example.com",
+ }),
+ "Sending" : ConfigDict({
+ "Username" : "plugh",
+ }),
+ "AddressPatterns" : ["mailto:.*"],
+ }),
+ }),
+ })
+ new = ConfigDict({
+ "Scheduling" : ConfigDict({
+ "iMIP" : ConfigDict({
+ "Enabled" : False,
+ "Receiving" : ConfigDict({
+ "Username" : "changed",
+ }),
+ }),
+ }),
+ })
+ mergeData(old, new)
+ self.assertEquals(old.Scheduling.iMIP.Receiving.Server, "example.com")
+ self.assertEquals(old.Scheduling.iMIP.Sending.Username, "plugh")
+
def test_SimpleInclude(self):
testConfigMaster = """<?xml version="1.0" encoding="UTF-8"?>
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/sql.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/sql.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -1134,7 +1134,7 @@
# Determine attachment mode (ignore inbox's) - NB we have to do this
# after setting up other properties as UID at least is needed
self._attachment = _ATTACHMENTS_MODE_NONE
- if self._dropboxID is None:
+ if not self._dropboxID:
if self._parentCollection.name() != "inbox":
if component.hasPropertyInAnyComponent("X-APPLE-DROPBOX"):
self._attachment = _ATTACHMENTS_MODE_WRITE
@@ -1554,7 +1554,7 @@
for managed_id in added:
changed[managed_id] = newattached[managed_id]
- if self._dropboxID is None:
+ if not self._dropboxID:
self._dropboxID = str(uuid.uuid4())
changes = yield self._addingManagedIDs(self._txn, self._parentCollection, self._dropboxID, changed, component.resourceUID())
@@ -1686,7 +1686,7 @@
raise AttachmentStoreFailed
yield t.loseConnection()
- if self._dropboxID is None:
+ if not self._dropboxID:
self._dropboxID = str(uuid.uuid4())
attachment._objectDropboxID = self._dropboxID
@@ -2091,7 +2091,7 @@
@return: C{True} if this attachment exists, C{False} otherwise.
"""
att = schema.ATTACHMENT
- if self._dropboxID is not None:
+ if self._dropboxID:
where = (att.DROPBOX_ID == self._dropboxID).And(
att.PATH == self._name)
else:
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py 2013-03-15 21:46:49 UTC (rev 10936)
@@ -50,7 +50,7 @@
from txdav.base.datastore.util import QueryCacher
-from twext.internet.decorate import memoizedKey
+from twext.internet.decorate import memoizedKey, Memoizable
from txdav.caldav.icalendarstore import ICalendarTransaction, ICalendarStore
@@ -352,7 +352,7 @@
toFile.write("\n")
toFile.write("SQL: %s\n" % (sql,))
toFile.write("Rows: %s\n" % (rows,))
- toFile.write("Time (ms): %.3f\n" % (t,))
+ toFile.write("Time (ms): %.3f\n" % (t * 1000.0,))
toFile.write("***\n\n")
if self.logFileName:
@@ -740,6 +740,7 @@
imip.ICALUID: Parameter("icaluid"),
})
+
@inlineCallbacks
def imipCreateToken(self, organizer, attendee, icaluid, token=None):
if not (organizer and attendee and icaluid):
@@ -759,17 +760,20 @@
# Lookup IMIP organizer+attendee+icaluid for token
+
@classproperty
def _selectIMIPTokenByTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Select([imip.ORGANIZER, imip.ATTENDEE, imip.ICALUID], From=imip,
Where=(imip.TOKEN == Parameter("token")))
+
def imipLookupByToken(self, token):
return self._selectIMIPTokenByTokenQuery.on(self, token=token)
# Lookup IMIP token for organizer+attendee+icaluid
+
@classproperty
def _selectIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
@@ -778,6 +782,7 @@
imip.ATTENDEE == Parameter("attendee")).And(
imip.ICALUID == Parameter("icaluid")))
+
@classproperty
def _updateIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
@@ -800,25 +805,27 @@
token = None
returnValue(token)
+
# Remove IMIP token
-
@classproperty
def _removeIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Delete(From=imip,
Where=(imip.TOKEN == Parameter("token")))
+
def imipRemoveToken(self, token):
return self._removeIMIPTokenQuery.on(self, token=token)
+
# Purge old IMIP tokens
-
@classproperty
def _purgeOldIMIPTokensQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Delete(From=imip,
Where=(imip.ACCESSED < Parameter("olderThan")))
+
def purgeOldIMIPTokens(self, olderThan):
"""
@type olderThan: datetime
@@ -1605,6 +1612,7 @@
results = (yield self._childClass.loadAllObjects(self))
for result in results:
self._children[result.name()] = result
+ self._children[result._resourceID] = result
self._childrenLoaded = True
returnValue(results)
@@ -1617,7 +1625,7 @@
"""
if self._childrenLoaded:
- return succeed(self._children.keys())
+ return succeed([k for k in self._children.keys() if isinstance(k, str)])
else:
return self._childClass.listObjects(self)
@@ -2142,6 +2150,7 @@
returnValue(ownerHome)
+
class _SharedSyncLogic(object):
"""
Logic for maintaining sync-token shared between notification collections and
@@ -2867,7 +2876,7 @@
)
-class CommonHomeChild(LoggingMixIn, FancyEqMixin, _SharedSyncLogic, HomeChildBase, SharingMixIn):
+class CommonHomeChild(LoggingMixIn, FancyEqMixin, Memoizable, _SharedSyncLogic, HomeChildBase, SharingMixIn):
"""
Common ancestor class of AddressBooks and Calendars.
"""
@@ -2912,6 +2921,19 @@
self._index = None # Derived classes need to set this
+ def memoMe(self, key, memo):
+ """
+ Add this object to the memo dictionary in whatever fashion is appropriate.
+
+ @param key: key used for lookup
+ @type key: C{object} (typically C{str} or C{int})
+ @param memo: the dict to store to
+ @type memo: C{dict}
+ """
+ memo[self._name] = self
+ memo[self._resourceID] = self
+
+
@classmethod
@inlineCallbacks
def listObjects(cls, home):
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_12_to_13.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_12_to_13.sql 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_12_to_13.sql 2013-03-15 21:46:49 UTC (rev 10936)
@@ -22,13 +22,24 @@
create sequence ATTACHMENT_ID_SEQ;
+alter table ATTACHMENT
+ drop primary key;
+-- not needed; DROPBOX_ID becomes nullable after dropping primary key above
+-- alter table ATTACHMENT
+-- modify (DROPBOX_ID null);
+
+-- We want ATTACHMENT_ID as a pkey, but can't yet since it needs unique values,
+-- and oracle can't set a default column value from a sequence
alter table ATTACHMENT
- drop primary key ("DROPBOX_ID", "PATH");
+ add ("ATTACHMENT_ID" integer);
+
+-- fill in ATTACHMENT_ID with unique values from the sequence
+update ATTACHMENT set ATTACHMENT_ID = ATTACHMENT_ID_SEQ.nextval;
+
+-- now set ATTACHMENT_ID as primary key, which implies unique and not null
alter table ATTACHMENT
- modify (DROPBOX_ID null);
-alter table ATTACHMENT
- add ("ATTACHMENT_ID" integer primary key);
+ add primary key(ATTACHMENT_ID);
create table ATTACHMENT_CALENDAR_OBJECT (
"ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql 2013-03-15 21:46:03 UTC (rev 10935)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql 2013-03-15 21:46:49 UTC (rev 10936)
@@ -27,7 +27,7 @@
alter table CALENDAR_BIND
drop column SEEN_BY_SHAREE;
alter table CALENDAR_BIND
- modify (ADDRESSBOOK_RESOURCE_NAME not null);
+ modify (CALENDAR_RESOURCE_NAME not null);
alter table ADDRESSBOOK_BIND
drop column SEEN_BY_OWNER;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130315/f01319c8/attachment-0001.html>
More information about the calendarserver-changes
mailing list