OwlCyberSecurity - MANAGER
Edit File: mkcol.py
# -*- test-case-name: twistedcaldav.test.test_DAV.MKCOL -*- ## # Copyright (c) 2005-2017 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. ## """ Extended MKCOL method. """ __all__ = ["http_MKCOL"] from twisted.python.failure import Failure from twisted.internet.defer import inlineCallbacks, returnValue from twext.python.log import Logger from txweb2 import responsecode from txdav.xml import element as davxml from txweb2.http import Response from txweb2.dav.http import ErrorResponse, PropertyStatusResponseQueue from txweb2.dav.util import davXMLFromStream from txweb2.dav.util import parentForURL from txweb2.http import HTTPError from txweb2.http import StatusResponse from twistedcaldav import caldavxml, carddavxml, mkcolxml from twistedcaldav.config import config from twistedcaldav.resource import isAddressBookCollectionResource, \ CalDAVResource from twistedcaldav.resource import isPseudoCalendarCollectionResource log = Logger() @inlineCallbacks def http_MKCOL(self, request): # # Check authentication and access controls # parent = (yield request.locateResource(parentForURL(request.uri))) yield parent.authorize(request, (davxml.Bind(),)) if self.exists(): log.error("Attempt to create collection where resource exists: {s!r}", s=self) raise HTTPError(ErrorResponse( responsecode.FORBIDDEN, (davxml.dav_namespace, "resource-must-be-null"), "Resource already exists", )) if not parent.isCollection(): log.error("Attempt to create collection with non-collection parent: {s!r}", s=self) raise HTTPError(ErrorResponse( responsecode.CONFLICT, (davxml.dav_namespace, "collection-location-ok"), "Cannot create calendar inside another calendar", )) # # Don't allow DAV collections in a calendar or address book collection # if config.EnableCalDAV: parent = (yield self._checkParents(request, isPseudoCalendarCollectionResource)) if parent is not None: raise HTTPError(StatusResponse( responsecode.FORBIDDEN, "Cannot create collection within calendar collection %s" % (parent,) )) if config.EnableCardDAV: parent = (yield self._checkParents(request, isAddressBookCollectionResource)) if parent is not None: raise HTTPError(StatusResponse( responsecode.FORBIDDEN, "Cannot create collection within address book collection %s" % (parent,) )) # # Read request body # try: doc = (yield davXMLFromStream(request.stream)) except ValueError, e: log.error("Error while handling MKCOL: {ex}", ex=e) # TODO: txweb2.dav 'MKCOL' tests demand this particular response # code, but should we really be looking at the XML content or the # content-type header? It seems to me like this ought to be considered # a BAD_REQUEST if it claims to be XML but isn't, but an # UNSUPPORTED_MEDIA_TYPE if it claims to be something else. -glyph raise HTTPError( StatusResponse(responsecode.UNSUPPORTED_MEDIA_TYPE, str(e)) ) if doc is not None: # Can ignore Prefer:return=minimal as we don't return a body for success by default # Parse response body mkcol = doc.root_element if not isinstance(mkcol, mkcolxml.MakeCollection): error = ("Non-%s element in MKCOL request body: %s" % (mkcolxml.MakeCollection.name, mkcol)) log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.UNSUPPORTED_MEDIA_TYPE, error)) errors = PropertyStatusResponseQueue("PROPPATCH", request.uri, responsecode.NO_CONTENT) got_an_error = False set_supported_component_set = False if mkcol.children: # mkcol -> set -> prop -> property* properties = mkcol.children[0].children[0].children # First determine the resource type rtype = None for property in properties: if isinstance(property, davxml.ResourceType): if rtype: error = "Multiple {DAV:}resourcetype properties in MKCOL request body: %s" % (mkcol,) log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error)) else: if property.childrenOfType(davxml.Collection): if property.childrenOfType(caldavxml.Calendar): rtype = "calendar" elif property.childrenOfType(carddavxml.AddressBook): rtype = "addressbook" if not rtype: error = "No {DAV:}resourcetype property in MKCOL request body: %s" % (mkcol,) log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error)) elif rtype not in ("calendar", "addressbook"): error = "{DAV:}resourcetype property in MKCOL request body not supported: %s" % (mkcol,) log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error)) # Make sure feature is enabled if ( rtype == "calendar" and not config.EnableCalDAV or rtype == "addressbook" and not config.EnableCardDAV ): error = "{DAV:}resourcetype property in MKCOL request body not supported: %s" % (mkcol,) log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error)) # Now create the special collection if rtype == "calendar": yield self.createCalendar(request) elif rtype == "addressbook": yield self.createAddressBook(request) # Now handle other properties for property in properties: try: if rtype == "calendar" and property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"): yield self.setSupportedComponentSet(property) set_supported_component_set = True elif not isinstance(property, davxml.ResourceType): yield self.writeProperty(property, request) except HTTPError: errors.add(Failure(), property) got_an_error = True else: errors.add(responsecode.OK, property) if got_an_error: # Clean up errors.error() raise HTTPError(Response( code=responsecode.FORBIDDEN, stream=mkcolxml.MakeCollectionResponse(errors.response()).toxml() )) # When calendar collections are single component only, default MKCALENDAR is VEVENT only if rtype == "calendar" and not set_supported_component_set and config.RestrictCalendarsToOneComponentType: yield self.setSupportedComponents(("VEVENT",)) yield returnValue(responsecode.CREATED) else: # No request body so it is a standard MKCOL result = yield super(CalDAVResource, self).http_MKCOL(request) returnValue(result)