[CalendarServer-changes] [11100] CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav /method

source_changes at macosforge.org source_changes at macosforge.org
Thu Apr 25 11:25:14 PDT 2013


Revision: 11100
          http://trac.calendarserver.org//changeset/11100
Author:   cdaboo at apple.com
Date:     2013-04-25 11:25:14 -0700 (Thu, 25 Apr 2013)
Log Message:
-----------
Checkpoint: get rid of redundant twistedcaldav.method modules.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/__init__.py

Removed Paths:
-------------
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete_common.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_addressbook_common.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_common.py

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/__init__.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/__init__.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -23,14 +23,11 @@
 
 __all__ = [
     "acl",
-    "copymove",
-    "delete",
     "get",
     "mkcalendar",
     "mkcol",
     "post",
     "propfind",
-    "put",
     "report",
     "report_freebusy",
     "report_calendar_multiget",

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,276 +0,0 @@
-##
-# Copyrightg (c) 2006-2012 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.
-##
-
-"""
-CalDAV COPY and MOVE methods.
-"""
-
-__all__ = ["http_COPY", "http_MOVE"]
-
-from urlparse import urlsplit
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.web2 import responsecode
-from twext.web2.filter.location import addLocation
-from txdav.xml import element as davxml
-from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.util import parentForURL
-from twext.web2.http import StatusResponse, HTTPError
-
-from twext.python.log import Logger
-
-from twistedcaldav.caldavxml import caldav_namespace
-from twistedcaldav.method.put_common import StoreCalendarObjectResource
-from twistedcaldav.method.copymove_contact import (
-    maybeCOPYContact, maybeMOVEContact, KEEP_GOING
-)
-
-from twistedcaldav.resource import isCalendarCollectionResource,\
-    isPseudoCalendarCollectionResource, CalDAVResource,\
-    isAddressBookCollectionResource
-
-log = Logger()
-
- at inlineCallbacks
-def http_COPY(self, request):
-    """
-    Special handling of COPY request if parents are calendar collections.
-    When copying we do not have to worry about the source resource as it
-    is not being changed in any way. We do need to do an index update for
-    the destination if its a calendar collection.
-    """
-
-    raise AssertionError("Never use this")
-
-    # Copy of calendar collections isn't allowed.
-    if isPseudoCalendarCollectionResource(self):
-        returnValue(responsecode.FORBIDDEN)
-
-    result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = (yield checkForCalendarAction(self, request))
-    if not result or not destinationcal:
-        # Check with CardDAV first (XXX might want to check EnableCardDAV switch?)
-        result = yield maybeCOPYContact(self, request)
-        if result is KEEP_GOING:
-            result = yield super(CalDAVResource, self).http_COPY(request)
-        returnValue(result)
-
-    #
-    # Check authentication and access controls
-    #
-    yield self.authorize(request, (davxml.Read(),), recurse=True)
-
-    if destination.exists():
-        yield destination.authorize(request, (davxml.WriteContent(), davxml.WriteProperties()), recurse=True)
-    else:
-        destparent = (yield request.locateResource(parentForURL(destination_uri)))
-        yield destparent.authorize(request, (davxml.Bind(),))
-
-    # Check for existing destination resource
-    overwrite = request.headers.getHeader("overwrite", True)
-    if destination.exists() and not overwrite:
-        log.err("Attempt to copy onto existing resource without overwrite flag enabled: %s"
-                % (destination,))
-        raise HTTPError(StatusResponse(
-            responsecode.PRECONDITION_FAILED,
-            "Destination %s already exists." % (destination_uri,))
-        )
-
-    # Checks for copying a calendar collection
-    if self.isCalendarCollection():
-        log.err("Attempt to copy a calendar collection into another calendar collection %s" % destination)
-        raise HTTPError(ErrorResponse(
-            responsecode.FORBIDDEN,
-            (caldav_namespace, "calendar-collection-location-ok"),
-            "Cannot copy calendar collection inside another calendar collection",
-        ))
-
-    # We also do not allow regular collections in calendar collections
-    if self.isCollection():
-        log.err("Attempt to copy a collection into a calendar collection")
-        raise HTTPError(StatusResponse(
-            responsecode.FORBIDDEN,
-            "Cannot create collection within special collection %s" % (destination,))
-        )
-
-    # May need to add a location header
-    addLocation(request, destination_uri)
-
-    storer = StoreCalendarObjectResource(
-        request = request,
-        source = self,
-        source_uri = request.uri,
-        sourceparent = sourceparent,
-        sourcecal = sourcecal,
-        destination = destination,
-        destination_uri = destination_uri,
-        destinationparent = destinationparent,
-        destinationcal = destinationcal,
-    )
-    result = (yield storer.run())
-    returnValue(result)
-
- at inlineCallbacks
-def http_MOVE(self, request):
-    """
-    Special handling of MOVE request if parent is a calendar collection.
-    When moving we may need to remove the index entry for the source resource
-    since its effectively being deleted. We do need to do an index update for
-    the destination if its a calendar collection
-    """
-    raise AssertionError("Never use this")
-
-    result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = (yield checkForCalendarAction(self, request))
-    if not result:
-        is_calendar_collection = isPseudoCalendarCollectionResource(self)
-        defaultCalendarType = (yield self.isDefaultCalendar(request)) if is_calendar_collection else None
-        is_addressbook_collection = isAddressBookCollectionResource(self)
-        defaultAddressBook = (yield self.isDefaultAddressBook(request)) if is_addressbook_collection else False
-
-        if not is_calendar_collection:
-            result = yield maybeMOVEContact(self, request)
-            if result is not KEEP_GOING:
-                returnValue(result)
-
-        # Do default WebDAV action
-        result = (yield super(CalDAVResource, self).http_MOVE(request))
-        
-        if result == responsecode.NO_CONTENT:
-            if is_calendar_collection:
-                # Do some clean up
-                yield self.movedCalendar(request, defaultCalendarType, destination, destination_uri)
-            elif is_addressbook_collection:
-                # Do some clean up
-                yield self.movedAddressBook(request, defaultAddressBook, destination, destination_uri)
-
-        returnValue(result)
-        
-    #
-    # Check authentication and access controls
-    #
-    parent = (yield request.locateResource(parentForURL(request.uri)))
-    yield parent.authorize(request, (davxml.Unbind(),))
-
-    if destination.exists():
-        yield destination.authorize(request, (davxml.Bind(), davxml.Unbind()), recurse=True)
-    else:
-        destparent = (yield request.locateResource(parentForURL(destination_uri)))
-        yield destparent.authorize(request, (davxml.Bind(),))
-
-    # Check for existing destination resource
-    overwrite = request.headers.getHeader("overwrite", True)
-    if destination.exists() and not overwrite:
-        log.err("Attempt to copy onto existing resource without overwrite flag enabled: %s"
-                % (destination,))
-        raise HTTPError(StatusResponse(
-            responsecode.PRECONDITION_FAILED,
-            "Destination %s already exists." % (destination_uri,)
-        ))
-
-    if destinationcal:
-        # Checks for copying a calendar collection
-        if self.isCalendarCollection():
-            log.err("Attempt to move a calendar collection into another calendar collection %s" % destination)
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                (caldav_namespace, "calendar-collection-location-ok"),
-                "Cannot move calendar collection inside another calendar collection",
-            ))
-    
-        # We also do not allow regular collections in calendar collections
-        if self.isCollection():
-            log.err("Attempt to move a collection into a calendar collection")
-            raise HTTPError(StatusResponse(
-                responsecode.FORBIDDEN,
-                "Cannot create collection within special collection %s" % (destination,)
-            ))
-
-    # May need to add a location header
-    addLocation(request, destination_uri)
-
-    storer = StoreCalendarObjectResource(
-        request = request,
-        source = self,
-        source_uri = request.uri,
-        sourceparent = sourceparent,
-        sourcecal = sourcecal,
-        deletesource = True,
-        destination = destination,
-        destination_uri = destination_uri,
-        destinationparent = destinationparent,
-        destinationcal = destinationcal,
-    )
-    result = (yield storer.run())
-    returnValue(result)
-
- at inlineCallbacks
-def checkForCalendarAction(self, request):
-    """
-    Check to see whether the source or destination of the copy/move
-    is a calendar collection, since we need to do special processing
-    if that is the case.
-    @return: tuple::
-        result:           True if special CalDAV processing required, False otherwise
-            NB If there is any type of error with the request, return False
-            and allow normal COPY/MOVE processing to return the error.
-        sourcecal:        True if source is in a calendar collection, False otherwise
-        sourceparent:     The parent resource for the source
-        destination_uri:  The URI of the destination resource
-        destination:      CalDAVResource of destination if special processing required,
-        None otherwise
-        destinationcal:   True if the destination is in a calendar collection,
-            False otherwise
-        destinationparent:The parent resource for the destination
-        
-    """
-    
-    result = False
-    sourcecal = False
-    destinationcal = False
-    
-    # Check the source path first
-    if not self.exists():
-        log.err("Resource not found: %s" % (self,))
-        raise HTTPError(StatusResponse(
-            responsecode.NOT_FOUND,
-            "Source resource %s not found." % (request.uri,)
-        ))
-
-    # Check for parent calendar collection
-    sourceparent = (yield request.locateResource(parentForURL(request.uri)))
-    if isCalendarCollectionResource(sourceparent):
-        result = True
-        sourcecal = True
-    
-    #
-    # Find the destination resource
-    #
-    destination_uri = request.headers.getHeader("destination")
-
-    if not destination_uri:
-        msg = "No destination header in %s request." % (request.method,)
-        log.err(msg)
-        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
-    
-    destination = (yield request.locateResource(destination_uri))
-
-    # Check for parent calendar collection
-    destination_uri = urlsplit(destination_uri)[2]
-    destinationparent = (yield request.locateResource(parentForURL(destination_uri)))
-    if isCalendarCollectionResource(destinationparent):
-        result = True
-        destinationcal = True
-
-    returnValue((result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent))

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,249 +0,0 @@
-##
-# Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-CalDAV COPY and MOVE methods.
-"""
-
-__all__ = ["maybeCOPYContact", "maybeMOVEContact"]
-
-from urlparse import urlsplit
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.web2 import responsecode
-from twext.web2.filter.location import addLocation
-from txdav.xml import element as davxml
-from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.util import parentForURL
-from twext.web2.http import StatusResponse, HTTPError
-
-from twistedcaldav.carddavxml import carddav_namespace
-from twistedcaldav.method.put_addressbook_common import StoreAddressObjectResource
-from twistedcaldav.resource import isAddressBookCollectionResource
-from twext.python.log import Logger
-
-log = Logger()
-
-KEEP_GOING = object()
-
- at inlineCallbacks
-def maybeCOPYContact(self, request):
-    """
-    Special handling of COPY request if parents are addressbook collections.
-    When copying we do not have to worry about the source resource as it
-    is not being changed in any way. We do need to do an index update for
-    the destination if its an addressbook collection.
-    """
-    raise AssertionError("Never use this")
-
-    # Copy of addressbook collections isn't allowed.
-    if isAddressBookCollectionResource(self):
-        returnValue(responsecode.FORBIDDEN)
-
-    result, sourceadbk, sourceparent, destination_uri, destination, destinationadbk, destinationparent = (yield checkForAddressBookAction(self, request))
-    if not result or not destinationadbk:
-        # Give up, do default action.
-        
-        returnValue(KEEP_GOING)
-
-    #
-    # Check authentication and access controls
-    #
-    yield self.authorize(request, (davxml.Read(),), recurse=True)
-
-    if destination.exists():
-        yield destination.authorize(request, (davxml.WriteContent(), davxml.WriteProperties()), recurse=True)
-    else:
-        destparent = (yield request.locateResource(parentForURL(destination_uri)))
-        yield destparent.authorize(request, (davxml.Bind(),))
-
-    # Check for existing destination resource
-    overwrite = request.headers.getHeader("overwrite", True)
-    if destination.exists() and not overwrite:
-        log.err("Attempt to copy onto existing resource without overwrite flag enabled: %s"
-                % (destination,))
-        raise HTTPError(StatusResponse(
-            responsecode.PRECONDITION_FAILED,
-            "Destination %s already exists." % (destination_uri,))
-        )
-
-    # Checks for copying an addressbook collection
-    if self.isAddressBookCollection():
-        log.err("Attempt to copy an addressbook collection into another addressbook collection %s" % destination)
-        raise HTTPError(ErrorResponse(
-            responsecode.FORBIDDEN,
-            (carddav_namespace, "addressbook-collection-location-ok"),
-            "Cannot copy address book collection inside another address book collection",
-        ))
-
-    # We also do not allow regular collections in addressbook collections
-    if self.isCollection():
-        log.err("Attempt to copy a collection into an addressbook collection")
-        raise HTTPError(StatusResponse(
-            responsecode.FORBIDDEN,
-            "Cannot create collection within special collection %s" % (destination,))
-        )
-
-    # May need to add a location header
-    addLocation(request, destination_uri)
-
-    storer = StoreAddressObjectResource(
-        request = request,
-        source = self,
-        source_uri = request.uri,
-        sourceparent = sourceparent,
-        sourceadbk = sourceadbk,
-        destination = destination,
-        destination_uri = destination_uri,
-        destinationparent = destinationparent,
-        destinationadbk = destinationadbk,
-    )
-    result = (yield storer.run())
-    returnValue(result)
-
- at inlineCallbacks
-def maybeMOVEContact(self, request):
-    """
-    Special handling of MOVE request if parent is an addressbook collection.
-    When moving we may need to remove the index entry for the source resource
-    since its effectively being deleted. We do need to do an index update for
-    the destination if its an addressbook collection
-    """
-    raise AssertionError("Never use this")
-
-    result, sourceadbk, sourceparent, destination_uri, destination, destinationadbk, destinationparent = (yield checkForAddressBookAction(self, request))
-    if not result or not destinationadbk:
-
-        # Do default WebDAV action
-        returnValue(KEEP_GOING)
-        
-    #
-    # Check authentication and access controls
-    #
-    parent = (yield request.locateResource(parentForURL(request.uri)))
-    yield parent.authorize(request, (davxml.Unbind(),))
-
-    if destination.exists():
-        yield destination.authorize(request, (davxml.Bind(), davxml.Unbind()), recurse=True)
-    else:
-        destparent = (yield request.locateResource(parentForURL(destination_uri)))
-        yield destparent.authorize(request, (davxml.Bind(),))
-
-    # Check for existing destination resource
-    overwrite = request.headers.getHeader("overwrite", True)
-    if destination.exists() and not overwrite:
-        log.err("Attempt to copy onto existing resource without overwrite flag enabled: %s"
-                % (destination,))
-        raise HTTPError(StatusResponse(
-            responsecode.PRECONDITION_FAILED,
-            "Destination %s already exists." % (destination_uri,)
-        ))
-
-    if destinationadbk:
-        # Checks for copying an addressbook collection
-        if self.isAddressBookCollection():
-            log.err("Attempt to move an addressbook collection into another addressbook collection %s" % destination)
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                (carddav_namespace, "addressbook-collection-location-ok"),
-                "Cannot move address book collection inside another address book collection",
-            ))
-    
-        # We also do not allow regular collections in addressbook collections
-        if self.isCollection():
-            log.err("Attempt to move a collection into an addressbook collection")
-            raise HTTPError(StatusResponse(
-                responsecode.FORBIDDEN,
-                "Cannot create collection within special collection %s" % (destination,)
-            ))
-
-    # May need to add a location header
-    addLocation(request, destination_uri)
-
-    storer = StoreAddressObjectResource(
-        request = request,
-        source = self,
-        source_uri = request.uri,
-        sourceparent = sourceparent,
-        sourceadbk = sourceadbk,
-        deletesource = True,
-        destination = destination,
-        destination_uri = destination_uri,
-        destinationparent = destinationparent,
-        destinationadbk = destinationadbk,
-    )
-    result = (yield storer.run())
-    returnValue(result)
-
- at inlineCallbacks
-def checkForAddressBookAction(self, request):
-    """
-    Check to see whether the source or destination of the copy/move
-    is an addressbook collection, since we need to do special processing
-    if that is the case.
-    @return: tuple::
-        result:           True if special CalDAV processing required, False otherwise
-            NB If there is any type of error with the request, return False
-            and allow normal COPY/MOVE processing to return the error.
-        sourceadbk:        True if source is in an addressbook collection, False otherwise
-        sourceparent:     The parent resource for the source
-        destination_uri:  The URI of the destination resource
-        destination:      CalDAVResource of destination if special processing required,
-        None otherwise
-        destinationadbk:   True if the destination is in an addressbook collection,
-            False otherwise
-        destinationparent:The parent resource for the destination
-        
-    """
-    
-    result = False
-    sourceadbk = False
-    destinationadbk = False
-    
-    # Check the source path first
-    if not self.exists():
-        log.err("Resource not found: %s" % (self,))
-        raise HTTPError(StatusResponse(
-            responsecode.NOT_FOUND,
-            "Source resource %s not found." % (request.uri,)
-        ))
-
-    # Check for parent addressbook collection
-    sourceparent = (yield request.locateResource(parentForURL(request.uri)))
-    if isAddressBookCollectionResource(sourceparent):
-        result = True
-        sourceadbk = True
-    
-    #
-    # Find the destination resource
-    #
-    destination_uri = request.headers.getHeader("destination")
-
-    if not destination_uri:
-        msg = "No destination header in %s request." % (request.method,)
-        log.err(msg)
-        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
-    
-    destination = (yield request.locateResource(destination_uri))
-
-    # Check for parent addressbook collection
-    destination_uri = urlsplit(destination_uri)[2]
-    destinationparent = (yield request.locateResource(parentForURL(destination_uri)))
-    if isAddressBookCollectionResource(destinationparent):
-        result = True
-        destinationadbk = True
-
-    returnValue((result, sourceadbk, sourceparent, destination_uri, destination, destinationadbk, destinationparent))

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,62 +0,0 @@
-##
-# Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-CalDAV DELETE method.
-"""
-
-__all__ = ["http_DELETE"]
-
-from twext.python.log import Logger
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.web2 import responsecode
-from txdav.xml import element as davxml
-from twext.web2.dav.util import parentForURL
-from twext.web2.http import HTTPError
-
-from twistedcaldav.method.delete_common import DeleteResource
-
-log = Logger()
-
- at inlineCallbacks
-def http_DELETE(self, request):
-    #
-    # Override base DELETE request handling to ensure that the calendar
-    # index file has the entry for the deleted calendar component removed.
-    #
-
-    raise AssertionError("Never use this")
-
-    if not self.exists():
-        log.err("Resource not found: %s" % (self,))
-        raise HTTPError(responsecode.NOT_FOUND)
-
-    depth = request.headers.getHeader("depth", "infinity")
-
-    #
-    # Check authentication and access controls
-    #
-    parentURL = parentForURL(request.uri)
-    parent = (yield request.locateResource(parentURL))
-
-    yield parent.authorize(request, (davxml.Unbind(),))
-
-    # Do smart delete taking into account the need to do implicit CANCELs etc
-    deleter = DeleteResource(request, self, request.uri, parent, depth)
-    response = (yield deleter.run())
-
-    returnValue(response)

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete_common.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/delete_common.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,58 +0,0 @@
-##
-# Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-
-"""
-CalDAV DELETE behaviors.
-"""
-
-__all__ = ["DeleteResource"]
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-from twext.python.log import Logger
-
-log = Logger()
-
-class DeleteResource(object):
-
-    def __init__(self, request, resource, resource_uri, parent, depth,
-        internal_request=False, allowImplicitSchedule=True):
-
-        raise AssertionError("Never use this")
-
-        self.request = request
-        self.resource = resource
-        self.resource_uri = resource_uri
-        self.parent = parent
-        self.depth = depth
-        self.internal_request = internal_request
-        self.allowImplicitSchedule = allowImplicitSchedule
-
-
-    @inlineCallbacks
-    def run(self):
-        # FIXME: this code-path shouldn't actually be used, as the things
-        # with storeRemove on them also have their own http_DELETEs.
-        response = (
-            yield self.resource.storeRemove(
-                self.request,
-                not self.internal_request and self.allowImplicitSchedule,
-                self.resource_uri
-            )
-        )
-
-        returnValue(response)

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,164 +0,0 @@
-##
-# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-CalDAV PUT method.
-"""
-
-__all__ = ["http_PUT"]
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.web2 import responsecode
-from twext.web2.dav.util import allDataFromStream, parentForURL
-from twext.web2.http import HTTPError, StatusResponse
-
-from twext.python.log import Logger
-from twext.web2.dav.http import ErrorResponse
-
-from twistedcaldav.caldavxml import caldav_namespace
-
-from twistedcaldav.method.put_common import StoreCalendarObjectResource
-from twistedcaldav.resource import isPseudoCalendarCollectionResource
-
-log = Logger()
-
-from twistedcaldav.carddavxml import carddav_namespace
-from twistedcaldav.method.put_addressbook_common import StoreAddressObjectResource
-from twistedcaldav.resource import isAddressBookCollectionResource
-
- at inlineCallbacks
-def http_PUT(self, request):
-
-    raise AssertionError("Never use this")
-
-    parentURL = parentForURL(request.uri)
-    parent = (yield request.locateResource(parentURL))
-
-    if isPseudoCalendarCollectionResource(parent):
-
-        # Content-type check
-        content_type = request.headers.getHeader("content-type")
-        if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "calendar"):
-            log.err("MIME type %s not allowed in calendar collection" % (content_type,))
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                (caldav_namespace, "supported-calendar-data"),
-                "Invalid MIME type for calendar collection",
-            ))
-
-        # Read the calendar component from the stream
-        try:
-            calendardata = (yield allDataFromStream(request.stream))
-            if not hasattr(request, "extendedLogItems"):
-                request.extendedLogItems = {}
-            request.extendedLogItems["cl"] = str(len(calendardata)) if calendardata else "0"
-
-            # We must have some data at this point
-            if calendardata is None:
-                # Use correct DAV:error response
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-calendar-data"),
-                    description="No calendar data"
-                ))
-
-            storer = StoreCalendarObjectResource(
-                request=request,
-                destination=self,
-                destination_uri=request.uri,
-                destinationcal=True,
-                destinationparent=parent,
-                calendar=calendardata,
-            )
-            result = (yield storer.run())
-
-            # Look for Prefer header
-            prefer = request.headers.getHeader("prefer", {})
-            returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
-
-            if returnRepresentation and result.code / 100 == 2:
-                oldcode = result.code
-                result = (yield self.http_GET(request))
-                if oldcode == responsecode.CREATED:
-                    result.code = responsecode.CREATED
-                result.headers.setHeader("content-location", request.path)
-
-            returnValue(result)
-
-        except ValueError, e:
-            log.err("Error while handling (calendar) PUT: %s" % (e,))
-            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
-
-    elif isAddressBookCollectionResource(parent):
-
-        # Content-type check
-        content_type = request.headers.getHeader("content-type")
-        if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "vcard"):
-            log.err("MIME type %s not allowed in address book collection" % (content_type,))
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                (carddav_namespace, "supported-address-data"),
-                "Invalid MIME type for address book collection",
-            ))
-
-        # Read the vcard component from the stream
-        try:
-            vcarddata = (yield allDataFromStream(request.stream))
-            if not hasattr(request, "extendedLogItems"):
-                request.extendedLogItems = {}
-            request.extendedLogItems["cl"] = str(len(vcarddata)) if vcarddata else "0"
-
-            # We must have some data at this point
-            if vcarddata is None:
-                # Use correct DAV:error response
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (carddav_namespace, "valid-address-data"),
-                    description="No vcard data"
-                ))
-
-            storer = StoreAddressObjectResource(
-                request=request,
-                sourceadbk=False,
-                vcard=vcarddata,
-                destination=self,
-                destination_uri=request.uri,
-                destinationadbk=True,
-                destinationparent=parent,
-            )
-            result = (yield storer.run())
-
-            # Look for Prefer header
-            prefer = request.headers.getHeader("prefer", {})
-            returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
-
-            if returnRepresentation and result.code / 100 == 2:
-                oldcode = result.code
-                result = (yield self.http_GET(request))
-                if oldcode == responsecode.CREATED:
-                    result.code = responsecode.CREATED
-                result.headers.setHeader("content-location", request.path)
-
-            returnValue(result)
-
-        except ValueError, e:
-            log.err("Error while handling (address book) PUT: %s" % (e,))
-            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
-
-    else:
-        # No longer support arbitrary PUTs to resource. Instead we are going to require all the
-        # resource classes we care about to explicitly define their own http_PUT.
-        raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "PUTs not allowed here"))

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_addressbook_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_addressbook_common.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_addressbook_common.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,599 +0,0 @@
-##
-# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-PUT/COPY/MOVE common behavior.
-"""
-
-__all__ = ["StoreAddressObjectResource"]
-
-from twext.python.log import Logger
-from twext.web2 import responsecode
-from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.util import joinURL, parentForURL
-from twext.web2.http import HTTPError
-from twext.web2.http import StatusResponse
-from twext.web2.stream import MemoryStream
-
-from twisted.internet import reactor
-from twisted.internet.defer import Deferred, inlineCallbacks
-from twisted.internet.defer import returnValue
-from twisted.python.failure import Failure
-
-from twistedcaldav import customxml
-from twistedcaldav.carddavxml import NoUIDConflict, carddav_namespace
-from twistedcaldav.config import config
-from twistedcaldav.vcard import Component
-
-from txdav.common.icommondatastore import ReservationError
-from txdav.xml import element as davxml
-
-import types
-
-log = Logger()
-
-class StoreAddressObjectResource(object):
-
-    class UIDReservation(object):
-
-        def __init__(self, index, uid, uri):
-            self.reserved = False
-            self.index = index
-            self.uid = uid
-            self.uri = uri
-
-        @inlineCallbacks
-        def reserve(self):
-
-            # Lets use a deferred for this and loop a few times if we cannot reserve so that we give
-            # time to whoever has the reservation to finish and release it.
-            failure_count = 0
-            while(failure_count < 10):
-                try:
-                    yield self.index.reserveUID(self.uid)
-                    self.reserved = True
-                    break
-                except ReservationError:
-                    self.reserved = False
-                failure_count += 1
-
-                pause = Deferred()
-                def _timedDeferred():
-                    pause.callback(True)
-                reactor.callLater(0.5, _timedDeferred) #@UndefinedVariable
-                yield pause
-
-            if self.uri and not self.reserved:
-                raise HTTPError(StatusResponse(responsecode.CONFLICT, "Resource: %s currently in use." % (self.uri,)))
-
-        @inlineCallbacks
-        def unreserve(self):
-            if self.reserved:
-                yield self.index.unreserveUID(self.uid)
-                self.reserved = False
-
-
-    def __init__(
-        self,
-        request,
-        source=None, source_uri=None, sourceparent=None, sourceadbk=False, deletesource=False,
-        destination=None, destination_uri=None, destinationparent=None, destinationadbk=True,
-        vcard=None,
-        indexdestination=True,
-        returnData=False,
-   ):
-        """
-        Function that does common PUT/COPY/MOVE behavior.
-
-        @param request:           the L{twext.web2.server.Request} for the current HTTP request.
-        @param source:            the L{CalDAVResource} for the source resource to copy from, or None if source data
-            is to be read from the request.
-        @param source_uri:        the URI for the source resource.
-        @param destination:       the L{CalDAVResource} for the destination resource to copy into.
-        @param destination_uri:   the URI for the destination resource.
-        @param vcard:             the C{str} or L{Component} vcard data if there is no source, None otherwise.
-        @param sourceadbk:        True if the source resource is in a vcard collection, False otherwise.
-        @param destinationadbk:   True if the destination resource is in a vcard collection, False otherwise
-        @param sourceparent:      the L{CalDAVResource} for the source resource's parent collection, or None if source is None.
-        @param destinationparent: the L{CalDAVResource} for the destination resource's parent collection.
-        @param deletesource:      True if the source resource is to be deleted on successful completion, False otherwise.
-        @param returnData:         True if the caller wants the actual data written to the store returned
-        """
-
-        raise AssertionError("Never use this")
-
-        # Check that all arguments are valid
-        try:
-            assert destination is not None and destinationparent is not None and destination_uri is not None
-            assert (source is None and sourceparent is None) or (source is not None and sourceparent is not None)
-            assert (vcard is None and source is not None) or (vcard is not None and source is None)
-            assert not deletesource or (deletesource and source is not None)
-        except AssertionError:
-            log.err("Invalid arguments to StoreAddressObjectResource.__init__():")
-            log.err("request=%s\n" % (request,))
-            log.err("sourceadbk=%s\n" % (sourceadbk,))
-            log.err("destinationadbk=%s\n" % (destinationadbk,))
-            log.err("source=%s\n" % (source,))
-            log.err("source_uri=%s\n" % (source_uri,))
-            log.err("sourceparent=%s\n" % (sourceparent,))
-            log.err("destination=%s\n" % (destination,))
-            log.err("destination_uri=%s\n" % (destination_uri,))
-            log.err("destinationparent=%s\n" % (destinationparent,))
-            log.err("vcard=%s\n" % (vcard,))
-            log.err("deletesource=%s\n" % (deletesource,))
-            raise
-
-        self.request = request
-        self.sourceadbk = sourceadbk
-        self.destinationadbk = destinationadbk
-        self.source = source
-        self.source_uri = source_uri
-        self.sourceparent = sourceparent
-        self.destination = destination
-        self.destination_uri = destination_uri
-        self.destinationparent = destinationparent
-        self.vcard = vcard
-        self.vcarddata = None
-        self.deletesource = deletesource
-        self.indexdestination = indexdestination
-        self.returnData = returnData
-
-        self.access = None
-
-
-    @inlineCallbacks
-    def fullValidation(self):
-        """
-        Do full validation of source and destination vcard data.
-        """
-
-        if self.destinationadbk:
-            # Valid resource name check
-            result, message = self.validResourceName()
-            if not result:
-                log.err(message)
-                raise HTTPError(StatusResponse(responsecode.FORBIDDEN, message))
-
-            # Valid collection size check on the destination parent resource
-            result, message = (yield self.validCollectionSize())
-            if not result:
-                log.err(message)
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    customxml.MaxResources(),
-                    message,
-                ))
-
-            if not self.sourceadbk:
-                # Valid content type check on the source resource if its not in a vcard collection
-                if self.source is not None:
-                    result, message = self.validContentType()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (carddav_namespace, "supported-address-data"),
-                            message,
-                        ))
-
-                    # At this point we need the calendar data to do more tests
-                    self.vcard = (yield self.source.vCard())
-                else:
-                    try:
-                        if type(self.vcard) in (types.StringType, types.UnicodeType,):
-                            self.vcard = Component.fromString(self.vcard)
-                    except ValueError, e:
-                        log.err(str(e))
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (carddav_namespace, "valid-address-data"),
-                            "Could not parse vCard",
-                        ))
-
-                # Valid vcard data check
-                result, message = self.validAddressDataCheck()
-                if not result:
-                    log.err(message)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        (carddav_namespace, "valid-address-data"),
-                        message
-                    ))
-
-                # Valid vcard data for CalDAV check
-                result, message = self.validCardDAVDataCheck()
-                if not result:
-                    log.err(message)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        (carddav_namespace, "valid-addressbook-object-resource"),
-                        message,
-                    ))
-
-                # Must have a valid UID at this point
-                self.uid = self.vcard.resourceUID()
-            else:
-                # Get UID from original resource
-                self.source_index = self.sourceparent.index()
-                self.uid = yield self.source_index.resourceUIDForName(self.source.name())
-                if self.uid is None:
-                    log.err("Source vcard does not have a UID: %s" % self.source.name())
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        (carddav_namespace, "valid-addressbook-object-resource"),
-                        "Missing UID in vCard",
-                    ))
-
-                # FIXME: We need this here because we have to re-index the destination. Ideally it
-                # would be better to copy the index entries from the source and add to the destination.
-                self.vcard = (yield self.source.vCard())
-
-            # Valid vcard data size check
-            result, message = self.validSizeCheck()
-            if not result:
-                log.err(message)
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (carddav_namespace, "max-resource-size"),
-                    message,
-                ))
-
-            # Check access
-            returnValue(None)
-
-
-    def validResourceName(self):
-        """
-        Make sure that the resource name for the new resource is valid.
-        """
-        result = True
-        message = ""
-        filename = self.destination.name()
-        if filename.startswith("."):
-            result = False
-            message = "Resource name %s not allowed in vcard collection" % (filename,)
-
-        return result, message
-
-
-    def validContentType(self):
-        """
-        Make sure that the content-type of the source resource is text/vcard.
-        This test is only needed when the source is not in a vcard collection.
-        """
-        result = True
-        message = ""
-        content_type = self.source.contentType()
-        if not ((content_type.mediaType == "text") and (content_type.mediaSubtype == "vcard")):
-            result = False
-            message = "MIME type %s not allowed in vcard collection" % (content_type,)
-
-        return result, message
-
-
-    @inlineCallbacks
-    def validCollectionSize(self):
-        """
-        Make sure that any limits on the number of resources in a collection are enforced.
-        """
-        result = True
-        message = ""
-        if not self.destination.exists() and \
-            config.MaxResourcesPerCollection and \
-            (yield self.destinationparent.countChildren()) >= config.MaxResourcesPerCollection:
-                result = False
-                message = "Too many resources in collection %s" % (self.destinationparent,)
-
-        returnValue((result, message,))
-
-
-    def validAddressDataCheck(self):
-        """
-        Check that the calendar data is valid iCalendar.
-        @return:         tuple: (True/False if the calendar data is valid,
-                                 log message string).
-        """
-        result = True
-        message = ""
-        if self.vcard is None:
-            result = False
-            message = "Empty resource not allowed in vcard collection"
-        else:
-            try:
-                self.vcard.validVCardData()
-            except ValueError, e:
-                result = False
-                message = "Invalid vcard data: %s" % (e,)
-
-        return result, message
-
-
-    def validCardDAVDataCheck(self):
-        """
-        Check that the vcard data is valid vCard.
-        @return:         tuple: (True/False if the vcard data is valid,
-                                 log message string).
-        """
-        result = True
-        message = ""
-        try:
-            self.vcard.validForCardDAV()
-        except ValueError, e:
-            result = False
-            message = "vCard data does not conform to CardDAV requirements: %s" % (e,)
-
-        return result, message
-
-
-    def validSizeCheck(self):
-        """
-        Make sure that the content-type of the source resource is text/vcard.
-        This test is only needed when the source is not in a vcard collection.
-        """
-        result = True
-        message = ""
-        if config.MaxResourceSize:
-            vcardsize = len(str(self.vcard))
-            if vcardsize > config.MaxResourceSize:
-                result = False
-                message = "Data size %d bytes is larger than allowed limit %d bytes" % (vcardsize, config.MaxResourceSize)
-
-        return result, message
-
-
-    @inlineCallbacks
-    def noUIDConflict(self, uid):
-        """
-        Check that the UID of the new vcard object conforms to the requirements of
-        CardDAV, i.e. it must be unique in the collection and we must not overwrite a
-        different UID.
-        @param uid: the UID for the resource being stored.
-        @return: tuple: (True/False if the UID is valid, log message string,
-            name of conflicted resource).
-        """
-
-        result = True
-        message = ""
-        rname = ""
-
-        # Adjust for a move into same vcard collection
-        oldname = None
-        if self.sourceparent and (self.sourceparent == self.destinationparent) and self.deletesource:
-            oldname = self.source.name()
-
-        # UID must be unique
-        index = self.destinationparent.index()
-        if not (yield index.isAllowedUID(uid, oldname, self.destination.name())):
-            rname = yield index.resourceNameForUID(uid)
-            # This can happen if two simultaneous PUTs occur with the same UID.
-            # i.e. one PUT has reserved the UID but has not yet written the resource,
-            # the other PUT tries to reserve and fails but no index entry exists yet.
-            if rname is None:
-                rname = "<<Unknown Resource>>"
-
-            result = False
-            message = "Address book resource %s already exists with same UID %s" % (rname, uid)
-        else:
-            # Cannot overwrite a resource with different UID
-            if self.destination.exists():
-                olduid = yield index.resourceUIDForName(self.destination.name())
-                if olduid != uid:
-                    rname = self.destination.name()
-                    result = False
-                    message = "Cannot overwrite vcard resource %s with different UID %s" % (rname, olduid)
-
-        returnValue((result, message, rname))
-
-
-    @inlineCallbacks
-    def doStore(self):
-        # Do put or copy based on whether source exists
-        source = self.source
-        if source is not None:
-            # Retrieve information from the source, in case we have to delete
-            # it.
-            sourceProperties = dict(source.newStoreProperties().iteritems())
-            sourceText = yield source.vCardText()
-
-            # Delete the original source if needed (for example, if this is a
-            # same-calendar MOVE of a calendar object, implemented as an
-            # effective DELETE-then-PUT).
-            if self.deletesource:
-                yield self.doSourceDelete()
-
-            response = (yield self.destination.storeStream(MemoryStream(sourceText)))
-            self.destination.newStoreProperties().update(sourceProperties)
-        else:
-            response = (yield self.doStorePut())
-
-        returnValue(response)
-
-
-    @inlineCallbacks
-    def doStorePut(self):
-
-        stream = MemoryStream(str(self.vcard))
-        response = (yield self.destination.storeStream(stream))
-        returnValue(response)
-
-
-    @inlineCallbacks
-    def doSourceDelete(self):
-        # Delete the source resource
-        yield self.source.storeRemove(self.request, False, self.source_uri)
-        log.debug("Source removed %s" % (self.source,))
-        returnValue(None)
-
-
-    @inlineCallbacks
-    def run(self):
-        """
-        Function that does common PUT/COPY/MOVE behavior.
-
-        @return: a Deferred with a status response result.
-        """
-
-        try:
-            reservation = None
-
-            # Handle all validation operations here.
-            yield self.fullValidation()
-
-            # Reservation and UID conflict checking is next.
-            if self.destinationadbk:
-                # Reserve UID
-                self.destination_index = self.destinationparent.index()
-                reservation = StoreAddressObjectResource.UIDReservation(
-                    self.destination_index, self.uid, self.destination_uri
-                )
-                if self.indexdestination:
-                    yield reservation.reserve()
-
-                # UID conflict check - note we do this after reserving the UID to avoid a race condition where two requests
-                # try to write the same vcard data to two different resource URIs.
-                result, message, rname = yield self.noUIDConflict(self.uid)
-                if not result:
-                    log.err(message)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        NoUIDConflict(
-                            davxml.HRef.fromString(
-                                joinURL(
-                                    parentForURL(self.destination_uri),
-                                    rname.encode("utf-8")
-                                )
-                            )
-                        ),
-                        "UID already used in another resource",
-                    ))
-
-            # Do the actual put or copy
-            response = (yield self.doStore())
-
-            if reservation:
-                yield reservation.unreserve()
-
-            returnValue(response)
-
-        except Exception:
-
-            # Grab the current exception state here so we can use it in a re-raise - we need this because
-            # an inlineCallback might be called and that raises an exception when it returns, wiping out the
-            # original exception "context".
-            ex = Failure()
-
-            if reservation:
-                yield reservation.unreserve()
-
-            # Return the original failure (exception) state
-            ex.raiseException()
-
-
-    @inlineCallbacks
-    def moveValidation(self):
-        """
-        Do full validation of source and destination calendar data.
-        """
-
-        # Valid resource name check
-        result, message = self.validResourceName()
-        if not result:
-            log.err(message)
-            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, message))
-
-        # Valid collection size check on the destination parent resource
-        result, message = (yield self.validCollectionSize())
-        if not result:
-            log.err(message)
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                customxml.MaxResources(),
-                message,
-            ))
-
-        returnValue(None)
-
-
-    @inlineCallbacks
-    def doStoreMove(self):
-
-        # Do move
-        response = (yield self.source.storeMove(self.request, self.destinationparent, self.destination._name))
-        returnValue(response)
-
-
-    @inlineCallbacks
-    def move(self):
-        """
-        Function that does common MOVE behavior.
-
-        @return: a Deferred with a status response result.
-        """
-
-        try:
-            reservation = None
-
-            # Handle all validation operations here.
-            yield self.moveValidation()
-
-            # Reservation and UID conflict checking is next.
-
-            # Reserve UID
-            self.destination_index = self.destinationparent.index()
-            reservation = StoreAddressObjectResource.UIDReservation(
-                self.destination_index, self.source.uid(), self.destination_uri
-            )
-            if self.indexdestination:
-                yield reservation.reserve()
-
-            # UID conflict check - note we do this after reserving the UID to avoid a race condition where two requests
-            # try to write the same vcard data to two different resource URIs.
-            result, message, rname = yield self.noUIDConflict(self.source.uid())
-            if not result:
-                log.err(message)
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    NoUIDConflict(
-                        davxml.HRef.fromString(
-                            joinURL(
-                                parentForURL(self.destination_uri),
-                                rname.encode("utf-8")
-                            )
-                        )
-                    ),
-                    "UID already used in another resource",
-                ))
-
-            # Do the actual put or copy
-            response = (yield self.doStoreMove())
-
-            if reservation:
-                yield reservation.unreserve()
-
-            returnValue(response)
-
-        except Exception:
-
-            # Grab the current exception state here so we can use it in a re-raise - we need this because
-            # an inlineCallback might be called and that raises an exception when it returns, wiping out the
-            # original exception "context".
-            ex = Failure()
-
-            if reservation:
-                yield reservation.unreserve()
-
-            # Return the original failure (exception) state
-            ex.raiseException()

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_common.py	2013-04-25 17:56:21 UTC (rev 11099)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/put_common.py	2013-04-25 18:25:14 UTC (rev 11100)
@@ -1,1493 +0,0 @@
-# -*- test-case-name: twistedcaldav.test.test_validation -*-
-##
-# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-PUT/COPY/MOVE common behavior.
-"""
-
-__all__ = ["StoreCalendarObjectResource"]
-
-from twext.python.log import Logger
-from twext.web2 import responsecode
-from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.util import joinURL, parentForURL
-from twext.web2.http import HTTPError
-from twext.web2.http import StatusResponse
-from twext.web2.iweb import IResponse
-from twext.web2.stream import MemoryStream
-
-from twisted.internet import reactor
-from twisted.internet.defer import Deferred, inlineCallbacks, succeed
-from twisted.internet.defer import returnValue
-from twisted.python import hashlib
-from twisted.python.failure import Failure
-
-from twistedcaldav import customxml
-from twistedcaldav.caldavxml import caldav_namespace, NoUIDConflict, MaxInstances, MaxAttendeesPerInstance
-from twistedcaldav.config import config
-from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
-from twistedcaldav.ical import Component, Property
-from twistedcaldav.instance import TooManyInstancesError, \
-    InvalidOverriddenInstanceError
-from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
-from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
-
-from txdav.caldav.icalendarstore import AttachmentStoreValidManagedID
-from txdav.common.icommondatastore import ReservationError
-from txdav.xml import element as davxml
-
-from urlparse import urlparse, urlunparse
-
-import types
-import uuid
-
-log = Logger()
-
-class StoreCalendarObjectResource(object):
-
-    class UIDReservation(object):
-
-        def __init__(self, index, uid, uri, internal_request, transaction):
-            if internal_request:
-                self.lock = None
-            else:
-                self.lock = MemcacheLock(
-                    "ImplicitUIDLock",
-                    uid,
-                    timeout=config.Scheduling.Options.UIDLockTimeoutSeconds,
-                    expire_time=config.Scheduling.Options.UIDLockExpirySeconds
-                )
-            self.reserved = False
-            self.index = index
-            self.uid = uid
-            self.uri = uri
-            self.transaction = transaction
-
-        @inlineCallbacks
-        def reserve(self):
-
-            # Implicit lock
-            if self.lock:
-                try:
-                    yield self.lock.acquire()
-                except MemcacheLockTimeoutError:
-                    raise HTTPError(StatusResponse(responsecode.CONFLICT, "Resource: %s currently in use on the server." % (self.uri,)))
-
-            # Lets use a deferred for this and loop a few times if we cannot reserve so that we give
-            # time to whoever has the reservation to finish and release it.
-            failure_count = 0
-            while(failure_count < 10):
-                try:
-                    yield self.index.reserveUID(self.uid)
-                    self.reserved = True
-                    break
-                except ReservationError:
-                    self.reserved = False
-                failure_count += 1
-
-                pause = Deferred()
-                def _timedDeferred():
-                    pause.callback(True)
-                reactor.callLater(0.5, _timedDeferred)
-                yield pause
-
-            if self.uri and not self.reserved:
-                if self.lock:
-                    # Can release immediately as nothing happened
-                    yield self.lock.release()
-                raise HTTPError(StatusResponse(responsecode.CONFLICT, "Resource: %s currently in use in calendar." % (self.uri,)))
-
-        @inlineCallbacks
-        def unreserve(self):
-            if self.reserved:
-                yield self.index.unreserveUID(self.uid)
-                self.reserved = False
-            if self.lock:
-                # Release lock after commit or abort
-                self.transaction.postCommit(self.lock.clean)
-                self.transaction.postAbort(self.lock.clean)
-
-
-    def __init__(
-        self,
-        request,
-        source=None, source_uri=None, sourceparent=None, sourcecal=False, deletesource=False,
-        destination=None, destination_uri=None, destinationparent=None, destinationcal=True,
-        calendar=None,
-        isiTIP=False,
-        allowImplicitSchedule=True,
-        internal_request=False,
-        processing_organizer=None,
-        returnData=False,
-        attachmentProcessingDone=False,
-    ):
-        """
-        Function that does common PUT/COPY/MOVE behavior.
-
-        @param request:           the L{twext.web2.server.Request} for the current HTTP request.
-        @param source:            the L{CalDAVResource} for the source resource to copy from, or None if source data
-            is to be read from the request.
-        @param source_uri:        the URI for the source resource.
-        @param destination:       the L{CalDAVResource} for the destination resource to copy into.
-        @param destination_uri:   the URI for the destination resource.
-        @param calendar:          the C{str} or L{Component} calendar data if there is no source, None otherwise.
-        @param sourcecal:         True if the source resource is in a calendar collection, False otherwise.
-        @param destinationcal:    True if the destination resource is in a calendar collection, False otherwise
-        @param sourceparent:      the L{CalDAVResource} for the source resource's parent collection, or None if source is None.
-        @param destinationparent: the L{CalDAVResource} for the destination resource's parent collection.
-        @param deletesource:      True if the source resource is to be deleted on successful completion, False otherwise.
-        @param isiTIP:                True if relaxed calendar data validation is to be done, False otherwise.
-        @param allowImplicitSchedule: True if implicit scheduling should be attempted, False otherwise.
-        @param internal_request:   True if this request originates internally and needs to bypass scheduling authorization checks.
-        @param processing_organizer: True if implicit processing for an organizer, False if for an attendee, None if not implicit processing.
-        @param returnData:         True if the caller wants the actual data written to the store returned
-        @param attachmentProcessingDone    True if the caller has already processed managed attachment changes
-        """
-
-        raise AssertionError("Never use this")
-
-        # Check that all arguments are valid
-        try:
-            assert destination is not None and destinationparent is not None and destination_uri is not None
-            assert (source is None and sourceparent is None) or (source is not None and sourceparent is not None)
-            assert (calendar is None and source is not None) or (calendar is not None and source is None)
-            assert not deletesource or (deletesource and source is not None)
-        except AssertionError:
-            log.err("Invalid arguments to StoreCalendarObjectResource.__init__():")
-            log.err("request=%s\n" % (request,))
-            log.err("sourcecal=%s\n" % (sourcecal,))
-            log.err("destinationcal=%s\n" % (destinationcal,))
-            log.err("source=%s\n" % (source,))
-            log.err("source_uri=%s\n" % (source_uri,))
-            log.err("sourceparent=%s\n" % (sourceparent,))
-            log.err("destination=%s\n" % (destination,))
-            log.err("destination_uri=%s\n" % (destination_uri,))
-            log.err("destinationparent=%s\n" % (destinationparent,))
-            log.err("calendar=%s\n" % (calendar,))
-            log.err("deletesource=%s\n" % (deletesource,))
-            log.err("isiTIP=%s\n" % (isiTIP,))
-            raise
-
-        self.request = request
-        self.sourcecal = sourcecal
-        self.destinationcal = destinationcal
-        self.source = source
-        self.source_uri = source_uri
-        self.sourceparent = sourceparent
-        self.destination = destination
-        self.destination_uri = destination_uri
-        self.destinationparent = destinationparent
-        self.calendar = calendar
-        self.deletesource = deletesource
-        self.isiTIP = isiTIP
-        self.allowImplicitSchedule = allowImplicitSchedule
-        self.internal_request = internal_request
-        self.processing_organizer = processing_organizer
-        self.returnData = returnData
-        self.attachmentProcessingDone = attachmentProcessingDone
-
-        self.access = None
-        self.hasPrivateComments = False
-        self.isScheduleResource = False
-        self.dataChanged = False
-
-
-    @inlineCallbacks
-    def fullValidation(self):
-        """
-        Do full validation of source and destination calendar data.
-        """
-
-        # Basic validation
-        self.validIfScheduleMatch()
-
-        if self.destinationcal:
-
-            # Skip validation on internal requests
-            if not self.internal_request:
-
-                # Valid resource name check
-                result, message = self.validResourceName()
-                if not result:
-                    log.err(message)
-                    raise HTTPError(StatusResponse(responsecode.FORBIDDEN, message))
-
-                # Valid collection size check on the destination parent resource
-                result, message = (yield self.validCollectionSize())
-                if not result:
-                    log.err(message)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        customxml.MaxResources(),
-                        message,
-                    ))
-
-                # Valid data sizes - do before parsing the data
-                if self.source is not None:
-                    # Valid content length check on the source resource
-                    result, message = self.validContentLength()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "max-resource-size"),
-                            message,
-                        ))
-                else:
-                    # Valid calendar data size check
-                    result, message = self.validSizeCheck()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "max-resource-size"),
-                            message,
-                        ))
-
-            if not self.sourcecal:
-                # Valid content type check on the source resource if its not in a calendar collection
-                if self.source is not None:
-                    result, message = self.validContentType()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "supported-calendar-data"),
-                            message,
-                        ))
-
-                    # At this point we need the calendar data to do more tests
-                    try:
-                        self.calendar = (yield self.source.iCalendarForUser(self.request))
-                    except ValueError, e:
-                        log.err(str(e))
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "valid-calendar-data"),
-                            "Can't parse calendar data"
-                        ))
-                else:
-                    try:
-                        if type(self.calendar) in (types.StringType, types.UnicodeType,):
-                            self.calendar = Component.fromString(self.calendar)
-                    except ValueError, e:
-                        log.err(str(e))
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "valid-calendar-data"),
-                            "Can't parse calendar data"
-                        ))
-
-                # Possible timezone stripping
-                if config.EnableTimezonesByReference:
-                    self.calendar.stripKnownTimezones()
-
-                # Skip validation on internal requests
-                if not self.internal_request:
-
-                    # Valid calendar data check
-                    result, message = self.validCalendarDataCheck()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "valid-calendar-data"),
-                            message
-                        ))
-
-                    # Valid calendar data for CalDAV check
-                    result, message = self.validCalDAVDataCheck()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "valid-calendar-object-resource"),
-                            message,
-                        ))
-
-                    # Valid calendar component for check
-                    result, message = self.validComponentType()
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "supported-component"),
-                            message,
-                        ))
-
-                    # Valid attendee list size check
-                    result, message = (yield self.validAttendeeListSizeCheck())
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(
-                            ErrorResponse(
-                                responsecode.FORBIDDEN,
-                                MaxAttendeesPerInstance.fromString(str(config.MaxAttendeesPerInstance)),
-                                message,
-                            )
-                        )
-
-                    # Normalize the calendar user addresses once we know we have valid
-                    # calendar data
-                    self.destination.iCalendarAddressDoNormalization(self.calendar)
-
-                # Must have a valid UID at this point
-                self.uid = self.calendar.resourceUID()
-            else:
-                # Get UID from original resource
-                self.source_index = self.sourceparent.index()
-                self.uid = yield self.source_index.resourceUIDForName(self.source.name())
-                if self.uid is None:
-                    log.err("Source calendar does not have a UID: %s" % self.source)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        (caldav_namespace, "valid-calendar-object-resource"),
-                        "Missing UID in calendar data",
-                    ))
-
-                # FIXME: We need this here because we have to re-index the destination. Ideally it
-                # would be better to copy the index entries from the source and add to the destination.
-                self.calendar = (yield self.source.iCalendarForUser(self.request))
-
-            # Check that moves to shared calendars are OK
-            yield self.validCopyMoveOperation()
-
-            # Check location/resource organizer requirement
-            yield self.validLocationResourceOrganizer()
-
-            # Check access
-            if self.destinationcal and config.EnablePrivateEvents:
-                result = (yield self.validAccess())
-                returnValue(result)
-            else:
-                returnValue(None)
-
-        elif self.sourcecal:
-            self.source_index = self.sourceparent.index()
-            self.calendar = (yield self.source.iCalendarForUser(self.request))
-
-        # Check that moves to shared calendars are OK
-        yield self.validCopyMoveOperation()
-
-
-    @inlineCallbacks
-    def validCopyMoveOperation(self):
-        """
-        Check that copy/move type behavior is valid.
-        """
-        if self.source:
-            if not self.destinationcal:
-                # Don't care about copies/moves to non-calendar destinations
-                # In theory this state should not occur here as COPY/MOVE won't call into this as
-                # they detect this state and do regular WebDAV copy/move.
-                pass
-            elif not self.sourcecal:
-                # Moving into a calendar requires regular checks
-                pass
-            else:
-                # Calendar to calendar moves are OK if the resource owner is the same.
-                # Use resourceOwnerPrincipal for this as that takes into account sharing such that the
-                # returned principal relates to the URI path used to access the resource rather than the
-                # underlying resource owner (sharee).
-                sourceowner = (yield self.sourceparent.resourceOwnerPrincipal(self.request))
-                destowner = (yield self.destinationparent.resourceOwnerPrincipal(self.request))
-
-                if sourceowner != destowner:
-                    msg = "Calendar-to-calendar %s with different homes are not supported" % ("moves" if self.deletesource else "copies",)
-                    log.debug(msg)
-                    raise HTTPError(StatusResponse(responsecode.FORBIDDEN, msg))
-
-                # Calendar to calendar moves where Organizer is present are not OK if the owners are different.
-                sourceowner = (yield self.sourceparent.ownerPrincipal(self.request))
-                destowner = (yield self.destinationparent.ownerPrincipal(self.request))
-
-                if sourceowner != destowner:
-                    # Now check whether an ORGANIZER property is present in either sourcecal or destcal
-                    organizer = self.calendar.getOrganizer()
-                    if organizer is None and self.destination.exists() and self.destinationcal:
-                        oldCal = yield self.destination.iCalendar()
-                        organizer = oldCal.getOrganizer()
-
-                    if organizer is not None:
-                        msg = "Calendar-to-calendar %s with an organizer property present and different owners are not supported" % ("moves" if self.deletesource else "copies",)
-                        log.debug(msg)
-                        raise HTTPError(StatusResponse(responsecode.FORBIDDEN, msg))
-
-
-    def validIfScheduleMatch(self):
-        """
-        Check for If-ScheduleTag-Match header behavior.
-        """
-        # Only when a direct request
-        self.schedule_tag_match = False
-        if not self.isiTIP and not self.internal_request:
-            header = self.request.headers.getHeader("If-Schedule-Tag-Match")
-            if header:
-                # If COPY/MOVE get Schedule-Tag on source, PUT use destination
-                if self.source:
-                    matcher = self.source
-                    self.source.validIfScheduleMatch(self.request)
-                else:
-                    matcher = self.destination
-                matcher.validIfScheduleMatch(self.request)
-                self.schedule_tag_match = True
-            elif config.Scheduling.CalDAV.ScheduleTagCompatibility:
-                # Compatibility with old clients. Policy:
-                #
-                # 1. If If-Match header is not present, never do smart merge.
-                # 2. If If-Match is present and the specified ETag is
-                #    considered a "weak" match to the current Schedule-Tag,
-                #    then do smart merge, else reject with a 412.
-                #
-                # Actually by the time we get here the precondition will
-                # already have been tested and found to be OK, so we can just
-                # always do smart merge now if If-Match is present.
-                self.schedule_tag_match = self.request.headers.getHeader("If-Match") is not None
-
-
-    def validResourceName(self):
-        """
-        Make sure that the resource name for the new resource is valid.
-        """
-        result = True
-        message = ""
-        filename = self.destination.name()
-        if filename.startswith("."):
-            result = False
-            message = "File name %s not allowed in calendar collection" % (filename,)
-
-        return result, message
-
-
-    def validContentType(self):
-        """
-        Make sure that the content-type of the source resource is text/calendar.
-        This test is only needed when the source is not in a calendar collection.
-        """
-        result = True
-        message = ""
-        content_type = self.source.contentType()
-        if not ((content_type.mediaType == "text") and (content_type.mediaSubtype == "calendar")):
-            result = False
-            message = "MIME type %s not allowed in calendar collection" % (content_type,)
-
-        return result, message
-
-
-    def validContentLength(self):
-        """
-        Make sure that the length of the source data is within bounds.
-        """
-        result = True
-        message = ""
-        if config.MaxResourceSize:
-            calsize = self.source.contentLength()
-            if calsize is not None and calsize > config.MaxResourceSize:
-                result = False
-                message = "File size %d bytes is larger than allowed limit %d bytes" % (calsize, config.MaxResourceSize)
-
-        return result, message
-
-
-    @inlineCallbacks
-    def validCollectionSize(self):
-        """
-        Make sure that any limits on the number of resources in a collection are enforced.
-        """
-        result = True
-        message = ""
-        if not self.destination.exists() and \
-            config.MaxResourcesPerCollection and \
-            (yield self.destinationparent.countChildren()) >= config.MaxResourcesPerCollection:
-                result = False
-                message = "Too many resources in collection %s" % (self.destinationparent,)
-
-        returnValue((result, message,))
-
-
-    def validCalendarDataCheck(self):
-        """
-        Check that the calendar data is valid iCalendar.
-        @return:         tuple: (True/False if the calendar data is valid,
-                                 log message string).
-        """
-        result = True
-        message = ""
-        if self.calendar is None:
-            result = False
-            message = "Empty resource not allowed in calendar collection"
-        else:
-            try:
-                self.calendar.validCalendarData()
-            except ValueError, e:
-                result = False
-                message = "Invalid calendar data: %s" % (e,)
-
-        return result, message
-
-
-    def validCalDAVDataCheck(self):
-        """
-        Check that the calendar data is valid as a CalDAV calendar object resource.
-        @return:         tuple: (True/False if the calendar data is valid,
-                                 log message string).
-        """
-        result = True
-        message = ""
-        try:
-            self.calendar.validCalendarForCalDAV(methodAllowed=self.isiTIP)
-        except ValueError, e:
-            result = False
-            message = "Calendar data does not conform to CalDAV requirements: %s" % (e,)
-
-        return result, message
-
-
-    def validComponentType(self):
-        """
-        Make sure that any limits on the number of resources in a collection are enforced.
-        """
-        result = True
-        message = ""
-
-        if not self.destinationparent.isSupportedComponent(self.calendar.mainType()):
-            result = False
-            message = "Invalid component type %s for calendar: %s" % (self.calendar.mainType(), self.destinationparent,)
-
-        return result, message
-
-
-    def validSizeCheck(self):
-        """
-        Make sure that the content-type of the source resource is text/calendar.
-        This test is only needed when the source is not in a calendar collection.
-        """
-        result = True
-        message = ""
-        if config.MaxResourceSize:
-            # FIXME PERF could be done more efficiently?
-            calsize = len(str(self.calendar))
-            if calsize > config.MaxResourceSize:
-                result = False
-                message = "Data size %d bytes is larger than allowed limit %d bytes" % (calsize, config.MaxResourceSize)
-
-        return result, message
-
-
-    @inlineCallbacks
-    def validAttendeeListSizeCheck(self):
-        """
-        Make sure that the Attendee list length is within bounds. We don't do this check for inbox because we
-        will assume that the limit has been applied on the PUT causing the iTIP message to be created.
-
-        FIXME: The inbox check might not take into account iSchedule stuff from outside. That needs to have
-        the max attendees check applied at the time of delivery.
-        """
-        result = True
-        message = ""
-        if config.MaxAttendeesPerInstance and not self.isiTIP:
-            uniqueAttendees = set()
-            for attendee in self.calendar.getAllAttendeeProperties():
-                uniqueAttendees.add(attendee.value())
-            attendeeListLength = len(uniqueAttendees)
-            if attendeeListLength > config.MaxAttendeesPerInstance:
-
-                # Check to see whether we are increasing the count on an existing resource
-                if self.destination.exists() and self.destinationcal:
-                    oldcalendar = (yield self.destination.iCalendarForUser(self.request))
-                    uniqueAttendees = set()
-                    for attendee in oldcalendar.getAllAttendeeProperties():
-                        uniqueAttendees.add(attendee.value())
-                    oldAttendeeListLength = len(uniqueAttendees)
-                else:
-                    oldAttendeeListLength = 0
-
-                if attendeeListLength > oldAttendeeListLength:
-                    result = False
-                    message = "Attendee list size %d is larger than allowed limit %d" % (attendeeListLength, config.MaxAttendeesPerInstance)
-
-        returnValue((result, message,))
-
-
-    def validAccess(self):
-        """
-        Make sure that the X-CALENDARSERVER-ACCESS property is properly dealt with.
-        """
-
-        if self.calendar.hasProperty(Component.ACCESS_PROPERTY):
-
-            # Must be a value we know about
-            self.access = self.calendar.accessLevel(default=None)
-            if self.access is None:
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (calendarserver_namespace, "valid-access-restriction"),
-                    "Private event access level not allowed",
-                ))
-
-            # Only DAV:owner is able to set the property to other than PUBLIC
-            if not self.internal_request:
-                def _callback(parent_owner):
-
-                    authz = self.destinationparent.currentPrincipal(self.request)
-                    if davxml.Principal(parent_owner) != authz and self.access != Component.ACCESS_PUBLIC:
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (calendarserver_namespace, "valid-access-restriction-change"),
-                            "Private event access level change not allowed",
-                        ))
-
-                    return None
-
-                d = self.destinationparent.owner(self.request)
-                d.addCallback(_callback)
-                return d
-        else:
-            # Check whether an access property was present before and write that into the calendar data
-            if not self.source and self.destination.exists() and self.destination.accessMode:
-                old_access = self.destination.accessMode
-                self.calendar.addProperty(Property(name=Component.ACCESS_PROPERTY, value=old_access))
-
-        return succeed(None)
-
-
-    @inlineCallbacks
-    def validLocationResourceOrganizer(self):
-        """
-        If the calendar owner is a location or resource, check whether an ORGANIZER property is required.
-        """
-
-        if not self.internal_request:
-            originatorPrincipal = (yield self.destination.ownerPrincipal(self.request))
-            cutype = originatorPrincipal.getCUType() if originatorPrincipal is not None else "INDIVIDUAL"
-            organizer = self.calendar.getOrganizer()
-
-            # Check for an allowed change
-            if organizer is None and (
-                cutype == "ROOM" and not config.Scheduling.Options.AllowLocationWithoutOrganizer or
-                cutype == "RESOURCE" and not config.Scheduling.Options.AllowResourceWithoutOrganizer):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (calendarserver_namespace, "valid-organizer"),
-                    "Organizer required in calendar data",
-                ))
-
-            # Check for tracking the modifier
-            if organizer is None and (
-                cutype == "ROOM" and config.Scheduling.Options.TrackUnscheduledLocationData or
-                cutype == "RESOURCE" and config.Scheduling.Options.TrackUnscheduledResourceData):
-
-                # Find current principal
-                authz = None
-                authz_principal = self.destinationparent.currentPrincipal(self.request).children[0]
-                if isinstance(authz_principal, davxml.HRef):
-                    principalURL = str(authz_principal)
-                    if principalURL:
-                        authz = (yield self.request.locateResource(principalURL))
-
-                if authz is not None:
-                    prop = Property("X-CALENDARSERVER-MODIFIED-BY", "urn:uuid:%s" % (authz.record.guid,))
-                    prop.setParameter("CN", authz.displayName())
-                    for candidate in authz.calendarUserAddresses():
-                        if candidate.startswith("mailto:"):
-                            prop.setParameter("EMAIL", candidate[7:])
-                            break
-                    self.calendar.replacePropertyInAllComponents(prop)
-                else:
-                    self.calendar.removeAllPropertiesWithName("X-CALENDARSERVER-MODIFIED-BY")
-                self.dataChanged = True
-
-
-    @inlineCallbacks
-    def preservePrivateComments(self):
-        # Check for private comments on the old resource and the new resource and re-insert
-        # ones that are lost.
-        #
-        # NB Do this before implicit scheduling as we don't want old clients to trigger scheduling when
-        # the X- property is missing.
-        self.hasPrivateComments = False
-        if config.Scheduling.CalDAV.get("EnablePrivateComments", True) and self.calendar is not None:
-            old_has_private_comments = self.destination.exists() and self.destinationcal and self.destination.hasPrivateComment
-            self.hasPrivateComments = self.calendar.hasPropertyInAnyComponent((
-                "X-CALENDARSERVER-PRIVATE-COMMENT",
-                "X-CALENDARSERVER-ATTENDEE-COMMENT",
-            ))
-
-            if old_has_private_comments and not self.hasPrivateComments:
-                # Transfer old comments to new calendar
-                log.debug("Private Comments properties were entirely removed by the client. Restoring existing properties.")
-                old_calendar = (yield self.destination.iCalendarForUser(self.request))
-                self.calendar.transferProperties(old_calendar, (
-                    "X-CALENDARSERVER-PRIVATE-COMMENT",
-                    "X-CALENDARSERVER-ATTENDEE-COMMENT",
-                ))
-
-
-    @inlineCallbacks
-    def replaceMissingToDoProperties(self):
-        """
-        Recover any lost ORGANIZER or ATTENDEE properties in non-recurring VTODOs.
-        """
-
-        if self.destination.exists() and self.calendar.resourceType() == "VTODO" and not self.calendar.isRecurring():
-
-            old_calendar = (yield self.destination.iCalendarForUser(self.request))
-
-            new_organizer = self.calendar.getOrganizer()
-            old_organizer = old_calendar.getOrganizerProperty()
-            new_attendees = self.calendar.getAttendees()
-            old_attendees = tuple(old_calendar.getAllAttendeeProperties())
-
-            new_completed = self.calendar.mainComponent().hasProperty("COMPLETED")
-            old_completed = old_calendar.mainComponent().hasProperty("COMPLETED")
-
-            if old_organizer and not new_organizer and len(old_attendees) > 0 and len(new_attendees) == 0:
-                # Transfer old organizer and attendees to new calendar
-                log.debug("Organizer and attendee properties were entirely removed by the client. Restoring existing properties.")
-
-                # Get the originator who is the owner of the calendar resource being modified
-                originatorPrincipal = (yield self.destination.ownerPrincipal(self.request))
-                originatorAddresses = originatorPrincipal.calendarUserAddresses()
-
-                for component in self.calendar.subcomponents():
-                    if component.name() != "VTODO":
-                        continue
-
-                    if not component.hasProperty("DTSTART"):
-                        # Need to put DTSTART back in or we get a date mismatch failure later
-                        for old_component in old_calendar.subcomponents():
-                            if old_component.name() != "VTODO":
-                                continue
-                            if old_component.hasProperty("DTSTART"):
-                                component.addProperty(old_component.getProperty("DTSTART").duplicate())
-                                break
-
-                    # Add organizer back in from previous resource
-                    component.addProperty(old_organizer.duplicate())
-
-                    # Add attendees back in from previous resource
-                    for anAttendee in old_attendees:
-                        anAttendee = anAttendee.duplicate()
-                        if component.hasProperty("COMPLETED") and anAttendee.value() in originatorAddresses:
-                            anAttendee.setParameter("PARTSTAT", "COMPLETED")
-                        component.addProperty(anAttendee)
-
-            elif new_completed ^ old_completed and not self.internal_request:
-                # COMPLETED changed - sync up attendee state
-                # We need this because many VTODO clients are not aware of scheduling,
-                # i.e. they do not adjust any ATTENDEE PARTSTATs. We are going to impose
-                # our own requirement that PARTSTAT is set to COMPLETED when the COMPLETED
-                # property is added.
-
-                # Transfer old organizer and attendees to new calendar
-                log.debug("Sync COMPLETED property change.")
-
-                # Get the originator who is the owner of the calendar resource being modified
-                originatorPrincipal = (yield self.destination.ownerPrincipal(self.request))
-                originatorAddresses = originatorPrincipal.calendarUserAddresses()
-
-                for component in self.calendar.subcomponents():
-                    if component.name() != "VTODO":
-                        continue
-
-                    # Change owner partstat
-                    for anAttendee in component.properties("ATTENDEE"):
-                        if anAttendee.value() in originatorAddresses:
-                            oldpartstat = anAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
-                            newpartstat = "COMPLETED" if component.hasProperty("COMPLETED") else "IN-PROCESS"
-                            if newpartstat != oldpartstat:
-                                anAttendee.setParameter("PARTSTAT", newpartstat)
-
-
-    @inlineCallbacks
-    def dropboxPathNormalization(self):
-        """
-        Make sure sharees only use dropbox paths of the sharer.
-        """
-
-        # Only relevant if calendar is sharee collection
-        if self.destinationparent.isShareeCollection():
-
-            # Get all X-APPLE-DROPBOX's and ATTACH's that are http URIs
-            xdropboxes = self.calendar.getAllPropertiesInAnyComponent(
-                "X-APPLE-DROPBOX",
-                depth=1,
-            )
-            attachments = self.calendar.getAllPropertiesInAnyComponent(
-                "ATTACH",
-                depth=1,
-            )
-            attachments = [
-                attachment for attachment in attachments
-                if attachment.parameterValue("VALUE", "TEXT") == "URI" and attachment.value().startswith("http")
-            ]
-
-            if len(xdropboxes) or len(attachments):
-
-                # Determine owner GUID
-                ownerPrincipal = (yield self.destinationparent.ownerPrincipal(self.request))
-                owner = ownerPrincipal.principalURL().split("/")[-2]
-
-                def uriNormalize(uri):
-                    urichanged = False
-                    scheme, netloc, path, params, query, fragment = urlparse(uri)
-                    pathbits = path.split("/")
-                    if pathbits[1] != "calendars":
-                        pathbits[1] = "calendars"
-                        urichanged = True
-                    if pathbits[2] != "__uids__":
-                        pathbits[2] = "__uids__"
-                        urichanged = True
-                    if pathbits[3] != owner:
-                        pathbits[3] = owner
-                        urichanged = True
-                    if urichanged:
-                        return urlunparse((scheme, netloc, "/".join(pathbits), params, query, fragment,))
-                    return None
-
-                for xdropbox in xdropboxes:
-                    uri = uriNormalize(xdropbox.value())
-                    if uri:
-                        xdropbox.setValue(uri)
-                        self.dataChanged = True
-                for attachment in attachments:
-                    uri = uriNormalize(attachment.value())
-                    if uri:
-                        attachment.setValue(uri)
-                        self.dataChanged = True
-
-
-    def processAlarms(self):
-        """
-        Remove duplicate alarms. Add a default alarm if required.
-        """
-
-        # Remove duplicate alarms
-        if config.RemoveDuplicateAlarms and self.calendar.hasDuplicateAlarms(doFix=True):
-            self.dataChanged = True
-
-        # Only if feature enabled
-        if not config.EnableDefaultAlarms:
-            return
-
-        # Check that we are creating and this is not the inbox
-        if not self.destinationcal or self.destination.exists() or self.isiTIP:
-            return
-
-        # Never add default alarms to calendar data in shared calendars
-        if self.destinationparent.isShareeCollection():
-            return
-
-        # Add default alarm for VEVENT and VTODO only
-        mtype = self.calendar.mainType().upper()
-        if self.calendar.mainType().upper() not in ("VEVENT", "VTODO"):
-            return
-        vevent = mtype == "VEVENT"
-
-        # Check timed or all-day
-        start, _ignore_end = self.calendar.mainComponent(allow_multiple=True).getEffectiveStartEnd()
-        if start is None:
-            # Yes VTODOs might have no DTSTART or DUE - in this case we do not add a default
-            return
-        timed = not start.isDateOnly()
-
-        # See if default exists and add using appropriate logic
-        alarm = self.destinationparent.getDefaultAlarm(vevent, timed)
-        if alarm and self.calendar.addAlarms(alarm):
-            self.dataChanged = True
-
-
-    @inlineCallbacks
-    def noUIDConflict(self, uid):
-        """
-        Check that the UID of the new calendar object conforms to the requirements of
-        CalDAV, i.e. it must be unique in the collection and we must not overwrite a
-        different UID.
-        @param uid: the UID for the resource being stored.
-        @return: tuple: (True/False if the UID is valid, log message string,
-            name of conflicted resource).
-        """
-
-        result = True
-        message = ""
-        rname = ""
-
-        # Adjust for a move into same calendar collection
-        oldname = None
-        if self.sourceparent and (self.sourceparent == self.destinationparent) and self.deletesource:
-            oldname = self.source.name()
-
-        # UID must be unique
-        index = self.destinationparent.index()
-        if not (yield index.isAllowedUID(uid, oldname, self.destination.name())):
-            rname = yield index.resourceNameForUID(uid)
-            # This can happen if two simultaneous PUTs occur with the same UID.
-            # i.e. one PUT has reserved the UID but has not yet written the resource,
-            # the other PUT tries to reserve and fails but no index entry exists yet.
-            if rname is None:
-                rname = "<<Unknown Resource>>"
-            result = False
-            message = "Calendar resource %s already exists with same UID %s" % (rname, uid)
-        else:
-            # Cannot overwrite a resource with different UID
-            if self.destination.exists():
-                olduid = yield index.resourceUIDForName(self.destination.name())
-                if olduid != uid:
-                    rname = self.destination.name()
-                    result = False
-                    message = "Cannot overwrite calendar resource %s with different UID %s" % (rname, olduid)
-
-        returnValue((result, message, rname))
-
-
-    @inlineCallbacks
-    def hasCalendarResourceUIDSomewhereElse(self, uid):
-        """
-        See if a calendar component with a matching UID exists anywhere in the calendar home of the
-        current recipient owner and is not the resource being targeted.
-        """
-
-        # Ignore for an overwrite or a MOVE
-        if self.destination.exists() or self.sourceparent and self.deletesource:
-            returnValue(None)
-
-        failed = False
-
-        # Always fail a copy
-        if self.sourceparent and self.sourcecal and not self.deletesource and self.destinationcal:
-            failed = True
-        else:
-            # Get owner's calendar-home
-            calendar_owner_principal = (yield self.destination.resourceOwnerPrincipal(self.request))
-            calendar_home = yield calendar_owner_principal.calendarHome(self.request)
-
-            # Check for matching resource somewhere else in the home use the "schedule" mode to prevent any kind of match
-            failed = (yield calendar_home.hasCalendarResourceUIDSomewhereElse(uid, self.destination, "schedule"))
-
-        if failed:
-            log.debug("Implicit - found component with same UID in a different collection: %s" % (self.destination_uri,))
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                (caldav_namespace, "unique-scheduling-object-resource"),
-                "Cannot duplicate scheduling object resource",
-            ))
-
-
-    @inlineCallbacks
-    def doImplicitScheduling(self):
-
-        data_changed = False
-        did_implicit_action = False
-
-        # Do scheduling
-        if not self.isiTIP:
-            scheduler = ImplicitScheduler()
-
-            # Determine type of operation PUT, COPY or DELETE
-            if not self.source:
-                # PUT
-                do_implicit_action, is_scheduling_resource = (yield scheduler.testImplicitSchedulingPUT(
-                    self.request,
-                    self.destination,
-                    self.destination_uri,
-                    self.calendar,
-                    internal_request=self.internal_request,
-                ))
-            elif self.deletesource:
-                # MOVE
-                do_implicit_action, is_scheduling_resource = (yield scheduler.testImplicitSchedulingMOVE(
-                    self.request,
-                    self.source,
-                    self.sourcecal,
-                    self.source_uri,
-                    self.destination,
-                    self.destinationcal,
-                    self.destination_uri,
-                    self.calendar,
-                    internal_request=self.internal_request,
-                ))
-            else:
-                # COPY
-                do_implicit_action, is_scheduling_resource = (yield scheduler.testImplicitSchedulingCOPY(
-                    self.request,
-                    self.source,
-                    self.sourcecal,
-                    self.source_uri,
-                    self.destination,
-                    self.destinationcal,
-                    self.destination_uri,
-                    self.calendar,
-                    internal_request=self.internal_request,
-                ))
-
-            if do_implicit_action and self.allowImplicitSchedule:
-
-                # Cannot do implicit in sharee's shared calendar
-                isShareeCollection = self.destinationparent.isShareeCollection()
-                if isShareeCollection:
-                    scheduler.setSchedulingNotAllowed(
-                        HTTPError,
-                        ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (calendarserver_namespace, "sharee-privilege-needed",),
-                            description="Sharee's cannot schedule",
-                        ),
-                    )
-
-                new_calendar = (yield scheduler.doImplicitScheduling(self.schedule_tag_match))
-                if new_calendar:
-                    if isinstance(new_calendar, int):
-                        returnValue(new_calendar)
-                    else:
-                        self.calendar = new_calendar
-                        data_changed = True
-                did_implicit_action = True
-        else:
-            is_scheduling_resource = False
-
-        returnValue((is_scheduling_resource, data_changed, did_implicit_action,))
-
-
-    @inlineCallbacks
-    def mergePerUserData(self):
-        if self.calendar:
-            accessUID = (yield self.destination.resourceOwnerPrincipal(self.request))
-            accessUID = accessUID.principalUID() if accessUID else ""
-            if self.destination.exists() and self.destinationcal:
-                oldCal = yield self.destination.iCalendar()
-            else:
-                oldCal = None
-
-            # Duplicate before we do the merge because someone else may "own" the calendar object
-            # and we should not change it. This is not ideal as we may duplicate it unnecessarily
-            # but we currently have no api to let the caller tell us whether it cares about the
-            # whether the calendar data is changed or not.
-            try:
-                self.calendar = PerUserDataFilter(accessUID).merge(self.calendar.duplicate(), oldCal)
-            except ValueError:
-                log.err("Invalid per-user data merge")
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-calendar-data"),
-                    "Cannot merge per-user data",
-                ))
-
-
-    @inlineCallbacks
-    def doStore(self, implicit):
-
-        # Stash the current calendar data as we may need to return it
-        if self.returnData:
-            self.storeddata = str(self.calendar)
-
-        # Always do the per-user data merge right before we store
-        yield self.mergePerUserData()
-
-        # Do put or copy based on whether source exists
-        source = self.source
-        if source is not None:
-            # Retrieve information from the source, in case we have to delete
-            # it.
-            sourceProperties = dict(source.newStoreProperties().iteritems())
-            if not implicit:
-                # Only needed in implicit case; see below.
-                sourceText = yield source.iCalendarText()
-
-            # Delete the original source if needed (for example, if this is a
-            # same-calendar MOVE of a calendar object, implemented as an
-            # effective DELETE-then-PUT).
-            if self.deletesource:
-                yield self.doSourceDelete()
-
-            if implicit:
-                response = (yield self.doStorePut())
-            else:
-                response = (yield self.doStorePut(sourceText))
-            self.destination.newStoreProperties().update(sourceProperties)
-        else:
-            response = (yield self.doStorePut())
-
-        returnValue(response)
-
-
-    @inlineCallbacks
-    def doStorePut(self, data=None):
-
-        if data is None:
-            # We'll be passing this component directly to storeComponent( )
-            componentToStore = self.calendar
-            data = str(self.calendar)
-        else:
-            # We'll be passing data as a stream to storeStream( )
-            componentToStore = None
-
-        # Update calendar-access property value on the resource. We need to do this before the
-        # store as the store will "commit" the new value.
-        if self.access:
-            self.destination.accessMode = self.access
-
-        # Do not remove the property if access was not specified and we are storing in a calendar.
-        # This ensure that clients that do not preserve the iCalendar property do not cause access
-        # restrictions to be lost.
-        elif not self.destinationcal:
-            self.destination.accessMode = ""
-
-        # Check for existence of private comments and write property
-        if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
-            self.destination.hasPrivateComment = self.hasPrivateComments
-
-        # Check for scheduling object resource and write property
-        self.destination.isScheduleObject = self.isScheduleResource
-        if self.isScheduleResource:
-            # Need to figure out when to change the schedule tag:
-            #
-            # 1. If this is not an internal request then the resource is being explicitly changed
-            # 2. If it is an internal request for the Organizer, schedule tag never changes
-            # 3. If it is an internal request for an Attendee and the message being processed came
-            #    from the Organizer then the schedule tag changes.
-
-            change_scheduletag = True
-            if self.internal_request:
-                # Check what kind of processing is going on
-                if self.processing_organizer == True:
-                    # All auto-processed updates for an Organizer leave the tag unchanged
-                    change_scheduletag = False
-                elif self.processing_organizer == False:
-                    # Auto-processed updates that are the result of an organizer "refresh" due
-                    # to another Attendee's REPLY should leave the tag unchanged
-                    change_scheduletag = not hasattr(self.request, "doing_attendee_refresh")
-
-            if change_scheduletag or not self.destination.scheduleTag:
-                self.destination.scheduleTag = str(uuid.uuid4())
-
-            # Handle weak etag compatibility
-            if config.Scheduling.CalDAV.ScheduleTagCompatibility:
-                if change_scheduletag:
-                    # Schedule-Tag change => weak ETag behavior must not happen
-                    etags = ()
-                else:
-                    # Schedule-Tag did not change => add current ETag to list of those that can
-                    # be used in a weak precondition test
-                    etags = self.destination.scheduleEtags
-                    if etags is None:
-                        etags = ()
-                etags += (hashlib.md5(data + (self.destination.scheduleTag if self.destination.scheduleTag else "")).hexdigest(),)
-                self.destination.scheduleEtags = etags
-            else:
-                self.destination.scheduleEtags = ()
-        else:
-            self.destination.scheduleTag = ""
-            self.destination.scheduleEtags = ()
-
-        if componentToStore is None:
-            stream = MemoryStream(data)
-            response = yield self.destination.storeStream(stream)
-        else:
-            # Since we already have a component, we can pass it directly
-            response = yield self.destination.storeComponent(componentToStore)
-        response = IResponse(response)
-
-        if self.isScheduleResource:
-            # Add a response header
-            response.headers.setHeader("Schedule-Tag", self.destination.scheduleTag)
-
-        returnValue(response)
-
-
-    @inlineCallbacks
-    def doSourceDelete(self):
-        # Delete the source resource
-        yield self.source.storeRemove(self.request, False, self.source_uri)
-        log.debug("Source removed %s" % (self.source,))
-        returnValue(None)
-
-
-    @inlineCallbacks
-    def run(self):
-        """
-        Function that does common PUT/COPY/MOVE behavior.
-
-        @return: a Deferred with a status response result.
-        """
-
-        try:
-            reservation = None
-
-            # Handle all validation operations here.
-            yield self.fullValidation()
-
-            # Reservation and UID conflict checking is next.
-            if self.destinationcal:
-                # Reserve UID
-                self.destination_index = self.destinationparent.index()
-                reservation = StoreCalendarObjectResource.UIDReservation(
-                    self.destination_index, self.uid, self.destination_uri,
-                    self.internal_request or self.isiTIP,
-                    self.destination._associatedTransaction,
-                )
-                yield reservation.reserve()
-                # UID conflict check - note we do this after reserving the UID to avoid a race condition where two requests
-                # try to write the same calendar data to two different resource URIs.
-                if not self.isiTIP:
-                    result, message, rname = yield self.noUIDConflict(self.uid)
-                    if not result:
-                        log.err(message)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            NoUIDConflict(
-                                davxml.HRef.fromString(
-                                    joinURL(
-                                        parentForURL(self.destination_uri),
-                                        rname.encode("utf-8")
-                                    )
-                                )
-                            ),
-                            "UID already exists",
-                        ))
-                    yield self.hasCalendarResourceUIDSomewhereElse(self.uid)
-
-            # Preserve private comments
-            yield self.preservePrivateComments()
-
-            # Fix broken VTODOs
-            yield self.replaceMissingToDoProperties()
-
-            # Handle sharing dropbox normalization
-            yield self.dropboxPathNormalization()
-
-            # Pre-process managed attachments
-            if not self.internal_request and not self.attachmentProcessingDone:
-                managed_copied, managed_removed = (yield self.destination.preProcessManagedAttachments(self.calendar))
-
-            # Default/duplicate alarms
-            self.processAlarms()
-
-            # Do scheduling
-            implicit_result = (yield self.doImplicitScheduling())
-            if isinstance(implicit_result, int):
-                if implicit_result == ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT:
-                    if reservation:
-                        yield reservation.unreserve()
-
-                    returnValue(StatusResponse(responsecode.CREATED, "Resource created but immediately deleted by the server."))
-
-                elif implicit_result == ImplicitScheduler.STATUS_ORPHANED_EVENT:
-                    if reservation:
-                        yield reservation.unreserve()
-
-                    # Now forcibly delete the event
-                    if self.destination.exists():
-                        yield self.destination.storeRemove(self.request, False, self.destination_uri)
-                    else:
-                        msg = "Attendee cannot create event for Organizer: %s" % (implicit_result,)
-                        log.err(msg)
-                        raise HTTPError(ErrorResponse(
-                            responsecode.FORBIDDEN,
-                            (caldav_namespace, "attendee-allowed"),
-                            description=msg
-                        ))
-
-                    returnValue(StatusResponse(responsecode.OK, "Resource modified but immediately deleted by the server."))
-
-                else:
-                    msg = "Invalid return status code from ImplicitScheduler: %s" % (implicit_result,)
-                    log.err(msg)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        (caldav_namespace, "valid-calendar-data"),
-                        description=msg
-                    ))
-            else:
-                self.isScheduleResource, data_changed, did_implicit_action = implicit_result
-
-            # Do the actual put or copy
-            response = (yield self.doStore(data_changed))
-
-            # Post process managed attachments
-            if not self.internal_request and not self.attachmentProcessingDone:
-                yield self.destination.postProcessManagedAttachments(managed_copied, managed_removed)
-
-            # Must not set ETag in response if data changed
-            if did_implicit_action or self.dataChanged:
-                def _removeEtag(request, response):
-                    response.headers.removeHeader('etag')
-                    return response
-                _removeEtag.handleErrors = True
-
-                self.request.addResponseFilter(_removeEtag, atEnd=True)
-
-            if reservation:
-                yield reservation.unreserve()
-
-            returnValue(response)
-
-        except Exception, err:
-
-            # Grab the current exception state here so we can use it in a re-raise - we need this because
-            # an inlineCallback might be called and that raises an exception when it returns, wiping out the
-            # original exception "context".
-            ex = Failure()
-
-            if reservation:
-                yield reservation.unreserve()
-
-            if isinstance(err, InvalidOverriddenInstanceError):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-calendar-data"),
-                    description="Invalid overridden instance"
-                ))
-            elif isinstance(err, TooManyInstancesError):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    MaxInstances.fromString(str(err.max_allowed)),
-                    "Too many recurrence instances",
-                ))
-            elif isinstance(err, AttachmentStoreValidManagedID):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-managed-id"),
-                    "Invalid Managed-ID parameter in calendar data",
-                ))
-            else:
-                # Return the original failure (exception) state
-                ex.raiseException()
-
-
-    @inlineCallbacks
-    def moveValidation(self):
-        """
-        Do full validation of source and destination calendar data.
-        """
-
-        # Basic validation
-        self.validIfScheduleMatch()
-
-        # Valid resource name check
-        result, message = self.validResourceName()
-        if not result:
-            log.err(message)
-            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, message))
-
-        # Valid collection size check on the destination parent resource
-        result, message = (yield self.validCollectionSize())
-        if not result:
-            log.err(message)
-            raise HTTPError(ErrorResponse(
-                responsecode.FORBIDDEN,
-                customxml.MaxResources(),
-                message,
-            ))
-
-        # Check that moves to shared calendars are OK
-        yield self.validCopyMoveOperation()
-
-        returnValue(None)
-
-
-    @inlineCallbacks
-    def doStoreMove(self):
-
-        # Do move
-        response = (yield self.source.storeMove(self.request, self.destinationparent, self.destination._name))
-        returnValue(response)
-
-
-    @inlineCallbacks
-    def move(self):
-        """
-        Function that does common MOVE behavior.
-
-        @return: a Deferred with a status response result.
-        """
-
-        try:
-            reservation = None
-
-            # Handle all validation operations here.
-            self.calendar = (yield self.source.iCalendarForUser(self.request))
-            yield self.moveValidation()
-
-            # Reservation and UID conflict checking is next.
-
-            # Reserve UID
-            self.destination_index = self.destinationparent.index()
-            reservation = StoreCalendarObjectResource.UIDReservation(
-                self.destination_index, self.source.uid(), self.destination_uri,
-                self.internal_request or self.isiTIP,
-                self.destination._associatedTransaction,
-            )
-            yield reservation.reserve()
-            # UID conflict check - note we do this after reserving the UID to avoid a race condition where two requests
-            # try to write the same calendar data to two different resource URIs.
-            if not self.isiTIP:
-                result, message, rname = yield self.noUIDConflict(self.source.uid())
-                if not result:
-                    log.err(message)
-                    raise HTTPError(ErrorResponse(
-                        responsecode.FORBIDDEN,
-                        NoUIDConflict(
-                            davxml.HRef.fromString(
-                                joinURL(
-                                    parentForURL(self.destination_uri),
-                                    rname.encode("utf-8")
-                                )
-                            )
-                        ),
-                        "UID already exists",
-                    ))
-
-            # Do the actual put or copy
-            response = (yield self.doStoreMove())
-
-            if reservation:
-                yield reservation.unreserve()
-
-            returnValue(response)
-
-        except Exception, err:
-
-            # Grab the current exception state here so we can use it in a re-raise - we need this because
-            # an inlineCallback might be called and that raises an exception when it returns, wiping out the
-            # original exception "context".
-            ex = Failure()
-
-            if reservation:
-                yield reservation.unreserve()
-
-            if isinstance(err, InvalidOverriddenInstanceError):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-calendar-data"),
-                    description="Invalid overridden instance"
-                ))
-            elif isinstance(err, TooManyInstancesError):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    MaxInstances.fromString(str(err.max_allowed)),
-                    "Too many recurrence instances",
-                ))
-            elif isinstance(err, AttachmentStoreValidManagedID):
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-managed-id"),
-                    "Invalid Managed-ID parameter in calendar data",
-                ))
-            else:
-                # Return the original failure (exception) state
-                ex.raiseException()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130425/0ba99e6e/attachment-0001.html>


More information about the calendarserver-changes mailing list