[CalendarServer-changes] [8186] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Oct 11 14:32:18 PDT 2011
Revision: 8186
http://trac.macosforge.org/projects/calendarserver/changeset/8186
Author: sagen at apple.com
Date: 2011-10-11 14:32:18 -0700 (Tue, 11 Oct 2011)
Log Message:
-----------
Implements direct-to-APN notifications (without going through XMPP and APN bridge).
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tap/util.py
CalendarServer/trunk/twistedcaldav/notify.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/twistedcaldav/test/test_config.py
CalendarServer/trunk/twistedcaldav/test/test_notify.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
CalendarServer/trunk/txdav/common/icommondatastore.py
Added Paths:
-----------
CalendarServer/trunk/calendarserver/push/
CalendarServer/trunk/calendarserver/push/__init__.py
CalendarServer/trunk/calendarserver/push/applepush.py
CalendarServer/trunk/calendarserver/push/test/
CalendarServer/trunk/calendarserver/push/test/__init__.py
CalendarServer/trunk/calendarserver/push/test/test_applepush.py
CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_5_to_6.sql
CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_5_to_6.sql
Property Changed:
----------------
CalendarServer/trunk/
CalendarServer/trunk/support/build.sh
CalendarServer/trunk/txdav/caldav/datastore/index_file.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
CalendarServer/trunk/txdav/carddav/datastore/index_file.py
CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
Property changes on: CalendarServer/trunk
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
+ /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
Added: CalendarServer/trunk/calendarserver/push/__init__.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/__init__.py (rev 0)
+++ CalendarServer/trunk/calendarserver/push/__init__.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2011 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.
+##
+
+"""
+CalendarServer push notification code.
+"""
Copied: CalendarServer/trunk/calendarserver/push/applepush.py (from rev 8184, CalendarServer/branches/users/sagen/applepush/twistedcaldav/applepush.py)
===================================================================
--- CalendarServer/trunk/calendarserver/push/applepush.py (rev 0)
+++ CalendarServer/trunk/calendarserver/push/applepush.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -0,0 +1,585 @@
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twext.internet.ssl import ChainingOpenSSLContextFactory
+from twext.python.log import Logger, LoggingMixIn
+from twext.python.log import LoggingMixIn
+from twext.web2 import responsecode
+from twext.web2.dav import davxml
+from twext.web2.dav.noneprops import NonePropertyStore
+from twext.web2.dav.resource import DAVResource
+from twext.web2.http import Response
+from twext.web2.http_headers import MimeType
+from twext.web2.server import parsePOSTData
+from twisted.application import service
+from twisted.internet import reactor, protocol
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.protocol import ClientFactory, ReconnectingClientFactory
+from twistedcaldav.extensions import DAVResource, DAVResourceWithoutChildrenMixin
+from twistedcaldav.resource import ReadOnlyNoCopyResourceMixIn
+import OpenSSL
+import struct
+import time
+
+
+
+log = Logger()
+
+
+class ApplePushNotifierService(service.MultiService, LoggingMixIn):
+ """
+ ApplePushNotifierService is a MultiService responsible for
+ setting up the APN provider and feedback connections. Once
+ connected, calling its enqueue( ) method sends notifications
+ to any device token which is subscribed to the enqueued key.
+
+ The Apple Push Notification protocol is described here:
+
+ http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html
+ """
+
+ @classmethod
+ def makeService(cls, settings, store, testConnectorClass=None,
+ reactor=None):
+ """
+ Creates the various "subservices" that work together to implement
+ APN, including "provider" and "feedback" services for CalDAV and
+ CardDAV.
+
+ @param settings: The portion of the configuration specific to APN
+ @type settings: C{dict}
+
+ @param store: The db store for storing/retrieving subscriptions
+ @type store: L{IDataStore}
+
+ @param testConnectorClass: Used for unit testing; implements
+ connect( ) and receiveData( )
+ @type testConnectorClass: C{class}
+
+ @param reactor: Used for unit testing; allows tests to advance the
+ clock in order to test the feedback polling service.
+ @type reactor: L{twisted.internet.task.Clock}
+
+ @return: instance of L{ApplePushNotifierService}
+ """
+
+ service = cls()
+
+ service.store = store
+ service.providers = {}
+ service.feedbacks = {}
+ service.dataHost = settings["DataHost"]
+
+ for protocol in ("CalDAV", "CardDAV"):
+
+ providerTestConnector = None
+ feedbackTestConnector = None
+ if testConnectorClass is not None:
+ providerTestConnector = testConnectorClass()
+ feedbackTestConnector = testConnectorClass()
+
+ provider = APNProviderService(
+ settings["ProviderHost"],
+ settings["ProviderPort"],
+ settings[protocol]["CertificatePath"],
+ settings[protocol]["PrivateKeyPath"],
+ testConnector=providerTestConnector,
+ reactor=reactor,
+ )
+ provider.setServiceParent(service)
+ service.providers[protocol] = provider
+ service.log_info("APNS %s topic: %s" %
+ (protocol, settings[protocol]["Topic"]))
+
+ feedback = APNFeedbackService(
+ service.store,
+ settings["FeedbackUpdateSeconds"],
+ settings["FeedbackHost"],
+ settings["FeedbackPort"],
+ settings[protocol]["CertificatePath"],
+ settings[protocol]["PrivateKeyPath"],
+ testConnector=feedbackTestConnector,
+ reactor=reactor,
+ )
+ feedback.setServiceParent(service)
+ service.feedbacks[protocol] = feedback
+
+ return service
+
+
+ @inlineCallbacks
+ def enqueue(self, op, id):
+ """
+ Sends an Apple Push Notification to any device token subscribed to
+ this id.
+
+ @param op: The operation that took place, either "create" or "update"
+ (ignored in this implementation)
+ @type op: C{str}
+
+ @param id: The identifier of the resource that was updated, including
+ a prefix indicating whether this is CalDAV or CardDAV related.
+ The prefix is separated from the id with "|", e.g.:
+
+ "CalDAV|abc/def"
+
+ The id is an opaque token as far as this code is concerned, and
+ is used in conjunction with the prefix and the server hostname
+ to build the actual key value that devices subscribe to.
+ @type id: C{str}
+ """
+
+ try:
+ protocol, id = id.split("|", 1)
+ except ValueError:
+ # id has no protocol, so we can't do anything with it
+ self.log_error("Notification id '%s' is missing protocol" % (id,))
+ return
+
+ provider = self.providers.get(protocol, None)
+ if provider is not None:
+ key = "/%s/%s/%s/" % (protocol, self.dataHost, id)
+
+ # Look up subscriptions for this key
+ txn = self.store.newTransaction()
+ subscriptions = (yield txn.apnSubscriptionsByKey(key))
+ yield txn.commit()
+
+ numSubscriptions = len(subscriptions)
+ if numSubscriptions > 0:
+ self.log_debug("Sending %d APNS notifications for %s" %
+ (numSubscriptions, key))
+ for token, guid in subscriptions:
+ provider.sendNotification(token, key)
+
+
+
+class APNProviderProtocol(protocol.Protocol, LoggingMixIn):
+ """
+ Implements the Provider portion of APNS
+ """
+
+ # Sent by provider
+ COMMAND_SIMPLE = 0
+ COMMAND_ENHANCED = 1
+
+ # Received by provider
+ COMMAND_ERROR = 8
+
+ # Returned only for an error. Successful notifications get no response.
+ STATUS_CODES = {
+ 0 : "No errors encountered",
+ 1 : "Processing error",
+ 2 : "Missing device token",
+ 3 : "Missing topic",
+ 4 : "Missing payload",
+ 5 : "Invalid token size",
+ 6 : "Invalid topic size",
+ 7 : "Invalid payload size",
+ 8 : "Invalid token",
+ 255 : "None (unknown)",
+ }
+
+ def makeConnection(self, transport):
+ self.identifier = 0
+ # self.log_debug("ProviderProtocol makeConnection")
+ protocol.Protocol.makeConnection(self, transport)
+
+ def connectionMade(self):
+ self.log_debug("ProviderProtocol connectionMade")
+ # Store a reference to ourself on the factory so the service can
+ # later call us
+ self.factory.connection = self
+ self.factory.clientConnectionMade()
+
+ def connectionLost(self, reason=None):
+ # self.log_debug("ProviderProtocol connectionLost: %s" % (reason,))
+ # Clear the reference to us from the factory
+ self.factory.connection = None
+
+ def dataReceived(self, data):
+ self.log_debug("ProviderProtocol dataReceived %d bytes" % (len(data),))
+ command, status, identifier = struct.unpack("!BBI", data)
+ if command == self.COMMAND_ERROR:
+ self.processError(status, identifier)
+
+ def processError(self, status, identifier):
+ """
+ Handles an error message we've received from on feedback channel.
+ Not much to do here besides logging the error.
+
+ @param status: The status value returned from APN Feedback server
+ @type status: C{int}
+
+ @param identifier: The identifier of the outbound push notification
+ message which had a problem.
+ @type status: C{int}
+ """
+ msg = self.STATUS_CODES.get(status, "Unknown status code")
+ self.log_error("Received APN error %d on identifier %d: %s" % (status, identifier, msg))
+
+ def sendNotification(self, token, key):
+ """
+ Sends a push notification message for the key to the device associated
+ with the token.
+
+ @param token: The device token subscribed to the key
+ @type token: C{str}
+
+ @param key: The key we're sending a notification about
+ @type key: C{str}
+ """
+
+ try:
+ binaryToken = token.replace(" ", "").decode("hex")
+ except:
+ self.log_error("Invalid APN token in database: %s" % (token,))
+ return
+
+ self.identifier += 1
+ payload = '{"key" : "%s"}' % (key,)
+ payloadLength = len(payload)
+ self.log_debug("Sending APNS notification to %s: id=%d payload=%s" %
+ (token, self.identifier, payload))
+
+ self.transport.write(
+ struct.pack("!BIIH32sH%ds" % (payloadLength,),
+ self.COMMAND_ENHANCED, # Command
+ self.identifier, # Identifier
+ 0, # Expiry
+ 32, # Token Length
+ binaryToken, # Token
+ payloadLength, # Payload Length
+ payload, # Payload in JSON format
+ )
+ )
+
+
+class APNProviderFactory(ReconnectingClientFactory, LoggingMixIn):
+
+ protocol = APNProviderProtocol
+
+ def __init__(self, service):
+ self.service = service
+
+ def clientConnectionMade(self):
+ self.service.clientConnectionMade()
+
+ def clientConnectionLost(self, connector, reason):
+ # self.log_info("Connection to APN server lost: %s" % (reason,))
+ ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
+
+ def clientConnectionFailed(self, connector, reason):
+ self.log_error("Unable to connect to APN server: %s" % (reason,))
+ self.connected = False
+ ReconnectingClientFactory.clientConnectionFailed(self, connector,
+ reason)
+
+
+class APNConnectionService(service.Service, LoggingMixIn):
+
+ def __init__(self, host, port, certPath, keyPath, chainPath="",
+ sslMethod="TLSv1_METHOD", testConnector=None, reactor=None):
+
+ self.host = host
+ self.port = port
+ self.certPath = certPath
+ self.keyPath = keyPath
+ self.chainPath = chainPath
+ self.sslMethod = sslMethod
+ self.testConnector = testConnector
+
+ if reactor is None:
+ from twisted.internet import reactor
+ self.reactor = reactor
+
+ def connect(self, factory):
+ if self.testConnector is not None:
+ # For testing purposes
+ self.testConnector.connect(self, factory)
+ else:
+ context = ChainingOpenSSLContextFactory(
+ self.keyPath,
+ self.certPath,
+ certificateChainFile=self.chainPath,
+ sslmethod=getattr(OpenSSL.SSL, self.sslMethod)
+ )
+ reactor.connectSSL(self.host, self.port, factory, context)
+
+
+class APNProviderService(APNConnectionService):
+
+ def __init__(self, host, port, certPath, keyPath, chainPath="",
+ sslMethod="TLSv1_METHOD", testConnector=None, reactor=None):
+
+ APNConnectionService.__init__(self, host, port, certPath, keyPath,
+ chainPath="", sslMethod=sslMethod,
+ testConnector=testConnector, reactor=reactor)
+
+ self.factory = None
+ self.queue = []
+
+ def startService(self):
+ self.log_debug("APNProviderService startService")
+ self.factory = APNProviderFactory(self)
+ self.connect(self.factory)
+
+ def stopService(self):
+ self.log_debug("APNProviderService stopService")
+
+ def clientConnectionMade(self):
+ # Service the queue
+ if self.queue:
+ # Copy and clear the queue. Any notifications that don't get
+ # sent will be put back into the queue.
+ queued = list(self.queue)
+ self.queue = []
+ for token, key in queued:
+ self.sendNotification(token, key)
+
+ def sendNotification(self, token, key):
+ # Service has reference to factory has reference to protocol instance
+ connection = getattr(self.factory, "connection", None)
+ if connection is None:
+ self.log_debug("APNProviderService has no connection; queuing: %s %s" % (token, key))
+ tokenKeyPair = (token, key)
+ if tokenKeyPair not in self.queue:
+ self.queue.append(tokenKeyPair)
+ else:
+ connection.sendNotification(token, key)
+
+
+class APNFeedbackProtocol(protocol.Protocol, LoggingMixIn):
+ """
+ Implements the Feedback portion of APNS
+ """
+
+ def connectionMade(self):
+ self.log_debug("FeedbackProtocol connectionMade")
+
+ def dataReceived(self, data):
+ self.log_debug("FeedbackProtocol dataReceived %d bytes" % (len(data),))
+ timestamp, tokenLength, binaryToken = struct.unpack("!IH32s", data)
+ token = binaryToken.encode("hex")
+ return self.processFeedback(timestamp, token)
+
+ @inlineCallbacks
+ def processFeedback(self, timestamp, token):
+ """
+ Handles a feedback message indicating that the given token is no
+ longer active as of the timestamp, and its subscription should be
+ removed as long as that device has not re-subscribed since the
+ timestamp.
+
+ @param timestamp: Seconds since the epoch
+ @type timestamp: C{int}
+
+ @param token: The device token to unsubscribe
+ @type token: C{str}
+ """
+
+ self.log_debug("FeedbackProtocol processFeedback time=%d token=%s" %
+ (timestamp, token))
+ txn = self.factory.store.newTransaction()
+ subscriptions = (yield txn.apnSubscriptionsByToken(token))
+
+ for key, modified, guid in subscriptions:
+ if timestamp > modified:
+ self.log_debug("FeedbackProtocol removing subscription: %s %s" %
+ (token, key))
+ yield txn.removeAPNSubscription(token, key)
+ yield txn.commit()
+
+
+class APNFeedbackFactory(ClientFactory, LoggingMixIn):
+
+ protocol = APNFeedbackProtocol
+
+ def __init__(self, store):
+ self.store = store
+
+ def clientConnectionFailed(self, connector, reason):
+ self.log_error("Unable to connect to APN feedback server: %s" %
+ (reason,))
+ self.connected = False
+ ClientFactory.clientConnectionFailed(self, connector, reason)
+
+
+class APNFeedbackService(APNConnectionService):
+
+ def __init__(self, store, updateSeconds, host, port, certPath, keyPath,
+ chainPath="", sslMethod="TLSv1_METHOD", testConnector=None,
+ reactor=None):
+
+ APNConnectionService.__init__(self, host, port, certPath, keyPath,
+ chainPath="", sslMethod=sslMethod,
+ testConnector=testConnector, reactor=reactor)
+
+ self.store = store
+ self.updateSeconds = updateSeconds
+
+ def startService(self):
+ self.log_debug("APNFeedbackService startService")
+ self.factory = APNFeedbackFactory(self.store)
+ self.checkForFeedback()
+
+ def stopService(self):
+ self.log_debug("APNFeedbackService stopService")
+ if self.nextCheck is not None:
+ self.nextCheck.cancel()
+
+ def checkForFeedback(self):
+ self.nextCheck = None
+ self.log_debug("APNFeedbackService checkForFeedback")
+ self.connect(self.factory)
+ self.nextCheck = self.reactor.callLater(self.updateSeconds,
+ self.checkForFeedback)
+
+
+class APNSubscriptionResource(ReadOnlyNoCopyResourceMixIn,
+ DAVResourceWithoutChildrenMixin, DAVResource, LoggingMixIn):
+ """
+ The DAV resource allowing clients to subscribe to Apple push notifications.
+ To subscribe, a client should first determine the key they are interested
+ in my examining the "pushkey" DAV property on the home or collection they
+ want to monitor. Next the client sends an authenticated HTTP GET or POST
+ request to this resource, passing their device token and the key in either
+ the URL params or in the POST body.
+ """
+
+ def __init__(self, parent, store):
+ DAVResource.__init__(
+ self, principalCollections=parent.principalCollections()
+ )
+ self.parent = parent
+ self.store = store
+
+ def deadProperties(self):
+ if not hasattr(self, "_dead_properties"):
+ self._dead_properties = NonePropertyStore(self)
+ return self._dead_properties
+
+ def etag(self):
+ return None
+
+ def checkPreconditions(self, request):
+ return None
+
+ def defaultAccessControlList(self):
+ return davxml.ACL(
+ # DAV:Read for authenticated principals
+ davxml.ACE(
+ davxml.Principal(davxml.Authenticated()),
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ ),
+ davxml.Protected(),
+ ),
+ # DAV:Write for authenticated principals
+ davxml.ACE(
+ davxml.Principal(davxml.Authenticated()),
+ davxml.Grant(
+ davxml.Privilege(davxml.Write()),
+ ),
+ davxml.Protected(),
+ ),
+ )
+
+ def contentType(self):
+ return MimeType.fromString("text/html; charset=utf-8");
+
+ def resourceType(self):
+ return None
+
+ def isCollection(self):
+ return False
+
+ def isCalendarCollection(self):
+ return False
+
+ def isPseudoCalendarCollection(self):
+ return False
+
+ @inlineCallbacks
+ def http_POST(self, request):
+ yield self.authorize(request, (davxml.Write(),))
+ yield parsePOSTData(request)
+ code, msg = (yield self.processSubscription(request))
+ returnValue(self.renderResponse(code, body=msg))
+
+ http_GET = http_POST
+
+ def principalFromRequest(self, request):
+ """
+ Given an authenticated request, return the principal based on
+ request.authnUser
+ """
+ principal = None
+ for collection in self.principalCollections():
+ data = request.authnUser.children[0].children[0].data
+ principal = collection._principalForURI(data)
+ if principal is not None:
+ return principal
+
+ @inlineCallbacks
+ def processSubscription(self, request):
+ """
+ Given an authenticated request, use the token and key arguments
+ to add a subscription entry to the database.
+
+ @param request: The request to process
+ @type request: L{twext.web2.server.Request}
+ """
+
+ token = request.args.get("token", None)
+ key = request.args.get("key", None)
+ if key and token:
+ key = key[0]
+ token = token[0].replace(" ", "")
+ principal = self.principalFromRequest(request)
+ guid = principal.record.guid
+ yield self.addSubscription(token, key, guid)
+ code = responsecode.OK
+ msg = None
+ else:
+ code = responsecode.BAD_REQUEST
+ msg = "Invalid request: both 'token' and 'key' must be provided"
+
+ returnValue((code, msg))
+
+ @inlineCallbacks
+ def addSubscription(self, token, key, guid):
+ """
+ Add a subscription (or update its timestamp if already there).
+
+ @param token: The device token
+ @type token: C{str}
+
+ @param key: The push key
+ @type key: C{str}
+
+ @param guid: The GUID of the subscriber principal
+ @type guid: C{str}
+ """
+ now = int(time.time()) # epoch seconds
+ txn = self.store.newTransaction()
+ yield txn.addAPNSubscription(token, key, now, guid)
+ yield txn.commit()
+
+ def renderResponse(self, code, body=None):
+ response = Response(code, {}, body)
+ response.headers.setHeader("content-type", MimeType("text", "html"))
+ return response
Added: CalendarServer/trunk/calendarserver/push/test/__init__.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/__init__.py (rev 0)
+++ CalendarServer/trunk/calendarserver/push/test/__init__.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2011 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.
+##
+
+"""
+CalendarServer push notification code.
+"""
Copied: CalendarServer/trunk/calendarserver/push/test/test_applepush.py (from rev 8184, CalendarServer/branches/users/sagen/applepush/twistedcaldav/test/test_applepush.py)
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/test_applepush.py (rev 0)
+++ CalendarServer/trunk/calendarserver/push/test/test_applepush.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -0,0 +1,153 @@
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from calendarserver.push.applepush import (
+ ApplePushNotifierService, APNProviderProtocol
+)
+from twistedcaldav.test.util import TestCase
+from twisted.internet.defer import inlineCallbacks
+from twisted.internet.task import Clock
+import struct
+from txdav.common.datastore.test.util import buildStore, CommonCommonTests
+
+class ApplePushNotifierServiceTests(CommonCommonTests, TestCase):
+
+ @inlineCallbacks
+ def setUp(self):
+ yield super(ApplePushNotifierServiceTests, self).setUp()
+ self.store = yield buildStore(self, None)
+
+ @inlineCallbacks
+ def test_ApplePushNotifierService(self):
+
+ settings = {
+ "Service" : "calendarserver.push.applepush.ApplePushNotifierService",
+ "Enabled" : True,
+ "SubscriptionURL" : "apn",
+ "DataHost" : "calendars.example.com",
+ "ProviderHost" : "gateway.push.apple.com",
+ "ProviderPort" : 2195,
+ "FeedbackHost" : "feedback.push.apple.com",
+ "FeedbackPort" : 2196,
+ "FeedbackUpdateSeconds" : 300,
+ "CalDAV" : {
+ "CertificatePath" : "caldav.cer",
+ "PrivateKeyPath" : "caldav.pem",
+ "Topic" : "caldav_topic",
+ },
+ "CardDAV" : {
+ "CertificatePath" : "carddav.cer",
+ "PrivateKeyPath" : "carddav.pem",
+ "Topic" : "carddav_topic",
+ },
+ }
+
+
+ # Add subscriptions
+ txn = self.store.newTransaction()
+ token = "2d0d55cd7f98bcb81c6e24abcdc35168254c7846a43e2828b1ba5a8f82e219df"
+ key1 = "/CalDAV/calendars.example.com/user01/calendar/"
+ timestamp1 = 1000
+ guid = "D2256BCC-48E2-42D1-BD89-CBA1E4CCDFFB"
+ yield txn.addAPNSubscription(token, key1, timestamp1, guid)
+
+ key2 = "/CalDAV/calendars.example.com/user02/calendar/"
+ timestamp2 = 3000
+ yield txn.addAPNSubscription(token, key2, timestamp2, guid)
+ yield txn.commit()
+
+ # Set up the service
+ clock = Clock()
+ service = (yield ApplePushNotifierService.makeService(settings,
+ self.store, testConnectorClass=TestConnector, reactor=clock))
+ self.assertEquals(set(service.providers.keys()), set(["CalDAV","CardDAV"]))
+ self.assertEquals(set(service.feedbacks.keys()), set(["CalDAV","CardDAV"]))
+
+ # First, enqueue a notification while we have no connection, in this
+ # case by doing it prior to startService()
+
+ # Notification arrives from calendar server
+ yield service.enqueue("update", "CalDAV|user01/calendar")
+
+ # The notification should be in the queue
+ self.assertEquals(service.providers["CalDAV"].queue, [(token, key1)])
+
+ # Start the service, making the connection which should service the
+ # queue
+ service.startService()
+
+ # The queue should be empty
+ self.assertEquals(service.providers["CalDAV"].queue, [])
+
+ # Verify data sent to APN
+ connector = service.providers["CalDAV"].testConnector
+ rawData = connector.transport.data
+ self.assertEquals(len(rawData), 103)
+ data = struct.unpack("!BIIH32sH", rawData[:45])
+ self.assertEquals(data[0], 1) # command
+ self.assertEquals(data[4].encode("hex"), token.replace(" ", "")) # token
+ payloadLength = data[5]
+ payload = struct.unpack("%ds" % (payloadLength,),
+ rawData[45:])
+ self.assertEquals(payload[0], '{"key" : "%s"}' % (key1,))
+
+ # Simulate an error
+ errorData = struct.pack("!BBI", APNProviderProtocol.COMMAND_ERROR, 1, 1)
+ yield connector.receiveData(errorData)
+ clock.advance(301)
+
+ # Prior to feedback, there are 2 subscriptions
+ txn = self.store.newTransaction()
+ subscriptions = (yield txn.apnSubscriptionsByToken(token))
+ yield txn.commit()
+ self.assertEquals(len(subscriptions), 2)
+
+ # Simulate feedback
+ timestamp = 2000
+ connector = service.feedbacks["CalDAV"].testConnector
+ binaryToken = token.decode("hex")
+ feedbackData = struct.pack("!IH32s", timestamp, len(binaryToken),
+ binaryToken)
+ yield connector.receiveData(feedbackData)
+
+ # The second subscription should now be gone
+ # Prior to feedback, there are 2 subscriptions
+ txn = self.store.newTransaction()
+ subscriptions = (yield txn.apnSubscriptionsByToken(token))
+ yield txn.commit()
+ self.assertEquals(len(subscriptions), 1)
+
+
+class TestConnector(object):
+
+ def connect(self, service, factory):
+ self.service = service
+ service.protocol = factory.buildProtocol(None)
+ service.connected = 1
+ self.transport = StubTransport()
+ service.protocol.makeConnection(self.transport)
+
+ def receiveData(self, data):
+ return self.service.protocol.dataReceived(data)
+
+
+class StubTransport(object):
+
+ def __init__(self):
+ self.data = None
+
+ def write(self, data):
+ self.data = data
Modified: CalendarServer/trunk/calendarserver/tap/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/util.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/calendarserver/tap/util.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -56,6 +56,7 @@
from twistedcaldav.directory.sudo import SudoDirectoryService
from twistedcaldav.directory.wiki import WikiDirectoryService
from twistedcaldav.notify import NotifierFactory, getPubSubConfiguration
+from calendarserver.push.applepush import APNSubscriptionResource
from twistedcaldav.directorybackedaddressbook import DirectoryBackedAddressBookResource
from twistedcaldav.resource import CalDAVResource, AuthenticationWrapper
from twistedcaldav.schedule import IScheduleInboxResource
@@ -358,6 +359,7 @@
webAdminResourceClass = WebAdminResource
addressBookResourceClass = DirectoryAddressBookHomeProvisioningResource
directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookResource
+ apnSubscriptionResourceClass = APNSubscriptionResource
directory = directoryFromConfig(config)
@@ -593,6 +595,16 @@
root.putChild("admin", webAdmin)
#
+ # Apple Push Notification Subscriptions
+ #
+ apnConfig = config.Notifications.Services["ApplePushNotifier"]
+ if apnConfig.Enabled:
+ log.info("Setting up APNS resource at /%s" %
+ (apnConfig["SubscriptionURL"],))
+ apnResource = apnSubscriptionResourceClass(root, newStore)
+ root.putChild(apnConfig["SubscriptionURL"], apnResource)
+
+ #
# Configure ancillary data
#
log.info("Setting up Timezone Cache")
Property changes on: CalendarServer/trunk/support/build.sh
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/support/build.sh:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pods/support/build.sh:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/support/build.sh:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/support/build.sh:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/new-export/support/build.sh:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/inboxitems/support/build.sh:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593
+ /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/support/build.sh:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pods/support/build.sh:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/support/build.sh:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/support/build.sh:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/new-export/support/build.sh:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/applepush/support/build.sh:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/support/build.sh:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593
Modified: CalendarServer/trunk/twistedcaldav/notify.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/notify.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/twistedcaldav/notify.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -147,15 +147,16 @@
id = self.getID(label=label)
pubSubConfig = self._notifierFactory.pubSubConfig
name = getPubSubPath(id, pubSubConfig)
- try:
- if self._notifierFactory.nodeCacher:
- nodeCacher = self._notifierFactory.nodeCacher
- else:
- nodeCacher = getNodeCacher()
- (yield nodeCacher.waitForNode(self, name))
- except NodeCreationException, e:
- self.log_warn(e)
- returnValue(None)
+ if pubSubConfig["enabled"]:
+ try:
+ if self._notifierFactory.nodeCacher:
+ nodeCacher = self._notifierFactory.nodeCacher
+ else:
+ nodeCacher = getNodeCacher()
+ (yield nodeCacher.waitForNode(self, name))
+ except NodeCreationException, e:
+ self.log_warn(e)
+ returnValue(None)
returnValue(name)
class NotificationClientLineProtocol(LineReceiver, LoggingMixIn):
@@ -458,7 +459,7 @@
def enqueue(self, op, id):
"""
- Let's the notifier object know that a change has been made for this
+ Let the notifier object know that a change has been made for this
id, and enough time has passed to allow for coalescence.
@type op: C{str}
@@ -1258,12 +1259,11 @@
def getPubSubConfiguration(config):
# TODO: Should probably cache this
- results = { 'enabled' : False }
+ results = { 'enabled' : False, 'host' : config.ServerHostName }
settings = getXMPPSettings(config)
if settings is not None:
results['enabled'] = True
results['service'] = settings['ServiceAddress']
- results['host'] = config.ServerHostName
results['port'] = config.SSLPort or config.HTTPPort
results['xmpp-server'] = (
settings['Host'] if settings['Port'] == 5222
@@ -1278,16 +1278,33 @@
Returns the Apple push notification settings specific to the notifier
ID, which includes a prefix that is either "CalDAV" or "CardDAV"
"""
- settings = getXMPPSettings(config)
- if settings is None:
- return None
-
try:
prefix, id = id.split("|", 1)
except ValueError:
# id has no prefix, so we can't look up APS config
return None
+ # If we are directly talking to apple push, advertise those settings
+ applePushSettings = config.Notifications.Services.ApplePushNotifier
+ if applePushSettings.Enabled:
+ settings = {}
+ settings["APSBundleID"] = applePushSettings[prefix]["Topic"]
+ if config.EnableSSL:
+ url = "https://%s:%s/%s" % (config.ServerHostName, config.SSLPort,
+ applePushSettings.SubscriptionURL)
+ else:
+ url = "http://%s:%s/%s" % (config.ServerHostName, config.HTTPPort,
+ applePushSettings.SubscriptionURL)
+ settings["SubscriptionURL"] = url
+ settings["APSEnvironment"] = applePushSettings.Environment
+ return settings
+
+ # ...otherwise pick up the apple push settings we get via XMPP and
+ # apn bridge
+ settings = getXMPPSettings(config)
+ if settings is None:
+ return None
+
if (settings.has_key(prefix) and
settings[prefix]["APSBundleID"] and
settings[prefix]["SubscriptionURL"]):
@@ -1441,12 +1458,49 @@
config.Memcached.MaxClients,
)
+ # TODO: This code is copied from makeService_Slave, and needs to be
+ # refactored so it can be shared instead.
+ from calendarserver.tap.util import (
+ storeFromConfig, pgConnectorFromConfig, oracleConnectorFromConfig,
+ pgServiceFromConfig
+ )
+ from twext.enterprise.ienterprise import POSTGRES_DIALECT
+ from twext.enterprise.ienterprise import ORACLE_DIALECT
+ from twext.enterprise.adbapi2 import ConnectionPool
+
+ pool = None
+ if not config.UseDatabase:
+ txnFactory = None
+ else:
+ dialect = POSTGRES_DIALECT
+ paramstyle = 'pyformat'
+ if config.DBType == '':
+ # get a PostgresService to tell us what the local connection
+ # info is, but *don't* start it (that would start one postgres
+ # master per slave, resulting in all kinds of mayhem...)
+ connectionFactory = pgServiceFromConfig(
+ config, None).produceConnection
+ elif config.DBType == 'postgres':
+ connectionFactory = pgConnectorFromConfig(config)
+ elif config.DBType == 'oracle':
+ dialect = ORACLE_DIALECT
+ paramstyle = 'numeric'
+ connectionFactory = oracleConnectorFromConfig(config)
+ else:
+ raise UsageError("unknown DB type: %r" % (config.DBType,))
+ pool = ConnectionPool(connectionFactory, dialect=dialect,
+ paramstyle=paramstyle)
+ txnFactory = pool.connection
+
+ store = storeFromConfig(config, txnFactory)
+
multiService = service.MultiService()
notifiers = []
for key, settings in config.Notifications.Services.iteritems():
if settings["Enabled"]:
- notifier = namedClass(settings["Service"])(settings)
+ notifier = namedClass(settings["Service"]).makeService(settings,
+ store)
notifier.setServiceParent(multiService)
notifiers.append(notifier)
@@ -1462,6 +1516,10 @@
class SimpleLineNotifierService(service.Service):
+ @classmethod
+ def makeService(cls, settings, store):
+ return cls(settings)
+
def __init__(self, settings):
self.notifier = SimpleLineNotifier(settings)
self.server = internet.TCPServer(settings["Port"],
@@ -1479,6 +1537,10 @@
class XMPPNotifierService(service.Service):
+ @classmethod
+ def makeService(cls, settings, store):
+ return cls(settings)
+
def __init__(self, settings):
self.notifier = XMPPNotifier(settings)
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -2224,7 +2224,10 @@
returnValue(customxml.MaxCollections.fromString(config.MaxCollectionsPerHome))
elif qname == (customxml.calendarserver_namespace, "push-transports"):
- if config.Notifications.Services.XMPPNotifier.Enabled:
+
+ if (config.Notifications.Services.XMPPNotifier.Enabled or
+ config.Notifications.Services.ApplePushNotifier.Enabled):
+
nodeName = (yield self._newStoreHome.nodeName())
if nodeName:
notifierID = self._newStoreHome.notifierID()
@@ -2251,7 +2254,8 @@
)
pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['xmpp-server']:
+ if (pubSubConfiguration['enabled'] and
+ pubSubConfiguration['xmpp-server']):
children.append(
customxml.PubSubTransportProperty(
customxml.PubSubXMPPServerProperty(
@@ -2268,7 +2272,8 @@
returnValue(None)
elif qname == (customxml.calendarserver_namespace, "pushkey"):
- if config.Notifications.Services.XMPPNotifier.Enabled:
+ if (config.Notifications.Services.XMPPNotifier.Enabled or
+ config.Notifications.Services.ApplePushNotifier.Enabled):
nodeName = (yield self._newStoreHome.nodeName())
if nodeName:
returnValue(customxml.PubSubXMPPPushKeyProperty(nodeName))
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -631,6 +631,28 @@
"Enabled" : False,
"Port" : 62308,
},
+ "ApplePushNotifier" : {
+ "Service" : "calendarserver.push.applepush.ApplePushNotifierService",
+ "Enabled" : False,
+ "SubscriptionURL" : "apns",
+ "DataHost" : "",
+ "ProviderHost" : "gateway.push.apple.com",
+ "ProviderPort" : 2195,
+ "FeedbackHost" : "feedback.push.apple.com",
+ "FeedbackPort" : 2196,
+ "FeedbackUpdateSeconds" : 300, # 5 minutes
+ "Environment" : "PRODUCTION",
+ "CalDAV" : {
+ "CertificatePath" : "",
+ "PrivateKeyPath" : "",
+ "Topic" : "",
+ },
+ "CardDAV" : {
+ "CertificatePath" : "",
+ "PrivateKeyPath" : "",
+ "Topic" : "",
+ },
+ },
"XMPPNotifier" : {
"Service" : "twistedcaldav.notify.XMPPNotifierService",
"Enabled" : False,
@@ -1185,7 +1207,15 @@
configDict.Notifications["Enabled"] = False
for key, service in configDict.Notifications["Services"].iteritems():
+
+ # The default for apple push DataHost is ServerHostName
if (
+ service["Service"] == "calendarserver.push.applepush.ApplePushNotifierService" and
+ service["DataHost"] == ""
+ ):
+ service["DataHost"] = configDict.ServerHostName
+
+ if (
service["Service"] == "twistedcaldav.notify.XMPPNotifierService" and
service["Enabled"]
):
Modified: CalendarServer/trunk/twistedcaldav/test/test_config.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_config.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/twistedcaldav/test/test_config.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -95,7 +95,7 @@
def testDefaults(self):
for key, value in DEFAULT_CONFIG.iteritems():
- if key in ("ServerHostName",):
+ if key in ("ServerHostName", "Notifications"):
# Value is calculated and may vary
continue
for item in RELATIVE_PATHS:
Modified: CalendarServer/trunk/twistedcaldav/test/test_notify.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_notify.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/twistedcaldav/test/test_notify.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -573,7 +573,7 @@
# Overall notifications are disabled
disabledConfig.Notifications["Enabled"] = False
conf = getPubSubConfiguration(disabledConfig)
- self.assertEquals(conf, { "enabled" : False })
+ self.assertEquals(conf, { "enabled" : False, "host" : "" })
conf = getXMPPSettings(disabledConfig)
self.assertEquals(conf, None)
Property changes on: CalendarServer/trunk/txdav/caldav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/caldav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/caldav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
Property changes on: CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
Property changes on: CalendarServer/trunk/txdav/carddav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/carddav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/carddav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
Property changes on: CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -232,6 +232,20 @@
return NotificationCollection.notificationsFromHome(self, home)
+ # File-based storage of APN subscriptions not implementated.
+ def addAPNSubscription(self, token, key, timestamp, subscriber):
+ return NotImplementedError
+
+ def removeAPNSubscription(self, token, key):
+ return NotImplementedError
+
+ def apnSubscriptionsByToken(self, token):
+ return NotImplementedError
+
+ def apnSubscriptionsByKey(self, key):
+ return NotImplementedError
+
+
class StubResource(object):
"""
Just enough resource to keep the shared sql DB classes going.
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -324,6 +324,91 @@
return NotificationCollection.notificationsWithUID(self, uid)
+ @classproperty
+ def _insertAPNSubscriptionQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Insert({apn.TOKEN: Parameter("token"),
+ apn.RESOURCE_KEY: Parameter("resourceKey"),
+ apn.MODIFIED: Parameter("modified"),
+ apn.SUBSCRIBER_GUID: Parameter("subscriber")})
+
+
+ @classproperty
+ def _updateAPNSubscriptionQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Update({apn.MODIFIED: Parameter("modified")},
+ Where=(apn.TOKEN == Parameter("token")).And(
+ apn.RESOURCE_KEY == Parameter("resourceKey")))
+
+
+ @classproperty
+ def _selectAPNSubscriptionQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Select([apn.MODIFIED, apn.SUBSCRIBER_GUID], From=apn,
+ Where=(
+ apn.TOKEN == Parameter("token")).And(
+ apn.RESOURCE_KEY == Parameter("resourceKey")
+ )
+ )
+
+
+ @inlineCallbacks
+ def addAPNSubscription(self, token, key, timestamp, subscriber):
+ row = yield self._selectAPNSubscriptionQuery.on(self,
+ token=token, resourceKey=key)
+ if not row: # Subscription does not yet exist
+ try:
+ yield self._insertAPNSubscriptionQuery.on(self,
+ token=token, resourceKey=key, modified=timestamp,
+ subscriber=subscriber)
+ except Exception:
+ # Subscription may have been added by someone else, which is fine
+ pass
+
+ else: # Subscription exists, so update with new timestamp
+ try:
+ yield self._updateAPNSubscriptionQuery.on(self,
+ token=token, resourceKey=key, modified=timestamp)
+ except Exception:
+ # Subscription may have been added by someone else, which is fine
+ pass
+
+
+ @classproperty
+ def _removeAPNSubscriptionQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Delete(From=apn,
+ Where=(apn.TOKEN == Parameter("token")).And(
+ apn.RESOURCE_KEY == Parameter("resourceKey")))
+
+
+ def removeAPNSubscription(self, token, key):
+ return self._removeAPNSubscriptionQuery.on(self,
+ token=token, resourceKey=key)
+
+
+ @classproperty
+ def _apnSubscriptionsByTokenQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Select([apn.RESOURCE_KEY, apn.MODIFIED, apn.SUBSCRIBER_GUID],
+ From=apn, Where=apn.TOKEN == Parameter("token"))
+
+
+ def apnSubscriptionsByToken(self, token):
+ return self._apnSubscriptionsByTokenQuery.on(self, token=token)
+
+
+ @classproperty
+ def _apnSubscriptionsByKeyQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Select([apn.TOKEN, apn.SUBSCRIBER_GUID],
+ From=apn, Where=apn.RESOURCE_KEY == Parameter("resourceKey"))
+
+
+ def apnSubscriptionsByKey(self, key):
+ return self._apnSubscriptionsByKeyQuery.on(self, resourceKey=key)
+
+
def postCommit(self, operation):
"""
Run things after C{commit}.
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql 2011-10-11 21:32:18 UTC (rev 8186)
@@ -435,6 +435,24 @@
);
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ unique(TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_TOKENS
+ on APN_SUBSCRIPTIONS(TOKEN);
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
--------------------
-- Schema Version --
--------------------
@@ -444,5 +462,5 @@
VALUE varchar(255)
);
-insert into CALENDARSERVER values ('VERSION', '5');
+insert into CALENDARSERVER values ('VERSION', '6');
Copied: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_5_to_6.sql (from rev 8184, CalendarServer/branches/users/sagen/applepush/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_5_to_6.sql)
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_5_to_6.sql (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_5_to_6.sql 2011-10-11 21:32:18 UTC (rev 8186)
@@ -0,0 +1,40 @@
+----
+-- Copyright (c) 2011 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+-------------------------------------------------
+-- Upgrade database schema from VERSION 5 to 6 --
+-------------------------------------------------
+
+---------------------------------------------------------
+-- New table for Apple Push Notification Subscriptions --
+---------------------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ unique(TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_TOKENS
+ on APN_SUBSCRIPTIONS(TOKEN);
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '6' where NAME = 'VERSION';
+
Copied: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_5_to_6.sql (from rev 8184, CalendarServer/branches/users/sagen/applepush/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_5_to_6.sql)
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_5_to_6.sql (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_5_to_6.sql 2011-10-11 21:32:18 UTC (rev 8186)
@@ -0,0 +1,40 @@
+----
+-- Copyright (c) 2011 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+-------------------------------------------------
+-- Upgrade database schema from VERSION 5 to 6 --
+-------------------------------------------------
+
+---------------------------------------------------------
+-- New table for Apple Push Notification Subscriptions --
+---------------------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ unique(TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_TOKENS
+ on APN_SUBSCRIPTIONS(TOKEN);
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '6' where NAME = 'VERSION';
+
Modified: CalendarServer/trunk/txdav/common/icommondatastore.py
===================================================================
--- CalendarServer/trunk/txdav/common/icommondatastore.py 2011-10-11 20:06:44 UTC (rev 8185)
+++ CalendarServer/trunk/txdav/common/icommondatastore.py 2011-10-11 21:32:18 UTC (rev 8186)
@@ -147,8 +147,54 @@
notification collection exists.
"""
+ def addAPNSubscription(token, key, timestamp, subscriber):
+ """
+ Add (or update) a subscription entry in the database.
+ @param token: The device token of the subscriber
+ @type token: C{str}
+ @param key: The push key to subscribe to
+ @type key: C{str}
+
+ @param timestamp: The number of seconds since the epoch
+ @type timestamp: C{int}
+
+ @param subscriber: The GUID of the subscribing principal
+ @type subscrbier: C{str}
+ """
+
+ def removeAPNSubscription(token, key):
+ """
+ Remove a subscription entry from the database.
+
+ @param token: The device token of the subscriber
+ @type token: C{str}
+
+ @param key: The push key
+ @type key: C{str}
+ """
+
+ def apnSubscriptionsByToken(token):
+ """
+ Retrieve all subscription entries for the token.
+
+ @param token: The device token of the subscriber
+ @type token: C{str}
+
+ @return: tuples of (key, timestamp, guid)
+ """
+
+ def apnSubscriptionsByKey(key):
+ """
+ Retrieve all subscription entries for the key.
+
+ @param key: The push key
+ @type key: C{str}
+
+ @return: tuples of (token, guid)
+ """
+
class IShareableCollection(Interface):
"""
A collection resource which may be shared.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111011/c6e60696/attachment-0001.html>
More information about the calendarserver-changes
mailing list