Revision: 827 http://trac.macosforge.org/projects/calendarserver/changeset/827 Author: wsanchez@apple.com Date: 2006-12-14 14:16:47 -0800 (Thu, 14 Dec 2006) Log Message: ----------- merge branches/users/wsanchez/dropbox Modified Paths: -------------- CalendarServer/trunk/testcaldav CalendarServer/trunk/twistedcaldav/caldavxml.py CalendarServer/trunk/twistedcaldav/customxml.py CalendarServer/trunk/twistedcaldav/directory/calendar.py CalendarServer/trunk/twistedcaldav/directory/principal.py CalendarServer/trunk/twistedcaldav/dropbox.py CalendarServer/trunk/twistedcaldav/method/__init__.py CalendarServer/trunk/twistedcaldav/method/delete.py CalendarServer/trunk/twistedcaldav/method/mkcol.py CalendarServer/trunk/twistedcaldav/method/put.py CalendarServer/trunk/twistedcaldav/notifications.py CalendarServer/trunk/twistedcaldav/resource.py CalendarServer/trunk/twistedcaldav/schedule.py CalendarServer/trunk/twistedcaldav/static.py CalendarServer/trunk/twistedcaldav/tap.py CalendarServer/trunk/twistedcaldav/test/data/makelargecalendars.py Removed Paths: ------------- CalendarServer/trunk/twistedcaldav/method/x_apple_subscribe.py CalendarServer/trunk/twistedcaldav/method/x_apple_unsubscribe.py Modified: CalendarServer/trunk/testcaldav =================================================================== --- CalendarServer/trunk/testcaldav 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/testcaldav 2006-12-14 22:16:47 UTC (rev 827) @@ -18,9 +18,6 @@ # DRI: Wilfredo Sanchez, wsanchez@apple.com ## -set -e -set -u - wd="$(cd "$(dirname "$0")" && pwd)"; cdt="${wd}/../CalDAVTester"; @@ -72,6 +69,6 @@ svn checkout http://svn.macosforge.org/repository/calendarserver/CalDAVTester/trunk "${cdt}" fi; -tar -C "${documentroot}/calendars/user/user01/" -x${verbose}zf "${cdt}/Resource/errors/calendar.1000.tgz" +cd "${documentroot}" && python "makelargecalendars.py"; cd "${cdt}" && python testcaldav.py -s "${serverinfo}" --all; Modified: CalendarServer/trunk/twistedcaldav/caldavxml.py =================================================================== --- CalendarServer/trunk/twistedcaldav/caldavxml.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/caldavxml.py 2006-12-14 22:16:47 UTC (rev 827) @@ -1525,3 +1525,5 @@ def _isCalendar(self): return bool(self.childrenOfType(Calendar)) davxml.ResourceType.isCalendar = _isCalendar davxml.ResourceType.calendar = davxml.ResourceType(davxml.Collection(), Calendar()) +davxml.ResourceType.scheduleInbox = davxml.ResourceType(davxml.Collection(), ScheduleInbox()) +davxml.ResourceType.scheduleOutbox = davxml.ResourceType(davxml.Collection(), ScheduleOutbox()) Modified: CalendarServer/trunk/twistedcaldav/customxml.py =================================================================== --- CalendarServer/trunk/twistedcaldav/customxml.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/customxml.py 2006-12-14 22:16:47 UTC (rev 827) @@ -28,7 +28,7 @@ from twisted.web2.dav.resource import twisted_dav_namespace from twisted.web2.dav import davxml -apple_namespace = "http://apple.com/ns/calendarserver/" +calendarserver_namespace = "http://org.calendarserver/ns/" class TwistedGUIDProperty (davxml.WebDAVTextElement): """ @@ -86,7 +86,7 @@ Denotes a drop box home collection (a collection that will contain drop boxes). (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "dropbox-home" class DropBox (davxml.WebDAVEmptyElement): @@ -94,7 +94,7 @@ Denotes a drop box collection. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "dropbox" class Notifications (davxml.WebDAVEmptyElement): @@ -102,7 +102,7 @@ Denotes a notifications collection. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "notifications" class DropBoxHomeURL (davxml.WebDAVElement): @@ -110,7 +110,7 @@ A principal property to indicate the location of the drop box home. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "dropbox-home-URL" hidden = True protected = True @@ -122,7 +122,7 @@ A principal property to indicate the location of the notification collection. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "notifications-URL" hidden = True protected = True @@ -133,12 +133,12 @@ """ Root element for XML data in a notification resource. """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "notification" allowed_children = { - (apple_namespace, "time-stamp" ): (1, 1), - (apple_namespace, "changed" ): (1, 1), + (calendarserver_namespace, "time-stamp" ): (1, 1), + (calendarserver_namespace, "changed" ): (1, 1), } class TimeStamp (davxml.WebDAVTextElement): @@ -146,7 +146,7 @@ A property to indicate the timestamp of a notification resource. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "time-stamp" hidden = True protected = True @@ -157,7 +157,7 @@ notification resource. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "changed" hidden = True protected = True @@ -169,7 +169,7 @@ A property to indicate which principals will receive notifications. (Apple Extension to CalDAV) """ - namespace = apple_namespace + namespace = calendarserver_namespace name = "subscribed" hidden = True protected = True Modified: CalendarServer/trunk/twistedcaldav/directory/calendar.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/calendar.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/directory/calendar.py 2006-12-14 22:16:47 UTC (rev 827) @@ -190,6 +190,9 @@ self.putChild(name, child) def provision(self): + return self.provisionDefaultCalendars() + + def provisionDefaultCalendars(self): # Create a calendar collection childName = "calendar" Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/principal.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/directory/principal.py 2006-12-14 22:16:47 UTC (rev 827) @@ -40,7 +40,6 @@ from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVFile from twistedcaldav.resource import CalendarPrincipalCollectionResource, CalendarPrincipalResource from twistedcaldav.static import provisionFile -from twistedcaldav.dropbox import DropBox from twistedcaldav.directory.idirectory import IDirectoryService # FIXME: These should not be tied to DAVFile @@ -373,10 +372,10 @@ return self._homeChildURL("outbox/") def dropboxURL(self): - return self._homeChildURL(DropBox.dropboxName + "/") + return self._homeChildURL("dropbox/") def notificationsURL(self): - return self._homeChildURL(DropBox.notificationName + "/") + return self._homeChildURL("notifications/") def _homeChildURL(self, name): home = self._calendarHome() Modified: CalendarServer/trunk/twistedcaldav/dropbox.py =================================================================== --- CalendarServer/trunk/twistedcaldav/dropbox.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/dropbox.py 2006-12-14 22:16:47 UTC (rev 827) @@ -22,80 +22,175 @@ """ __all__ = [ - "DropBox", + "DropBoxHomeResource", ] -from twistedcaldav.customxml import davxml, apple_namespace +from twisted.internet.defer import deferredGenerator, waitForDeferred +from twisted.python import log +from twisted.web2 import responsecode +from twisted.web2.dav import davxml +from twisted.web2.dav.http import HTTPError, ErrorResponse +from twisted.web2.dav.resource import DAVResource, TwistedACLInheritable +from twisted.web2.dav.util import parentForURL -import os +from twistedcaldav import customxml +from twistedcaldav.customxml import calendarserver_namespace +from twistedcaldav.notifications import Notification -class DropBox(object): - - # These are all options that will be set from a .plist configuration file. +class DropBoxHomeResource (DAVResource): + """ + Drop box collection resource. + """ + def resourceType(self): + return davxml.ResourceType( + davxml.ResourceType.collection, + davxml.ResourceType.dropboxhome, + ) - enabled = True # Whether or not drop box functionaility is enabled. - dropboxName = "dropbox" # Name of the collection in which drop boxes can be created. - inheritedACLs = True # Whether or not ACLs set on a drop box collection are automatically - # inherited by child resources. - notifications = True # Whether to post notification messages into per-user notification collection. - notificationName = "notifications" # Name of the collection in which notifications will be stored. - - @classmethod - def enable(clzz, enabled, notifications=None): - """ - This method must be used to enable drop box support as it will setup live properties etc, - and turn on the notification system. It must only be called once + def isCollection(self): + return True - @param enable: C{True} if drop box feature is enabled, C{False} otherwise - @param notifications: C{True} if automatic notifications are to be sent when a drop box changes, C{False} otherwise. - """ - DropBox.enabled = enabled - if notifications: - DropBox.notifications = notifications +class DropBoxCollectionResource (DAVResource): + """ + Drop box resource. + """ + def resourceType(self): + return davxml.ResourceType( + davxml.ResourceType.collection, + davxml.ResourceType.dropbox, + ) - if DropBox.enabled: + def isCollection(self): + return True - # Need to setup live properties - from twistedcaldav.resource import CalendarPrincipalResource - assert (apple_namespace, "dropbox-home-URL") not in CalendarPrincipalResource.liveProperties, \ - "DropBox.enable must only be called once" - - CalendarPrincipalResource.liveProperties += ( - (apple_namespace, "dropbox-home-URL" ), - (apple_namespace, "notifications-URL" ), - ) - - @classmethod - def provision(clzz, cuhome): + def writeNewACEs(self, newaces): """ - Provision user account with appropriate collections for drop box - and notifications. + Write a new ACL to the resource's property store. We override this for calendar collections + and force all the ACEs to be inheritable so that all calendar object resources within the + calendar collection have the same privileges unless explicitly overridden. The same applies + to drop box collections as we want all resources (attachments) to have the same privileges as + the drop box collection. - @param principal: the L{CalendarPrincipalResource} for the principal to provision - @param cuhome: L{DAVResource} - resource of user calendar home + @param newaces: C{list} of L{ACE} for ACL being set. """ + # Add inheritable option to each ACE in the list + edited_aces = [] + for ace in newaces: + if TwistedACLInheritable() not in ace.children: + children = list(ace.children) + children.append(TwistedACLInheritable()) + edited_aces.append(davxml.ACE(*children)) + else: + edited_aces.append(ace) - # Only if enabled - if not DropBox.enabled: - return + # Do inherited with possibly modified set of aces + super(DropBoxCollectionResource, self).writeNewACEs(edited_aces) + + def http_DELETE(self, request): + # + # Handle notificiations + # + parentURL=parentForURL(request.uri) + + def gotParent(parent): + def gotResponse(response): + notification = Notification(parentURL=parentURL) + d = notification.doNotification(request, parent) + d.addCallback(lambda _: response) + return d + + d = super(DropBoxCollectionResource, self).http_DELETE(request) + d.addCallback(gotResponse) + return d + + d = request.locateResource(parentURL) + d.addCallback(gotParent) + return d - # Create drop box collection in calendar-home collection resource if not already present. - - from twistedcaldav.static import CalDAVFile - child = CalDAVFile(os.path.join(cuhome.fp.path, DropBox.dropboxName)) - child_exists = child.exists() - if not child_exists: - c = child.createSpecialCollection(davxml.ResourceType.dropboxhome) - assert c.called - c = c.result - - if not DropBox.notifications: - return - - child = CalDAVFile(os.path.join(cuhome.fp.path, DropBox.notificationName)) - child_exists = child.exists() - if not child_exists: - c = child.createSpecialCollection(davxml.ResourceType.notifications) - assert c.called - c = c.result - \ No newline at end of file + def http_PUT(self, request): + return ErrorResponse( + responsecode.FORBIDDEN, + (calendarserver_namespace, "valid-drop-box") + ) + + def http_X_APPLE_SUBSCRIBE(self, request): + d = waitForDeferred(self.authorize(request, (davxml.Read(),))) + yield d + d.getResult() + authid = request.authnUser + + # Get current list of subscribed principals + principals = [] + if self.hasDeadProperty(customxml.Subscribed): + subs = self.readDeadProperty(customxml.Subscribed).children + principals.extend(subs) + + # Error if attempt to subscribe more than once + if authid in principals: + log.err("Cannot x_apple_subscribe to resource %s as principal %s is already subscribed" % (request.uri, repr(authid),)) + raise HTTPError(ErrorResponse( + responsecode.FORBIDDEN, + (calendarserver_namespace, "principal-must-not-be-subscribed")) + ) + + principals.append(authid) + self.writeDeadProperty(customxml.Subscribed(*principals)) + + yield responsecode.OK + + http_X_APPLE_SUBSCRIBE = deferredGenerator(http_X_APPLE_SUBSCRIBE) + + def http_X_APPLE_UNSUBSCRIBE(self, request): + # We do not check any privileges. If a principal is subscribed we always allow them to + # unsubscribe provided they have at least authenticated. + d = waitForDeferred(self.authorize(request, ())) + yield d + d.getResult() + authid = request.authnUser + + # Get current list of subscribed principals + principals = [] + if self.hasDeadProperty(customxml.Subscribed): + subs = self.readDeadProperty(customxml.Subscribed).children + principals.extend(subs) + + # Error if attempt to subscribe more than once + if authid not in principals: + log.err("Cannot x_apple_unsubscribe from resource %s as principal %s is not currently subscribed" % (request.uri, repr(authid),)) + raise HTTPError(ErrorResponse( + responsecode.FORBIDDEN, + (calendarserver_namespace, "principal-must-be-subscribed")) + ) + + principals.remove(authid) + self.writeDeadProperty(customxml.Subscribed(*principals)) + + yield responsecode.OK + + http_X_APPLE_UNSUBSCRIBE = deferredGenerator(http_X_APPLE_UNSUBSCRIBE) + +class DropBoxChildResource (DAVResource): + def http_MKCOL(self, request): + return responsecode.FORBIDDEN + + def http_PUT(self, request): + # + # Handle notificiations + # + parentURL=parentForURL(request.uri) + + def gotParent(parent): + def gotResponse(response): + if response.code in (responsecode.OK, responsecode.CREATED, responsecode.NO_CONTENT): + notification = Notification(parentURL=parentForURL(request.uri)) + d = notification.doNotification(request, parent) + d.addCallback(lambda _: response) + return d + + d = super(DropBoxChildResource, self).http_PUT(request) + d.addCallback(gotResponse) + return d + + d = request.locateResource(parentURL) + d.addCallback(gotParent) + return d Modified: CalendarServer/trunk/twistedcaldav/method/__init__.py =================================================================== --- CalendarServer/trunk/twistedcaldav/method/__init__.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/method/__init__.py 2006-12-14 22:16:47 UTC (rev 827) @@ -32,6 +32,4 @@ "report_calquery", "report_freebusy", "report_multiget", - "x_apple_subscribe", - "x_apple_unsubscribe", ] Modified: CalendarServer/trunk/twistedcaldav/method/delete.py =================================================================== --- CalendarServer/trunk/twistedcaldav/method/delete.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/method/delete.py 2006-12-14 22:16:47 UTC (rev 827) @@ -22,13 +22,9 @@ __all__ = ["http_DELETE"] -from twisted.internet.defer import deferredGenerator, waitForDeferred from twisted.web2 import responsecode from twisted.web2.dav.util import parentForURL -from twistedcaldav import customxml -from twistedcaldav.dropbox import DropBox -from twistedcaldav.notifications import Notification from twistedcaldav.resource import isPseudoCalendarCollectionResource def http_DELETE(self, request): @@ -36,31 +32,20 @@ # Override base DELETE request handling to ensure that the calendar # index file has the entry for the deleted calendar component removed. # - # Also handle notifications in a drop box collection. - # + def gotParent(parent): + def gotResponse(response): + if response == responsecode.NO_CONTENT: + if isPseudoCalendarCollectionResource(parent): + index = parent.index() + index.deleteResource(self.fp.basename()) - parentURL = parentForURL(request.uri) - parent = waitForDeferred(request.locateResource(parentURL)) - yield parent - parent = parent.getResult() + return response - d = waitForDeferred(super(CalDAVFile, self).http_DELETE(request)) - yield d - response = d.getResult() + d = super(CalDAVFile, self).http_DELETE(request) + d.addCallback(gotResponse) + return d - if response == responsecode.NO_CONTENT: - - if isPseudoCalendarCollectionResource(parent): - index = parent.index() - index.deleteResource(self.fp.basename()) - - elif DropBox.enabled and parent.isSpecialCollection(customxml.DropBox): - # We need to handle notificiations - notification = Notification(parentURL=parentURL) - d = waitForDeferred(notification.doNotification(request, parent)) - yield d - d.getResult() - - yield response - -http_DELETE = deferredGenerator(http_DELETE) + parentURL = parentForURL(request.uri) + d = request.locateResource(parentURL) + d.addCallback(gotParent) + return d Modified: CalendarServer/trunk/twistedcaldav/method/mkcol.py =================================================================== --- CalendarServer/trunk/twistedcaldav/method/mkcol.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/method/mkcol.py 2006-12-14 22:16:47 UTC (rev 827) @@ -22,49 +22,24 @@ __all__ = ["http_MKCOL"] -from twisted.internet.defer import deferredGenerator, waitForDeferred from twisted.web2 import responsecode -from twisted.web2.dav import davxml -from twisted.web2.dav.util import parentForURL -from twisted.web2.http import HTTPError, StatusResponse +from twisted.web2.http import StatusResponse -from twistedcaldav import customxml -from twistedcaldav.icaldav import ICalDAVResource +from twistedcaldav.resource import isPseudoCalendarCollectionResource def http_MKCOL(self, request): # - # Don't allow DAV collections in a calendar collection for now + # Don't allow DAV collections in a calendar collection # - def isNonCollectionParentResource(resource): - try: - resource = ICalDAVResource(resource) - except TypeError: - return False - else: - return resource.isPseudoCalendarCollection() or resource.isSpecialCollection(customxml.DropBox) + def gotParent(parent): + if parent is not None: + return StatusResponse( + responsecode.FORBIDDEN, + "Cannot create collection within calendar collection %s" % (parent,) + ) - parent = waitForDeferred(self._checkParents(request, isNonCollectionParentResource)) - yield parent - parent = parent.getResult() - if parent is not None: - raise HTTPError(StatusResponse( - responsecode.FORBIDDEN, - "Cannot create collection within special collection %s" % (parent,)) - ) + return super(CalDAVFile, self).http_MKCOL(request) - d = waitForDeferred(super(CalDAVFile, self).http_MKCOL(request)) - yield d - result = d.getResult() - - # Check for drop box creation and give it a special resource type - from twistedcaldav.dropbox import DropBox - if result == responsecode.CREATED and DropBox.enabled: - parent = waitForDeferred(request.locateResource(parentForURL(request.uri))) - yield parent - parent = parent.getResult() - if parent.isSpecialCollection(customxml.DropBoxHome): - self.writeDeadProperty(davxml.ResourceType.dropbox) - - yield result - -http_MKCOL = deferredGenerator(http_MKCOL) \ No newline at end of file + d = self._checkParents(request, isPseudoCalendarCollectionResource) + d.addCallback(gotParent) + return d Modified: CalendarServer/trunk/twistedcaldav/method/put.py =================================================================== --- CalendarServer/trunk/twistedcaldav/method/put.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/method/put.py 2006-12-14 22:16:47 UTC (rev 827) @@ -25,16 +25,12 @@ from twisted.internet.defer import deferredGenerator, waitForDeferred from twisted.python import log from twisted.web2 import responsecode -from twisted.web2.dav.element.base import twisted_dav_namespace from twisted.web2.dav.http import ErrorResponse from twisted.web2.dav.util import allDataFromStream, parentForURL from twisted.web2.http import HTTPError, StatusResponse -from twistedcaldav import customxml from twistedcaldav.caldavxml import caldav_namespace -from twistedcaldav.dropbox import DropBox from twistedcaldav.method.put_common import storeCalendarObjectResource -from twistedcaldav.notifications import Notification from twistedcaldav.resource import isPseudoCalendarCollectionResource def http_PUT(self, request): @@ -81,27 +77,6 @@ log.err("Error while handling (calendar) PUT: %s" % (e,)) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e))) - elif DropBox.enabled and parent.isSpecialCollection(customxml.DropBoxHome): - # Cannot create resources in a drop box home collection - raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (twisted_dav_namespace, "valid-drop-box"))) - - elif DropBox.enabled and parent.isSpecialCollection(customxml.DropBox): - # We need to handle notificiations - - # Do the normal http_PUT behavior - d = waitForDeferred(super(CalDAVFile, self).http_PUT(request)) - yield d - response = d.getResult() - - if response.code in (responsecode.OK, responsecode.CREATED, responsecode.NO_CONTENT): - notification = Notification(parentURL=parentURL) - d = waitForDeferred(notification.doNotification(request, parent)) - yield d - d.getResult() - - yield response - return - else: d = waitForDeferred(super(CalDAVFile, self).http_PUT(request)) yield d Deleted: CalendarServer/trunk/twistedcaldav/method/x_apple_subscribe.py =================================================================== --- CalendarServer/trunk/twistedcaldav/method/x_apple_subscribe.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/method/x_apple_subscribe.py 2006-12-14 22:16:47 UTC (rev 827) @@ -1,70 +0,0 @@ -## -# Copyright (c) 2005-2006 Apple Computer, 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. -# -# DRI: Cyrus Daboo, cdaboo@apple.com -## - -""" -CalDAV X_APPLE_SUBSCRIBE method. -""" - -__all__ = ["http_X_APPLE_SUBSCRIBE"] - -from twisted.internet.defer import deferredGenerator, waitForDeferred -from twisted.python import log -from twisted.web2 import responsecode -from twisted.web2.dav import davxml -from twisted.web2.dav.element.base import twisted_dav_namespace -from twisted.web2.dav.http import ErrorResponse -from twisted.web2.http import HTTPError, StatusResponse - -from twistedcaldav import customxml -from twistedcaldav.dropbox import DropBox - -def http_X_APPLE_SUBSCRIBE(self, request): - - # Only for drop box collections - if not DropBox.enabled or not self.isSpecialCollection(customxml.DropBox): - log.err("Cannot x-apple-subscribe to resource %s" % (request.uri,)) - raise HTTPError(StatusResponse( - responsecode.FORBIDDEN, - "Cannot x-apple-subscribe to resource %s" % (request.uri,)) - ) - - d = waitForDeferred(self.authorize(request, (davxml.Read(),))) - yield d - d.getResult() - authid = request.authnUser - - # Get current list of subscribed principals - principals = [] - if self.hasDeadProperty(customxml.Subscribed): - subs = self.readDeadProperty(customxml.Subscribed).children - principals.extend(subs) - - # Error if attempt to subscribe more than once - if authid in principals: - log.err("Cannot x_apple_subscribe to resource %s as principal %s is already subscribed" % (request.uri, repr(authid),)) - raise HTTPError(ErrorResponse( - responsecode.FORBIDDEN, - (twisted_dav_namespace, "principal-must-not-be-subscribed")) - ) - - principals.append(authid) - self.writeDeadProperty(customxml.Subscribed(*principals)) - - yield responsecode.OK - -http_X_APPLE_SUBSCRIBE = deferredGenerator(http_X_APPLE_SUBSCRIBE) Deleted: CalendarServer/trunk/twistedcaldav/method/x_apple_unsubscribe.py =================================================================== --- CalendarServer/trunk/twistedcaldav/method/x_apple_unsubscribe.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/method/x_apple_unsubscribe.py 2006-12-14 22:16:47 UTC (rev 827) @@ -1,71 +0,0 @@ -## -# Copyright (c) 2005-2006 Apple Computer, 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. -# -# DRI: Cyrus Daboo, cdaboo@apple.com -## - -""" -CalDAV X_APPLE_UNSUBSCRIBE method. -""" - -__all__ = ["http_X_APPLE_UNSUBSCRIBE"] - -from twisted.internet.defer import deferredGenerator, waitForDeferred -from twisted.python import log -from twisted.web2 import responsecode -from twisted.web2.dav.element.base import twisted_dav_namespace -from twisted.web2.dav.http import ErrorResponse -from twisted.web2.http import HTTPError, StatusResponse - -from twistedcaldav import customxml -from twistedcaldav.dropbox import DropBox - -def http_X_APPLE_UNSUBSCRIBE(self, request): - - # Only for drop box collections - if not DropBox.enabled or not self.isSpecialCollection(customxml.DropBox): - log.err("Cannot x_apple_unsubscribe to resource %s" % (request.uri,)) - raise HTTPError(StatusResponse( - responsecode.FORBIDDEN, - "Cannot x_apple_unsubscribe to resource %s" % (request.uri,)) - ) - - # We do not check any privileges. If a principal is subscribed we always allow them to - # unsubscribe provided they have at least authenticated. - d = waitForDeferred(self.authorize(request, ())) - yield d - d.getResult() - authid = request.authnUser - - # Get current list of subscribed principals - principals = [] - if self.hasDeadProperty(customxml.Subscribed): - subs = self.readDeadProperty(customxml.Subscribed).children - principals.extend(subs) - - # Error if attempt to subscribe more than once - if authid not in principals: - log.err("Cannot x_apple_unsubscribe from resource %s as principal %s is not currently subscribed" % (request.uri, repr(authid),)) - raise HTTPError(ErrorResponse( - responsecode.FORBIDDEN, - (twisted_dav_namespace, "principal-must-be-subscribed")) - ) - - principals.remove(authid) - self.writeDeadProperty(customxml.Subscribed(*principals)) - - yield responsecode.OK - -http_X_APPLE_UNSUBSCRIBE = deferredGenerator(http_X_APPLE_UNSUBSCRIBE) Modified: CalendarServer/trunk/twistedcaldav/notifications.py =================================================================== --- CalendarServer/trunk/twistedcaldav/notifications.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/notifications.py 2006-12-14 22:16:47 UTC (rev 827) @@ -23,7 +23,7 @@ from twisted.web2.dav import davxml from twistedcaldav import customxml -from twistedcaldav.customxml import apple_namespace +from twistedcaldav.customxml import calendarserver_namespace from twistedcaldav.extensions import DAVFile from twistedcaldav.extensions import DAVResource @@ -54,7 +54,7 @@ def doNotification(self, request, parent): """ - Put the supplied noitification into the notification collection of the specified principal. + Put the supplied notification into the notification collection of the specified principal. @param request: L{Request} for request in progress. @param parent: L{DAVResource} for parent of resource trigerring the notification. @@ -143,19 +143,29 @@ doNotification = deferredGenerator(doNotification) +class NotificationCollectionResource (DAVResource): + def resourceType(self): + return davxml.ResourceType( + davxml.ResourceType.collection, + davxml.ResourceType.notifications, + ) + + def notify(self): + # FIXME: Move doNotification() logic from above class to here + pass + class NotificationResource(DAVResource): """ Resource that gets stored in a notification collection and which contains the notification details in its content as well as via properties. """ - liveProperties = DAVResource.liveProperties + ( - (apple_namespace, "time-stamp" ), - (apple_namespace, "changed" ), + (calendarserver_namespace, "time-stamp"), + (calendarserver_namespace, "changed" ), ) -class NotificationFile(DAVResource, DAVFile): - +# FIXME: This needs to be in static.py, but it's referred to in doNotification() above, which is probably incorrect. +class NotificationFile(NotificationResource, DAVFile): def __init__(self, path): super(NotificationFile, self).__init__(path) @@ -163,7 +173,6 @@ """ Create the resource, fill out the body, and add properties. """ - # Create body XML elements = [] elements.append(customxml.TimeStamp.fromString(notification.timestamp)) Modified: CalendarServer/trunk/twistedcaldav/resource.py =================================================================== --- CalendarServer/trunk/twistedcaldav/resource.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/resource.py 2006-12-14 22:16:47 UTC (rev 827) @@ -54,9 +54,8 @@ from twistedcaldav.extensions import DAVResource from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource from twistedcaldav.caldavxml import caldav_namespace -from twistedcaldav.customxml import apple_namespace +from twistedcaldav.customxml import calendarserver_namespace from twistedcaldav.ical import Component as iComponent -from twistedcaldav.dropbox import DropBox if twistedcaldav.__version__: serverVersion = twisted.web2.server.VERSION + " TwistedCalDAV/" + twistedcaldav.__version__ @@ -449,17 +448,20 @@ """ # Do this only for regular calendar collections and Inbox/Outbox - if self.isPseudoCalendarCollection() or \ - DropBox.enabled and self.isSpecialCollection(customxml.DropBox): - # Add inheritable option to each ACE in the list + if self.isPseudoCalendarCollection(): + edited_aces = [] for ace in newaces: if TwistedACLInheritable() not in ace.children: children = list(ace.children) children.append(TwistedACLInheritable()) - ace.children = children + edited_aces.append(davxml.ACE(*children)) + else: + edited_aces.append(ace) + else: + edited_aces = newaces # Do inherited with possibly modified set of aces - super(CalDAVResource, self).writeNewACEs(newaces) + super(CalDAVResource, self).writeNewACEs(edited_aces) ## # Utilities @@ -535,6 +537,8 @@ (caldav_namespace, "calendar-user-address-set"), (caldav_namespace, "schedule-inbox-URL" ), (caldav_namespace, "schedule-outbox-URL" ), + (calendarserver_namespace, "dropbox-home-URL" ), + (calendarserver_namespace, "notifications-URL"), ) def readProperty(self, property, request): @@ -573,7 +577,7 @@ else: return caldavxml.ScheduleOutboxURL(davxml.HRef(url)) - elif namespace == apple_namespace: + elif namespace == calendarserver_namespace: if name == "dropbox-home-URL": url = self.dropboxURL() if url is None: @@ -659,19 +663,21 @@ """ @return: the drop box home collection URL for this principal. """ - # Use the first calendar home only - for home in self.calendarHomeURLs(): - return joinURL(home, DropBox.dropboxName) - return None + if self.hasDeadProperty((calendarserver_namespace, "dropbox-home-URL")): + inbox = self.readDeadProperty((caldav_namespace, "dropbox-home-URL")) + return str(inbox.children[0]) + else: + return None def notificationsURL(self): """ @return: the notifications collection URL for this principal. """ - # Use the first calendar home only - for home in self.calendarHomeURLs(): - return joinURL(home, DropBox.notificationName) - return None + if self.hasDeadProperty((calendarserver_namespace, "notifications-URL")): + inbox = self.readDeadProperty((caldav_namespace, "notifications-URL")) + return str(inbox.children[0]) + else: + return None ## # Utilities Modified: CalendarServer/trunk/twistedcaldav/schedule.py =================================================================== --- CalendarServer/trunk/twistedcaldav/schedule.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/schedule.py 2006-12-14 22:16:47 UTC (rev 827) @@ -79,7 +79,10 @@ Extends L{DAVResource} to provide CalDAV functionality. """ def resourceType(self): - return davxml.ResourceType(davxml.Collection(), caldavxml.ScheduleInbox()) + return davxml.ResourceType( + davxml.ResourceType.collection, + davxml.ResourceType.scheduleInbox, + ) def defaultAccessControlList(self): return davxml.ACL( @@ -99,7 +102,10 @@ Extends L{DAVResource} to provide CalDAV functionality. """ def resourceType(self): - return davxml.ResourceType(davxml.Collection(), caldavxml.ScheduleOutbox()) + return davxml.ResourceType( + davxml.ResourceType.collection, + davxml.ResourceType.scheduleOutbox, + ) @deferredGenerator def http_POST(self, request): Modified: CalendarServer/trunk/twistedcaldav/static.py =================================================================== --- CalendarServer/trunk/twistedcaldav/static.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/static.py 2006-12-14 22:16:47 UTC (rev 827) @@ -22,11 +22,13 @@ __all__ = [ "CalDAVFile", + "CalendarHomeProvisioningFile", "CalendarHomeFile", - "CalendarHomeProvisioningFile", - "CalendarPrincipalCollectionFile", "ScheduleInboxFile", "ScheduleOutboxFile", + "DropBoxHomeFile", + "DropBoxCollectionFile", + "DropBoxChildFile", ] import os @@ -46,14 +48,14 @@ from twistedcaldav import caldavxml from twistedcaldav import customxml +from twistedcaldav.config import config +from twistedcaldav.extensions import DAVFile from twistedcaldav.ical import Component as iComponent from twistedcaldav.ical import Property as iProperty -from twistedcaldav.icaldav import ICalDAVResource from twistedcaldav.index import Index, IndexSchedule, db_basename -from twistedcaldav.resource import CalDAVResource, isCalendarCollectionResource +from twistedcaldav.resource import CalDAVResource, isCalendarCollectionResource, isPseudoCalendarCollectionResource from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource -from twistedcaldav.extensions import DAVFile -from twistedcaldav.dropbox import DropBox +from twistedcaldav.dropbox import DropBoxHomeResource, DropBoxCollectionResource, DropBoxChildResource from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource from twistedcaldav.directory.calendar import DirectoryCalendarHomeTypeProvisioningResource from twistedcaldav.directory.calendar import DirectoryCalendarHomeResource @@ -101,15 +103,7 @@ return self.createCalendarCollection() - def isNonCalendarCollectionParentResource(resource): - try: - resource = ICalDAVResource(resource) - except TypeError: - return False - else: - return resource.isPseudoCalendarCollection() or resource.isSpecialCollection(customxml.DropBoxHome) - - parent = self._checkParents(request, isNonCalendarCollectionParentResource) + parent = self._checkParents(request, isPseudoCalendarCollectionResource) parent.addCallback(_defer) return parent @@ -430,19 +424,21 @@ if not provisionFile(self, self._parent): return succeed(None) - d = super(CalendarHomeFile, self).provision() + return super(CalendarHomeFile, self).provision() - # FIXME: This should provision itself also - # Provision a drop box - if self.record.recordType == "user": - DropBox.provision(self) + def provisionChild(self, name): + if config.DropBoxEnabled: + DropBoxHomeFileClass = DropBoxHomeFile + #NotificationsCollectionFileClass = NotificationsCollectionFile + else: + DropBoxHomeFileClass = None + #NotificationsCollectionFileClass = None - return d - - def provisionChild(self, name): cls = { - "inbox" : ScheduleInboxFile, - "outbox": ScheduleOutboxFile, + "inbox" : ScheduleInboxFile, + "outbox" : ScheduleOutboxFile, + "dropbox" : DropBoxHomeFileClass, + #"notifications": NotificationsCollectionFileClass, }.get(name, None) if cls is not None: @@ -526,6 +522,45 @@ def __repr__(self): return "<%s (calendar outbox collection): %s>" % (self.__class__.__name__, self.fp.path) +class DropBoxHomeFile (DropBoxHomeResource, CalDAVFile): + def __init__(self, path, parent): + DropBoxHomeResource.__init__(self) + CalDAVFile.__init__(self, path, principalCollections=parent.principalCollections()) + self._parent = parent + + def provision(self): + provisionFile(self, self._parent) + + def createSimilarFile(self, path): + if path == self.fp.path: + return self + else: + return DropBoxCollectionFile(path, self) + +class DropBoxCollectionFile (DropBoxCollectionResource, CalDAVFile): + def __init__(self, path, parent): + DropBoxCollectionResource.__init__(self) + CalDAVFile.__init__(self, path, principalCollections=parent.principalCollections()) + + def createSimilarFile(self, path): + if path == self.fp.path: + return self + else: + return DropBoxChildFile(path, self) + +class DropBoxChildFile (DropBoxChildResource, CalDAVFile): + def __init__(self, path, parent): + DropBoxChildResource.__init__(self) + CalDAVFile.__init__(self, path, principalCollections=parent.principalCollections()) + + assert self.fp.isfile() or not self.fp.exists + + def createSimilarFile(self, path): + if path == self.fp.path: + return self + else: + return responsecode.NOT_FOUND + ## # Utilities ## Modified: CalendarServer/trunk/twistedcaldav/tap.py =================================================================== --- CalendarServer/trunk/twistedcaldav/tap.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/tap.py 2006-12-14 22:16:47 UTC (rev 827) @@ -43,7 +43,6 @@ from twisted.web2.server import Site from twistedcaldav.config import config, parseConfig -from twistedcaldav.dropbox import DropBox from twistedcaldav.logging import RotatingFileAccessLoggingObserver from twistedcaldav.root import RootResource from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource @@ -86,16 +85,9 @@ def makeService(self, options): # - # Turn on drop box support before setting up the repository - # - DropBox.enable(config.DropBoxEnabled, config.NotificationsEnabled) - - # # Setup the Directory # - directoryClass = namedClass(config.DirectoryService['type']) - directory = directoryClass(**config.DirectoryService['params']) # Modified: CalendarServer/trunk/twistedcaldav/test/data/makelargecalendars.py =================================================================== --- CalendarServer/trunk/twistedcaldav/test/data/makelargecalendars.py 2006-12-14 22:04:25 UTC (rev 826) +++ CalendarServer/trunk/twistedcaldav/test/data/makelargecalendars.py 2006-12-14 22:16:47 UTC (rev 827) @@ -23,10 +23,17 @@ user_max = 20 calendars = ("calendar.10", "calendar.100", "calendar.1000",) -for calendar in calendars: - for ctr in xrange(1, user_max + 1): - path = "calendars/user/user%02d" % (ctr,) - if not os.path.exists("%s/%s/" % (path, calendar,)): - print "Expanding %s to %s" % (calendar, path,) - cmd = "cd %s; tar zxf ../../../%s.tgz" % (path, calendar,) +for ctr in xrange(1, user_max + 1): + path = "calendars/user/user%02d" % (ctr,) + + try: os.makedirs(path) + except OSError: pass + + try: os.makedirs(os.path.join(path, "calendar")) + except OSError: pass + + for calendar in calendars: + if not os.path.isdir(os.path.join(path, calendar)): + print "Expanding %s to %s" % (calendar, path) + cmd = "tar -C %r -zx -f %r" % (path, calendar + ".tgz") os.system(cmd)
participants (1)
-
source_changes@macosforge.org